snippetssettingspage.cpp 18.5 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
7
8
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
con's avatar
con committed
9
** No Commercial Usage
10
**
con's avatar
con committed
11
12
13
14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
15
16
17
18
19
20
21
22
23
24
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
con's avatar
con committed
25
26
27
28
29
30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
31
32
33
34
35
**
**************************************************************************/

#include "snippetssettingspage.h"
#include "snippeteditor.h"
36
#include "isnippetprovider.h"
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "snippet.h"
#include "snippetscollection.h"
#include "snippetssettings.h"
#include "reuse.h"
#include "ui_snippetssettingspage.h"

#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>

#include <QtCore/QModelIndex>
#include <QtCore/QAbstractTableModel>
#include <QtCore/QList>
#include <QtCore/QSettings>
#include <QtCore/QTextStream>
51
#include <QtCore/QHash>
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <QtGui/QMessageBox>

namespace TextEditor {
namespace Internal {

// SnippetsTableModel
class SnippetsTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    SnippetsTableModel(QObject *parent);
    virtual ~SnippetsTableModel() {}

    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
    virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
    virtual QVariant data(const QModelIndex &modelIndex, int role = Qt::DisplayRole) const;
    virtual bool setData(const QModelIndex &modelIndex, const QVariant &value,
                         int role = Qt::EditRole);
    virtual QVariant headerData(int section, Qt::Orientation orientation,
                                int role = Qt::DisplayRole) const;

74
75
    QList<QString> groupIds() const;
    void load(const QString &groupId);
76
77
78
79
80
81

    QModelIndex createSnippet();
    QModelIndex insertSnippet(const Snippet &snippet);
    void removeSnippet(const QModelIndex &modelIndex);
    const Snippet &snippetAt(const QModelIndex &modelIndex) const;
    void setSnippetContent(const QModelIndex &modelIndex, const QString &content);
Leandro Melo's avatar
Leandro Melo committed
82
83
84
    void revertBuitInSnippet(const QModelIndex &modelIndex);
    void restoreRemovedBuiltInSnippets();
    void resetSnippets();
85
86

private:
Leandro Melo's avatar
Leandro Melo committed
87
    void replaceSnippet(const Snippet &snippet, const QModelIndex &modelIndex);
88
89
    static bool isValidTrigger(const QString &s);

90
    SnippetsCollection* m_collection;
91
    QString m_activeGroupId;
92
93
94
95
};

SnippetsTableModel::SnippetsTableModel(QObject *parent) :
    QAbstractTableModel(parent),
96
    m_collection(SnippetsCollection::instance())
97
98
99
100
{}

int SnippetsTableModel::rowCount(const QModelIndex &) const
{
101
    return m_collection->totalActiveSnippets(m_activeGroupId);
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
}

int SnippetsTableModel::columnCount(const QModelIndex &) const
{
    return 2;
}

Qt::ItemFlags SnippetsTableModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags itemFlags = QAbstractTableModel::flags(index);
    if (index.isValid())
        itemFlags |= Qt::ItemIsEditable;
    return itemFlags;
}

QVariant SnippetsTableModel::data(const QModelIndex &modelIndex, int role) const
{
    if (!modelIndex.isValid())
        return QVariant();

    if (role == Qt::DisplayRole || role == Qt::EditRole) {
123
        const Snippet &snippet = m_collection->snippet(modelIndex.row(), m_activeGroupId);
124
125
126
127
128
129
130
131
132
133
134
135
        if (modelIndex.column() == 0)
            return snippet.trigger();
        else
            return snippet.complement();
    } else {
        return QVariant();
    }
}

bool SnippetsTableModel::setData(const QModelIndex &modelIndex, const QVariant &value, int role)
{
    if (modelIndex.isValid() && role == Qt::EditRole) {
136
        Snippet snippet(m_collection->snippet(modelIndex.row(), m_activeGroupId));
137
138
139
140
141
142
143
144
145
146
147
148
149
        if (modelIndex.column() == 0) {
            const QString &s = value.toString();
            if (!isValidTrigger(s)) {
                QMessageBox::critical(0, tr("Error"), tr("Not a valid trigger."));
                if (snippet.trigger().isEmpty())
                    removeSnippet(modelIndex);
                return false;
            }
            snippet.setTrigger(s);
        } else {
            snippet.setComplement(value.toString());
        }

Leandro Melo's avatar
Leandro Melo committed
150
        replaceSnippet(snippet, modelIndex);
151
152
153
154
155
156
157
158
159
160
161
162
163
        return true;
    }
    return false;
}

QVariant SnippetsTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
        return QVariant();

    if (section == 0)
        return tr("Trigger");
    else
164
        return tr("Trigger Variant");
165
166
}

167
void SnippetsTableModel::load(const QString &groupId)
168
{
169
    m_activeGroupId = groupId;
170
171
172
    reset();
}

173
174
175
176
177
QList<QString> SnippetsTableModel::groupIds() const
{
    return m_collection->groupIds();
}

178
179
QModelIndex SnippetsTableModel::createSnippet()
{
180
    Snippet snippet(m_activeGroupId);
181
182
183
184
185
    return insertSnippet(snippet);
}

QModelIndex SnippetsTableModel::insertSnippet(const Snippet &snippet)
{
186
    const SnippetsCollection::Hint &hint = m_collection->computeInsertionHint(snippet);
187
    beginInsertRows(QModelIndex(), hint.index(), hint.index());
188
    m_collection->insertSnippet(snippet, hint);
189
190
191
192
193
194
195
196
    endInsertRows();

    return index(hint.index(), 0);
}

void SnippetsTableModel::removeSnippet(const QModelIndex &modelIndex)
{
    beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
197
    m_collection->removeSnippet(modelIndex.row(), m_activeGroupId);
198
199
200
201
202
    endRemoveRows();
}

const Snippet &SnippetsTableModel::snippetAt(const QModelIndex &modelIndex) const
{
203
    return m_collection->snippet(modelIndex.row(), m_activeGroupId);
204
205
206
207
}

void SnippetsTableModel::setSnippetContent(const QModelIndex &modelIndex, const QString &content)
{
208
    m_collection->setSnippetContent(modelIndex.row(), m_activeGroupId, content);
209
210
}

Leandro Melo's avatar
Leandro Melo committed
211
212
void SnippetsTableModel::revertBuitInSnippet(const QModelIndex &modelIndex)
{
213
    const Snippet &snippet = m_collection->revertedSnippet(modelIndex.row(), m_activeGroupId);
Leandro Melo's avatar
Leandro Melo committed
214
215
216
217
218
219
220
221
222
    if (snippet.id().isEmpty()) {
        QMessageBox::critical(0, tr("Error"), tr("Error reverting snippet."));
        return;
    }
    replaceSnippet(snippet, modelIndex);
}

void SnippetsTableModel::restoreRemovedBuiltInSnippets()
{
223
    m_collection->restoreRemovedSnippets(m_activeGroupId);
Leandro Melo's avatar
Leandro Melo committed
224
225
226
227
228
    reset();
}

void SnippetsTableModel::resetSnippets()
{
229
    m_collection->reset(m_activeGroupId);
Leandro Melo's avatar
Leandro Melo committed
230
231
232
233
234
235
236
    reset();
}

void SnippetsTableModel::replaceSnippet(const Snippet &snippet, const QModelIndex &modelIndex)
{
    const int row = modelIndex.row();
    const SnippetsCollection::Hint &hint =
237
        m_collection->computeReplacementHint(row, snippet);
Leandro Melo's avatar
Leandro Melo committed
238
    if (modelIndex.row() == hint.index()) {
239
        m_collection->replaceSnippet(row, snippet, hint);
240
241
242
243
        if (modelIndex.column() == 0)
            emit dataChanged(modelIndex, modelIndex.sibling(row, 1));
        else
            emit dataChanged(modelIndex.sibling(row, 0), modelIndex);
Leandro Melo's avatar
Leandro Melo committed
244
245
246
247
248
249
    } else {
        if (row < hint.index())
            // Rows will be moved down.
            beginMoveRows(QModelIndex(), row, row, QModelIndex(), hint.index() + 1);
        else
            beginMoveRows(QModelIndex(), row, row, QModelIndex(), hint.index());
250
        m_collection->replaceSnippet(row, snippet, hint);
Leandro Melo's avatar
Leandro Melo committed
251
252
253
254
        endMoveRows();
    }
}

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
bool SnippetsTableModel::isValidTrigger(const QString &s)
{
    if (s.isEmpty())
        return false;
    for (int i = 0; i < s.length(); ++i)
        if (!s.at(i).isLetter())
            return false;
    return true;
}

// SnippetsSettingsPagePrivate
class SnippetsSettingsPagePrivate : public QObject
{
    Q_OBJECT
public:
    SnippetsSettingsPagePrivate(const QString &id);
    ~SnippetsSettingsPagePrivate() { delete m_model; }

    const QString &id() const { return m_id; }
    const QString &displayName() const { return m_displayName; }
    bool isKeyword(const QString &s) const { return m_keywords.contains(s, Qt::CaseInsensitive); }
    void configureUi(QWidget *parent);

    void apply();
    void finish();

private slots:
    void loadSnippetGroup(int index);
    void markSnippetsCollection();
    void addSnippet();
    void removeSnippet();
Leandro Melo's avatar
Leandro Melo committed
286
287
288
    void revertBuiltInSnippet();
    void restoreRemovedBuiltInSnippets();
    void resetAllSnippets();
289
290
    void selectSnippet(const QModelIndex &parent, int row);
    void selectMovedSnippet(const QModelIndex &, int, int, const QModelIndex &, int row);
Leandro Melo's avatar
Leandro Melo committed
291
292
    void setSnippetContent();
    void updateCurrentSnippetDependent(const QModelIndex &modelIndex = QModelIndex());
293
294

private:
295
296
    SnippetEditorWidget *currentEditor() const;
    SnippetEditorWidget *editorAt(int i) const;
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

    void loadSettings();
    bool settingsChanged() const;
    void writeSettings();

    const QString m_id;
    const QString m_displayName;
    const QString m_settingsPrefix;
    SnippetsTableModel *m_model;
    bool m_snippetsCollectionChanged;
    QString m_keywords;
    SnippetsSettings m_settings;
    Ui::SnippetsSettingsPage m_ui;
};

SnippetsSettingsPagePrivate::SnippetsSettingsPagePrivate(const QString &id) :
    m_id(id),
    m_displayName(tr("Snippets")),
    m_settingsPrefix(QLatin1String("Text")),
    m_model(new SnippetsTableModel(0)),
    m_snippetsCollectionChanged(false)
{}

320
SnippetEditorWidget *SnippetsSettingsPagePrivate::currentEditor() const
321
322
323
324
{
    return editorAt(m_ui.snippetsEditorStack->currentIndex());
}

325
SnippetEditorWidget *SnippetsSettingsPagePrivate::editorAt(int i) const
326
{
327
    return static_cast<SnippetEditorWidget *>(m_ui.snippetsEditorStack->widget(i));
328
329
330
331
332
333
}

void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
{
    m_ui.setupUi(w);

334
335
336
337
    const QList<ISnippetProvider *> &providers =
        ExtensionSystem::PluginManager::instance()->getObjects<ISnippetProvider>();
    foreach (ISnippetProvider *provider, providers) {
        m_ui.groupCombo->addItem(provider->displayName(), provider->groupId());
338
        SnippetEditorWidget *snippetEditor = new SnippetEditorWidget(w);
339
340
341
342
        provider->decorateEditor(snippetEditor);
        m_ui.snippetsEditorStack->insertWidget(m_ui.groupCombo->count() - 1, snippetEditor);
        connect(snippetEditor, SIGNAL(snippetContentChanged()), this, SLOT(setSnippetContent()));
    }
343
344
345
346
347
348
349
350
351

    m_ui.snippetsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    m_ui.snippetsTable->setSelectionMode(QAbstractItemView::SingleSelection);
    m_ui.snippetsTable->horizontalHeader()->setStretchLastSection(true);
    m_ui.snippetsTable->horizontalHeader()->setHighlightSections(false);
    m_ui.snippetsTable->verticalHeader()->setVisible(false);
    m_ui.snippetsTable->verticalHeader()->setDefaultSectionSize(20);
    m_ui.snippetsTable->setModel(m_model);

Leandro Melo's avatar
Leandro Melo committed
352
353
    m_ui.revertButton->setEnabled(false);

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    QTextStream(&m_keywords) << m_displayName;

    loadSettings();
    loadSnippetGroup(m_ui.groupCombo->currentIndex());

    connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
            this, SLOT(selectSnippet(QModelIndex,int)));
    connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
            this, SLOT(markSnippetsCollection()));
    connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
            this, SLOT(markSnippetsCollection()));
    connect(m_model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
            this, SLOT(selectMovedSnippet(QModelIndex,int,int,QModelIndex,int)));
    connect(m_model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
            this, SLOT(markSnippetsCollection()));
    connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
            this, SLOT(markSnippetsCollection()));
Leandro Melo's avatar
Leandro Melo committed
371
372
    connect(m_model, SIGNAL(modelReset()), this, SLOT(updateCurrentSnippetDependent()));
    connect(m_model, SIGNAL(modelReset()), this, SLOT(markSnippetsCollection()));
373
374
375
376

    connect(m_ui.groupCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(loadSnippetGroup(int)));
    connect(m_ui.addButton, SIGNAL(clicked()), this, SLOT(addSnippet()));
    connect(m_ui.removeButton, SIGNAL(clicked()), this, SLOT(removeSnippet()));
Leandro Melo's avatar
Leandro Melo committed
377
378
379
380
    connect(m_ui.resetAllButton, SIGNAL(clicked()), this, SLOT(resetAllSnippets()));
    connect(m_ui.restoreRemovedButton, SIGNAL(clicked()),
            this, SLOT(restoreRemovedBuiltInSnippets()));
    connect(m_ui.revertButton, SIGNAL(clicked()), this, SLOT(revertBuiltInSnippet()));
381
    connect(m_ui.snippetsTable->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
Leandro Melo's avatar
Leandro Melo committed
382
            this, SLOT(updateCurrentSnippetDependent(QModelIndex)));
383
384
385
386
387
388
389
}

void SnippetsSettingsPagePrivate::apply()
{
    if (settingsChanged())
        writeSettings();

390
391
392
    if (currentEditor()->document()->isModified())
        setSnippetContent();

393
    if (m_snippetsCollectionChanged) {
394
        SnippetsCollection::instance()->synchronize();
395
396
        m_snippetsCollectionChanged = false;
    }
397
398
399
400
401
}

void SnippetsSettingsPagePrivate::finish()
{
    if (m_snippetsCollectionChanged) {
402
        SnippetsCollection::instance()->reload();
403
404
405
406
407
408
        m_snippetsCollectionChanged = false;
    }
}

void SnippetsSettingsPagePrivate::loadSettings()
{
409
410
411
    if (m_ui.groupCombo->count() == 0)
        return;

412
413
    if (QSettings *s = Core::ICore::instance()->settings()) {
        m_settings.fromSettings(m_settingsPrefix, s);
414
415
416
417
418
419
        const QString &lastGroupName = m_settings.lastUsedSnippetGroup();
        const int index = m_ui.groupCombo->findText(lastGroupName);
        if (index != -1)
            m_ui.groupCombo->setCurrentIndex(index);
        else
            m_ui.groupCombo->setCurrentIndex(0);
420
421
422
423
424
    }
}

void SnippetsSettingsPagePrivate::writeSettings()
{
425
426
427
    if (m_ui.groupCombo->count() == 0)
        return;

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
    if (QSettings *s = Core::ICore::instance()->settings()) {
        m_settings.setLastUsedSnippetGroup(m_ui.groupCombo->currentText());
        m_settings.toSettings(m_settingsPrefix, s);
    }
}

bool SnippetsSettingsPagePrivate::settingsChanged() const
{
    if (m_settings.lastUsedSnippetGroup() != m_ui.groupCombo->currentText())
        return true;
    return false;
}

void SnippetsSettingsPagePrivate::loadSnippetGroup(int index)
{
443
444
445
    if (index == -1)
        return;

446
447
    m_ui.snippetsEditorStack->setCurrentIndex(index);
    currentEditor()->clear();
448
    m_model->load(m_ui.groupCombo->itemData(index).toString());
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
}

void SnippetsSettingsPagePrivate::markSnippetsCollection()
{
    if (!m_snippetsCollectionChanged)
        m_snippetsCollectionChanged = true;
}

void SnippetsSettingsPagePrivate::addSnippet()
{
    const QModelIndex &modelIndex = m_model->createSnippet();
    selectSnippet(QModelIndex(), modelIndex.row());
    m_ui.snippetsTable->edit(modelIndex);
}

void SnippetsSettingsPagePrivate::removeSnippet()
{
    const QModelIndex &modelIndex = m_ui.snippetsTable->selectionModel()->currentIndex();
    if (!modelIndex.isValid()) {
        QMessageBox::critical(0, tr("Error"), tr("No snippet selected."));
        return;
    }
    m_model->removeSnippet(modelIndex);
}

Leandro Melo's avatar
Leandro Melo committed
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
void SnippetsSettingsPagePrivate::restoreRemovedBuiltInSnippets()
{
    m_model->restoreRemovedBuiltInSnippets();
}

void SnippetsSettingsPagePrivate::revertBuiltInSnippet()
{
    m_model->revertBuitInSnippet(m_ui.snippetsTable->selectionModel()->currentIndex());
}

void SnippetsSettingsPagePrivate::resetAllSnippets()
{
    m_model->resetSnippets();
}

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
void SnippetsSettingsPagePrivate::selectSnippet(const QModelIndex &parent, int row)
{
    QModelIndex topLeft = m_model->index(row, 0, parent);
    QModelIndex bottomRight = m_model->index(row, 1, parent);
    QItemSelection selection(topLeft, bottomRight);
    m_ui.snippetsTable->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
    m_ui.snippetsTable->setCurrentIndex(topLeft);
    m_ui.snippetsTable->scrollTo(topLeft);
}

void SnippetsSettingsPagePrivate::selectMovedSnippet(const QModelIndex &,
                                                     int sourceRow,
                                                     int,
                                                     const QModelIndex &destinationParent,
                                                     int destinationRow)
{
    QModelIndex modelIndex;
    if (sourceRow < destinationRow)
        modelIndex = m_model->index(destinationRow - 1, 0, destinationParent);
    else
        modelIndex = m_model->index(destinationRow, 0, destinationParent);
    m_ui.snippetsTable->scrollTo(modelIndex);
Leandro Melo's avatar
Leandro Melo committed
511
    currentEditor()->setPlainText(m_model->snippetAt(modelIndex).content());
512
513
}

Leandro Melo's avatar
Leandro Melo committed
514
void SnippetsSettingsPagePrivate::updateCurrentSnippetDependent(const QModelIndex &modelIndex)
515
{
Leandro Melo's avatar
Leandro Melo committed
516
517
518
519
520
521
522
523
    if (modelIndex.isValid()) {
        const Snippet &snippet = m_model->snippetAt(modelIndex);
        currentEditor()->setPlainText(snippet.content());
        m_ui.revertButton->setEnabled(snippet.isBuiltIn());
    } else {
        currentEditor()->clear();
        m_ui.revertButton->setEnabled(false);
    }
524
525
}

Leandro Melo's avatar
Leandro Melo committed
526
void SnippetsSettingsPagePrivate::setSnippetContent()
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
{
    const QModelIndex &modelIndex = m_ui.snippetsTable->selectionModel()->currentIndex();
    if (modelIndex.isValid()) {
        m_model->setSnippetContent(modelIndex, currentEditor()->toPlainText());
        markSnippetsCollection();
    }
}

// SnippetsSettingsPage
SnippetsSettingsPage::SnippetsSettingsPage(const QString &id, QObject *parent) :
    TextEditorOptionsPage(parent),
    m_d(new SnippetsSettingsPagePrivate(id))
{}

SnippetsSettingsPage::~SnippetsSettingsPage()
{
    delete m_d;
}

QString SnippetsSettingsPage::id() const
{
    return m_d->id();
}

QString SnippetsSettingsPage::displayName() const
{
    return m_d->displayName();
}

bool SnippetsSettingsPage::matches(const QString &s) const
{
    return m_d->isKeyword(s);
}

QWidget *SnippetsSettingsPage::createPage(QWidget *parent)
{
    QWidget *w = new QWidget(parent);
    m_d->configureUi(w);
    return w;
}

void SnippetsSettingsPage::apply()
{
    m_d->apply();
}

void SnippetsSettingsPage::finish()
{
    m_d->finish();
}

} // Internal
} // TextEditor

#include "snippetssettingspage.moc"