OSDN Git Service

bd870a2ea938e5826930ba87c56a20869442c5e0
[qt-creator-jp/qt-creator-jp.git] / src / plugins / texteditor / snippets / snippetssettingspage.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "snippetssettingspage.h"
35 #include "snippeteditor.h"
36 #include "isnippetprovider.h"
37 #include "snippet.h"
38 #include "snippetscollection.h"
39 #include "snippetssettings.h"
40 #include "reuse.h"
41 #include "ui_snippetssettingspage.h"
42
43 #include <coreplugin/icore.h>
44 #include <extensionsystem/pluginmanager.h>
45
46 #include <QtCore/QModelIndex>
47 #include <QtCore/QAbstractTableModel>
48 #include <QtCore/QList>
49 #include <QtCore/QSettings>
50 #include <QtCore/QTextStream>
51 #include <QtCore/QHash>
52 #include <QtGui/QMessageBox>
53
54 namespace TextEditor {
55 namespace Internal {
56
57 // SnippetsTableModel
58 class SnippetsTableModel : public QAbstractTableModel
59 {
60     Q_OBJECT
61 public:
62     SnippetsTableModel(QObject *parent);
63     virtual ~SnippetsTableModel() {}
64
65     virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
66     virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
67     virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
68     virtual QVariant data(const QModelIndex &modelIndex, int role = Qt::DisplayRole) const;
69     virtual bool setData(const QModelIndex &modelIndex, const QVariant &value,
70                          int role = Qt::EditRole);
71     virtual QVariant headerData(int section, Qt::Orientation orientation,
72                                 int role = Qt::DisplayRole) const;
73
74     QList<QString> groupIds() const;
75     void load(const QString &groupId);
76
77     QModelIndex createSnippet();
78     QModelIndex insertSnippet(const Snippet &snippet);
79     void removeSnippet(const QModelIndex &modelIndex);
80     const Snippet &snippetAt(const QModelIndex &modelIndex) const;
81     void setSnippetContent(const QModelIndex &modelIndex, const QString &content);
82     void revertBuitInSnippet(const QModelIndex &modelIndex);
83     void restoreRemovedBuiltInSnippets();
84     void resetSnippets();
85
86 private:
87     void replaceSnippet(const Snippet &snippet, const QModelIndex &modelIndex);
88     static bool isValidTrigger(const QString &s);
89
90     SnippetsCollection* m_collection;
91     QString m_activeGroupId;
92 };
93
94 SnippetsTableModel::SnippetsTableModel(QObject *parent) :
95     QAbstractTableModel(parent),
96     m_collection(SnippetsCollection::instance())
97 {}
98
99 int SnippetsTableModel::rowCount(const QModelIndex &) const
100 {
101     return m_collection->totalActiveSnippets(m_activeGroupId);
102 }
103
104 int SnippetsTableModel::columnCount(const QModelIndex &) const
105 {
106     return 2;
107 }
108
109 Qt::ItemFlags SnippetsTableModel::flags(const QModelIndex &index) const
110 {
111     Qt::ItemFlags itemFlags = QAbstractTableModel::flags(index);
112     if (index.isValid())
113         itemFlags |= Qt::ItemIsEditable;
114     return itemFlags;
115 }
116
117 QVariant SnippetsTableModel::data(const QModelIndex &modelIndex, int role) const
118 {
119     if (!modelIndex.isValid())
120         return QVariant();
121
122     if (role == Qt::DisplayRole || role == Qt::EditRole) {
123         const Snippet &snippet = m_collection->snippet(modelIndex.row(), m_activeGroupId);
124         if (modelIndex.column() == 0)
125             return snippet.trigger();
126         else
127             return snippet.complement();
128     } else {
129         return QVariant();
130     }
131 }
132
133 bool SnippetsTableModel::setData(const QModelIndex &modelIndex, const QVariant &value, int role)
134 {
135     if (modelIndex.isValid() && role == Qt::EditRole) {
136         Snippet snippet(m_collection->snippet(modelIndex.row(), m_activeGroupId));
137         if (modelIndex.column() == 0) {
138             const QString &s = value.toString();
139             if (!isValidTrigger(s)) {
140                 QMessageBox::critical(0, tr("Error"), tr("Not a valid trigger."));
141                 if (snippet.trigger().isEmpty())
142                     removeSnippet(modelIndex);
143                 return false;
144             }
145             snippet.setTrigger(s);
146         } else {
147             snippet.setComplement(value.toString());
148         }
149
150         replaceSnippet(snippet, modelIndex);
151         return true;
152     }
153     return false;
154 }
155
156 QVariant SnippetsTableModel::headerData(int section, Qt::Orientation orientation, int role) const
157 {
158     if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
159         return QVariant();
160
161     if (section == 0)
162         return tr("Trigger");
163     else
164         return tr("Trigger Variant");
165 }
166
167 void SnippetsTableModel::load(const QString &groupId)
168 {
169     m_activeGroupId = groupId;
170     reset();
171 }
172
173 QList<QString> SnippetsTableModel::groupIds() const
174 {
175     return m_collection->groupIds();
176 }
177
178 QModelIndex SnippetsTableModel::createSnippet()
179 {
180     Snippet snippet(m_activeGroupId);
181     return insertSnippet(snippet);
182 }
183
184 QModelIndex SnippetsTableModel::insertSnippet(const Snippet &snippet)
185 {
186     const SnippetsCollection::Hint &hint = m_collection->computeInsertionHint(snippet);
187     beginInsertRows(QModelIndex(), hint.index(), hint.index());
188     m_collection->insertSnippet(snippet, hint);
189     endInsertRows();
190
191     return index(hint.index(), 0);
192 }
193
194 void SnippetsTableModel::removeSnippet(const QModelIndex &modelIndex)
195 {
196     beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
197     m_collection->removeSnippet(modelIndex.row(), m_activeGroupId);
198     endRemoveRows();
199 }
200
201 const Snippet &SnippetsTableModel::snippetAt(const QModelIndex &modelIndex) const
202 {
203     return m_collection->snippet(modelIndex.row(), m_activeGroupId);
204 }
205
206 void SnippetsTableModel::setSnippetContent(const QModelIndex &modelIndex, const QString &content)
207 {
208     m_collection->setSnippetContent(modelIndex.row(), m_activeGroupId, content);
209 }
210
211 void SnippetsTableModel::revertBuitInSnippet(const QModelIndex &modelIndex)
212 {
213     const Snippet &snippet = m_collection->revertedSnippet(modelIndex.row(), m_activeGroupId);
214     if (snippet.id().isEmpty()) {
215         QMessageBox::critical(0, tr("Error"), tr("Error reverting snippet."));
216         return;
217     }
218     replaceSnippet(snippet, modelIndex);
219 }
220
221 void SnippetsTableModel::restoreRemovedBuiltInSnippets()
222 {
223     m_collection->restoreRemovedSnippets(m_activeGroupId);
224     reset();
225 }
226
227 void SnippetsTableModel::resetSnippets()
228 {
229     m_collection->reset(m_activeGroupId);
230     reset();
231 }
232
233 void SnippetsTableModel::replaceSnippet(const Snippet &snippet, const QModelIndex &modelIndex)
234 {
235     const int row = modelIndex.row();
236     const SnippetsCollection::Hint &hint =
237         m_collection->computeReplacementHint(row, snippet);
238     if (modelIndex.row() == hint.index()) {
239         m_collection->replaceSnippet(row, snippet, hint);
240         if (modelIndex.column() == 0)
241             emit dataChanged(modelIndex, modelIndex.sibling(row, 1));
242         else
243             emit dataChanged(modelIndex.sibling(row, 0), modelIndex);
244     } else {
245         if (row < hint.index())
246             // Rows will be moved down.
247             beginMoveRows(QModelIndex(), row, row, QModelIndex(), hint.index() + 1);
248         else
249             beginMoveRows(QModelIndex(), row, row, QModelIndex(), hint.index());
250         m_collection->replaceSnippet(row, snippet, hint);
251         endMoveRows();
252     }
253 }
254
255 bool SnippetsTableModel::isValidTrigger(const QString &s)
256 {
257     if (s.isEmpty())
258         return false;
259     for (int i = 0; i < s.length(); ++i)
260         if (!s.at(i).isLetter())
261             return false;
262     return true;
263 }
264
265 // SnippetsSettingsPagePrivate
266 class SnippetsSettingsPagePrivate : public QObject
267 {
268     Q_OBJECT
269 public:
270     SnippetsSettingsPagePrivate(const QString &id);
271     ~SnippetsSettingsPagePrivate() { delete m_model; }
272
273     const QString &id() const { return m_id; }
274     const QString &displayName() const { return m_displayName; }
275     bool isKeyword(const QString &s) const { return m_keywords.contains(s, Qt::CaseInsensitive); }
276     void configureUi(QWidget *parent);
277
278     void apply();
279     void finish();
280
281 private slots:
282     void loadSnippetGroup(int index);
283     void markSnippetsCollection();
284     void addSnippet();
285     void removeSnippet();
286     void revertBuiltInSnippet();
287     void restoreRemovedBuiltInSnippets();
288     void resetAllSnippets();
289     void selectSnippet(const QModelIndex &parent, int row);
290     void selectMovedSnippet(const QModelIndex &, int, int, const QModelIndex &, int row);
291     void setSnippetContent();
292     void updateCurrentSnippetDependent(const QModelIndex &modelIndex = QModelIndex());
293
294 private:
295     SnippetEditorWidget *currentEditor() const;
296     SnippetEditorWidget *editorAt(int i) const;
297
298     void loadSettings();
299     bool settingsChanged() const;
300     void writeSettings();
301
302     const QString m_id;
303     const QString m_displayName;
304     const QString m_settingsPrefix;
305     SnippetsTableModel *m_model;
306     bool m_snippetsCollectionChanged;
307     QString m_keywords;
308     SnippetsSettings m_settings;
309     Ui::SnippetsSettingsPage m_ui;
310 };
311
312 SnippetsSettingsPagePrivate::SnippetsSettingsPagePrivate(const QString &id) :
313     m_id(id),
314     m_displayName(tr("Snippets")),
315     m_settingsPrefix(QLatin1String("Text")),
316     m_model(new SnippetsTableModel(0)),
317     m_snippetsCollectionChanged(false)
318 {}
319
320 SnippetEditorWidget *SnippetsSettingsPagePrivate::currentEditor() const
321 {
322     return editorAt(m_ui.snippetsEditorStack->currentIndex());
323 }
324
325 SnippetEditorWidget *SnippetsSettingsPagePrivate::editorAt(int i) const
326 {
327     return static_cast<SnippetEditorWidget *>(m_ui.snippetsEditorStack->widget(i));
328 }
329
330 void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
331 {
332     m_ui.setupUi(w);
333
334     const QList<ISnippetProvider *> &providers =
335         ExtensionSystem::PluginManager::instance()->getObjects<ISnippetProvider>();
336     foreach (ISnippetProvider *provider, providers) {
337         m_ui.groupCombo->addItem(provider->displayName(), provider->groupId());
338         SnippetEditorWidget *snippetEditor = new SnippetEditorWidget(w);
339         provider->decorateEditor(snippetEditor);
340         m_ui.snippetsEditorStack->insertWidget(m_ui.groupCombo->count() - 1, snippetEditor);
341         connect(snippetEditor, SIGNAL(snippetContentChanged()), this, SLOT(setSnippetContent()));
342     }
343
344     m_ui.snippetsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
345     m_ui.snippetsTable->setSelectionMode(QAbstractItemView::SingleSelection);
346     m_ui.snippetsTable->horizontalHeader()->setStretchLastSection(true);
347     m_ui.snippetsTable->horizontalHeader()->setHighlightSections(false);
348     m_ui.snippetsTable->verticalHeader()->setVisible(false);
349     m_ui.snippetsTable->verticalHeader()->setDefaultSectionSize(20);
350     m_ui.snippetsTable->setModel(m_model);
351
352     m_ui.revertButton->setEnabled(false);
353
354     QTextStream(&m_keywords) << m_displayName;
355
356     loadSettings();
357     loadSnippetGroup(m_ui.groupCombo->currentIndex());
358
359     connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
360             this, SLOT(selectSnippet(QModelIndex,int)));
361     connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
362             this, SLOT(markSnippetsCollection()));
363     connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
364             this, SLOT(markSnippetsCollection()));
365     connect(m_model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
366             this, SLOT(selectMovedSnippet(QModelIndex,int,int,QModelIndex,int)));
367     connect(m_model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
368             this, SLOT(markSnippetsCollection()));
369     connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
370             this, SLOT(markSnippetsCollection()));
371     connect(m_model, SIGNAL(modelReset()), this, SLOT(updateCurrentSnippetDependent()));
372     connect(m_model, SIGNAL(modelReset()), this, SLOT(markSnippetsCollection()));
373
374     connect(m_ui.groupCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(loadSnippetGroup(int)));
375     connect(m_ui.addButton, SIGNAL(clicked()), this, SLOT(addSnippet()));
376     connect(m_ui.removeButton, SIGNAL(clicked()), this, SLOT(removeSnippet()));
377     connect(m_ui.resetAllButton, SIGNAL(clicked()), this, SLOT(resetAllSnippets()));
378     connect(m_ui.restoreRemovedButton, SIGNAL(clicked()),
379             this, SLOT(restoreRemovedBuiltInSnippets()));
380     connect(m_ui.revertButton, SIGNAL(clicked()), this, SLOT(revertBuiltInSnippet()));
381     connect(m_ui.snippetsTable->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
382             this, SLOT(updateCurrentSnippetDependent(QModelIndex)));
383 }
384
385 void SnippetsSettingsPagePrivate::apply()
386 {
387     if (settingsChanged())
388         writeSettings();
389
390     if (currentEditor()->document()->isModified())
391         setSnippetContent();
392
393     if (m_snippetsCollectionChanged) {
394         SnippetsCollection::instance()->synchronize();
395         m_snippetsCollectionChanged = false;
396     }
397 }
398
399 void SnippetsSettingsPagePrivate::finish()
400 {
401     if (m_snippetsCollectionChanged) {
402         SnippetsCollection::instance()->reload();
403         m_snippetsCollectionChanged = false;
404     }
405 }
406
407 void SnippetsSettingsPagePrivate::loadSettings()
408 {
409     if (m_ui.groupCombo->count() == 0)
410         return;
411
412     if (QSettings *s = Core::ICore::instance()->settings()) {
413         m_settings.fromSettings(m_settingsPrefix, s);
414         const QString &lastGroupName = m_settings.lastUsedSnippetGroup();
415         const int index = m_ui.groupCombo->findText(lastGroupName);
416         if (index != -1)
417             m_ui.groupCombo->setCurrentIndex(index);
418         else
419             m_ui.groupCombo->setCurrentIndex(0);
420     }
421 }
422
423 void SnippetsSettingsPagePrivate::writeSettings()
424 {
425     if (m_ui.groupCombo->count() == 0)
426         return;
427
428     if (QSettings *s = Core::ICore::instance()->settings()) {
429         m_settings.setLastUsedSnippetGroup(m_ui.groupCombo->currentText());
430         m_settings.toSettings(m_settingsPrefix, s);
431     }
432 }
433
434 bool SnippetsSettingsPagePrivate::settingsChanged() const
435 {
436     if (m_settings.lastUsedSnippetGroup() != m_ui.groupCombo->currentText())
437         return true;
438     return false;
439 }
440
441 void SnippetsSettingsPagePrivate::loadSnippetGroup(int index)
442 {
443     if (index == -1)
444         return;
445
446     m_ui.snippetsEditorStack->setCurrentIndex(index);
447     currentEditor()->clear();
448     m_model->load(m_ui.groupCombo->itemData(index).toString());
449 }
450
451 void SnippetsSettingsPagePrivate::markSnippetsCollection()
452 {
453     if (!m_snippetsCollectionChanged)
454         m_snippetsCollectionChanged = true;
455 }
456
457 void SnippetsSettingsPagePrivate::addSnippet()
458 {
459     const QModelIndex &modelIndex = m_model->createSnippet();
460     selectSnippet(QModelIndex(), modelIndex.row());
461     m_ui.snippetsTable->edit(modelIndex);
462 }
463
464 void SnippetsSettingsPagePrivate::removeSnippet()
465 {
466     const QModelIndex &modelIndex = m_ui.snippetsTable->selectionModel()->currentIndex();
467     if (!modelIndex.isValid()) {
468         QMessageBox::critical(0, tr("Error"), tr("No snippet selected."));
469         return;
470     }
471     m_model->removeSnippet(modelIndex);
472 }
473
474 void SnippetsSettingsPagePrivate::restoreRemovedBuiltInSnippets()
475 {
476     m_model->restoreRemovedBuiltInSnippets();
477 }
478
479 void SnippetsSettingsPagePrivate::revertBuiltInSnippet()
480 {
481     m_model->revertBuitInSnippet(m_ui.snippetsTable->selectionModel()->currentIndex());
482 }
483
484 void SnippetsSettingsPagePrivate::resetAllSnippets()
485 {
486     m_model->resetSnippets();
487 }
488
489 void SnippetsSettingsPagePrivate::selectSnippet(const QModelIndex &parent, int row)
490 {
491     QModelIndex topLeft = m_model->index(row, 0, parent);
492     QModelIndex bottomRight = m_model->index(row, 1, parent);
493     QItemSelection selection(topLeft, bottomRight);
494     m_ui.snippetsTable->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
495     m_ui.snippetsTable->setCurrentIndex(topLeft);
496     m_ui.snippetsTable->scrollTo(topLeft);
497 }
498
499 void SnippetsSettingsPagePrivate::selectMovedSnippet(const QModelIndex &,
500                                                      int sourceRow,
501                                                      int,
502                                                      const QModelIndex &destinationParent,
503                                                      int destinationRow)
504 {
505     QModelIndex modelIndex;
506     if (sourceRow < destinationRow)
507         modelIndex = m_model->index(destinationRow - 1, 0, destinationParent);
508     else
509         modelIndex = m_model->index(destinationRow, 0, destinationParent);
510     m_ui.snippetsTable->scrollTo(modelIndex);
511     currentEditor()->setPlainText(m_model->snippetAt(modelIndex).content());
512 }
513
514 void SnippetsSettingsPagePrivate::updateCurrentSnippetDependent(const QModelIndex &modelIndex)
515 {
516     if (modelIndex.isValid()) {
517         const Snippet &snippet = m_model->snippetAt(modelIndex);
518         currentEditor()->setPlainText(snippet.content());
519         m_ui.revertButton->setEnabled(snippet.isBuiltIn());
520     } else {
521         currentEditor()->clear();
522         m_ui.revertButton->setEnabled(false);
523     }
524 }
525
526 void SnippetsSettingsPagePrivate::setSnippetContent()
527 {
528     const QModelIndex &modelIndex = m_ui.snippetsTable->selectionModel()->currentIndex();
529     if (modelIndex.isValid()) {
530         m_model->setSnippetContent(modelIndex, currentEditor()->toPlainText());
531         markSnippetsCollection();
532     }
533 }
534
535 // SnippetsSettingsPage
536 SnippetsSettingsPage::SnippetsSettingsPage(const QString &id, QObject *parent) :
537     TextEditorOptionsPage(parent),
538     m_d(new SnippetsSettingsPagePrivate(id))
539 {}
540
541 SnippetsSettingsPage::~SnippetsSettingsPage()
542 {
543     delete m_d;
544 }
545
546 QString SnippetsSettingsPage::id() const
547 {
548     return m_d->id();
549 }
550
551 QString SnippetsSettingsPage::displayName() const
552 {
553     return m_d->displayName();
554 }
555
556 bool SnippetsSettingsPage::matches(const QString &s) const
557 {
558     return m_d->isKeyword(s);
559 }
560
561 QWidget *SnippetsSettingsPage::createPage(QWidget *parent)
562 {
563     QWidget *w = new QWidget(parent);
564     m_d->configureUi(w);
565     return w;
566 }
567
568 void SnippetsSettingsPage::apply()
569 {
570     m_d->apply();
571 }
572
573 void SnippetsSettingsPage::finish()
574 {
575     m_d->finish();
576 }
577
578 } // Internal
579 } // TextEditor
580
581 #include "snippetssettingspage.moc"