diff --git a/src/snippets/snippetsmanager.cpp b/src/snippets/snippetsmanager.cpp index 4a410d4..73eddd3 100644 --- a/src/snippets/snippetsmanager.cpp +++ b/src/snippets/snippetsmanager.cpp @@ -1,738 +1,605 @@ /* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "snippetsmanager.h" #include "mailcommon_debug.h" #include "snippetdialog.h" #include "snippetsmodel.h" #include "snippetvariabledialog.h" #include #include #include #include #include #include #include #include #include using namespace MailCommon; class Q_DECL_HIDDEN SnippetsManager::Private { public: Private(SnippetsManager *qq, QWidget *parent) : q(qq) , mParent(parent) { } QModelIndex currentGroupIndex() const; void selectionChanged(); void dndDone(); void addSnippet(); void editSnippet(); void deleteSnippet(); void addSnippetGroup(); void editSnippetGroup(); void deleteSnippetGroup(); void insertSelectedSnippet(); void insertActionSnippet(); void createSnippet(const QString &text = QString()); void slotAddNewDndSnippset(const QString &); void updateActionCollection(const QString &oldName, const QString &newName, const QKeySequence &keySequence, const QString &text); QString replaceVariables(const QString &text); - QModelIndex createGroup(const QString &groupName); - void createSnippet(const QModelIndex &groupIndex, const QString &snippetName, const QString &snippetText, const QString &snippetKeySequence); - void load(); void save(); SnippetsManager *q = nullptr; SnippetsModel *mModel = nullptr; QItemSelectionModel *mSelectionModel = nullptr; KActionCollection *mActionCollection = nullptr; QObject *mEditor = nullptr; QByteArray mEditorInsertMethod; - QMap mSavedVariables; QAction *mAddSnippetAction = nullptr; QAction *mEditSnippetAction = nullptr; QAction *mDeleteSnippetAction = nullptr; QAction *mAddSnippetGroupAction = nullptr; QAction *mEditSnippetGroupAction = nullptr; QAction *mDeleteSnippetGroupAction = nullptr; QAction *mInsertSnippetAction = nullptr; QWidget *mParent = nullptr; bool mDirty = false; }; QModelIndex SnippetsManager::Private::currentGroupIndex() const { if (mSelectionModel->selectedIndexes().isEmpty()) { return QModelIndex(); } const QModelIndex index = mSelectionModel->selectedIndexes().first(); if (index.data(SnippetsModel::IsGroupRole).toBool()) { return index; } else { return mModel->parent(index); } } void SnippetsManager::Private::selectionChanged() { const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty(); if (itemSelected) { const QModelIndex index = mSelectionModel->selectedIndexes().first(); const bool isGroup = index.data(SnippetsModel::IsGroupRole).toBool(); if (isGroup) { mEditSnippetAction->setEnabled(false); mDeleteSnippetAction->setEnabled(false); mEditSnippetGroupAction->setEnabled(true); mDeleteSnippetGroupAction->setEnabled(true); mInsertSnippetAction->setEnabled(false); } else { mEditSnippetAction->setEnabled(true); mDeleteSnippetAction->setEnabled(true); mEditSnippetGroupAction->setEnabled(false); mDeleteSnippetGroupAction->setEnabled(false); mInsertSnippetAction->setEnabled(true); } } else { mEditSnippetAction->setEnabled(false); mDeleteSnippetAction->setEnabled(false); mEditSnippetGroupAction->setEnabled(false); mDeleteSnippetGroupAction->setEnabled(false); mInsertSnippetAction->setEnabled(false); } } void SnippetsManager::Private::addSnippet() { createSnippet(); } void SnippetsManager::Private::createSnippet(const QString &text) { const bool noGroupAvailable = (mModel->rowCount() == 0); if (noGroupAvailable) { // create a 'General' snippet group if (!mModel->insertRow(mModel->rowCount(), QModelIndex())) { return; } const QModelIndex groupIndex = mModel->index(mModel->rowCount() - 1, 0, QModelIndex()); mModel->setData(groupIndex, i18n("General"), SnippetsModel::NameRole); mSelectionModel->select(groupIndex, QItemSelectionModel::ClearAndSelect); } QPointer dlg = new SnippetDialog(mActionCollection, false, mParent); dlg->setWindowTitle(i18nc("@title:window", "Add Snippet")); dlg->setGroupModel(mModel); dlg->setGroupIndex(currentGroupIndex()); dlg->setText(text); if (dlg->exec()) { const QModelIndex groupIndex = dlg->groupIndex(); if (!mModel->insertRow(mModel->rowCount(groupIndex), groupIndex)) { delete dlg; return; } const QModelIndex index = mModel->index(mModel->rowCount(groupIndex) - 1, 0, groupIndex); mModel->setData(index, dlg->name(), SnippetsModel::NameRole); mModel->setData(index, dlg->text(), SnippetsModel::TextRole); mModel->setData(index, dlg->keySequence().toString(), SnippetsModel::KeySequenceRole); updateActionCollection(QString(), dlg->name(), dlg->keySequence(), dlg->text()); mDirty = true; save(); } delete dlg; } void SnippetsManager::Private::slotAddNewDndSnippset(const QString &text) { createSnippet(text); } void SnippetsManager::Private::dndDone() { mDirty = true; } void SnippetsManager::Private::editSnippet() { QModelIndex index = mSelectionModel->selectedIndexes().first(); if (!index.isValid() || index.data(SnippetsModel::IsGroupRole).toBool()) { return; } const QModelIndex oldGroupIndex = currentGroupIndex(); const QString oldSnippetName = index.data(SnippetsModel::NameRole).toString(); QPointer dlg = new SnippetDialog(mActionCollection, false, mParent); dlg->setWindowTitle(i18nc("@title:window", "Edit Snippet")); dlg->setGroupModel(mModel); dlg->setGroupIndex(oldGroupIndex); dlg->setName(oldSnippetName); dlg->setText(index.data(SnippetsModel::TextRole).toString()); dlg->setKeySequence( QKeySequence::fromString( index.data(SnippetsModel::KeySequenceRole).toString())); if (dlg->exec()) { const QModelIndex newGroupIndex = dlg->groupIndex(); if (oldGroupIndex != newGroupIndex) { mModel->removeRow(index.row(), oldGroupIndex); mModel->insertRow(mModel->rowCount(newGroupIndex), newGroupIndex); index = mModel->index(mModel->rowCount(newGroupIndex) - 1, 0, newGroupIndex); } mModel->setData(index, dlg->name(), SnippetsModel::NameRole); mModel->setData(index, dlg->text(), SnippetsModel::TextRole); mModel->setData(index, dlg->keySequence().toString(), SnippetsModel::KeySequenceRole); updateActionCollection(oldSnippetName, dlg->name(), dlg->keySequence(), dlg->text()); mDirty = true; save(); } delete dlg; } void SnippetsManager::Private::deleteSnippet() { const QModelIndex index = mSelectionModel->selectedIndexes().first(); const QString snippetName = index.data(SnippetsModel::NameRole).toString(); if (KMessageBox::warningContinueCancel( nullptr, xi18nc("@info", "Do you really want to remove snippet \"%1\"?" "There is no way to undo the removal.", snippetName), QString(), KStandardGuiItem::remove()) == KMessageBox::Cancel) { return; } mModel->removeRow(index.row(), currentGroupIndex()); updateActionCollection(snippetName, QString(), QKeySequence(), QString()); mDirty = true; save(); } void SnippetsManager::Private::addSnippetGroup() { QPointer dlg = new SnippetDialog(mActionCollection, true, mParent); dlg->setWindowTitle(i18nc("@title:window", "Add Group")); if (dlg->exec()) { if (!mModel->insertRow(mModel->rowCount(), QModelIndex())) { qCDebug(MAILCOMMON_LOG) << "unable to insert row"; delete dlg; return; } const QModelIndex groupIndex = mModel->index(mModel->rowCount() - 1, 0, QModelIndex()); mModel->setData(groupIndex, dlg->name(), SnippetsModel::NameRole); mDirty = true; save(); } delete dlg; } void SnippetsManager::Private::editSnippetGroup() { const QModelIndex groupIndex = currentGroupIndex(); if (!groupIndex.isValid() || !groupIndex.data(SnippetsModel::IsGroupRole).toBool()) { return; } QPointer dlg = new SnippetDialog(mActionCollection, true, mParent); dlg->setWindowTitle(i18nc("@title:window", "Edit Group")); const QString oldGroupName = groupIndex.data(SnippetsModel::NameRole).toString(); dlg->setName(oldGroupName); if (dlg->exec()) { if (oldGroupName == dlg->name()) { delete dlg; return; } mModel->setData(groupIndex, dlg->name(), SnippetsModel::NameRole); mDirty = true; save(); } delete dlg; } void SnippetsManager::Private::deleteSnippetGroup() { const QModelIndex groupIndex = currentGroupIndex(); if (!groupIndex.isValid()) { return; } const QString groupName = groupIndex.data(SnippetsModel::NameRole).toString(); if (mModel->rowCount(groupIndex) > 0) { if (KMessageBox::warningContinueCancel( nullptr, xi18nc("@info", "Do you really want to remove group \"%1\" along with all its snippets?" "There is no way to undo the removal.", groupName), QString(), KStandardGuiItem::remove()) == KMessageBox::Cancel) { return; } } else { if (KMessageBox::warningContinueCancel( nullptr, i18nc("@info", "Do you really want to remove group \"%1\"?", groupName), QString(), KStandardGuiItem::remove()) == KMessageBox::Cancel) { return; } } mModel->removeRow(groupIndex.row(), QModelIndex()); mDirty = true; save(); } void SnippetsManager::Private::insertSelectedSnippet() { if (!mEditor) { return; } if (!mSelectionModel->hasSelection()) { return; } const QModelIndex index = mSelectionModel->selectedIndexes().first(); if (index.data(SnippetsModel::IsGroupRole).toBool()) { return; } const QString text = replaceVariables(index.data(SnippetsModel::TextRole).toString()); QMetaObject::invokeMethod(mEditor, mEditorInsertMethod.constData(), Qt::DirectConnection, Q_ARG(QString, text)); } void SnippetsManager::Private::insertActionSnippet() { if (!mEditor) { return; } QAction *action = qobject_cast(q->sender()); if (!action) { return; } const QString text = replaceVariables(action->property("snippetText").toString()); QMetaObject::invokeMethod(mEditor, mEditorInsertMethod.constData(), Qt::DirectConnection, Q_ARG(QString, text)); } void SnippetsManager::Private::updateActionCollection(const QString &oldName, const QString &newName, const QKeySequence &keySequence, const QString &text) { // remove previous action in case that the name changed if (!oldName.isEmpty()) { const QString actionName = i18nc("@action", "Snippet %1", oldName); const QString normalizedName = QString(actionName).replace(QLatin1Char(' '), QLatin1Char('_')); QAction *action = mActionCollection->action(normalizedName); if (action) { mActionCollection->removeAction(action); } } if (!newName.isEmpty()) { const QString actionName = i18nc("@action", "Snippet %1", newName); const QString normalizedName = QString(actionName).replace(QLatin1Char(' '), QLatin1Char('_')); QAction *action = mActionCollection->addAction(normalizedName, q); connect(action, &QAction::triggered, q, [this]() { insertActionSnippet(); }); action->setProperty("snippetText", text); action->setText(actionName); mActionCollection->setDefaultShortcut(action, keySequence); } } QString SnippetsManager::Private::replaceVariables(const QString &text) { QString result = text; QString variableName; QString variableValue; - QMap localVariables(mSavedVariables); + QMap localVariables(SnippetsModel::instance()->savedVariables()); int iFound = -1; int iEnd = -1; - + QMap tempLocalVariables(localVariables); do { //find the next variable by this QRegExp iFound = text.indexOf(QRegExp(QLatin1String("\\$[A-Za-z-_0-9\\s]*\\$")), iEnd + 1); if (iFound >= 0) { iEnd = text.indexOf(QLatin1Char('$'), iFound + 1) + 1; variableName = text.mid(iFound, iEnd - iFound); if (variableName != QLatin1String("$$")) { // if not double-delimiter if (!localVariables.contains(variableName)) { // and not already in map - QPointer dlg = new SnippetVariableDialog(variableName, &mSavedVariables, mParent); + QPointer dlg = new SnippetVariableDialog(variableName, &tempLocalVariables, mParent); if (dlg->exec()) { if (dlg->saveVariableIsChecked()) { mDirty = true; } variableValue = dlg->variableValue(); } else { delete dlg; return QString(); } delete dlg; } else { variableValue = localVariables.value(variableName); } } else { variableValue = QLatin1Char('$'); //if double-delimiter -> replace by single character } result.replace(variableName, variableValue); localVariables[ variableName ] = variableValue; } } while (iFound != -1); + SnippetsModel::instance()->setSavedVariables(tempLocalVariables); return result; } -QModelIndex SnippetsManager::Private::createGroup(const QString &groupName) -{ - mModel->insertRow(mModel->rowCount(), QModelIndex()); - const QModelIndex groupIndex = mModel->index(mModel->rowCount() - 1, 0, QModelIndex()); - mModel->setData(groupIndex, groupName, SnippetsModel::NameRole); - return groupIndex; -} - -void SnippetsManager::Private::createSnippet(const QModelIndex &groupIndex, const QString &snippetName, const QString &snippetText, const QString &snippetKeySequence) -{ - mModel->insertRow(mModel->rowCount(groupIndex), groupIndex); - const QModelIndex index = mModel->index(mModel->rowCount(groupIndex) - 1, 0, groupIndex); - - mModel->setData(index, snippetName, SnippetsModel::NameRole); - mModel->setData(index, snippetText, SnippetsModel::TextRole); - mModel->setData(index, snippetKeySequence, SnippetsModel::KeySequenceRole); - - updateActionCollection(QString(), - snippetName, - QKeySequence::fromString(snippetKeySequence), - snippetText); -} - -void SnippetsManager::Private::load() -{ - const KSharedConfig::Ptr config - = KSharedConfig::openConfig(QStringLiteral("kmailsnippetrc"), KConfig::NoGlobals); - - const KConfigGroup snippetPartGroup = config->group("SnippetPart"); - - const int groupCount = snippetPartGroup.readEntry("snippetGroupCount", 0); - for (int i = 0; i < groupCount; ++i) { - const KConfigGroup group - = config->group(QStringLiteral("SnippetGroup_%1").arg(i)); - - const QString groupName = group.readEntry("Name"); - - // create group - QModelIndex groupIndex = createGroup(groupName); - - const int snippetCount = group.readEntry("snippetCount", 0); - for (int j = 0; j < snippetCount; ++j) { - const QString snippetName - = group.readEntry(QStringLiteral("snippetName_%1").arg(j), QString()); - - const QString snippetText - = group.readEntry(QStringLiteral("snippetText_%1").arg(j), QString()); - - const QString snippetKeySequence - = group.readEntry(QStringLiteral("snippetKeySequence_%1").arg(j), QString()); - - createSnippet(groupIndex, snippetName, snippetText, snippetKeySequence); - } - } - - mSavedVariables.clear(); - const KConfigGroup group = config->group("SavedVariablesPart"); - const int variablesCount = group.readEntry("variablesCount", 0); - - for (int i = 0; i < variablesCount; ++i) { - const QString variableKey - = group.readEntry(QStringLiteral("variableName_%1").arg(i), QString()); - - const QString variableValue - = group.readEntry(QStringLiteral("variableValue_%1").arg(i), QString()); - - mSavedVariables.insert(variableKey, variableValue); - } -} void SnippetsManager::Private::save() { if (!mDirty) { return; } - KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kmailsnippetrc"), KConfig::NoGlobals); - - // clear everything - for (const QString &group : config->groupList()) { - config->deleteGroup(group); - } - - // write number of snippet groups - KConfigGroup group = config->group("SnippetPart"); - - const int groupCount = mModel->rowCount(); - group.writeEntry("snippetGroupCount", groupCount); - - for (int i = 0; i < groupCount; ++i) { - const QModelIndex groupIndex = mModel->index(i, 0, QModelIndex()); - const QString groupName = groupIndex.data(SnippetsModel::NameRole).toString(); - - KConfigGroup group = config->group(QStringLiteral("SnippetGroup_%1").arg(i)); - group.writeEntry("Name", groupName); - - const int snippetCount = mModel->rowCount(groupIndex); - - group.writeEntry("snippetCount", snippetCount); - for (int j = 0; j < snippetCount; ++j) { - const QModelIndex index = mModel->index(j, 0, groupIndex); - - const QString snippetName = index.data(SnippetsModel::NameRole).toString(); - if (!snippetName.isEmpty()) { - const QString snippetText = index.data(SnippetsModel::TextRole).toString(); - const QString snippetKeySequence = index.data(SnippetsModel::KeySequenceRole).toString(); - - group.writeEntry(QStringLiteral("snippetName_%1").arg(j), snippetName); - if (!snippetText.isEmpty()) { - group.writeEntry(QStringLiteral("snippetText_%1").arg(j), snippetText); - } - if (!snippetKeySequence.isEmpty()) { - group.writeEntry(QStringLiteral("snippetKeySequence_%1").arg(j), - snippetKeySequence); - } - } - } - } - - { - KConfigGroup group = config->group("SavedVariablesPart"); - - const int variablesCount = mSavedVariables.count(); - group.writeEntry("variablesCount", variablesCount); - - int counter = 0; - QMap::const_iterator it = mSavedVariables.cbegin(); - const QMap::const_iterator itEnd = mSavedVariables.cend(); - for (; it != itEnd; ++it) { - group.writeEntry(QStringLiteral("variableName_%1").arg(counter), it.key()); - group.writeEntry(QStringLiteral("variableValue_%1").arg(counter), it.value()); - counter++; - } - } - - config->sync(); + SnippetsModel::instance()->save(); mDirty = false; } SnippetsManager::SnippetsManager(KActionCollection *actionCollection, QObject *parent, QWidget *widget) : QObject(parent) , d(new Private(this, widget)) { - d->mModel = new SnippetsModel(this); + d->mModel = SnippetsModel::instance(); d->mSelectionModel = new QItemSelectionModel(d->mModel); d->mActionCollection = actionCollection; d->mAddSnippetAction = new QAction(i18n("Add Snippet..."), this); d->mAddSnippetAction->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); d->mEditSnippetAction = new QAction(i18n("Edit Snippet..."), this); d->mEditSnippetAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); d->mDeleteSnippetAction = new QAction(i18n("Remove Snippet"), this); d->mDeleteSnippetAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); d->mAddSnippetGroupAction = new QAction(i18n("Add Group..."), this); d->mAddSnippetGroupAction->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); d->mEditSnippetGroupAction = new QAction(i18n("Rename Group..."), this); d->mEditSnippetGroupAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); d->mDeleteSnippetGroupAction = new QAction(i18n("Remove Group"), this); d->mDeleteSnippetGroupAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); d->mInsertSnippetAction = new QAction(i18n("Insert Snippet"), this); connect(d->mSelectionModel, &QItemSelectionModel::selectionChanged, this, [this]() { d->selectionChanged(); }); connect(d->mModel, &SnippetsModel::dndDone, this, [this]() { d->dndDone(); }); connect(d->mModel, &SnippetsModel::addNewDndSnippset, this, [this](const QString &str) { d->slotAddNewDndSnippset(str); }); connect(d->mAddSnippetAction, &QAction::triggered, this, [this]() { d->addSnippet(); }); connect(d->mEditSnippetAction, &QAction::triggered, this, [this]() { d->editSnippet(); }); connect(d->mDeleteSnippetAction, &QAction::triggered, this, [this]() { d->deleteSnippet(); }); connect(d->mAddSnippetGroupAction, &QAction::triggered, this, [this]() { d->addSnippetGroup(); }); connect(d->mEditSnippetGroupAction, &QAction::triggered, this, [this]() { d->editSnippetGroup(); }); connect(d->mDeleteSnippetGroupAction, &QAction::triggered, this, [this]() { d->deleteSnippetGroup(); }); connect(d->mInsertSnippetAction, &QAction::triggered, this, [this]() { d->insertSelectedSnippet(); }); d->selectionChanged(); - - d->load(); } SnippetsManager::~SnippetsManager() { d->save(); delete d; } void SnippetsManager::setEditor(QObject *editor, const char *insertSnippetMethod, const char *dropSignal) { d->mEditor = editor; d->mEditorInsertMethod = insertSnippetMethod; if (dropSignal) { const int index = editor->metaObject()->indexOfSignal( QMetaObject::normalizedSignature(dropSignal + 1).data()); // skip the leading '2' if (index != -1) { connect(editor, dropSignal, this, SLOT(insertSelectedSnippet())); } } } QAbstractItemModel *SnippetsManager::model() const { return d->mModel; } QItemSelectionModel *SnippetsManager::selectionModel() const { return d->mSelectionModel; } QAction *SnippetsManager::addSnippetAction() const { return d->mAddSnippetAction; } QAction *SnippetsManager::editSnippetAction() const { return d->mEditSnippetAction; } QAction *SnippetsManager::deleteSnippetAction() const { return d->mDeleteSnippetAction; } QAction *SnippetsManager::addSnippetGroupAction() const { return d->mAddSnippetGroupAction; } QAction *SnippetsManager::editSnippetGroupAction() const { return d->mEditSnippetGroupAction; } QAction *SnippetsManager::deleteSnippetGroupAction() const { return d->mDeleteSnippetGroupAction; } QAction *SnippetsManager::insertSnippetAction() const { return d->mInsertSnippetAction; } bool SnippetsManager::snippetGroupSelected() const { if (d->mSelectionModel->selectedIndexes().isEmpty()) { return false; } return d->mSelectionModel->selectedIndexes().first().data(SnippetsModel::IsGroupRole).toBool(); } QString SnippetsManager::selectedName() const { if (d->mSelectionModel->selectedIndexes().isEmpty()) { return QString(); } return d->mSelectionModel->selectedIndexes().first().data(SnippetsModel::NameRole).toString(); } #include "moc_snippetsmanager.cpp" diff --git a/src/snippets/snippetsmodel.cpp b/src/snippets/snippetsmodel.cpp index 271c3e3..c793d98 100644 --- a/src/snippets/snippetsmodel.cpp +++ b/src/snippets/snippetsmodel.cpp @@ -1,435 +1,585 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, Author: Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "snippetsmodel.h" #include #include #include #include #include +#include +#include using namespace MailCommon; class MailCommon::SnippetItem { public: SnippetItem(bool isGroup = false, SnippetItem *parent = nullptr); ~SnippetItem(); bool isGroup() const; void setName(const QString &name); QString name() const; void setText(const QString &text); QString text() const; void setKeySequence(const QString &sequence); QString keySequence() const; void appendChild(SnippetItem *child); void removeChild(SnippetItem *child); SnippetItem *child(int row) const; int childCount() const; int row() const; SnippetItem *parent() const; private: QList mChildItems; SnippetItem *mParentItem = nullptr; bool mIsGroup = false; QString mName; QString mText; QString mKeySequence; }; SnippetItem::SnippetItem(bool isGroup, SnippetItem *parent) : mParentItem(parent) , mIsGroup(isGroup) { } SnippetItem::~SnippetItem() { qDeleteAll(mChildItems); mChildItems.clear(); } bool SnippetItem::isGroup() const { return mIsGroup; } void SnippetItem::setName(const QString &name) { mName = name; } QString SnippetItem::name() const { return mName; } void SnippetItem::setText(const QString &text) { mText = text; } QString SnippetItem::text() const { return mText; } void SnippetItem::setKeySequence(const QString &sequence) { mKeySequence = sequence; } QString SnippetItem::keySequence() const { return mKeySequence; } void SnippetItem::appendChild(SnippetItem *item) { mChildItems.append(item); } void SnippetItem::removeChild(SnippetItem *item) { mChildItems.removeAll(item); delete item; } SnippetItem *SnippetItem::child(int row) const { return mChildItems.value(row); } int SnippetItem::childCount() const { return mChildItems.count(); } SnippetItem *SnippetItem::parent() const { return mParentItem; } int SnippetItem::row() const { if (mParentItem) { return mParentItem->mChildItems.indexOf(const_cast(this)); } return 0; } SnippetsModel *SnippetsModel::instance() { static SnippetsModel s_self; return &s_self; } SnippetsModel::SnippetsModel(QObject *parent) : QAbstractItemModel(parent) { mRootItem = new SnippetItem(true); + load(); } SnippetsModel::~SnippetsModel() { delete mRootItem; } int SnippetsModel::columnCount(const QModelIndex &) const { return 1; } bool SnippetsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return false; } SnippetItem *item = static_cast(index.internalPointer()); Q_ASSERT(item); switch (role) { case NameRole: item->setName(value.toString()); Q_EMIT dataChanged(index, index); return true; case TextRole: item->setText(value.toString()); Q_EMIT dataChanged(index, index); return true; case KeySequenceRole: item->setKeySequence(value.toString()); Q_EMIT dataChanged(index, index); return true; default: return false; } return false; } QVariant SnippetsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } SnippetItem *item = static_cast(index.internalPointer()); switch (role) { case Qt::DisplayRole: return item->name(); case IsGroupRole: return item->isGroup(); case NameRole: return item->name(); case TextRole: return item->text(); case KeySequenceRole: return item->keySequence(); } return QVariant(); } Qt::ItemFlags SnippetsModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); if (index.isValid()) { const SnippetItem *item = static_cast(index.internalPointer()); if (!item->isGroup()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; } } return Qt::ItemIsDropEnabled | defaultFlags; } QModelIndex SnippetsModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } SnippetItem *parentItem = nullptr; if (!parent.isValid()) { parentItem = mRootItem; } else { parentItem = static_cast(parent.internalPointer()); } SnippetItem *childItem = parentItem->child(row); if (childItem) { return createIndex(row, column, childItem); } else { return QModelIndex(); } } QModelIndex SnippetsModel::parent(const QModelIndex &index) const { if (!index.isValid()) { return QModelIndex(); } SnippetItem *childItem = static_cast(index.internalPointer()); SnippetItem *parentItem = childItem->parent(); if (parentItem == mRootItem) { return QModelIndex(); } return createIndex(parentItem->row(), 0, parentItem); } int SnippetsModel::rowCount(const QModelIndex &parent) const { SnippetItem *parentItem = nullptr; if (parent.column() > 0) { return 0; } if (!parent.isValid()) { parentItem = mRootItem; } else { parentItem = static_cast(parent.internalPointer()); } return parentItem->childCount(); } bool SnippetsModel::insertRows(int row, int count, const QModelIndex &parent) { SnippetItem *parentItem = nullptr; if (!parent.isValid()) { parentItem = mRootItem; } else { parentItem = static_cast(parent.internalPointer()); } beginInsertRows(parent, row, row + count - 1); for (int i = 0; i < count; ++i) { SnippetItem *snippet = new SnippetItem(!parent.isValid(), parentItem); parentItem->appendChild(snippet); } endInsertRows(); return true; } bool SnippetsModel::removeRows(int row, int count, const QModelIndex &parent) { SnippetItem *parentItem = nullptr; if (!parent.isValid()) { parentItem = mRootItem; } else { parentItem = static_cast(parent.internalPointer()); } beginRemoveRows(parent, row, row + count - 1); for (int i = 0; i < count; ++i) { parentItem->removeChild(parentItem->child(row)); } endRemoveRows(); return true; } QStringList SnippetsModel::mimeTypes() const { return QStringList() << QStringLiteral("text/x-kmail-textsnippet") << QStringLiteral("text/plain"); } QMimeData *SnippetsModel::mimeData(const QModelIndexList &indexes) const { if (indexes.isEmpty()) { return nullptr; } const QModelIndex index = indexes.first(); SnippetItem *item = static_cast(index.internalPointer()); if (item->isGroup()) { return nullptr; } QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << index.parent().internalId() << item->name() << item->text() << item->keySequence(); mimeData->setData(QStringLiteral("text/x-kmail-textsnippet"), encodedData); mimeData->setText(item->text()); return mimeData; } bool SnippetsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row); if (action == Qt::IgnoreAction) { return true; } if (data->hasFormat(QStringLiteral("text/plain"))) { if (column > 1) { return false; } const QString encodedData = QString::fromUtf8(data->data(QStringLiteral("text/plain"))); if (!parent.isValid()) { Q_EMIT addNewDndSnippset(encodedData); return false; } SnippetItem *item = static_cast(parent.internalPointer()); if (item->isGroup()) { Q_EMIT addNewDndSnippset(encodedData); } else { if (KMessageBox::Yes == KMessageBox::questionYesNo(nullptr, i18n("Do you want to update snippet?"), i18n("Update snippet"))) { item->setText(encodedData); } } return false; } else { if (!parent.isValid()) { return false; } if (!data->hasFormat(QStringLiteral("text/x-kmail-textsnippet"))) { return false; } if (column > 1) { return false; } SnippetItem *item = static_cast(parent.internalPointer()); if (!item->isGroup()) { return false; } QByteArray encodedData = data->data(QStringLiteral("text/x-kmail-textsnippet")); QDataStream stream(&encodedData, QIODevice::ReadOnly); quintptr id; QString name; QString text; QString keySequence; stream >> id >> name >> text >> keySequence; if (parent.internalId() == id) { return false; } insertRow(rowCount(parent), parent); const QModelIndex idx = index(rowCount(parent) - 1, 0, parent); setData(idx, name, SnippetsModel::NameRole); setData(idx, text, SnippetsModel::TextRole); setData(idx, keySequence, SnippetsModel::KeySequenceRole); Q_EMIT dndDone(); return true; } } Qt::DropActions SnippetsModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } + +QModelIndex SnippetsModel::createGroup(const QString &groupName) +{ + insertRow(rowCount(), QModelIndex()); + const QModelIndex groupIndex = index(rowCount() - 1, 0, QModelIndex()); + setData(groupIndex, groupName, SnippetsModel::NameRole); + return groupIndex; +} + +void SnippetsModel::load() +{ + const KSharedConfig::Ptr config + = KSharedConfig::openConfig(QStringLiteral("kmailsnippetrc"), KConfig::NoGlobals); + + const KConfigGroup snippetPartGroup = config->group("SnippetPart"); + + const int groupCount = snippetPartGroup.readEntry("snippetGroupCount", 0); + + for (int i = 0; i < groupCount; ++i) { + const KConfigGroup group + = config->group(QStringLiteral("SnippetGroup_%1").arg(i)); + + const QString groupName = group.readEntry("Name"); + + // create group + QModelIndex groupIndex = createGroup(groupName); + + const int snippetCount = group.readEntry("snippetCount", 0); + for (int j = 0; j < snippetCount; ++j) { + const QString snippetName + = group.readEntry(QStringLiteral("snippetName_%1").arg(j), QString()); + + const QString snippetText + = group.readEntry(QStringLiteral("snippetText_%1").arg(j), QString()); + + const QString snippetKeySequence + = group.readEntry(QStringLiteral("snippetKeySequence_%1").arg(j), QString()); + + createSnippet(groupIndex, snippetName, snippetText, snippetKeySequence); + } + } + + mSavedVariables.clear(); + const KConfigGroup group = config->group("SavedVariablesPart"); + const int variablesCount = group.readEntry("variablesCount", 0); + + for (int i = 0; i < variablesCount; ++i) { + const QString variableKey + = group.readEntry(QStringLiteral("variableName_%1").arg(i), QString()); + + const QString variableValue + = group.readEntry(QStringLiteral("variableValue_%1").arg(i), QString()); + + mSavedVariables.insert(variableKey, variableValue); + } + +} + +void SnippetsModel::createSnippet(const QModelIndex &groupIndex, const QString &snippetName, const QString &snippetText, const QString &snippetKeySequence) +{ + insertRow(rowCount(groupIndex), groupIndex); + const QModelIndex modelIndex = index(rowCount(groupIndex) - 1, 0, groupIndex); + + setData(modelIndex, snippetName, SnippetsModel::NameRole); + setData(modelIndex, snippetText, SnippetsModel::TextRole); + setData(modelIndex, snippetKeySequence, SnippetsModel::KeySequenceRole); + + //TODO +// updateActionCollection(QString(), +// snippetName, +// QKeySequence::fromString(snippetKeySequence), +// snippetText); +} + +void SnippetsModel::setSavedVariables(const QMap &savedVariables) +{ + mSavedVariables = savedVariables; +} + +QMap SnippetsModel::savedVariables() const +{ + return mSavedVariables; +} + +void SnippetsModel::save() +{ + KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kmailsnippetrc"), KConfig::NoGlobals); + + // clear everything + for (const QString &group : config->groupList()) { + config->deleteGroup(group); + } + + // write number of snippet groups + KConfigGroup group = config->group("SnippetPart"); + + const int groupCount = rowCount(); + group.writeEntry("snippetGroupCount", groupCount); + + for (int i = 0; i < groupCount; ++i) { + const QModelIndex groupIndex = index(i, 0, QModelIndex()); + const QString groupName = groupIndex.data(SnippetsModel::NameRole).toString(); + + KConfigGroup group = config->group(QStringLiteral("SnippetGroup_%1").arg(i)); + group.writeEntry("Name", groupName); + + const int snippetCount = rowCount(groupIndex); + + group.writeEntry("snippetCount", snippetCount); + for (int j = 0; j < snippetCount; ++j) { + const QModelIndex modelIndex = index(j, 0, groupIndex); + + const QString snippetName = modelIndex.data(SnippetsModel::NameRole).toString(); + if (!snippetName.isEmpty()) { + const QString snippetText = modelIndex.data(SnippetsModel::TextRole).toString(); + const QString snippetKeySequence = modelIndex.data(SnippetsModel::KeySequenceRole).toString(); + + group.writeEntry(QStringLiteral("snippetName_%1").arg(j), snippetName); + if (!snippetText.isEmpty()) { + group.writeEntry(QStringLiteral("snippetText_%1").arg(j), snippetText); + } + if (!snippetKeySequence.isEmpty()) { + group.writeEntry(QStringLiteral("snippetKeySequence_%1").arg(j), + snippetKeySequence); + } + } + } + } + + { + KConfigGroup group = config->group("SavedVariablesPart"); + + const int variablesCount = mSavedVariables.count(); + group.writeEntry("variablesCount", variablesCount); + + int counter = 0; + QMap::const_iterator it = mSavedVariables.cbegin(); + const QMap::const_iterator itEnd = mSavedVariables.cend(); + for (; it != itEnd; ++it) { + group.writeEntry(QStringLiteral("variableName_%1").arg(counter), it.key()); + group.writeEntry(QStringLiteral("variableValue_%1").arg(counter), it.value()); + counter++; + } + } + config->sync(); + +} diff --git a/src/snippets/snippetsmodel.h b/src/snippets/snippetsmodel.h index b2c55f9..fa6f970 100644 --- a/src/snippets/snippetsmodel.h +++ b/src/snippets/snippetsmodel.h @@ -1,80 +1,89 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, Author: Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAILCOMMON_SNIPPETSMODEL_P_H #define MAILCOMMON_SNIPPETSMODEL_P_H #include namespace MailCommon { class SnippetItem; class SnippetsModel : public QAbstractItemModel { Q_OBJECT public: enum Role { IsGroupRole = Qt::UserRole + 1, ///< Returns whether the index represents a group NameRole, ///< The name of a snippet or group TextRole, ///< The text of a snippet KeySequenceRole ///< The key sequence to activate a snippet }; static SnippetsModel *instance(); explicit SnippetsModel(QObject *parent = nullptr); ~SnippetsModel() override; Q_REQUIRED_RESULT bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Q_REQUIRED_RESULT QVariant data(const QModelIndex &index, int role) const override; Q_REQUIRED_RESULT Qt::ItemFlags flags(const QModelIndex &index) const override; Q_REQUIRED_RESULT QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; Q_REQUIRED_RESULT QModelIndex parent(const QModelIndex &index) const override; Q_REQUIRED_RESULT int rowCount(const QModelIndex &parent = QModelIndex()) const override; Q_REQUIRED_RESULT int columnCount(const QModelIndex &parent = QModelIndex()) const override; Q_REQUIRED_RESULT QStringList mimeTypes() const override; Q_REQUIRED_RESULT QMimeData *mimeData(const QModelIndexList &indexes) const override; Q_REQUIRED_RESULT bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; Q_REQUIRED_RESULT Qt::DropActions supportedDropActions() const override; + void load(); + void save(); + + Q_REQUIRED_RESULT QMap savedVariables() const; + void setSavedVariables(const QMap &savedVariables); + protected: bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; Q_SIGNALS: void dndDone(); void addNewDndSnippset(const QString &); private: + QModelIndex createGroup(const QString &groupName); + void createSnippet(const QModelIndex &groupIndex, const QString &snippetName, const QString &snippetText, const QString &snippetKeySequence); SnippetItem *mRootItem = nullptr; + QMap mSavedVariables; }; } #endif