diff --git a/src/KJotsMain.cpp b/src/KJotsMain.cpp index 9c327dc..588bc58 100644 --- a/src/KJotsMain.cpp +++ b/src/KJotsMain.cpp @@ -1,58 +1,58 @@ /* - kjots + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee - Copyright (C) 2007-2008 Stephen Kelly + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2007-2008 Stephen Kelly This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KJotsMain.h" #include #include #include #include #include "KJotsSettings.h" #include "kjotsbookmarks.h" #include "kjotsbrowser.h" #include "kjotsedit.h" #include "kjotswidget.h" //---------------------------------------------------------------------- // KJOTSMAIN //---------------------------------------------------------------------- KJotsMain::KJotsMain(QWidget *parent) : KXmlGuiWindow(parent) , component(new KJotsWidget(this, this)) { KStandardAction::quit(this, &KJotsMain::close, actionCollection()); setCentralWidget(component); setupGUI(); connect(component, &KJotsWidget::captionChanged, this, qOverload(&KJotsMain::setCaption)); } bool KJotsMain::queryClose() { return component->queryClose(); } diff --git a/src/aboutdata.cpp b/src/aboutdata.cpp index 67d0b2e..cbdd409 100644 --- a/src/aboutdata.cpp +++ b/src/aboutdata.cpp @@ -1,46 +1,47 @@ /* This file is part of KJots. Copyright (c) 2008 Stephen Kelly This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "aboutdata.h" #include "kjots-version.h" #include AboutData::AboutData() : KAboutData(QStringLiteral("kjots"), i18n("KJots"), QStringLiteral(KJOTS_VERSION), i18n("KDE note taking utility"), KAboutLicense::GPL, i18n("Copyright © 1997–2015 KJots authors")) { - addAuthor(i18n("Daniel Vrátil"), i18n("Maintainer"), QStringLiteral("dvraitl@kde.org")); + addAuthor(i18n("Igor Poboiko"), i18n("Maintainer"), QStringLiteral("igor.poboiko@gmail.com")); + addAuthor(i18n("Daniel Vrátil"), i18n("Port to KDE Frameworks 5"), QStringLiteral("dvraitl@kde.org")); addAuthor(i18n("Stephen Kelly"), QString(), QStringLiteral("steveire@gmail.com")); addAuthor(i18n("Pradeepto K. Bhattacharya"), QString(), QStringLiteral("pradeepto@kde.org")); addAuthor(i18n("Jaison Lee"), QString(), QStringLiteral("lee.jaison@gmail.com")); addAuthor(i18n("Aaron J. Seigo"), QString(), QStringLiteral("aseigo@kde.org")); addAuthor(i18n("Stanislav Kljuhhin"), QString(), QStringLiteral("crz@starman.ee")); addAuthor(i18n("Christoph Neerfeld"), i18n("Original author"), QStringLiteral("chris@kde.org")); addAuthor(i18n("Laurent Montel"), QString(), QStringLiteral("montel@kde.org")); } diff --git a/src/kjotsbookmarks.cpp b/src/kjotsbookmarks.cpp index 1fad1d4..df15aff 100644 --- a/src/kjotsbookmarks.cpp +++ b/src/kjotsbookmarks.cpp @@ -1,82 +1,83 @@ /* - kjotsbookmarks + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kjotsbookmarks.h" #include "kjotsmodel.h" #include using namespace Akonadi; KJotsBookmarks::KJotsBookmarks(QItemSelectionModel *model, QObject *parent) : QObject(parent) , m_model(model) { } void KJotsBookmarks::openBookmark(const KBookmark &bm, Qt::MouseButtons /*mb*/, Qt::KeyboardModifiers /*km*/) { if (bm.url().scheme() != QLatin1String("akonadi")) { return; } Q_EMIT openLink(bm.url()); } QString KJotsBookmarks::currentIcon() const { const QModelIndexList rows = m_model->selectedRows(); if (rows.size() != 1) { return QString(); } const QModelIndex idx = rows.first(); const auto collection = idx.data(EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { return QStringLiteral("x-office-address-book"); } const auto item = idx.data(EntityTreeModel::ItemRole).value(); if (item.isValid()) { return QStringLiteral("x-office-document"); } return QString(); } QUrl KJotsBookmarks::currentUrl() const { const QModelIndexList rows = m_model->selectedRows(); if (rows.size() != 1) { return QUrl(); } else { return rows.first().data(KJotsModel::EntityUrlRole).toUrl(); } } QString KJotsBookmarks::currentTitle() const { const QModelIndexList rows = m_model->selectedRows(); if (rows.size() != 1) { return QString(); } else { return KJotsModel::itemPath(rows.first(), QStringLiteral(": ")); } } diff --git a/src/kjotsbookmarks.h b/src/kjotsbookmarks.h index 643b6b1..36d1bcf 100644 --- a/src/kjotsbookmarks.h +++ b/src/kjotsbookmarks.h @@ -1,51 +1,52 @@ /* - kjotsbookmarks + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KJOTSBOOKMARKS #define KJOTSBOOKMARKS #include class QItemSelectionModel; class KJotsBookmarks : public QObject, public KBookmarkOwner { Q_OBJECT public: explicit KJotsBookmarks(QItemSelectionModel *model, QObject *parent = nullptr); QUrl currentUrl() const override; QString currentIcon() const override; QString currentTitle() const override; void openBookmark(const KBookmark &bm, Qt::MouseButtons mb, Qt::KeyboardModifiers km) override; Q_SIGNALS: void openLink(const QUrl &url); private: QItemSelectionModel *m_model = nullptr; }; #endif /* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/kjotsbrowser.cpp b/src/kjotsbrowser.cpp index e8fcf21..36334e7 100644 --- a/src/kjotsbrowser.cpp +++ b/src/kjotsbrowser.cpp @@ -1,206 +1,207 @@ /* - kjotsbookmarks + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //Own Header #include "kjotsbrowser.h" #include "kjotsmodel.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; class Q_DECL_HIDDEN KJotsBrowserWidgetPrivate { public: explicit KJotsBrowserWidgetPrivate(std::unique_ptr browser, QWidget *widget) : mBrowser(std::move(browser)) , mSliderContainer(widget) , mFindBar(mBrowser.get(), &mSliderContainer) , mTextToSpeechWidget(widget) { } std::unique_ptr mBrowser; KPIMTextEdit::SlideContainer mSliderContainer; KPIMTextEdit::RichTextEditFindBar mFindBar; KPIMTextEdit::TextToSpeechWidget mTextToSpeechWidget; }; KJotsBrowserWidget::KJotsBrowserWidget(std::unique_ptr browser, QWidget *parent) : QWidget(parent) , d(new KJotsBrowserWidgetPrivate(std::move(browser), this)) { d->mBrowser->setParent(this); d->mSliderContainer.setContent(&d->mFindBar); d->mFindBar.setHideWhenClose(false); connect(&d->mFindBar, &KPIMTextEdit::RichTextEditFindBar::hideFindBar, this, &KJotsBrowserWidget::slotHideFindBar); connect(d->mBrowser.get(), &KJotsBrowser::say, &d->mTextToSpeechWidget, &KPIMTextEdit::TextToSpeechWidget::say); QVBoxLayout *lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); lay->addWidget(&d->mTextToSpeechWidget); lay->addWidget(d->mBrowser.get()); lay->addWidget(&d->mSliderContainer); } KJotsBrowserWidget::~KJotsBrowserWidget() = default; KJotsBrowser* KJotsBrowserWidget::browser() { return d->mBrowser.get(); } void KJotsBrowserWidget::slotFind() { if (d->mBrowser->textCursor().hasSelection()) { d->mFindBar.setText(d->mBrowser->textCursor().selectedText()); } d->mBrowser->moveCursor(QTextCursor::Start); d->mFindBar.showFind(); d->mSliderContainer.slideIn(); d->mFindBar.focusAndSetCursor(); } void KJotsBrowserWidget::slotFindNext() { if (d->mFindBar.isVisible()) { d->mFindBar.findNext(); } else { slotFind(); } } void KJotsBrowserWidget::slotHideFindBar() { d->mSliderContainer.slideOut(); d->mBrowser->setFocus(); } KJotsBrowser::KJotsBrowser(KActionCollection *actionCollection, QWidget *parent) : QTextBrowser(parent) , m_actionCollection(actionCollection) { setWordWrapMode(QTextOption::WordWrap); connect(this, &KJotsBrowser::anchorClicked, this, [this](const QUrl &url){ if (!url.toString().startsWith(QLatin1Char('#'))) { // QTextBrowser tries to automatically handle the url. We only want it for anchor navigation // (i.e. "#page12" links). This can be overridden by setting the source to an invalid QUrl setSource(QUrl()); Q_EMIT linkClicked(url); } }); } void KJotsBrowser::setModel(QAbstractItemModel *model) { m_model = model; } void KJotsBrowser::contextMenuEvent(QContextMenuEvent *event) { QMenu *popup = createStandardContextMenu(event->pos()); if (!popup) { return; } popup->addSeparator(); popup->addAction(m_actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Find)))); popup->addSeparator(); if (!document()->isEmpty() && KPIMTextEdit::TextToSpeech::self()->isReady()) { QAction *speakAction = popup->addAction(i18nc("@info:action", "Speak Text")); speakAction->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-text-to-speech"))); connect(speakAction, &QAction::triggered, this, [this](){ const QString text = textCursor().hasSelection() ? textCursor().selectedText() : document()->toPlainText(); Q_EMIT say(text); }); } popup->exec(event->globalPos()); delete popup; } bool KJotsBrowser::event(QEvent *event) { if (event->type() == QEvent::ToolTip) { tooltipEvent(static_cast(event)); } return QTextBrowser::event(event); } void KJotsBrowser::tooltipEvent(QHelpEvent *event) { if (!m_model) { return; } // This code is somewhat shared with KJotsEdit QUrl url(anchorAt(event->pos())); QString message; if (url.isValid()) { QModelIndex idx; if (url.scheme() == QStringLiteral("akonadi")) { idx = KJotsModel::modelIndexForUrl(m_model, url); } else // This is #page_XXX internal links if (url.scheme().isEmpty() && url.host().isEmpty() && url.path().isEmpty() && url.query().isEmpty() && url.fragment().startsWith(QLatin1String("page_"))) { bool ok; Item::Id id = url.fragment().midRef(5).toInt(&ok); const QModelIndexList idxs = EntityTreeModel::modelIndexesForItem(m_model, Item(id)); if (ok && !idxs.isEmpty()) { idx = idxs.first(); } } else { message = i18nc("@info:tooltip %1 is hyperlink address", "Click to follow the hyperlink: %1", url.toString(QUrl::RemovePassword)); } if (idx.isValid()) { if (idx.data(EntityTreeModel::ItemRole).value().isValid()) { message = i18nc("@info:tooltip %1 is a full path to note (i.e. Notes / Notebook / Note)", "Click to open note: %1", KJotsModel::itemPath(idx)); } else if (idx.data(EntityTreeModel::CollectionRole).value().isValid()) { message = i18nc("@info:tooltip %1 is a full path to book (i.e. Notes / Notebook)", "Click to open book: %1", KJotsModel::itemPath(idx)); } } } if (!message.isEmpty()) { QToolTip::showText(event->globalPos(), message); } else { QToolTip::hideText(); } } /* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/kjotsbrowser.h b/src/kjotsbrowser.h index adfdbac..6f9e8e4 100644 --- a/src/kjotsbrowser.h +++ b/src/kjotsbrowser.h @@ -1,82 +1,83 @@ /* - kjotsbookmarks + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KJOTSBROWSER_H #define KJOTSBROWSER_H #include class QHelpEvent; class QAbstractItemModel; class KActionCollection; class KJotsBrowserWidgetPrivate; class KJotsBrowser : public QTextBrowser { Q_OBJECT public: explicit KJotsBrowser(KActionCollection *actionCollection, QWidget *parent = nullptr); /** * @brief set the ETM which will be used to display * additional information in tooltips */ void setModel(QAbstractItemModel *model); protected: bool event(QEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; void tooltipEvent(QHelpEvent *event); Q_SIGNALS: void linkClicked(const QUrl &); void say(const QString &text); private: QAbstractItemModel *m_model = nullptr; KActionCollection *m_actionCollection = nullptr; }; /** * @brief A widget-wrapper around KJotsBrowser * * It also contains: find bar, text-to-speech widget. * @see KPIMTextEdit::RichTextEditorWidget */ class KJotsBrowserWidget : public QWidget { Q_OBJECT public: explicit KJotsBrowserWidget(std::unique_ptr browser, QWidget *parent = nullptr); ~KJotsBrowserWidget(); KJotsBrowser *browser(); public Q_SLOTS: void slotFind(); void slotFindNext(); private: void slotHideFindBar(); std::unique_ptr const d; }; #endif /* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/kjotsconfigdlg.cpp b/src/kjotsconfigdlg.cpp index f4a006f..e0209be 100644 --- a/src/kjotsconfigdlg.cpp +++ b/src/kjotsconfigdlg.cpp @@ -1,35 +1,38 @@ /* + This file is part of KJots. + Copyright (c) 2009 Montel Laurent + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kjotsconfigdlg.h" #include "KJotsSettings.h" #include KJotsConfigMisc::KJotsConfigMisc(QWidget *parent, const QVariantList &args) : KCModule(parent, args) , ui(new Ui::confPageMisc) { auto *lay = new QHBoxLayout(this); auto miscPage = new QWidget(this); ui->setupUi(miscPage); lay->addWidget(miscPage); addConfig(KJotsSettings::self(), miscPage); load(); } #include "moc_kjotsconfigdlg.cpp" diff --git a/src/kjotsconfigdlg.h b/src/kjotsconfigdlg.h index c9f1b51..2c2f315 100644 --- a/src/kjotsconfigdlg.h +++ b/src/kjotsconfigdlg.h @@ -1,37 +1,40 @@ /* + This file is part of KJots. + Copyright (c) 2009 Montel Laurent + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KJOTSCONFIGDLG_H #define KJOTSCONFIGDLG_H #include #include #include "ui_confpagemisc.h" class KJotsConfigMisc : public KCModule { Q_OBJECT public: explicit KJotsConfigMisc(QWidget *parent, const QVariantList &args = QVariantList()); private: std::unique_ptr ui; }; #endif /* KJOTSCONFIGDLG_H */ diff --git a/src/kjotsedit.cpp b/src/kjotsedit.cpp index 8666c8c..65cd212 100644 --- a/src/kjotsedit.cpp +++ b/src/kjotsedit.cpp @@ -1,488 +1,489 @@ /* - kjots + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee - Copyright (C) 2007-2008 Stephen Kelly + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2007-2008 Stephen Kelly + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //Own Header #include "kjotsedit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kjotslinkdialog.h" #include "kjotsmodel.h" #include "noteshared/noteeditorutils.h" #include "noteshared/notelockattribute.h" Q_DECLARE_METATYPE(QTextCursor) Q_DECLARE_METATYPE(KPIMTextEdit::ImageList) using namespace Akonadi; using namespace KPIMTextEdit; class Q_DECL_HIDDEN KJotsEdit::Private { public: Private() = default; ~Private() = default; QPersistentModelIndex index; QAbstractItemModel *model = nullptr; QAction *action_copy_into_title = nullptr; QAction *action_manage_link = nullptr; QAction *action_auto_bullet = nullptr; QAction *action_auto_decimal = nullptr; QAction *action_insert_date = nullptr; QVector richTextActionList; }; KJotsEdit::KJotsEdit(QWidget *parent, KActionCollection *actionCollection) : RichTextComposer(parent) , d(new Private) , m_actionCollection(actionCollection) , allowAutoDecimal(false) { setMouseTracking(true); setAcceptRichText(true); setWordWrapMode(QTextOption::WordWrap); setCheckSpellingEnabled(true); setFocusPolicy(Qt::StrongFocus); createActions(m_actionCollection); activateRichText(); } KJotsEdit::~KJotsEdit() = default; void KJotsEdit::createActions(KActionCollection *ac) { RichTextComposer::createActions(ac); d->action_copy_into_title = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action", "Copy &Into Page Title"), this); connect(d->action_copy_into_title, &QAction::triggered, this, &KJotsEdit::copySelectionIntoTitle); connect(this, &KJotsEdit::copyAvailable, d->action_copy_into_title, &QAction::setEnabled); d->action_copy_into_title->setEnabled(false); d->richTextActionList.append(d->action_copy_into_title); if (ac) { ac->addAction(QStringLiteral("copy_into_title"), d->action_copy_into_title); } d->action_manage_link = new QAction(QIcon::fromTheme(QStringLiteral("insert-link")), i18nc("@action creates and manages hyperlinks", "Link"), this); connect(d->action_manage_link, &QAction::triggered, this, &KJotsEdit::onLinkify); d->richTextActionList.append(d->action_manage_link); if (ac) { ac->addAction(QStringLiteral("manage_note_link"), d->action_manage_link); } d->action_auto_bullet = new QAction(QIcon::fromTheme(QStringLiteral("format-list-unordered")), i18nc("@action", "Auto Bullet List"), this); d->action_auto_bullet->setCheckable(true); connect(d->action_auto_bullet, &QAction::triggered, this, &KJotsEdit::onAutoBullet); d->richTextActionList.append(d->action_auto_bullet); if (ac) { ac->addAction(QStringLiteral("auto_bullet"), d->action_auto_bullet); } d->action_auto_decimal = new QAction(QIcon::fromTheme(QStringLiteral("format-list-ordered")), i18nc("@action", "Auto Decimal List"), this); d->action_auto_decimal->setCheckable(true); connect(d->action_auto_decimal, &QAction::triggered, this, &KJotsEdit::onAutoDecimal); d->richTextActionList.append(d->action_auto_decimal); if (ac) { ac->addAction(QStringLiteral("auto_decimal"), d->action_auto_decimal); } d->action_insert_date = new QAction(QIcon::fromTheme(QStringLiteral("view-calendar-time-spent")), i18nc("@action", "Insert Date"), this); connect(d->action_insert_date, &QAction::triggered, this, &KJotsEdit::insertDate); d->richTextActionList.append(d->action_insert_date); if (ac) { ac->addAction(QStringLiteral("insert_date"), d->action_insert_date); ac->setDefaultShortcut(d->action_insert_date, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I)); } } void KJotsEdit::setEnableActions(bool enable) { // FIXME: RichTextComposer::setEnableActions(enable) messes with indent actions // due to bug in KPIMTextEdit (should be fixed in 20.08?) composerActions()->setActionsEnabled(enable); for (QAction *action : qAsConst(d->richTextActionList)) { action->setEnabled(enable); } } void KJotsEdit::contextMenuEvent(QContextMenuEvent *event) { QMenu *popup = mousePopupMenu(event->pos()); if (popup) { const QList actionList = popup->actions(); if (!qApp->clipboard()->text().isEmpty()) { QAction *act = m_actionCollection->action(QStringLiteral("paste_without_formatting")); act->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); act->setEnabled(!isReadOnly()); // HACK: menu actions are following: Undo, Redo, Separator, Cut, Copy, Paste, Delete, Clear // We want to insert "Paste Without Formatting" right after standard Paste (which is at pos 6) // Let's hope QTextEdit and KPIMTextEdit::RichTextEditor doesn't break it // (and we don't break anything either) const int pasteActionPosition = 6; if (actionList.count() >= pasteActionPosition) { popup->insertAction(popup->actions().at(pasteActionPosition), act); } else { popup->addAction(act); } } popup->addSeparator(); popup->addAction(d->action_copy_into_title); if (!anchorAt(event->pos()).isNull()) { popup->addAction(d->action_manage_link); } popup->exec(event->globalPos()); delete popup; } } bool KJotsEdit::modified() { return document()->isModified(); } void KJotsEdit::insertDate() { NoteShared::NoteEditorUtils::insertDate(this); } bool KJotsEdit::setModelIndex(const QModelIndex &index) { // Mapping index to ETM QModelIndex etmIndex = KJotsModel::etmIndex(index); // Saving the old document, if it was changed bool newDocument = d->index.isValid() && (d->index != etmIndex); if (newDocument) { savePage(); } d->model = const_cast(etmIndex.model()); d->index = QPersistentModelIndex(etmIndex); // Loading document auto *doc = d->index.data(KJotsModel::DocumentRole).value(); if (!doc) { setReadOnly(true); return false; } disconnect(document(), &QTextDocument::modificationChanged, this, &KJotsEdit::documentModified); setDocument(doc); connect(doc, &QTextDocument::modificationChanged, this, &KJotsEdit::documentModified); // Setting cursor auto cursor = doc->property("textCursor").value(); if (!cursor.isNull()) { setTextCursor(cursor); } else { // This is a work-around for QTextEdit bug. If the first letter of the document is formatted, // QTextCursor doesn't follow this format. One can either move the cursor 1 symbol to the right // and then 1 symbol to the left as a workaround, or just explicitly move it to the start. // Submitted to qt-bugs, id 192886. // -- (don't know the fate of this bug, as for April 2020 it is inaccessible) moveCursor(QTextCursor::Start); } // Setting focus if document was changed if (newDocument) { setFocus(); } // Setting ReadOnly auto item = d->index.data(EntityTreeModel::ItemRole).value(); if (!item.isValid()) { setReadOnly(true); return false; } else if (item.hasAttribute()) { setReadOnly(true); return true; } else { setReadOnly(false); return true; } } void KJotsEdit::onAutoBullet() { KTextEdit::AutoFormatting currentFormatting = autoFormatting(); //TODO: set line spacing properly. if (currentFormatting == KTextEdit::AutoBulletList) { setAutoFormatting(KTextEdit::AutoNone); d->action_auto_bullet->setChecked(false); } else { setAutoFormatting(KTextEdit::AutoBulletList); d->action_auto_bullet->setChecked(true); } } void KJotsEdit::createAutoDecimalList() { //this is an adaptation of Qt's createAutoBulletList() function for creating a bulleted list, except in this case I use it to create a decimal list. QTextCursor cursor = textCursor(); cursor.beginEditBlock(); QTextBlockFormat blockFmt = cursor.blockFormat(); QTextListFormat listFmt; listFmt.setStyle(QTextListFormat::ListDecimal); listFmt.setIndent(blockFmt.indent() + 1); blockFmt.setIndent(0); cursor.setBlockFormat(blockFmt); cursor.createList(listFmt); cursor.endEditBlock(); setTextCursor(cursor); } void KJotsEdit::DecimalList() { QTextCursor cursor = textCursor(); if (cursor.currentList()) { return; } QString blockText = cursor.block().text(); if (blockText.length() == 2 && blockText == QLatin1String("1.")) { cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); cursor.removeSelectedText(); createAutoDecimalList(); } } void KJotsEdit::onAutoDecimal() { if (allowAutoDecimal) { allowAutoDecimal = false; disconnect(this, &KJotsEdit::textChanged, this, &KJotsEdit::DecimalList); d->action_auto_decimal->setChecked(false); } else { allowAutoDecimal = true; connect(this, &KJotsEdit::textChanged, this, &KJotsEdit::DecimalList); d->action_auto_decimal->setChecked(true); } } void KJotsEdit::onLinkify() { // Nothing is yet opened, ignoring if (!d->index.isValid()) { return; } composerControler()->selectLinkText(); auto linkDialog = std::make_unique(const_cast(d->index.model()), this); linkDialog->setLinkText(composerControler()->currentLinkText()); linkDialog->setLinkUrl(composerControler()->currentLinkUrl()); if (linkDialog->exec()) { composerControler()->updateLink(linkDialog->linkUrl(), linkDialog->linkText()); } } void KJotsEdit::copySelectionIntoTitle() { if (!d->index.isValid()) { return; } const QString newTitle(textCursor().selectedText()); d->model->setData(d->index, newTitle); } bool KJotsEdit::canInsertFromMimeData(const QMimeData *source) const { if (source->hasUrls()) { return true; } else { return RichTextComposer::canInsertFromMimeData(source); } } void KJotsEdit::insertFromMimeData(const QMimeData *source) { // Nothing is opened, ignoring if (!d->index.isValid()) { return; } if (source->hasUrls()) { const QList urls = source->urls(); for (const QUrl &url : urls) { if (url.scheme() == QStringLiteral("akonadi")) { QModelIndex idx = KJotsModel::modelIndexForUrl(d->model, url); if (idx.isValid()) { insertHtml(QStringLiteral("%2").arg(idx.data(KJotsModel::EntityUrlRole).toString(), idx.data().toString())); } } else { QString text = source->hasText() ? source->text() : url.toString(QUrl::RemovePassword); insertHtml(QStringLiteral("%2").arg(QString::fromUtf8(url.toEncoded()), text)); } } } else if (source->hasHtml()) { // Don't have an action to set top and bottom margins on paragraphs yet. // Remove the margins for all inserted html. QTextDocument dummy; dummy.setHtml(source->html()); QTextCursor c(&dummy); QTextBlockFormat fmt = c.blockFormat(); fmt.setTopMargin(0); fmt.setBottomMargin(0); fmt.setLeftMargin(0); fmt.setRightMargin(0); c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); c.mergeBlockFormat(fmt); textCursor().insertFragment(QTextDocumentFragment(c)); ensureCursorVisible(); } else { RichTextComposer::insertFromMimeData(source); } } void KJotsEdit::mouseMoveEvent(QMouseEvent *event) { if ((event->modifiers() & Qt::ControlModifier) && !anchorAt(event->pos()).isEmpty()) { if (!m_cursorChanged) { QApplication::setOverrideCursor(Qt::PointingHandCursor); m_cursorChanged = true; } } else { if (m_cursorChanged) { QApplication::restoreOverrideCursor(); m_cursorChanged = false; } } RichTextComposer::mouseMoveEvent(event); } void KJotsEdit::leaveEvent(QEvent *event) { if (m_cursorChanged) { QApplication::restoreOverrideCursor(); m_cursorChanged = false; } RichTextComposer::leaveEvent(event); } void KJotsEdit::mousePressEvent(QMouseEvent *event) { QUrl url = anchorAt(event->pos()); if ((event->modifiers() & Qt::ControlModifier) && (event->button() & Qt::LeftButton) && !url.isEmpty()) { Q_EMIT linkClicked(url); } else { RichTextComposer::mousePressEvent(event); } } bool KJotsEdit::event(QEvent *event) { if (event->type() == QEvent::WindowDeactivate) { savePage(); } else if (event->type() == QEvent::ToolTip) { tooltipEvent(static_cast(event)); } return RichTextComposer::event(event); } void KJotsEdit::tooltipEvent(QHelpEvent *event) { // Nothing is opened, ignoring if (!d->index.isValid()) { return; } QUrl url(anchorAt(event->pos())); QString message; if (url.isValid()) { if (url.scheme() == QStringLiteral("akonadi")) { const QModelIndex idx = KJotsModel::modelIndexForUrl(d->model, url); if (idx.data(EntityTreeModel::ItemRole).value().isValid()) { message = i18nc("@info:tooltip %1 is a full path to note (i.e. Notes / Notebook / Note)", "Ctrl+click to open note: %1", KJotsModel::itemPath(idx)); } else if (idx.data(EntityTreeModel::CollectionRole).value().isValid()) { message = i18nc("@info:tooltip %1 is a full path to book (i.e. Notes / Notebook)", "Ctrl+click to open book: %1", KJotsModel::itemPath(idx)); } } else { message = i18nc("@info:tooltip %1 is hyperlink address", "Ctrl+click to follow the hyperlink: %1", url.toString(QUrl::RemovePassword)); } } if (!message.isEmpty()) { QToolTip::showText(event->globalPos(), message); } else { QToolTip::hideText(); } } void KJotsEdit::focusOutEvent(QFocusEvent *event) { savePage(); RichTextComposer::focusOutEvent(event); } void KJotsEdit::prepareDocumentForSaving() { document()->setModified(false); document()->setProperty("textCursor", QVariant::fromValue(textCursor())); document()->setProperty("images", QVariant::fromValue(composerControler()->composerImages()->embeddedImages())); } void KJotsEdit::savePage() { if (!document()->isModified() || !d->index.isValid()) { return; } prepareDocumentForSaving(); d->model->setData(d->index, QVariant::fromValue(document()), KJotsModel::DocumentRole); } /* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/kjotslinkdialog.cpp b/src/kjotslinkdialog.cpp index 0ac0e91..ebe3e69 100644 --- a/src/kjotslinkdialog.cpp +++ b/src/kjotslinkdialog.cpp @@ -1,99 +1,100 @@ /* - kjots + This file is a part of KJots. Copyright (C) 2008 Stephen Kelly + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kjotslinkdialog.h" #include "ui_linkdialog.h" #include "kjotsmodel.h" #include #include #include #include KJotsLinkDialog::KJotsLinkDialog(QAbstractItemModel *kjotsModel, QWidget *parent) : QDialog(parent) , ui(new Ui::LinkDialog) , m_descendantsProxyModel(new KDescendantsProxyModel(this)) { ui->setupUi(this); m_descendantsProxyModel->setSourceModel(kjotsModel); m_descendantsProxyModel->setAncestorSeparator(QStringLiteral(" / ")); m_descendantsProxyModel->setDisplayAncestorData(true); ui->hrefCombo->lineEdit()->setPlaceholderText(i18n("Enter link URL, or another note or note book...")); ui->hrefCombo->setModel(m_descendantsProxyModel.get()); // This is required because otherwise QComboBox will catch Enter, insert a new item and clear ui->hrefCombo->setInsertPolicy(QComboBox::NoInsert); ui->hrefCombo->setCurrentIndex(-1); auto *completer = new QCompleter(m_descendantsProxyModel.get(), this); completer->setCaseSensitivity(Qt::CaseInsensitive); ui->hrefCombo->setCompleter(completer); connect(ui->hrefCombo, &QComboBox::editTextChanged, this, &KJotsLinkDialog::slotTextChanged); connect(ui->textEdit, &QLineEdit::textChanged, this, &KJotsLinkDialog::slotTextChanged); slotTextChanged(); } KJotsLinkDialog::~KJotsLinkDialog() = default; void KJotsLinkDialog::slotTextChanged() { const bool ok = !ui->hrefCombo->currentText().trimmed().isEmpty() && !ui->textEdit->text().trimmed().isEmpty(); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok); } void KJotsLinkDialog::setLinkText(const QString &linkText) { ui->textEdit->setText(linkText); if (!linkText.isEmpty()) { ui->textEdit->setFocus(); } } void KJotsLinkDialog::setLinkUrl(const QString &linkUrl) { const QModelIndex idx = KJotsModel::modelIndexForUrl(m_descendantsProxyModel.get(), QUrl(linkUrl)); if (idx.isValid()) { ui->hrefCombo->setCurrentIndex(idx.row()); } else { ui->hrefCombo->setCurrentIndex(-1); ui->hrefCombo->setCurrentText(linkUrl); } } QString KJotsLinkDialog::linkText() const { return ui->textEdit->text().trimmed(); } QString KJotsLinkDialog::linkUrl() const { const int row = ui->hrefCombo->currentIndex(); if (row != -1) { return ui->hrefCombo->model()->index(row, 0).data(KJotsModel::EntityUrlRole).toString(); } else { return ui->hrefCombo->currentText().trimmed(); } } diff --git a/src/kjotslinkdialog.h b/src/kjotslinkdialog.h index 400adc7..99030c3 100644 --- a/src/kjotslinkdialog.h +++ b/src/kjotslinkdialog.h @@ -1,75 +1,76 @@ /* - kjots + This file is a part of KJots. Copyright (C) 2008 Stephen Kelly + 2020 Igor Poboiko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KJOTSLINKDIALOG_H #define KJOTSLINKDIALOG_H #include namespace Ui { class LinkDialog; } class QListView; class KDescendantsProxyModel; class QAbstractItemModel; class QString; class KJotsLinkDialog : public QDialog { Q_OBJECT public: explicit KJotsLinkDialog(QAbstractItemModel *kjotsModel, QWidget *parent = nullptr); ~KJotsLinkDialog(); /** * Returns the link text shown in the dialog * @param linkText The initial text */ void setLinkText(const QString &linkText); /** * Sets the target link url shown in the dialog * @param linkUrl The initial link target url */ void setLinkUrl(const QString &linkUrl); /** * Returns the link text entered by the user. * @return The link text */ QString linkText() const; /** * Returns the target link url entered by the user. * @return The link url */ QString linkUrl() const; private Q_SLOTS: void slotTextChanged(); private: std::unique_ptr ui; std::unique_ptr m_descendantsProxyModel; }; #endif diff --git a/src/kjotsmodel.cpp b/src/kjotsmodel.cpp index f694bd1..648b628 100644 --- a/src/kjotsmodel.cpp +++ b/src/kjotsmodel.cpp @@ -1,401 +1,402 @@ /* This file is part of KJots. Copyright (c) 2009 Stephen Kelly + 2020 Igor Poboiko 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 "kjotsmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "noteshared/notelockattribute.h" Q_DECLARE_METATYPE(QTextDocument *) Q_DECLARE_METATYPE(KPIMTextEdit::ImageList) using namespace Akonadi; KJotsEntity::KJotsEntity(const QModelIndex &index, QObject *parent) : QObject(parent) , m_index(index) { } void KJotsEntity::setIndex(const QModelIndex &index) { m_index = QPersistentModelIndex(index); } QString KJotsEntity::title() const { return m_index.data().toString(); } QString KJotsEntity::content() const { auto *document = m_index.data(KJotsModel::DocumentRole).value(); if (!document) { return QString(); } Grantlee::TextHTMLBuilder builder; Grantlee::MarkupDirector director(&builder); director.processDocument(document); QString result = builder.getResult(); return result; } QString KJotsEntity::plainContent() const { auto *document = m_index.data(KJotsModel::DocumentRole).value(); if (!document) { return QString(); } Grantlee::PlainTextMarkupBuilder builder; Grantlee::MarkupDirector director(&builder); director.processDocument(document); QString result = builder.getResult(); return result; } QString KJotsEntity::url() const { return m_index.data(KJotsModel::EntityUrlRole).toString(); } qint64 KJotsEntity::entityId() const { Item item = m_index.data(EntityTreeModel::ItemRole).value(); if (!item.isValid()) { Collection col = m_index.data(EntityTreeModel::CollectionRole).value(); if (!col.isValid()) { return -1; } return col.id(); } return item.id(); } bool KJotsEntity::isBook() const { Collection col = m_index.data(EntityTreeModel::CollectionRole).value(); if (col.isValid()) { return col.contentMimeTypes().contains(Akonadi::NoteUtils::noteMimeType()); } return false; } bool KJotsEntity::isPage() const { Item item = m_index.data(EntityTreeModel::ItemRole).value(); if (item.isValid()) { return item.hasPayload(); } return false; } QVariantList KJotsEntity::entities() const { const QAbstractItemModel *model = m_index.model(); QVariantList list; int row = 0; const int column = 0; QModelIndex childIndex = model->index(row++, column, m_index); while (childIndex.isValid()) { auto *obj = new KJotsEntity(childIndex); list << QVariant::fromValue(obj); childIndex = model->index(row++, column, m_index); } return list; } QVariantList KJotsEntity::breadcrumbs() const { QVariantList list; QModelIndex parent = m_index.parent(); while (parent.isValid()) { QObject *obj = new KJotsEntity(parent); list << QVariant::fromValue(obj); parent = parent.parent(); } return list; } KJotsModel::KJotsModel(ChangeRecorder *monitor, QObject *parent) : EntityTreeModel(monitor, parent) { } KJotsModel::~KJotsModel() { qDeleteAll(m_documents); } bool KJotsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole) { Item item = index.data(ItemRole).value(); if (!item.isValid()) { Collection col = index.data(CollectionRole).value(); col.setName(value.toString()); if (col.hasAttribute()) { auto *eda = col.attribute(); eda->setDisplayName(value.toString()); } return EntityTreeModel::setData(index, QVariant::fromValue(col), CollectionRole); } NoteUtils::NoteMessageWrapper note(item.payload()); note.setTitle(value.toString()); item.setPayload(note.message()); if (item.hasAttribute()) { auto *displayAttribute = item.attribute(); displayAttribute->setDisplayName(value.toString()); } return EntityTreeModel::setData(index, QVariant::fromValue(item), ItemRole); } if (role == KJotsModel::DocumentRole) { Item item = updateItem(index.data(ItemRole).value(), value.value()); return EntityTreeModel::setData(index, QVariant::fromValue(item), ItemRole); } return EntityTreeModel::setData(index, value, role); } QVariant KJotsModel::data(const QModelIndex &index, int role) const { if (GrantleeObjectRole == role) { auto *obj = new KJotsEntity(index); obj->setIndex(index); return QVariant::fromValue(obj); } if (role == KJotsModel::DocumentRole) { const Item item = index.data(ItemRole).value(); Item::Id itemId = item.id(); if (m_documents.contains(itemId)) { return QVariant::fromValue(m_documents.value(itemId)); } if (!item.hasPayload()) { return QVariant(); } NoteUtils::NoteMessageWrapper note(item.payload()); const QString doc = note.text(); auto *document = new QTextDocument(); if (note.textFormat() == Qt::RichText || doc.startsWith(u"")) { document->setHtml(doc); } else { document->setPlainText(doc); } document->setModified(false); // Loading embedded images const QVector attachments = note.attachments(); for (const auto &attachment : attachments) { if (attachment.mimetype() == QStringLiteral("image/png") && !attachment.contentID().isEmpty()) { QImage img = QImage::fromData(attachment.data(), "PNG"); document->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("cid:")+attachment.contentID()), img); } } m_documents.insert(itemId, document); return QVariant::fromValue(document); } if (role == Qt::DecorationRole && index.column() == Title) { const Item item = index.data(ItemRole).value(); if (item.isValid() && item.hasAttribute()) { return QIcon::fromTheme(QStringLiteral("emblem-locked")); } const Collection col = index.data(CollectionRole).value(); if (col.isValid() && col.hasAttribute()) { return QIcon::fromTheme(QStringLiteral("emblem-locked")); } } return EntityTreeModel::data(index, role); } QVariant KJotsModel::entityData(const Akonadi::Item &item, int column, int role) const { if (item.hasPayload()) { auto message = item.payload(); NoteUtils::NoteMessageWrapper note(message); if (role == Qt::DisplayRole) { switch (column) { case Title: return note.title(); case ModificationTime: return KFormat().formatRelativeDateTime(note.lastModifiedDate(), QLocale::ShortFormat); case CreationTime: return KFormat().formatRelativeDateTime(note.creationDate(), QLocale::ShortFormat); case Size: return KFormat().formatByteSize(message->storageSize()); } } else if (role == Qt::EditRole) { switch (column) { case Title: return note.title(); case ModificationTime: return note.lastModifiedDate(); case CreationTime: return note.creationDate(); case Size: return message->size(); } } } return EntityTreeModel::entityData(item, column, role); } QVariant KJotsModel::entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) { return EntityTreeModel:: entityHeaderData(section, orientation, role, headerGroup); } if (headerGroup == EntityTreeModel::CollectionTreeHeaders) { return i18nc("@title:column", "Name"); } else if (headerGroup == EntityTreeModel::ItemListHeaders) { switch (section) { case Title: return i18nc("@title:column title of a note", "Title"); case CreationTime: return i18nc("@title:column creation date and time of a note", "Created"); case ModificationTime: return i18nc("@title:column last modification date and time of a note", "Modified"); case Size: return i18nc("@title:column size of a note", "Size"); } } return EntityTreeModel::entityHeaderData(section, orientation, role, headerGroup); } int KJotsModel::entityColumnCount(HeaderGroup headerGroup) const { if (headerGroup == EntityTreeModel::CollectionTreeHeaders) { return 1; } else if (headerGroup == EntityTreeModel::ItemListHeaders) { return 4; } else { return EntityTreeModel::entityColumnCount(headerGroup); } } QModelIndex KJotsModel::modelIndexForUrl(const QAbstractItemModel *model, const QUrl &url) { if (url.scheme() != QStringLiteral("akonadi")) { return {}; } const auto item = Item::fromUrl(url); const auto col = Collection::fromUrl(url); if (item.isValid()) { const QModelIndexList idxs = EntityTreeModel::modelIndexesForItem(model, item); if (!idxs.isEmpty()) { return idxs.first(); } } else if (col.isValid()) { return EntityTreeModel::modelIndexForCollection(model, col); } return {}; } Item KJotsModel::updateItem(const Item &item, QTextDocument *document) { if (!item.hasPayload()) { return {}; } NoteUtils::NoteMessageWrapper note(item.payload()); // Saving embedded images const auto images = document->property("images").value(); QVector &attachments = note.attachments(); attachments.clear(); attachments.reserve(images.count()); std::transform(images.cbegin(), images.cend(), std::back_inserter(attachments), [](const QSharedPointer &img) { NoteUtils::Attachment attachment(img->image, QStringLiteral("image/png")); attachment.setDataBase64Encoded(true); attachment.setContentID(img->contentID); return attachment; }); // Setting text bool isRichText = KPIMTextEdit::TextUtils::containsFormatting(document); if (isRichText) { const QByteArray html = KPIMTextEdit::RichTextComposerImages::imageNamesToContentIds( document->toHtml().toUtf8(), images); note.setText( QString::fromUtf8(html), Qt::RichText ); } else { note.setText( document->toPlainText(), Qt::PlainText ); } note.setLastModifiedDate(QDateTime::currentDateTime()); Item newItem = item; newItem.setPayload(note.message()); return newItem; } QString KJotsModel::itemPath(const QModelIndex &index, const QString &sep) { QStringList path; QModelIndex curIndex = index; while (curIndex.isValid()) { path.prepend(curIndex.data().toString()); curIndex = curIndex.parent(); } return path.join(sep); } QModelIndex KJotsModel::etmIndex(const QModelIndex &index) { QModelIndex result = index; const QAbstractProxyModel *proxy = qobject_cast(index.model()); while (proxy) { result = proxy->mapToSource(result); proxy = qobject_cast(result.model()); } return result; } diff --git a/src/kjotsmodel.h b/src/kjotsmodel.h index 03afb39..da3d1dc 100644 --- a/src/kjotsmodel.h +++ b/src/kjotsmodel.h @@ -1,131 +1,132 @@ /* This file is part of KJots. Copyright (c) 2009 Stephen Kelly + 2020 Igor Poboiko 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 KJOTSMODEL_H #define KJOTSMODEL_H #include class QTextDocument; namespace Akonadi { class ChangeRecorder; } /** * A wrapper QObject making some book and page properties available to Grantlee. */ class KJotsEntity : public QObject { Q_OBJECT Q_PROPERTY(QString title READ title) Q_PROPERTY(QString content READ content) Q_PROPERTY(QString plainContent READ plainContent) Q_PROPERTY(QString url READ url) Q_PROPERTY(qint64 entityId READ entityId) Q_PROPERTY(bool isBook READ isBook) Q_PROPERTY(bool isPage READ isPage) Q_PROPERTY(QVariantList entities READ entities) Q_PROPERTY(QVariantList breadcrumbs READ breadcrumbs) public: explicit KJotsEntity(const QModelIndex &index, QObject *parent = nullptr); void setIndex(const QModelIndex &index); bool isBook() const; bool isPage() const; QString title() const; QString content() const; QString plainContent() const; QString url() const; qint64 entityId() const; QVariantList entities() const; QVariantList breadcrumbs() const; private: QPersistentModelIndex m_index; }; class KJotsModel : public Akonadi::EntityTreeModel { Q_OBJECT public: explicit KJotsModel(Akonadi::ChangeRecorder *monitor, QObject *parent = nullptr); ~KJotsModel(); enum KJotsRoles { GrantleeObjectRole = EntityTreeModel::UserRole, DocumentRole }; enum Column { // Title of a note Title, // Modification date / time of a note ModificationTime, // Creation date / time of a note CreationTime, // Size of a note Size }; // We don't reimplement the Collection overload. using EntityTreeModel::entityData; QVariant entityData(const Akonadi::Item &item, int column, int role = Qt::DisplayRole) const override; int entityColumnCount(HeaderGroup headerGroup) const override; QVariant entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; static QModelIndex modelIndexForUrl(const QAbstractItemModel *model, const QUrl &url); /** * Sets the content of @p item to @p document */ static Akonadi::Item updateItem(const Akonadi::Item &item, QTextDocument *document); /** * A helper function which returns a full "path" to the @p item (e.g. "Resource / Notebook / Note") * using @p sep as a separator. If multiple items are selected, returns "Multiple selection" */ static QString itemPath(const QModelIndex &index, const QString &sep = QStringLiteral(" / ")); /** * A helper function which returns an index belonging to the ETM model through bunch of proxy models */ static QModelIndex etmIndex(const QModelIndex &index); private: QHash m_colors; mutable QHash m_documents; }; #endif diff --git a/src/kjotswidget.cpp b/src/kjotswidget.cpp index 150d73d..ce9cc09 100644 --- a/src/kjotswidget.cpp +++ b/src/kjotswidget.cpp @@ -1,777 +1,778 @@ /* This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee - Copyright (C) 2007-2009 Stephen Kelly + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2007-2009 Stephen Kelly + 2020 Igor Poboiko 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 "kjotswidget.h" // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Akonadi #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Grantlee #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KMime #include // KJots #include "kjotsbookmarks.h" #include "kjotsmodel.h" #include "kjotsedit.h" #include "kjotsconfigdlg.h" #include "KJotsSettings.h" #include "kjotsbrowser.h" #include "noteshared/notelockattribute.h" #include "noteshared/standardnoteactionmanager.h" #include using namespace Akonadi; using namespace Grantlee; KJotsWidget::KJotsWidget(QWidget *parent, KXMLGUIClient *xmlGuiClient, Qt::WindowFlags f) : QWidget(parent, f) , m_xmlGuiClient(xmlGuiClient) { ControlGui::widgetNeedsAkonadi(this); // Grantlee m_loader = QSharedPointer(new FileSystemTemplateLoader()); m_loader->setTemplateDirs(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kjots/themes"), QStandardPaths::LocateDirectory)); m_loader->setTheme(QStringLiteral("default")); m_templateEngine = new Engine(this); m_templateEngine->addTemplateLoader(m_loader); // GUI & Actions setupGui(); setupActions(); // Models ItemFetchScope scope; scope.fetchFullPayload(true); // Need to have full item when adding it to the internal data structure scope.fetchAttribute< EntityDisplayAttribute >(); scope.fetchAttribute< NoteShared::NoteLockAttribute >(); auto *monitor = new ChangeRecorder(this); monitor->fetchCollection(true); monitor->setItemFetchScope(scope); monitor->setCollectionMonitored(Collection::root()); monitor->setMimeTypeMonitored(NoteUtils::noteMimeType()); m_kjotsModel = new KJotsModel(monitor, this); m_browserWidget->browser()->setModel(m_kjotsModel); m_collectionModel = new EntityMimeTypeFilterModel(this); m_collectionModel->setSourceModel(m_kjotsModel); m_collectionModel->addMimeTypeInclusionFilter(Collection::mimeType()); m_collectionModel->setHeaderGroup(EntityTreeModel::CollectionTreeHeaders); m_collectionModel->setDynamicSortFilter(true); m_collectionModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_orderProxy = new EntityOrderProxyModel(this); m_orderProxy->setSourceModel(m_collectionModel); KConfigGroup cfg(KSharedConfig::openConfig(), "KJotsEntityOrder"); m_orderProxy->setOrderConfig(cfg); m_collectionView->setModel(m_orderProxy); m_collectionSelectionProxyModel = new KSelectionProxyModel(m_collectionView->selectionModel(), this); m_collectionSelectionProxyModel->setSourceModel(m_kjotsModel); m_collectionSelectionProxyModel->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection); m_itemModel = new EntityMimeTypeFilterModel(this); m_itemModel->setSourceModel(m_collectionSelectionProxyModel); m_itemModel->addMimeTypeExclusionFilter(Collection::mimeType()); m_itemModel->setHeaderGroup(EntityTreeModel::ItemListHeaders); m_itemModel->setSortRole(Qt::EditRole); m_itemView->setModel(m_itemModel); m_actionManager->setCollectionSelectionModel(m_collectionView->selectionModel()); m_actionManager->setItemSelectionModel(m_itemView->selectionModel()); connect(m_kjotsModel, &EntityTreeModel::modelAboutToBeReset, this, &KJotsWidget::saveState); connect(m_kjotsModel, &EntityTreeModel::modelReset, this, &KJotsWidget::restoreState); // TODO: handle dataChanged properly, i.e. if item was changed from outside connect(m_collectionView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KJotsWidget::renderSelection); connect(m_collectionView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KJotsWidget::updateMenu); connect(m_collectionView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KJotsWidget::updateCaption); connect(m_collectionView->model(), &QAbstractItemModel::dataChanged, this, &KJotsWidget::renderSelection); connect(m_collectionView->model(), &QAbstractItemModel::dataChanged, this, &KJotsWidget::updateCaption); connect(m_itemView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KJotsWidget::renderSelection); connect(m_itemView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KJotsWidget::updateMenu); connect(m_itemView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KJotsWidget::updateCaption); connect(m_itemView->model(), &QAbstractItemModel::dataChanged, this, &KJotsWidget::renderSelection); connect(m_itemView->model(), &QAbstractItemModel::dataChanged, this, &KJotsWidget::updateCaption); connect(m_editor, &KJotsEdit::documentModified, this, &KJotsWidget::updateCaption); QTimer::singleShot(0, this, &KJotsWidget::delayedInitialization); // Autosave timer m_autosaveTimer = new QTimer(this); updateConfiguration(); connect(m_autosaveTimer, &QTimer::timeout, m_editor, &KJotsEdit::savePage); connect(m_collectionView->selectionModel(), &QItemSelectionModel::selectionChanged, m_autosaveTimer, qOverload<>(&QTimer::start)); restoreState(); QDBusConnection::sessionBus().registerObject(QStringLiteral("/KJotsWidget"), this, QDBusConnection::ExportScriptableContents); } KJotsWidget::~KJotsWidget() { saveState(); } void KJotsWidget::setupGui() { // Main horizontal layout auto *layout = new QHBoxLayout(this); layout->setMargin(0); // Splitter between (collection view) and (item view + editor) m_splitter1 = new QSplitter(this); m_splitter1->setObjectName(QStringLiteral("CollectionSplitter")); m_splitter1->setStretchFactor(1, 1); layout->addWidget(m_splitter1); // Collection view m_collectionView = new EntityTreeView(m_xmlGuiClient, m_splitter1); m_collectionView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_collectionView->setEditTriggers(QAbstractItemView::DoubleClicked); m_collectionView->setManualSortingActive(true); m_collectionView->header()->setDefaultAlignment(Qt::AlignCenter); // Splitter between item view and editor m_splitter2 = new QSplitter(m_splitter1); m_splitter2->setObjectName(QStringLiteral("EditorSplitter")); // Item view m_itemView = new EntityTreeView(m_xmlGuiClient, m_splitter2); m_itemView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_itemView->setSelectionBehavior(QAbstractItemView::SelectRows); m_itemView->setEditTriggers(QAbstractItemView::DoubleClicked); m_itemView->setRootIsDecorated(false); m_itemView->header()->setDefaultAlignment(Qt::AlignCenter); // Stacked widget containing editor & browser m_stackedWidget = new QStackedWidget(m_splitter2); // Editor m_editor = new KJotsEdit(m_stackedWidget, m_xmlGuiClient->actionCollection()); m_editorWidget = new KPIMTextEdit::RichTextEditorWidget(m_editor, m_stackedWidget); m_editor->setParent(m_editorWidget); m_stackedWidget->addWidget(m_editorWidget); connect(m_editor, &KJotsEdit::linkClicked, this, &KJotsWidget::openLink); // Browser m_browserWidget = new KJotsBrowserWidget(std::make_unique(m_xmlGuiClient->actionCollection()), m_stackedWidget); m_stackedWidget->addWidget(m_browserWidget); m_stackedWidget->setCurrentWidget(m_browserWidget); connect(m_browserWidget->browser(), &KJotsBrowser::linkClicked, this, &KJotsWidget::openLink); // Make sure the editor gets focus again after naming a new book/page. connect(m_collectionView->itemDelegate(), &QItemDelegate::closeEditor, this, [this](){ activeEditor()->setFocus(); }); } void KJotsWidget::restoreState() { { auto *saver = new ETMViewStateSaver; saver->setView(m_collectionView); KConfigGroup cfg(KSharedConfig::openConfig(), "CollectionViewState"); saver->restoreState(cfg); } { auto *saver = new ETMViewStateSaver; saver->setView(m_itemView); KConfigGroup cfg(KSharedConfig::openConfig(), "ItemViewState"); saver->restoreState(cfg); } } void KJotsWidget::saveState() { { ETMViewStateSaver saver; saver.setView(m_collectionView); KConfigGroup cfg(KSharedConfig::openConfig(), "CollectionViewState"); saver.saveState(cfg); cfg.sync(); } { ETMViewStateSaver saver; saver.setView(m_itemView); KConfigGroup cfg(KSharedConfig::openConfig(), "ItemViewState"); saver.saveState(cfg); cfg.sync(); } } void KJotsWidget::saveSplitterStates() const { const QString groupName = QStringLiteral("UiState_MainWidgetSplitter_%1").arg(KJotsSettings::viewMode()); KConfigGroup group(KSharedConfig::openConfig(), groupName); KPIM::UiStateSaver::saveState(m_splitter1, group); KPIM::UiStateSaver::saveState(m_splitter2, group); } void KJotsWidget::restoreSplitterStates() { const QString groupName = QStringLiteral("UiState_MainWidgetSplitter_%1").arg(KJotsSettings::viewMode()); KConfigGroup group(KSharedConfig::openConfig(), groupName); KPIM::UiStateSaver::restoreState(m_splitter1, group); KPIM::UiStateSaver::restoreState(m_splitter2, group); } void KJotsWidget::setViewMode(int mode) { const int newMode = (mode == 0) ? KJotsSettings::viewMode() : mode; m_splitter2->setOrientation(newMode == 1 ? Qt::Vertical : Qt::Horizontal); if (mode != 0) { KJotsSettings::setViewMode(mode); saveSplitterStates(); } restoreSplitterStates(); m_viewModeGroup->actions().at(newMode-1)->setChecked(true); } void KJotsWidget::setupActions() { KActionCollection *actionCollection = m_xmlGuiClient->actionCollection(); // Default Akonadi Notes actions m_actionManager = new StandardNoteActionManager(actionCollection, this); m_actionManager->createAllActions(); actionCollection->setDefaultShortcut(m_actionManager->action(StandardActionManager::CreateCollection), QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N)); QAction *action; // Standard actions KStandardAction::preferences(this, &KJotsWidget::configure, actionCollection); KStandardAction::save( m_editor, &KJotsEdit::savePage, actionCollection); action = KStandardAction::next(this, [this](){ m_itemView->selectionModel()->select(previousNextEntity(m_itemView, +1), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); }, actionCollection); actionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_PageDown)); connect(this, &KJotsWidget::canGoNextPageChanged, action, &QAction::setEnabled); action = KStandardAction::prior(this, [this](){ m_itemView->selectionModel()->select(previousNextEntity(m_itemView, -1), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); }, actionCollection); actionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_PageUp)); connect(this, &KJotsWidget::canGoPreviousPageChanged, action, &QAction::setEnabled); KStandardAction::renameFile(this, [this](){ EntityTreeView *activeView; if (m_collectionView->hasFocus()) { activeView = m_collectionView; } else { activeView = m_itemView; } const QModelIndexList rows = activeView->selectionModel()->selectedRows(); if (rows.size() != 1) { return; } activeView->edit(rows.first()); }, actionCollection); action = KStandardAction::deleteFile(this, [this](){ if (m_collectionView->hasFocus()) { m_actionManager->action(StandardActionManager::DeleteCollections)->trigger(); } else { m_actionManager->action(StandardActionManager::DeleteItems)->trigger(); } }, actionCollection); // Default Shift+Delete is ambiguous and used both for cut and delete actionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Delete)); action = KStandardAction::cut(m_editor, &KJotsEdit::cut, actionCollection); connect(m_editor, &KJotsEdit::copyAvailable, action, &QAction::setEnabled); action->setEnabled(false); action = KStandardAction::copy(this, [this](){ activeEditor()->copy(); }, actionCollection); connect(m_editor, &KJotsEdit::copyAvailable, action, &QAction::setEnabled); connect(m_browserWidget->browser(), &KJotsBrowser::copyAvailable, action, &QAction::setEnabled); action->setEnabled(false); KStandardAction::paste(m_editor, &KJotsEdit::paste, actionCollection); KStandardAction::undo(m_editor, &KJotsEdit::undo, actionCollection); KStandardAction::redo(m_editor, &KJotsEdit::redo, actionCollection); KStandardAction::selectAll(m_editor, &KJotsEdit::selectAll, actionCollection); KStandardAction::find(this, [this](){ if (m_editorWidget->isVisible()) { m_editorWidget->slotFind(); } else { m_browserWidget->slotFind(); } }, actionCollection); KStandardAction::findNext(this, [this](){ if (m_editorWidget->isVisible()) { m_editorWidget->slotFindNext(); } else { m_browserWidget->slotFindNext(); } }, actionCollection); KStandardAction::replace(this, [this](){ if (m_editorWidget->isVisible()) { m_editorWidget->slotReplace(); } }, actionCollection); KStandardAction::print(this, &KJotsWidget::printSelection, actionCollection); KStandardAction::printPreview(this, &KJotsWidget::printPreviewSelection, actionCollection); // Bookmarks actions auto *bookmarkMenu = actionCollection->add(QStringLiteral("bookmarks")); bookmarkMenu->setText(i18n("&Bookmarks")); auto *bookmarks = new KJotsBookmarks(m_collectionView->selectionModel(), this); connect(bookmarks, &KJotsBookmarks::openLink, this, &KJotsWidget::openLink); auto *bmm = new KBookmarkMenu( KBookmarkManager::managerForFile( QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kjots/bookmarks.xml"), QStringLiteral("kjots")), bookmarks, bookmarkMenu->menu()); // "Add bookmark" and "make text bold" actions have conflicting shortcuts (ctrl + b) // Make add_bookmark use ctrl+shift+b to resolve that. action = bmm->addBookmarkAction(); actionCollection->addAction(QStringLiteral("add_bookmark"), action); actionCollection->setDefaultShortcut(action, Qt::CTRL | Qt::SHIFT | Qt::Key_B); actionCollection->addAction(QStringLiteral("edit_bookmark"), bmm->editBookmarksAction()); actionCollection->addAction(QStringLiteral("add_bookmarks_list"), bmm->bookmarkTabsAsFolderAction()); // Export actions auto *exportMenu = actionCollection->add(QStringLiteral("save_to")); exportMenu->setText(i18n("Export")); exportMenu->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); action = actionCollection->addAction(QStringLiteral("save_to_ascii")); action->setText(i18n("To Text File...")); action->setIcon(QIcon::fromTheme(QStringLiteral("text-plain"))); connect(action, &QAction::triggered, this, [this](){ exportSelection(QStringLiteral("plain_text"), QStringLiteral("template.txt")); }); exportMenu->menu()->addAction(action); action = actionCollection->addAction(QStringLiteral("save_to_html")); action->setText(i18n("To HTML File...")); action->setIcon(QIcon::fromTheme(QStringLiteral("text-html"))); connect(action, &QAction::triggered, this, [this](){ exportSelection(QStringLiteral("default"), QStringLiteral("template.html")); }); exportMenu->menu()->addAction(action); // Move actions action = actionCollection->addAction(QStringLiteral("go_next_book")); action->setText(i18n("Next Book")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); actionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_PageDown)); connect(action, &QAction::triggered, this, [this](){ const QModelIndex idx = previousNextEntity(m_collectionView, +1); m_collectionView->selectionModel()->select(idx, QItemSelectionModel::SelectCurrent); m_collectionView->expand(idx); }); connect(this, &KJotsWidget::canGoNextBookChanged, action, &QAction::setEnabled); action = actionCollection->addAction(QStringLiteral("go_prev_book")); action->setText(i18n("Previous Book")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); actionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_PageUp)); connect(action, &QAction::triggered, this, [this](){ const QModelIndex idx = previousNextEntity(m_collectionView, -1); m_collectionView->selectionModel()->select(idx, QItemSelectionModel::SelectCurrent); m_collectionView->expand(idx); }); connect(this, &KJotsWidget::canGoPreviousBookChanged, action, &QAction::setEnabled); // View mode actions m_viewModeGroup = new QActionGroup(this); action = new QAction(i18nc("@action:inmenu", "Two Columns"), m_viewModeGroup); action->setCheckable(true); action->setData(1); actionCollection->addAction(QStringLiteral("view_mode_two_columns"), action); action = new QAction(i18nc("@action:inmenu", "Three Columns"), m_viewModeGroup); action->setCheckable(true); action->setData(2); actionCollection->addAction(QStringLiteral("view_mode_three_columns"), action); connect(m_viewModeGroup, &QActionGroup::triggered, this, [this](QAction *action){ setViewMode(action->data().toInt()); }); } void KJotsWidget::delayedInitialization() { // anySelectionActions are available when at least something is selected (i.e. editor/browser are not empty // editorActions are only available when there is a single selection, i.e. when the editor is visible KActionCollection *actionCollection = m_xmlGuiClient->actionCollection(); // Actions for a single item selection. anySelectionActions = { actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Find))), actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Print))), actionCollection->action(QStringLiteral("save_to")) }; // Actions that are used only when a note is selected. editorActions = { actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Cut))), actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Paste))), actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Replace))), actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save))) }; updateMenu(); // Load view mode and splitters setViewMode(0); } inline QTextEdit *KJotsWidget::activeEditor() { if (m_browserWidget->isVisible()) { return m_browserWidget->browser(); } else { return m_editor; } } void KJotsWidget::updateMenu() { const int collectionsSelected = m_collectionView->selectionModel()->selectedRows().count(); const int itemsSelected = m_itemView->selectionModel()->selectedRows().count(); const int selectionSize = itemsSelected + collectionsSelected; // Actions available only when editor is shown m_editor->setEnableActions(itemsSelected == 1); for (QAction *action : qAsConst(editorActions)) { action->setEnabled(itemsSelected == 1); } // Rename is available only when single something is selected m_xmlGuiClient->actionCollection() ->action(QString::fromLatin1(KStandardAction::name(KStandardAction::RenameFile))) ->setEnabled((itemsSelected == 1) || (m_collectionView->hasFocus() && collectionsSelected == 1)); // Actions available when at least something is shown for (QAction *action : qAsConst(anySelectionActions)) { action->setEnabled(selectionSize >= 1); } } void KJotsWidget::configure() { if (KConfigDialog::showDialog(QStringLiteral("kjotssettings"))) { return; } auto* dialog = new KConfigDialog(this, QStringLiteral("kjotssettings"), KJotsSettings::self()); dialog->addPage(new KJotsConfigMisc(dialog), i18nc("@title:window config dialog page", "Misc"), QStringLiteral("preferences-other")); connect(dialog, &KConfigDialog::settingsChanged, this, &KJotsWidget::updateConfiguration); dialog->show(); } void KJotsWidget::updateConfiguration() { if (KJotsSettings::autoSave()) { m_autosaveTimer->setInterval(KJotsSettings::autoSaveInterval() * 1000 * 60); m_autosaveTimer->start(); } else { m_autosaveTimer->stop(); } } QString KJotsWidget::renderSelectionTo(const QString &theme, const QString &templ) { QList objectList; const QModelIndexList selectedItems = m_itemView->selectionModel()->selectedRows(); if (selectedItems.count() > 0) { objectList.reserve(selectedItems.size()); std::transform(selectedItems.cbegin(), selectedItems.cend(), std::back_inserter(objectList), [](const QModelIndex &idx){ return idx.data(KJotsModel::GrantleeObjectRole); }); } else { const QModelIndexList selectedCollections = m_collectionView->selectionModel()->selectedRows(); objectList.reserve(selectedCollections.size()); std::transform(selectedCollections.cbegin(), selectedCollections.cend(), std::back_inserter(objectList), [](const QModelIndex &idx){ return idx.data(KJotsModel::GrantleeObjectRole); }); } QHash hash = {{QStringLiteral("entities"), objectList}, {QStringLiteral("i18n_TABLE_OF_CONTENTS"), i18nc("Header for 'Table of contents' section of rendered output", "Table of contents")}}; Context c(hash); const QString currentTheme = m_loader->themeName(); m_loader->setTheme(theme); Template t = m_templateEngine->loadByName(templ); const QString result = t->render(&c); m_loader->setTheme(currentTheme); return result; } QString KJotsWidget::renderSelectionToHtml() { return renderSelectionTo(QStringLiteral("default"), QStringLiteral("template.html")); } void KJotsWidget::exportSelection(const QString &theme, const QString &templ) { // TODO: dialog captions & etc QString filename = QFileDialog::getSaveFileName(); if (filename.isEmpty()) { return; } QFile exportFile(filename); if (!exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { KMessageBox::error(this, i18n("Could not open \"%1\" for writing", filename)); return; } exportFile.write(renderSelectionTo(theme, templ).toUtf8()); exportFile.close(); } std::unique_ptr KJotsWidget::setupPrinter(QPrinter::PrinterMode mode) { auto printer = std::make_unique(mode); printer->setDocName(QStringLiteral("KJots_Print")); printer->setCreator(QStringLiteral("KJots")); if (!activeEditor()->textCursor().selection().isEmpty()) { printer->setPrintRange(QPrinter::Selection); } return printer; } void KJotsWidget::printPreviewSelection() { auto printer = setupPrinter(QPrinter::ScreenResolution); QPrintPreviewDialog previewdlg(printer.get(), this); connect(&previewdlg, &QPrintPreviewDialog::paintRequested, this, &KJotsWidget::print); previewdlg.exec(); } void KJotsWidget::printSelection() { auto printer = setupPrinter(QPrinter::HighResolution); QPrintDialog printDialog(printer.get(), this); if (printDialog.exec() == QDialog::Accepted) { print(printer.get()); } } void KJotsWidget::print(QPrinter *printer) { QTextDocument printDocument; if (printer->printRange() == QPrinter::Selection) { printDocument.setHtml(activeEditor()->textCursor().selection().toHtml()); } else { QString currentTheme = m_loader->themeName(); m_loader->setTheme(QStringLiteral("default")); printDocument.setHtml(renderSelectionToHtml()); m_loader->setTheme(currentTheme); } printDocument.print(printer); } QModelIndex KJotsWidget::previousNextEntity(QTreeView *view, int step) { const QModelIndexList selection = view->selectionModel()->selectedRows(); if (selection.size() == 0) { return step > 0 ? view->model()->index(0, 0) : view->model()->index(view->model()->rowCount()-1, 0); } if (selection.size() != 1) { return {}; } return step > 0 ? view->indexBelow(selection.first()) : view->indexAbove(selection.first()); } void KJotsWidget::renderSelection() { Q_EMIT canGoNextBookChanged(previousNextEntity(m_collectionView, +1).isValid()); Q_EMIT canGoNextPageChanged(previousNextEntity(m_itemView, +1).isValid()); Q_EMIT canGoPreviousBookChanged(previousNextEntity(m_collectionView, -1).isValid()); Q_EMIT canGoPreviousPageChanged(previousNextEntity(m_itemView, -1).isValid()); const QModelIndexList selectedItems = m_itemView->selectionModel()->selectedRows(); // If the selection is a single note, present it for editing... if (selectedItems.count() == 1) { if (m_editor->setModelIndex(selectedItems.first())) { m_stackedWidget->setCurrentWidget(m_editorWidget); return; } // If something went wrong, we show user the browser } // ... Otherwise, render the selection read-only. m_browserWidget->browser()->setHtml(renderSelectionToHtml()); m_stackedWidget->setCurrentWidget(m_browserWidget); } void KJotsWidget::updateCaption() { QString caption; const QModelIndexList itemSelection = m_itemView->selectionModel()->selectedRows(); const QModelIndexList collectionSelection = m_collectionView->selectionModel()->selectedRows(); if (itemSelection.size() == 1) { caption = KJotsModel::itemPath(KJotsModel::etmIndex(itemSelection.first())); if (m_editor->modified()) { caption.append(QStringLiteral(" *")); } } else if (itemSelection.size() == 0 && collectionSelection.size() == 1) { caption = KJotsModel::itemPath(collectionSelection.first()); } else if (itemSelection.size() > 1 || collectionSelection.size() > 1) { caption = i18nc("@title:window", "Multiple selection"); } Q_EMIT captionChanged(caption); } bool KJotsWidget::queryClose() { // Saving the current note // We cannot use async interface (i.e. ETM) here // because we need to abort the close if something went wrong const QModelIndexList selection = m_itemView->selectionModel()->selectedRows(); if ((selection.size() == 1) && (m_editor->document()->isModified())) { QModelIndex idx = selection.first(); m_editor->prepareDocumentForSaving(); auto job = new ItemModifyJob(KJotsModel::updateItem(idx.data(EntityTreeModel::ItemRole).value(), m_editor->document())); if (!job->exec()) { int res = KMessageBox::warningContinueCancelDetailed(this, i18n("Unable to save the note.\n" "You can save your note to a local file using the \"File - Export\" menu,\n" "otherwise you will lose your changes!\n" "Do you want to close anyways?"), i18n("Close Document"), KStandardGuiItem::quit(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify, i18n("Error message:\n" "%1", job->errorString())); if (res == KMessageBox::Cancel) { return false; } } } saveSplitterStates(); KJotsSettings::self()->save(); m_orderProxy->saveOrder(); return true; } void KJotsWidget::openLink(const QUrl &url) { if (url.scheme() == QStringLiteral("akonadi")) { QModelIndex idx = KJotsModel::modelIndexForUrl(m_kjotsModel, url); // Trying to map it to collection view model QModelIndex colIdx = m_collectionModel->mapFromSource(idx); if (colIdx.isValid()) { colIdx = m_orderProxy->mapFromSource(colIdx); m_collectionView->selectionModel()->select(colIdx, QItemSelectionModel::SelectCurrent); m_itemView->selectionModel()->clearSelection(); } else { // Selecting parent collection QModelIndex parentCollectionIdx = EntityTreeModel::modelIndexForCollection(m_collectionView->model(), idx.data(EntityTreeModel::ParentCollectionRole).value()); m_collectionView->selectionModel()->select(parentCollectionIdx, QItemSelectionModel::SelectCurrent); // Mapping idx to item view model idx = m_collectionSelectionProxyModel->mapFromSource(idx); idx = m_itemModel->mapFromSource(idx); m_itemView->selectionModel()->select(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); } } else { new KRun(url, this); } } diff --git a/src/kjotswidget.h b/src/kjotswidget.h index c93f5ac..2345613 100644 --- a/src/kjotswidget.h +++ b/src/kjotswidget.h @@ -1,166 +1,167 @@ /* This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee - Copyright (C) 2007-2009 Stephen Kelly + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee + 2007-2009 Stephen Kelly + 2020 Igor Poboiko 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 KJOTSWIDGET_H #define KJOTSWIDGET_H #include #include #include #include #include class QActionGroup; class QCheckBox; class QTextEdit; class QTextCharFormat; class QSplitter; class QStackedWidget; class QModelIndex; class QTreeView; class KActionMenu; class KFindDialog; class KJob; class KReplaceDialog; class KSelectionProxyModel; class KJotsBrowser; class KJotsBrowserWidget; class KXMLGUIClient; namespace Akonadi { class EntityMimeTypeFilterModel; class EntityOrderProxyModel; class EntityTreeView; class StandardNoteActionManager; } namespace Grantlee { class Engine; } namespace KPIMTextEdit { class RichTextEditorWidget; } class KJotsEdit; class KJotsModel; class KJotsTreeView; class KJotsWidget : public QWidget { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KJotsWidget") public: explicit KJotsWidget(QWidget *parent, KXMLGUIClient *xmlGuiclient, Qt::WindowFlags f = Qt::WindowFlags()); ~KJotsWidget(); QTextEdit *activeEditor(); public Q_SLOTS: void configure(); void updateCaption(); void updateMenu(); Q_SCRIPTABLE bool queryClose(); void setViewMode(int mode); Q_SIGNALS: void canGoNextPageChanged(bool); void canGoPreviousPageChanged(bool); void canGoNextBookChanged(bool); void canGoPreviousBookChanged(bool); void captionChanged(const QString &newCaption); protected: static QModelIndex previousNextEntity(QTreeView *view, int step); void setupGui(); void setupActions(); QString renderSelectionTo(const QString &theme, const QString &templ); QString renderSelectionToHtml(); std::unique_ptr setupPrinter(QPrinter::PrinterMode mode = QPrinter::ScreenResolution); void saveSplitterStates() const; void restoreSplitterStates(); protected Q_SLOTS: /** * Renders contents on either KJotsEdit or KJotsBrowser based on KJotsTreeView selection */ void renderSelection(); void exportSelection(const QString &theme, const QString &templ); void printSelection(); void printPreviewSelection(); void openLink(const QUrl &url); private Q_SLOTS: void delayedInitialization(); void saveState(); void restoreState(); void updateConfiguration(); void print(QPrinter *printer); private: // Grantlee Grantlee::Engine *m_templateEngine = nullptr; QSharedPointer m_loader; // XMLGui && Actions KXMLGUIClient *m_xmlGuiClient = nullptr; Akonadi::StandardNoteActionManager *m_actionManager = nullptr; QSet anySelectionActions, editorActions; QActionGroup *m_viewModeGroup = nullptr; // UI QSplitter *m_splitter1 = nullptr; QSplitter *m_splitter2 = nullptr; QStackedWidget *m_stackedWidget = nullptr; Akonadi::EntityTreeView *m_collectionView = nullptr; Akonadi::EntityTreeView *m_itemView = nullptr; KJotsEdit *m_editor = nullptr; KPIMTextEdit::RichTextEditorWidget *m_editorWidget = nullptr; KJotsBrowserWidget *m_browserWidget = nullptr; // Models KJotsModel *m_kjotsModel = nullptr; Akonadi::EntityMimeTypeFilterModel *m_collectionModel = nullptr; Akonadi::EntityMimeTypeFilterModel *m_itemModel = nullptr; KSelectionProxyModel *m_collectionSelectionProxyModel = nullptr; Akonadi::EntityOrderProxyModel *m_orderProxy = nullptr; QTimer *m_autosaveTimer = nullptr; }; #endif diff --git a/src/kontact_plugin/kjots_plugin.cpp b/src/kontact_plugin/kjots_plugin.cpp index 640ab48..68db22b 100644 --- a/src/kontact_plugin/kjots_plugin.cpp +++ b/src/kontact_plugin/kjots_plugin.cpp @@ -1,102 +1,104 @@ /* + This file is part of KJots. + Copyright (C) 2016 Daniel Vrátil This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "kjots_plugin.h" #include "kjotspart.h" //#include "akregator_options.h" //#include "partinterface.h" #include #include #include #include #include #include #if KONTACTINTERFACE_VERSION < QT_VERSION_CHECK(5, 14, 42) /** Exports Kontact plugin. @param pluginclass the class to instantiate (must derive from KontactInterface::Plugin @param jsonFile filename of the JSON file, generated from a .desktop file */ #define EXPORT_KONTACT_PLUGIN_WITH_JSON( pluginclass, jsonFile ) \ class Instance \ { \ public: \ static QObject *createInstance( QWidget *, QObject *parent, const QVariantList &list ) \ { return new pluginclass( static_cast( parent ), list ); } \ }; \ K_PLUGIN_FACTORY_WITH_JSON( KontactPluginFactory, jsonFile, registerPlugin< pluginclass > \ ( QString(), Instance::createInstance ); ) \ K_EXPORT_PLUGIN_VERSION(KONTACT_PLUGIN_VERSION) #endif EXPORT_KONTACT_PLUGIN_WITH_JSON(KJotsPlugin, "kjotsplugin.json") KJotsPlugin::KJotsPlugin(KontactInterface::Core *core, const QVariantList &/*args*/) : KontactInterface::Plugin(core, core, "kjots") { setComponentName(QStringLiteral("kjots"), i18n("KJots")); mUniqueAppWatcher = new KontactInterface::UniqueAppWatcher( new KontactInterface::UniqueAppHandlerFactory(), this); } bool KJotsPlugin::isRunningStandalone() const { return mUniqueAppWatcher->isRunningStandalone(); } QStringList KJotsPlugin::invisibleToolbarActions() const { return { QStringLiteral("akonadi_note_create"), QStringLiteral("akonadi_collection_create") }; } #if KONTACTINTERFACE_VERSION >= QT_VERSION_CHECK(5, 14, 42) KParts::Part *KJotsPlugin::createPart() { return loadPart(); } #else KParts::ReadOnlyPart *KJotsPlugin::createPart() { return loadPart(); } #endif void KJotsUniqueAppHandler::loadCommandLineOptions(QCommandLineParser *parser) { Q_UNUSED(parser) } int KJotsUniqueAppHandler::activate(const QStringList &args, const QString &workingDir) { // Ensure part is loaded (void)plugin()->part(); return KontactInterface::UniqueAppHandler::activate(args, workingDir); } #include "kjots_plugin.moc" diff --git a/src/main.cpp b/src/main.cpp index fd7923b..61af582 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,65 +1,65 @@ /* - kjots + This file is part of KJots. Copyright (C) 1997 Christoph Neerfeld - Copyright (C) 2002, 2003 Aaron J. Seigo - Copyright (C) 2003 Stanislav Kljuhhin - Copyright (C) 2005-2006 Jaison Lee + 2002, 2003 Aaron J. Seigo + 2003 Stanislav Kljuhhin + 2005-2006 Jaison Lee This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KJotsMain.h" #include "aboutdata.h" #include #include #include #include #include int main(int argc, char **argv) { AboutData aboutData; KontactInterface::PimUniqueApplication app(argc, &argv); KLocalizedString::setApplicationDomain("kjots"); app.setAboutData(aboutData); QCommandLineParser *cmdArgs = app.cmdArgs(); const QStringList args = QApplication::arguments(); cmdArgs->process(args); aboutData.processCommandLine(cmdArgs); if (!KontactInterface::PimUniqueApplication::start(args)) { qWarning() << "kjots is already running!"; exit(0); } auto *jots = new KJotsMain; if (app.isSessionRestored()) { if (KJotsMain::canBeRestored(1)) { jots->restore(1); } } jots->show(); jots->resize(jots->size()); return app.exec(); } /* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/noteshared/notecreatorandselector.cpp b/src/noteshared/notecreatorandselector.cpp index b193882..f537bb9 100644 --- a/src/noteshared/notecreatorandselector.cpp +++ b/src/noteshared/notecreatorandselector.cpp @@ -1,126 +1,128 @@ /* + This file is part of KJots. + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly 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 "notecreatorandselector.h" #include #include #include #include #include #include #include "noteshared_debug.h" using namespace Akonadi; using namespace NoteShared; NoteCreatorAndSelector::NoteCreatorAndSelector(QItemSelectionModel *primaryModel, QItemSelectionModel *secondaryModel, QObject *parent) : QObject(parent), m_primarySelectionModel(primaryModel), m_secondarySelectionModel(secondaryModel == nullptr ? primaryModel : secondaryModel), m_containerCollectionId(-1), m_newNoteId(-1), m_giveupTimer(new QTimer(this)) { // If the note doesn't exist after 5 seconds, give up waiting for it. m_giveupTimer->setInterval(5000); connect(m_giveupTimer, &QTimer::timeout, this, &NoteCreatorAndSelector::deleteLater); } NoteCreatorAndSelector::~NoteCreatorAndSelector() = default; void NoteCreatorAndSelector::createNote(const Akonadi::Collection &containerCollection) { m_containerCollectionId = containerCollection.id(); if (m_primarySelectionModel == m_secondarySelectionModel) { doCreateNote(); } else { m_giveupTimer->start(); connect(m_primarySelectionModel->model(), &QAbstractItemModel::rowsInserted, this, &NoteCreatorAndSelector::trySelectCollection); trySelectCollection(); } } void NoteCreatorAndSelector::trySelectCollection() { QModelIndex idx = EntityTreeModel::modelIndexForCollection(m_primarySelectionModel->model(), Collection(m_containerCollectionId)); if (!idx.isValid()) { return; } m_giveupTimer->stop(); m_primarySelectionModel->select(idx, QItemSelectionModel::SelectCurrent); disconnect(m_primarySelectionModel->model(), &QAbstractItemModel::rowsInserted, this, &NoteCreatorAndSelector::trySelectCollection); doCreateNote(); } void NoteCreatorAndSelector::doCreateNote() { Item newItem; newItem.setMimeType(Akonadi::NoteUtils::noteMimeType()); Akonadi::NoteUtils::NoteMessageWrapper note(KMime::Message::Ptr(new KMime::Message)); note.setFrom(QStringLiteral("KJots@KDE5")); note.setTitle(i18nc("The default name for new pages.", "New Page")); note.setCreationDate(QDateTime::currentDateTime()); note.setLastModifiedDate(QDateTime::currentDateTime()); // Need a non-empty body part so that the serializer regards this as a valid message. note.setText(QStringLiteral(" ")); newItem.setPayload(note.message()); newItem.attribute(Akonadi::Item::AddIfMissing)->setIconName(QStringLiteral("text-plain")); auto *job = new Akonadi::ItemCreateJob(newItem, Collection(m_containerCollectionId), this); connect(job, &Akonadi::ItemCreateJob::result, this, &NoteCreatorAndSelector::noteCreationFinished); } void NoteCreatorAndSelector::noteCreationFinished(KJob *job) { if (job->error()) { qCWarning(NOTESHARED_LOG) << job->errorString(); return; } auto *createJob = qobject_cast(job); Q_ASSERT(createJob); Item newItem = createJob->item(); m_newNoteId = newItem.id(); m_giveupTimer->start(); connect(m_secondarySelectionModel->model(), &QAbstractItemModel::rowsInserted, this, &NoteCreatorAndSelector::trySelectNote); trySelectNote(); } void NoteCreatorAndSelector::trySelectNote() { QModelIndexList list = EntityTreeModel::modelIndexesForItem(m_secondarySelectionModel->model(), Item(m_newNoteId)); if (list.isEmpty()) { return; } const QModelIndex idx = list.first(); m_secondarySelectionModel->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } diff --git a/src/noteshared/notecreatorandselector.h b/src/noteshared/notecreatorandselector.h index 290ddaf..871d58b 100644 --- a/src/noteshared/notecreatorandselector.h +++ b/src/noteshared/notecreatorandselector.h @@ -1,77 +1,79 @@ /* + This file is part of KJots. + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly 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 NOTECREATORANDSELECTOR_H #define NOTECREATORANDSELECTOR_H #include #include #include #include class KJob; namespace NoteShared { /** * @brief Creates and selects the newly created note. * * The note is created in the supplied collection. That collection is * selected in the primaryModel. The new note is selected in the * secondaryModel. If the secondaryModel is null, the primaryModel is * used to select the note too. That is relevant for mixed tree models like * KJots uses. */ class NoteCreatorAndSelector : public QObject { Q_OBJECT public: explicit NoteCreatorAndSelector(QItemSelectionModel *primaryModel, QItemSelectionModel *secondaryModel = nullptr, QObject *parent = nullptr); virtual ~NoteCreatorAndSelector(); void createNote(const Akonadi::Collection &containerCollection); private: void doCreateNote(); private Q_SLOTS: void trySelectCollection(); void noteCreationFinished(KJob *job); void trySelectNote(); private: QItemSelectionModel *m_primarySelectionModel; QItemSelectionModel *m_secondarySelectionModel; Akonadi::Collection::Id m_containerCollectionId; Akonadi::Item::Id m_newNoteId; QTimer *m_giveupTimer; }; } #endif diff --git a/src/noteshared/noteeditorutils.cpp b/src/noteshared/noteeditorutils.cpp index 745d32f..8d38ca2 100644 --- a/src/noteshared/noteeditorutils.cpp +++ b/src/noteshared/noteeditorutils.cpp @@ -1,44 +1,46 @@ /* + This file is part of KJots. + Copyright (c) 2013-2015 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "noteeditorutils.h" #include #include #include #include #include #include #include #include #include #include using namespace NoteShared::NoteEditorUtils; void NoteShared::NoteEditorUtils::addCheckmark(QTextCursor &cursor) { const int position = cursor.position(); cursor.movePosition(QTextCursor::StartOfLine); cursor.insertText(QString::fromUtf16(u"\U00002713")); cursor.setPosition(position + 1); } void NoteShared::NoteEditorUtils::insertDate(QTextEdit *editor) { editor->insertPlainText(QLocale().toString(QDateTime::currentDateTime(), QLocale::ShortFormat) + QLatin1Char(' ')); } diff --git a/src/noteshared/noteeditorutils.h b/src/noteshared/noteeditorutils.h index e9e9b8a..7086ced 100644 --- a/src/noteshared/noteeditorutils.h +++ b/src/noteshared/noteeditorutils.h @@ -1,34 +1,36 @@ /* + This file is part of KJots. + Copyright (c) 2013-2015 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NOTEEDITORUTILS_H #define NOTEEDITORUTILS_H class QTextCursor; class QTextEdit; class QTextDocument; class QWidget; namespace NoteShared { namespace NoteEditorUtils { void addCheckmark(QTextCursor &cursor); void insertDate(QTextEdit *editor); }; } #endif // NOTEEDITORUTILS_H diff --git a/src/noteshared/notelockattribute.cpp b/src/noteshared/notelockattribute.cpp index 42cb099..26c936b 100644 --- a/src/noteshared/notelockattribute.cpp +++ b/src/noteshared/notelockattribute.cpp @@ -1,53 +1,55 @@ /* + This file is part of KJots. + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly 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 "notelockattribute.h" #include using namespace NoteShared; NoteLockAttribute::NoteLockAttribute() : Akonadi::Attribute() { } NoteLockAttribute *NoteLockAttribute::clone() const { return new NoteLockAttribute(); } void NoteLockAttribute::deserialize(const QByteArray &data) { Q_UNUSED(data) } QByteArray NoteLockAttribute::serialized() const { return "-"; } QByteArray NoteLockAttribute::type() const { //We can't change this name! static const QByteArray sType("KJotsLockAttribute"); return sType; } diff --git a/src/noteshared/notelockattribute.h b/src/noteshared/notelockattribute.h index f010e2f..ba95d5f 100644 --- a/src/noteshared/notelockattribute.h +++ b/src/noteshared/notelockattribute.h @@ -1,44 +1,46 @@ /* + This file is part of KJots. + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly 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 KJOTS_LOCK_ATTRIBUTE_H #define KJOTS_LOCK_ATTRIBUTE_H #include namespace NoteShared { class NoteLockAttribute : public Akonadi::Attribute { public: explicit NoteLockAttribute(); QByteArray type() const Q_DECL_OVERRIDE; NoteLockAttribute *clone() const Q_DECL_OVERRIDE; QByteArray serialized() const Q_DECL_OVERRIDE; void deserialize(const QByteArray &data) Q_DECL_OVERRIDE; }; } #endif diff --git a/src/noteshared/standardnoteactionmanager.cpp b/src/noteshared/standardnoteactionmanager.cpp index 26a1b29..38ffad3 100644 --- a/src/noteshared/standardnoteactionmanager.cpp +++ b/src/noteshared/standardnoteactionmanager.cpp @@ -1,663 +1,663 @@ /* This file is part of KJots. - Copyright (c) 2020 Igor Poboiko - 2009 - 2010 Tobias Koenig + Copyright (c) 2009 - 2010 Tobias Koenig + 2020 Igor Poboiko 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 "standardnoteactionmanager.h" #include #include #include #include #include #include #include #include #include #include #include "notecreatorandselector.h" #include "notelockattribute.h" using namespace Akonadi; class Q_DECL_HIDDEN StandardNoteActionManager::Private { public: Private(KActionCollection *actionCollection, QWidget *parentWidget, StandardNoteActionManager *parent) : mActionCollection(actionCollection) , mParentWidget(parentWidget) , mGenericManager(std::make_unique(actionCollection, parentWidget)) , mParent(parent) { QObject::connect(mGenericManager.get(), &StandardActionManager::actionStateUpdated, mParent, &StandardNoteActionManager::actionStateUpdated); mGenericManager->setMimeTypeFilter({ NoteUtils::noteMimeType() }); mGenericManager->setCapabilityFilter({ QStringLiteral("Resource") }); } ~Private() = default; void updateGenericAllActions() { updateGenericAction(StandardActionManager::CreateCollection); updateGenericAction(StandardActionManager::CopyCollections); updateGenericAction(StandardActionManager::DeleteCollections); updateGenericAction(StandardActionManager::SynchronizeCollections); updateGenericAction(StandardActionManager::CollectionProperties); updateGenericAction(StandardActionManager::CopyItems); updateGenericAction(StandardActionManager::Paste); updateGenericAction(StandardActionManager::DeleteItems); updateGenericAction(StandardActionManager::ManageLocalSubscriptions); updateGenericAction(StandardActionManager::AddToFavoriteCollections); updateGenericAction(StandardActionManager::RemoveFromFavoriteCollections); updateGenericAction(StandardActionManager::RenameFavoriteCollection); updateGenericAction(StandardActionManager::CopyCollectionToMenu); updateGenericAction(StandardActionManager::CopyItemToMenu); updateGenericAction(StandardActionManager::MoveItemToMenu); updateGenericAction(StandardActionManager::MoveCollectionToMenu); updateGenericAction(StandardActionManager::CutItems); updateGenericAction(StandardActionManager::CutCollections); updateGenericAction(StandardActionManager::CreateResource); updateGenericAction(StandardActionManager::DeleteResources); updateGenericAction(StandardActionManager::ResourceProperties); updateGenericAction(StandardActionManager::SynchronizeResources); updateGenericAction(StandardActionManager::ToggleWorkOffline); updateGenericAction(StandardActionManager::CopyCollectionToDialog); updateGenericAction(StandardActionManager::MoveCollectionToDialog); updateGenericAction(StandardActionManager::CopyItemToDialog); updateGenericAction(StandardActionManager::MoveItemToDialog); updateGenericAction(StandardActionManager::SynchronizeCollectionsRecursive); updateGenericAction(StandardActionManager::MoveCollectionsToTrash); updateGenericAction(StandardActionManager::MoveItemsToTrash); updateGenericAction(StandardActionManager::RestoreCollectionsFromTrash); updateGenericAction(StandardActionManager::RestoreItemsFromTrash); updateGenericAction(StandardActionManager::MoveToTrashRestoreCollection); updateGenericAction(StandardActionManager::MoveToTrashRestoreCollectionAlternative); updateGenericAction(StandardActionManager::MoveToTrashRestoreItem); updateGenericAction(StandardActionManager::MoveToTrashRestoreItemAlternative); updateGenericAction(StandardActionManager::SynchronizeFavoriteCollections); } void updateGenericAction(StandardActionManager::Type type) { switch (type) { case StandardActionManager::CreateCollection: mGenericManager->action(StandardActionManager::CreateCollection)->setText( i18n("New Note Book...")); mGenericManager->action(StandardActionManager::CreateCollection)->setIcon( QIcon::fromTheme(QStringLiteral("address-book-new"))); mGenericManager->action(StandardActionManager::CreateCollection)->setWhatsThis( i18n("Add a new note book to the currently selected bookshelf.")); mGenericManager->setContextText( StandardActionManager::CreateCollection, StandardActionManager::DialogTitle, i18nc("@title:window", "New Note Book")); mGenericManager->setContextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText, ki18n("Could not create note book: %1")); mGenericManager->setContextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle, i18n("Note book creation failed")); mGenericManager->action(StandardActionManager::CreateCollection)->setProperty("ContentMimeTypes", { NoteUtils::noteMimeType() }); break; case StandardActionManager::CopyCollections: mGenericManager->setActionText(StandardActionManager::CopyCollections, ki18np("Copy Note Book", "Copy %1 Note Books")); mGenericManager->action(StandardActionManager::CopyCollections)->setWhatsThis( i18n("Copy the selected note books to the clipboard.")); break; case StandardActionManager::DeleteCollections: mGenericManager->setActionText(StandardActionManager::DeleteCollections, ki18np("Delete Note Book", "Delete %1 Note Books")); mGenericManager->action(StandardActionManager::DeleteCollections)->setWhatsThis( i18n("Delete the selected note books from the bookshelf.")); mGenericManager->setContextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText, ki18np("Do you really want to delete this note book and all its contents?", "Do you really want to delete %1 note books and all their contents?")); mGenericManager->setContextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle, ki18ncp("@title:window", "Delete note book?", "Delete note books?")); mGenericManager->setContextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText, ki18n("Could not delete note book: %1")); mGenericManager->setContextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle, i18n("Note book deletion failed")); break; case StandardActionManager::SynchronizeCollections: mGenericManager->setActionText(StandardActionManager::SynchronizeCollections, ki18np("Update Note Book", "Update %1 Note Books")); mGenericManager->action(StandardActionManager::SynchronizeCollections)->setWhatsThis( i18n("Update the content of the selected note books.")); break; case StandardActionManager::CutCollections: mGenericManager->setActionText(StandardActionManager::CutCollections, ki18np("Cut Note Book", "Cut %1 Note Books")); mGenericManager->action(StandardActionManager::CutCollections)->setWhatsThis( i18n("Cut the selected note books from the bookshelf.")); break; case StandardActionManager::CollectionProperties: mGenericManager->action(StandardActionManager::CollectionProperties)->setText( i18n("Note Book Properties...")); mGenericManager->action(StandardActionManager::CollectionProperties)->setWhatsThis( i18n("Open a dialog to edit the properties of the selected note book.")); mGenericManager->setContextText( StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle, ki18nc("@title:window", "Properties of Note Book %1")); break; case StandardActionManager::CopyItems: mGenericManager->setActionText(StandardActionManager::CopyItems, ki18np("Copy Note", "Copy %1 Notes")); mGenericManager->action(StandardActionManager::CopyItems)->setWhatsThis( i18n("Copy the selected notes to the clipboard.")); break; case StandardActionManager::DeleteItems: mGenericManager->setActionText(StandardActionManager::DeleteItems, ki18np("Delete Note", "Delete %1 Notes")); mGenericManager->action(StandardActionManager::DeleteItems)->setWhatsThis( i18n("Delete the selected notes from the note book.")); mGenericManager->setContextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText, ki18np("Do you really want to delete the selected note?", "Do you really want to delete %1 notes?")); mGenericManager->setContextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle, ki18ncp("@title:window", "Delete Note?", "Delete Notes?")); mGenericManager->setContextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText, ki18n("Could not delete note: %1")); mGenericManager->setContextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle, i18n("Note deletion failed")); break; case StandardActionManager::CutItems: mGenericManager->setActionText(StandardActionManager::CutItems, ki18np("Cut Note", "Cut %1 Notes")); mGenericManager->action(StandardActionManager::CutItems)->setWhatsThis( i18n("Cut the selected notes from the note book.")); break; case StandardActionManager::CreateResource: mGenericManager->action(StandardActionManager::CreateResource)->setText( i18n("Add &Bookshelf...")); mGenericManager->action(StandardActionManager::CreateResource)->setWhatsThis( i18n("Add a new bookshelf

" "You will be presented with a dialog where you can select " "the type of the bookshelf that shall be added.

")); mGenericManager->setContextText( StandardActionManager::CreateResource, StandardActionManager::DialogTitle, i18nc("@title:window", "Add Bookshelf")); mGenericManager->setContextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText, ki18n("Could not create bookshelf: %1")); mGenericManager->setContextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle, i18n("Bookshelf creation failed")); break; case StandardActionManager::DeleteResources: mGenericManager->setActionText(StandardActionManager::DeleteResources, ki18np("&Delete Bookshelf", "&Delete %1 Bookshelfs")); mGenericManager->action(StandardActionManager::DeleteResources)->setWhatsThis( i18n("Delete the selected bookshelfs

" "The currently selected bookshelfs will be deleted, " "along with all the notes they contain.

")); mGenericManager->setContextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText, ki18np("Do you really want to delete this bookshelf?", "Do you really want to delete %1 bookshelfs?")); mGenericManager->setContextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle, ki18ncp("@title:window", "Delete Bookshelf?", "Delete Bookshelfs?")); break; case StandardActionManager::ResourceProperties: mGenericManager->action(StandardActionManager::ResourceProperties)->setText( i18n("Bookshelf Properties...")); mGenericManager->action(StandardActionManager::ResourceProperties)->setWhatsThis( i18n("Open a dialog to edit properties of the selected bookshelf.")); break; case StandardActionManager::SynchronizeResources: mGenericManager->setActionText(StandardActionManager::SynchronizeResources, ki18np("Update Bookshelf", "Update %1 Bookshelfs")); mGenericManager->action(StandardActionManager::SynchronizeResources)->setWhatsThis (i18n("Updates the content of all note books of the selected bookshelfs.")); break; case StandardActionManager::Paste: mGenericManager->setContextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageText, ki18n("Could not paste note: %1")); mGenericManager->setContextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle, i18n("Paste failed")); break; default: break; } } void updateActions() { if (mItemSelectionModel) { const Item::List items = mParent->selectedItems(); const int countUnlocked = std::count_if(items.cbegin(), items.cend(), [](const Item &item){ return item.isValid() && !item.hasAttribute(); }); QAction *action = mActions.value(StandardNoteActionManager::LockUnlockNote); if (action) { action->setEnabled(items.size() > 0); if (countUnlocked > 0) { action->setData(true); action->setText(i18ncp("@action", "Lock Note", "Lock Notes", countUnlocked)); action->setIcon(QIcon::fromTheme(QStringLiteral("emblem-locked"))); action->setWhatsThis(i18n("Lock selected notes")); } else if (items.size() > 0) { action->setData(false); action->setText(i18ncp("@action", "Unlock Note", "Unlock Notes", items.size())); action->setIcon(QIcon::fromTheme(QStringLiteral("emblem-locked"))); action->setWhatsThis(i18n("Unlock selected notes")); } } action = mGenericManager->action(StandardActionManager::DeleteItems); if (action) { action->setEnabled(countUnlocked == items.size()); } } else { QAction *action = mActions.value(StandardNoteActionManager::LockUnlockNote); if (action) { action->setEnabled(false); } } if (mCollectionSelectionModel) { const Collection::List collections = mParent->selectedCollections(); const int countUnlocked = std::count_if(collections.cbegin(), collections.cend(), [](const Collection &collection){ return collection.isValid() && !collection.hasAttribute(); }); QAction *action = mActions.value(StandardNoteActionManager::LockUnlockNoteBook); if (action) { action->setEnabled(collections.size() > 0); if (countUnlocked > 0) { action->setData(true); action->setText(i18ncp("@action", "Lock Note Book", "Lock Note Books", countUnlocked)); action->setIcon(QIcon::fromTheme(QStringLiteral("emblem-locked"))); action->setWhatsThis(i18n("Lock selected note books")); } else if (collections.size() > 0) { action->setData(false); action->setText(i18ncp("@action", "Unlock Note Book", "Unlock Note Books", collections.size())); action->setIcon(QIcon::fromTheme(QStringLiteral("emblem-locked"))); action->setWhatsThis(i18n("Unlock selected note books")); } } action = mGenericManager->action(StandardActionManager::DeleteCollections); if (action) { action->setEnabled(countUnlocked == collections.size()); } } else { QAction *action = mActions.value(StandardNoteActionManager::LockUnlockNoteBook); if (action) { action->setEnabled(false); } } if (mItemSelectionModel && mCollectionSelectionModel) { const Collection::List collections = mParent->selectedCollections(); const Item::List items = mParent->selectedItems(); QAction *action = mActions.value(StandardNoteActionManager::CreateNote); if (action) { Akonadi::Collection collection; if (collections.count() == 1) { collection = collections.first(); } else if (collections.count() == 0) { if (items.count() > 0) { collection = mItemSelectionModel->selectedRows().first().data(EntityTreeModel::ParentCollectionRole).value(); } } action->setEnabled(collection.isValid() && (collection.rights() & Akonadi::Collection::CanCreateItem) && (!collection.hasAttribute())); } action = mActions.value(StandardNoteActionManager::ChangeNoteColor); if (action) { action->setEnabled(collections.count() > 0 || items.count() > 0); } } Q_EMIT mParent->actionStateUpdated(); } void slotLockUnlockNote() { if (!mItemSelectionModel || mInterceptedActions.contains(LockUnlockNote)) { return; } const bool lock = mActions[StandardNoteActionManager::LockUnlockNote]->data().toBool(); const Item::List items = mParent->selectedItems(); for (auto item : items) { if (item.isValid()) { if (lock) { item.addAttribute(new NoteShared::NoteLockAttribute); } else { item.removeAttribute(); } new ItemModifyJob(item, mParent); } } } void slotLockUnlockNoteBook() { if (!mCollectionSelectionModel || mInterceptedActions.contains(LockUnlockNoteBook)) { return; } const bool lock = mActions[StandardNoteActionManager::LockUnlockNoteBook]->data().toBool(); const Collection::List collections = mParent->selectedCollections(); for (auto collection : collections) { if (collection.isValid()) { if (lock) { collection.addAttribute(new NoteShared::NoteLockAttribute); } else { collection.removeAttribute(); } new CollectionModifyJob(collection, mParent); } } } void slotCreateNote() { if (mInterceptedActions.contains(CreateNote)) { return; } const Collection::List collections = mParent->selectedCollections(); if (collections.count() > 1) { return; } Akonadi::Collection collection; if (collections.count() == 1) { collection = collections.first(); } else { const Item::List items = mParent->selectedItems(); if (items.count() == 0) { return; } collection = items.first().parentCollection(); } auto *creatorAndSelector = new NoteShared::NoteCreatorAndSelector(mCollectionSelectionModel, mItemSelectionModel, mParent); creatorAndSelector->createNote(collection); } void slotChangeNoteColor() { if (mInterceptedActions.contains(ChangeNoteColor)) { return; } QColor color = Qt::white; const Item::List items = mParent->selectedItems(); if (items.size() == 1) { const EntityDisplayAttribute *attr = items.first().attribute(); if (attr) { color = attr->backgroundColor(); } } color = QColorDialog::getColor(color, mParentWidget, QString(), QColorDialog::ShowAlphaChannel); if (!color.isValid()) { return; } for (auto item : items) { item.attribute(Item::AddIfMissing)->setBackgroundColor(color); new ItemModifyJob(item, mParent); } } void slotChangeNoteBookColor() { if (mInterceptedActions.contains(ChangeNoteBookColor)) { return; } QColor color = Qt::white; const Collection::List collections = mParent->selectedCollections(); if (collections.size() == 1) { const EntityDisplayAttribute *attr = collections.first().attribute(); if (attr) { color = attr->backgroundColor(); } } color = QColorDialog::getColor(color, mParentWidget, QString(), QColorDialog::ShowAlphaChannel); if (!color.isValid()) { return; } for (auto collection : collections) { collection.attribute(Collection::AddIfMissing)->setBackgroundColor(color); new CollectionModifyJob(collection, mParent); } } KActionCollection *mActionCollection = nullptr; QWidget *mParentWidget = nullptr; std::unique_ptr mGenericManager; QItemSelectionModel *mCollectionSelectionModel = nullptr; QItemSelectionModel *mItemSelectionModel = nullptr; QHash mActions; QSet mInterceptedActions; StandardNoteActionManager *mParent = nullptr; }; StandardNoteActionManager::StandardNoteActionManager(KActionCollection *actionCollection, QWidget *parent) : QObject(parent) , d(std::make_unique(actionCollection, parent, this)) { } StandardNoteActionManager::~StandardNoteActionManager() = default; void StandardNoteActionManager::setCollectionSelectionModel(QItemSelectionModel *selectionModel) { d->mCollectionSelectionModel = selectionModel; d->mGenericManager->setCollectionSelectionModel(selectionModel); connect(selectionModel->model(), &QAbstractItemModel::dataChanged, this, [this]() { d->updateActions(); }); connect(selectionModel->model(), &QAbstractItemModel::rowsInserted, this, [this]() { d->updateActions(); }); connect(selectionModel->model(), &QAbstractItemModel::rowsRemoved, this, [this]() { d->updateActions(); }); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() { d->updateActions(); }); d->updateActions(); } void StandardNoteActionManager::setItemSelectionModel(QItemSelectionModel *selectionModel) { d->mItemSelectionModel = selectionModel; d->mGenericManager->setItemSelectionModel(selectionModel); connect(selectionModel->model(), &QAbstractItemModel::dataChanged, this, [this]() { d->updateActions(); }); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() { d->updateActions(); }); d->updateActions(); } QAction *StandardNoteActionManager::createAction(Type type) { QAction *action = d->mActions.value(type); if (action) { return action; } switch (type) { case CreateNote: action = new QAction(d->mParentWidget); action->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); action->setText(i18n("&New Note")); action->setWhatsThis(i18n("Add a new note to a selected note book")); d->mActions.insert(CreateNote, action); d->mActionCollection->addAction(QStringLiteral("akonadi_note_create"), action); d->mActionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_N)); connect(action, &QAction::triggered, this, [this](){ d->slotCreateNote(); }); break; case LockUnlockNote: action = new QAction(d->mParentWidget); d->mActions.insert(LockUnlockNote, action); d->mActionCollection->addAction(QStringLiteral("akonadi_note_lock_unlock"), action); connect(action, &QAction::triggered, this, [this](){ d->slotLockUnlockNote(); }); break; case LockUnlockNoteBook: action = new QAction(d->mParentWidget); d->mActions.insert(LockUnlockNoteBook, action); d->mActionCollection->addAction(QStringLiteral("akonadi_notebook_lock_unlock"), action); connect(action, &QAction::triggered, this, [this](){ d->slotLockUnlockNoteBook(); }); case ChangeNoteColor: action = new QAction(d->mParentWidget); action->setIcon(QIcon::fromTheme(QStringLiteral("format-fill-color"))); action->setText(i18n("Change Note Color...")); action->setWhatsThis(i18n("Changes the color of a selected notes")); d->mActions.insert(ChangeNoteColor, action); d->mActionCollection->addAction(QStringLiteral("akonadi_note_change_color"), action); connect(action, &QAction::triggered, this, [this](){ d->slotChangeNoteColor(); }); break; case ChangeNoteBookColor: action = new QAction(d->mParentWidget); action->setIcon(QIcon::fromTheme(QStringLiteral("format-fill-color"))); action->setText(i18n("Change Note Book Color...")); action->setWhatsThis(i18n("Changes the color of a selected note books")); d->mActions.insert(ChangeNoteColor, action); d->mActionCollection->addAction(QStringLiteral("akonadi_notebook_change_color"), action); connect(action, &QAction::triggered, this, [this](){ d->slotChangeNoteBookColor(); }); break; default: Q_ASSERT(false); // should never happen break; } return action; } QAction *StandardNoteActionManager::createAction(StandardActionManager::Type type) { QAction *act = d->mGenericManager->action(type); if (!act) { act = d->mGenericManager->createAction(type); } d->updateGenericAction(type); return act; } void StandardNoteActionManager::createAllActions() { (void)createAction(CreateNote); (void)createAction(LockUnlockNote); (void)createAction(LockUnlockNoteBook); (void)createAction(ChangeNoteColor); (void)createAction(ChangeNoteBookColor); d->mGenericManager->createAllActions(); d->updateGenericAllActions(); d->updateActions(); } QAction *StandardNoteActionManager::action(Type type) const { if (d->mActions.contains(type)) { return d->mActions.value(type); } return nullptr; } QAction *StandardNoteActionManager::action(StandardActionManager::Type type) const { return d->mGenericManager->action(type); } void StandardNoteActionManager::setActionText(StandardActionManager::Type type, const KLocalizedString &text) { d->mGenericManager->setActionText(type, text); } void StandardNoteActionManager::interceptAction(Type type, bool intercept) { if (intercept) { d->mInterceptedActions.insert(type); } else { d->mInterceptedActions.remove(type); } } void StandardNoteActionManager::interceptAction(StandardActionManager::Type type, bool intercept) { d->mGenericManager->interceptAction(type, intercept); } Collection::List StandardNoteActionManager::selectedCollections() const { return d->mGenericManager->selectedCollections(); } Item::List StandardNoteActionManager::selectedItems() const { return d->mGenericManager->selectedItems(); } void StandardNoteActionManager::setCollectionPropertiesPageNames(const QStringList &names) { d->mGenericManager->setCollectionPropertiesPageNames(names); }