diff --git a/messagelist/src/utils/configureaggregationsdialog.cpp b/messagelist/src/utils/configureaggregationsdialog.cpp index b36d35f9..2a4e710e 100644 --- a/messagelist/src/utils/configureaggregationsdialog.cpp +++ b/messagelist/src/utils/configureaggregationsdialog.cpp @@ -1,475 +1,471 @@ /****************************************************************************** * * Copyright 2008 Szymon Tomasz Stefanek * * 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 "utils/configureaggregationsdialog.h" #include "utils/configureaggregationsdialog_p.h" #include "utils/aggregationeditor.h" #include "core/aggregation.h" #include "core/manager.h" #include #include #include #include #include -#include #include #include #include #include #include #include namespace MessageList { namespace Utils { class AggregationListWidgetItem : public QListWidgetItem { private: Core::Aggregation *mAggregation = nullptr; public: AggregationListWidgetItem(QListWidget *par, const Core::Aggregation &set) : QListWidgetItem(set.name(), par) { mAggregation = new Core::Aggregation(set); } ~AggregationListWidgetItem() { delete mAggregation; } public: Core::Aggregation *aggregation() const { return mAggregation; } void forgetAggregation() { mAggregation = nullptr; } }; /** * The widget that lists the available Aggregations. * * At the moment of writing, derived from QListWidget only to override sizeHint(). */ class AggregationListWidget : public QListWidget { public: AggregationListWidget(QWidget *parent) : QListWidget(parent) { } public: // need a larger but shorter QListWidget QSize sizeHint() const override { return QSize(450, 128); } }; } // namespace Utils } // namespace MessageList using namespace MessageList::Core; using namespace MessageList::Utils; ConfigureAggregationsDialog::ConfigureAggregationsDialog(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setAttribute(Qt::WA_DeleteOnClose); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QVBoxLayout *mainLayout = new QVBoxLayout(this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &ConfigureAggregationsDialog::reject); setWindowTitle(i18n("Customize Message Aggregation Modes")); QWidget *base = new QWidget(this); mainLayout->addWidget(base); mainLayout->addWidget(buttonBox); QGridLayout *g = new QGridLayout(base); g->setContentsMargins(0, 0, 0, 0); d->mAggregationList = new AggregationListWidget(base); d->mAggregationList->setSelectionMode(QAbstractItemView::ExtendedSelection); d->mAggregationList->setSortingEnabled(true); g->addWidget(d->mAggregationList, 0, 0, 7, 1); connect(d->mAggregationList, &AggregationListWidget::itemClicked, this, [this](QListWidgetItem *item) { d->aggregationListItemClicked(item); }); d->mNewAggregationButton = new QPushButton(i18n("New Aggregation"), base); d->mNewAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); - d->mNewAggregationButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); g->addWidget(d->mNewAggregationButton, 0, 1); connect(d->mNewAggregationButton, &QPushButton::clicked, this, [this]() { d->newAggregationButtonClicked(); }); d->mCloneAggregationButton = new QPushButton(i18n("Clone Aggregation"), base); d->mCloneAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); - d->mCloneAggregationButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); g->addWidget(d->mCloneAggregationButton, 1, 1); connect(d->mCloneAggregationButton, &QPushButton::clicked, this, [this]() { d->cloneAggregationButtonClicked(); }); QFrame *f = new QFrame(base); f->setFrameStyle(QFrame::Sunken | QFrame::HLine); f->setMinimumHeight(24); g->addWidget(f, 2, 1, Qt::AlignVCenter); d->mExportAggregationButton = new QPushButton(i18n("Export Aggregation..."), base); g->addWidget(d->mExportAggregationButton, 3, 1); connect(d->mExportAggregationButton, &QPushButton::clicked, this, [this]() { d->exportAggregationButtonClicked(); }); d->mImportAggregationButton = new QPushButton(i18n("Import Aggregation..."), base); g->addWidget(d->mImportAggregationButton, 4, 1); connect(d->mImportAggregationButton, &QPushButton::clicked, this, [this]() { d->importAggregationButtonClicked(); }); f = new QFrame(base); f->setFrameStyle(QFrame::Sunken | QFrame::HLine); f->setMinimumHeight(24); g->addWidget(f, 5, 1, Qt::AlignVCenter); d->mDeleteAggregationButton = new QPushButton(i18n("Delete Aggregation"), base); d->mDeleteAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); - d->mDeleteAggregationButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); g->addWidget(d->mDeleteAggregationButton, 6, 1); connect(d->mDeleteAggregationButton, &QPushButton::clicked, this, [this]() { d->deleteAggregationButtonClicked(); }); d->mEditor = new AggregationEditor(base); g->addWidget(d->mEditor, 8, 0, 1, 2); connect(d->mEditor, &AggregationEditor::aggregationNameChanged, this, [this]() { d->editedAggregationNameChanged(); }); g->setColumnStretch(0, 1); g->setRowStretch(7, 1); connect(okButton, &QPushButton::clicked, this, [this]() { d->okButtonClicked(); }); d->fillAggregationList(); } ConfigureAggregationsDialog::~ConfigureAggregationsDialog() { delete d; } void ConfigureAggregationsDialog::selectAggregation(const QString &aggregationId) { AggregationListWidgetItem *item = d->findAggregationItemById(aggregationId); if (item) { d->mAggregationList->setCurrentItem(item); d->aggregationListItemClicked(item); } } void ConfigureAggregationsDialog::Private::okButtonClicked() { if (Manager::instance()) { commitEditor(); Manager::instance()->removeAllAggregations(); const int c = mAggregationList->count(); int i = 0; while (i < c) { AggregationListWidgetItem *item = dynamic_cast< AggregationListWidgetItem * >(mAggregationList->item(i)); if (item) { Manager::instance()->addAggregation(item->aggregation()); item->forgetAggregation(); } ++i; } Manager::instance()->aggregationsConfigurationCompleted(); } Q_EMIT q->okClicked(); q->close(); // this will delete too } void ConfigureAggregationsDialog::Private::commitEditor() { Aggregation *editedAggregation = mEditor->editedAggregation(); if (!editedAggregation) { return; } mEditor->commit(); AggregationListWidgetItem *editedItem = findAggregationItemByAggregation(editedAggregation); if (!editedItem) { return; } const QString goodName = uniqueNameForAggregation(editedAggregation->name(), editedAggregation); editedAggregation->setName(goodName); editedItem->setText(goodName); } void ConfigureAggregationsDialog::Private::editedAggregationNameChanged() { Aggregation *set = mEditor->editedAggregation(); if (!set) { return; } AggregationListWidgetItem *it = findAggregationItemByAggregation(set); if (!it) { return; } const QString goodName = uniqueNameForAggregation(set->name(), set); it->setText(goodName); } void ConfigureAggregationsDialog::Private::fillAggregationList() { if (!Manager::instance()) { return; } const QMap< QString, Aggregation * > &sets = Manager::instance()->aggregations(); QMap< QString, Aggregation * >::ConstIterator end(sets.constEnd()); for (QMap< QString, Aggregation * >::ConstIterator it = sets.constBegin(); it != end; ++it) { (void)new AggregationListWidgetItem(mAggregationList, *(*it)); } } void ConfigureAggregationsDialog::Private::aggregationListItemClicked(QListWidgetItem *cur) { commitEditor(); updateButton(cur); } void ConfigureAggregationsDialog::Private::updateButton(QListWidgetItem *cur) { const int numberOfSelectedItem(mAggregationList->selectedItems().count()); AggregationListWidgetItem *item = cur ? dynamic_cast< AggregationListWidgetItem * >(cur) : nullptr; mDeleteAggregationButton->setEnabled(item && !item->aggregation()->readOnly() && (mAggregationList->count() > 1)); mCloneAggregationButton->setEnabled(numberOfSelectedItem == 1); mExportAggregationButton->setEnabled(numberOfSelectedItem > 0); mEditor->editAggregation(item ? item->aggregation() : nullptr); if (item && !item->isSelected()) { item->setSelected(true); // make sure it's true } } AggregationListWidgetItem *ConfigureAggregationsDialog::Private::findAggregationItemByName(const QString &name, Aggregation *skipAggregation) { const int c = mAggregationList->count(); int i = 0; while (i < c) { AggregationListWidgetItem *item = dynamic_cast< AggregationListWidgetItem * >(mAggregationList->item(i)); if (item) { if (item->aggregation() != skipAggregation) { if (item->aggregation()->name() == name) { return item; } } } ++i; } return nullptr; } AggregationListWidgetItem *ConfigureAggregationsDialog::Private::findAggregationItemById(const QString &aggregationId) { const int c = mAggregationList->count(); int i = 0; while (i < c) { AggregationListWidgetItem *item = dynamic_cast< AggregationListWidgetItem * >(mAggregationList->item(i)); if (item) { if (item->aggregation()->id() == aggregationId) { return item; } } ++i; } return nullptr; } AggregationListWidgetItem *ConfigureAggregationsDialog::Private::findAggregationItemByAggregation(Aggregation *set) { const int c = mAggregationList->count(); int i = 0; while (i < c) { AggregationListWidgetItem *item = dynamic_cast< AggregationListWidgetItem * >(mAggregationList->item(i)); if (item) { if (item->aggregation() == set) { return item; } } ++i; } return nullptr; } QString ConfigureAggregationsDialog::Private::uniqueNameForAggregation(const QString &baseName, Aggregation *skipAggregation) { QString ret = baseName; if (ret.isEmpty()) { ret = i18n("Unnamed Aggregation"); } int idx = 1; AggregationListWidgetItem *item = findAggregationItemByName(ret, skipAggregation); while (item) { idx++; ret = QStringLiteral("%1 %2").arg(baseName).arg(idx); item = findAggregationItemByName(ret, skipAggregation); } return ret; } void ConfigureAggregationsDialog::Private::newAggregationButtonClicked() { Aggregation emptyAggregation; emptyAggregation.setName(uniqueNameForAggregation(i18n("New Aggregation"))); AggregationListWidgetItem *item = new AggregationListWidgetItem(mAggregationList, emptyAggregation); mAggregationList->setCurrentItem(item); mDeleteAggregationButton->setEnabled(item && !item->aggregation()->readOnly()); } void ConfigureAggregationsDialog::Private::cloneAggregationButtonClicked() { AggregationListWidgetItem *item = dynamic_cast< AggregationListWidgetItem * >(mAggregationList->currentItem()); if (!item) { return; } commitEditor(); item->setSelected(false); Aggregation copyAggregation(*(item->aggregation())); copyAggregation.setReadOnly(false); copyAggregation.generateUniqueId(); // regenerate id so it becomes different copyAggregation.setName(uniqueNameForAggregation(item->aggregation()->name())); item = new AggregationListWidgetItem(mAggregationList, copyAggregation); mAggregationList->setCurrentItem(item); aggregationListItemClicked(item); } void ConfigureAggregationsDialog::Private::deleteAggregationButtonClicked() { const QList list = mAggregationList->selectedItems(); if (list.isEmpty()) { return; } mEditor->editAggregation(nullptr); // forget it for (QListWidgetItem *it : list) { AggregationListWidgetItem *item = dynamic_cast< AggregationListWidgetItem * >(it); if (!item) { return; } if (!item->aggregation()->readOnly()) { delete item; // this will trigger aggregationListCurrentItemChanged() } if (mAggregationList->count() < 2) { break; // no way: desperately try to keep at least one option set alive :) } } AggregationListWidgetItem *newItem = dynamic_cast< AggregationListWidgetItem * >(mAggregationList->currentItem()); updateButton(newItem); } void ConfigureAggregationsDialog::Private::importAggregationButtonClicked() { const QString filename = QFileDialog::getOpenFileName(q, i18n("Import Aggregation")); if (!filename.isEmpty()) { KConfig config(filename); if (config.hasGroup(QStringLiteral("MessageListView::Aggregations"))) { KConfigGroup grp(&config, QStringLiteral("MessageListView::Aggregations")); const int cnt = grp.readEntry("Count", 0); int idx = 0; while (idx < cnt) { const QString data = grp.readEntry(QStringLiteral("Set%1").arg(idx), QString()); if (!data.isEmpty()) { Aggregation *set = new Aggregation(); if (set->loadFromString(data)) { set->setReadOnly(false); set->generateUniqueId(); // regenerate id so it becomes different set->setName(uniqueNameForAggregation(set->name())); (void)new AggregationListWidgetItem(mAggregationList, *set); } else { delete set; // b0rken } } ++idx; } } } } void ConfigureAggregationsDialog::Private::exportAggregationButtonClicked() { const QList list = mAggregationList->selectedItems(); if (list.isEmpty()) { return; } const QString filename = QFileDialog::getSaveFileName(q, i18n("Export Aggregation"), QString(), i18n("All Files (*)")); if (!filename.isEmpty()) { KConfig config(filename); KConfigGroup grp(&config, QStringLiteral("MessageListView::Aggregations")); grp.writeEntry("Count", list.count()); int idx = 0; for (QListWidgetItem *item : list) { AggregationListWidgetItem *themeItem = static_cast< AggregationListWidgetItem * >(item); grp.writeEntry(QStringLiteral("Set%1").arg(idx), themeItem->aggregation()->saveToString()); ++idx; } } } #include "moc_configureaggregationsdialog.cpp" diff --git a/messagelist/src/utils/configurethemesdialog.cpp b/messagelist/src/utils/configurethemesdialog.cpp index 930d4978..1bc80406 100644 --- a/messagelist/src/utils/configurethemesdialog.cpp +++ b/messagelist/src/utils/configurethemesdialog.cpp @@ -1,498 +1,494 @@ /****************************************************************************** * * Copyright 2008 Szymon Tomasz Stefanek * * 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 "utils/configurethemesdialog.h" #include "utils/configurethemesdialog_p.h" #include "utils/themeeditor.h" #include "core/theme.h" #include "core/manager.h" #include #include #include #include #include #include #include -#include #include #include #include #include #include #include namespace MessageList { namespace Utils { class ThemeListWidgetItem : public QListWidgetItem { public: ThemeListWidgetItem(QListWidget *par, const Core::Theme &set) : QListWidgetItem(set.name(), par) { mTheme = new Core::Theme(set); } ~ThemeListWidgetItem() { delete mTheme; } Core::Theme *theme() const { return mTheme; } void forgetTheme() { mTheme = nullptr; } private: Core::Theme *mTheme = nullptr; }; class ThemeListWidget : public QListWidget { public: ThemeListWidget(QWidget *parent) : QListWidget(parent) { } public: // need a larger but shorter QListWidget QSize sizeHint() const override { return QSize(450, 128); } }; } // namespace Utils } // namespace MessageList using namespace MessageList::Core; using namespace MessageList::Utils; ConfigureThemesDialog::ConfigureThemesDialog(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setAttribute(Qt::WA_DeleteOnClose); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &ConfigureThemesDialog::reject); setWindowTitle(i18n("Customize Themes")); QWidget *base = new QWidget(this); mainLayout->addWidget(base); mainLayout->addWidget(buttonBox); QGridLayout *g = new QGridLayout(base); g->setContentsMargins(0, 0, 0, 0); d->mThemeList = new ThemeListWidget(base); d->mThemeList->setSelectionMode(QAbstractItemView::ExtendedSelection); d->mThemeList->setSortingEnabled(true); g->addWidget(d->mThemeList, 0, 0, 7, 1); connect(d->mThemeList, &ThemeListWidget::itemClicked, this, [this](QListWidgetItem *item) { d->themeListItemClicked(item); }); d->mNewThemeButton = new QPushButton(i18n("New Theme"), base); d->mNewThemeButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); - d->mNewThemeButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); g->addWidget(d->mNewThemeButton, 0, 1); connect(d->mNewThemeButton, &QPushButton::clicked, this, [this]() { d->newThemeButtonClicked(); }); d->mCloneThemeButton = new QPushButton(i18n("Clone Theme"), base); d->mCloneThemeButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); - d->mCloneThemeButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); g->addWidget(d->mCloneThemeButton, 1, 1); connect(d->mCloneThemeButton, &QPushButton::clicked, this, [this]() { d->cloneThemeButtonClicked(); }); QFrame *f = new QFrame(base); f->setFrameStyle(QFrame::Sunken | QFrame::HLine); f->setMinimumHeight(24); g->addWidget(f, 2, 1, Qt::AlignVCenter); d->mExportThemeButton = new QPushButton(i18n("Export Theme..."), base); g->addWidget(d->mExportThemeButton, 3, 1); connect(d->mExportThemeButton, &QPushButton::clicked, this, [this]() { d->exportThemeButtonClicked(); }); d->mImportThemeButton = new QPushButton(i18n("Import Theme..."), base); g->addWidget(d->mImportThemeButton, 4, 1); connect(d->mImportThemeButton, &QPushButton::clicked, this, [this]() { d->importThemeButtonClicked(); }); f = new QFrame(base); f->setFrameStyle(QFrame::Sunken | QFrame::HLine); f->setMinimumHeight(24); g->addWidget(f, 5, 1, Qt::AlignVCenter); d->mDeleteThemeButton = new QPushButton(i18n("Delete Theme"), base); d->mDeleteThemeButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); - d->mDeleteThemeButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); g->addWidget(d->mDeleteThemeButton, 6, 1); connect(d->mDeleteThemeButton, &QPushButton::clicked, this, [this]() { d->deleteThemeButtonClicked(); }); d->mEditor = new ThemeEditor(base); g->addWidget(d->mEditor, 8, 0, 1, 2); connect(d->mEditor, &ThemeEditor::themeNameChanged, this, [this]() { d->editedThemeNameChanged(); }); g->setColumnStretch(0, 1); g->setRowStretch(4, 1); connect(okButton, &QPushButton::clicked, this, [this]() { d->okButtonClicked(); }); d->fillThemeList(); } ConfigureThemesDialog::~ConfigureThemesDialog() { delete d; } void ConfigureThemesDialog::selectTheme(const QString &themeId) { ThemeListWidgetItem *item = d->findThemeItemById(themeId); if (item) { d->mThemeList->setCurrentItem(item); d->themeListItemClicked(item); } } void ConfigureThemesDialog::Private::okButtonClicked() { commitEditor(); Manager::instance()->removeAllThemes(); const int c = mThemeList->count(); int i = 0; while (i < c) { ThemeListWidgetItem *item = dynamic_cast< ThemeListWidgetItem * >(mThemeList->item(i)); if (item) { Manager::instance()->addTheme(item->theme()); item->forgetTheme(); } ++i; } Manager::instance()->themesConfigurationCompleted(); Q_EMIT q->okClicked(); q->close(); // this will delete too } void ConfigureThemesDialog::Private::commitEditor() { Theme *editedTheme = mEditor->editedTheme(); if (!editedTheme) { return; } mEditor->commit(); ThemeListWidgetItem *editedItem = findThemeItemByTheme(editedTheme); if (!editedItem) { return; } // We must reset the runtime column state as the columns might have // totally changed in the editor editedTheme->resetColumnState(); QString goodName = uniqueNameForTheme(editedTheme->name(), editedTheme); editedTheme->setName(goodName); editedItem->setText(goodName); } void ConfigureThemesDialog::Private::editedThemeNameChanged() { Theme *set = mEditor->editedTheme(); if (!set) { return; } ThemeListWidgetItem *it = findThemeItemByTheme(set); if (!it) { return; } QString goodName = uniqueNameForTheme(set->name(), set); it->setText(goodName); } void ConfigureThemesDialog::Private::fillThemeList() { const QMap< QString, Theme * > &sets = Manager::instance()->themes(); QMap< QString, Theme * >::ConstIterator end(sets.constEnd()); for (QMap< QString, Theme * >::ConstIterator it = sets.constBegin(); it != end; ++it) { (void)new ThemeListWidgetItem(mThemeList, *(*it)); } } void ConfigureThemesDialog::Private::themeListItemClicked(QListWidgetItem *cur) { commitEditor(); const int numberOfSelectedItem(mThemeList->selectedItems().count()); ThemeListWidgetItem *item = cur ? dynamic_cast< ThemeListWidgetItem * >(cur) : nullptr; mDeleteThemeButton->setEnabled(item && !item->theme()->readOnly()); mCloneThemeButton->setEnabled(numberOfSelectedItem == 1); mEditor->editTheme(item ? item->theme() : nullptr); mExportThemeButton->setEnabled(numberOfSelectedItem > 0); if (item && !item->isSelected()) { item->setSelected(true); // make sure it's true } } ThemeListWidgetItem *ConfigureThemesDialog::Private::findThemeItemById(const QString &themeId) { const int c = mThemeList->count(); int i = 0; while (i < c) { ThemeListWidgetItem *item = dynamic_cast< ThemeListWidgetItem * >(mThemeList->item(i)); if (item) { if (item->theme()->id() == themeId) { return item; } } ++i; } return nullptr; } ThemeListWidgetItem *ConfigureThemesDialog::Private::findThemeItemByName(const QString &name, Theme *skipTheme) { const int c = mThemeList->count(); int i = 0; while (i < c) { ThemeListWidgetItem *item = dynamic_cast< ThemeListWidgetItem * >(mThemeList->item(i)); if (item) { if (item->theme() != skipTheme) { if (item->theme()->name() == name) { return item; } } } ++i; } return nullptr; } ThemeListWidgetItem *ConfigureThemesDialog::Private::findThemeItemByTheme(Theme *set) { const int c = mThemeList->count(); int i = 0; while (i < c) { ThemeListWidgetItem *item = dynamic_cast< ThemeListWidgetItem * >(mThemeList->item(i)); if (item) { if (item->theme() == set) { return item; } } ++i; } return nullptr; } QString ConfigureThemesDialog::Private::uniqueNameForTheme(const QString &baseName, Theme *skipTheme) { QString ret = baseName; if (ret.isEmpty()) { ret = i18n("Unnamed Theme"); } int idx = 1; ThemeListWidgetItem *item = findThemeItemByName(ret, skipTheme); while (item) { idx++; ret = QStringLiteral("%1 %2").arg(baseName, QString::number(idx)); item = findThemeItemByName(ret, skipTheme); } return ret; } void ConfigureThemesDialog::Private::newThemeButtonClicked() { const int numberOfSelectedItem(mThemeList->selectedItems().count()); Theme emptyTheme; emptyTheme.setName(uniqueNameForTheme(i18n("New Theme"))); Theme::Column *col = new Theme::Column(); col->setLabel(i18n("New Column")); col->setVisibleByDefault(true); col->addMessageRow(new Theme::Row()); col->addGroupHeaderRow(new Theme::Row()); emptyTheme.addColumn(col); ThemeListWidgetItem *item = new ThemeListWidgetItem(mThemeList, emptyTheme); mThemeList->setCurrentItem(item); Core::Theme *theme = item->theme(); if (theme) { mEditor->editTheme(theme); mDeleteThemeButton->setEnabled(!theme->readOnly()); mExportThemeButton->setEnabled(item); mCloneThemeButton->setEnabled(numberOfSelectedItem == 1); } else { mDeleteThemeButton->setEnabled(false); mExportThemeButton->setEnabled(false); mCloneThemeButton->setEnabled(false); } } void ConfigureThemesDialog::Private::cloneThemeButtonClicked() { ThemeListWidgetItem *item = dynamic_cast< ThemeListWidgetItem * >(mThemeList->currentItem()); if (!item) { return; } commitEditor(); item->setSelected(false); Theme copyTheme(*(item->theme())); copyTheme.setReadOnly(false); copyTheme.detach(); // detach shared data copyTheme.generateUniqueId(); // regenerate id so it becomes different copyTheme.setName(uniqueNameForTheme(item->theme()->name())); item = new ThemeListWidgetItem(mThemeList, copyTheme); mThemeList->setCurrentItem(item); mEditor->editTheme(item->theme()); const int numberOfSelectedItem(mThemeList->selectedItems().count()); mDeleteThemeButton->setEnabled(!item->theme()->readOnly()); mExportThemeButton->setEnabled(true); mCloneThemeButton->setEnabled(numberOfSelectedItem == 1); } void ConfigureThemesDialog::Private::deleteThemeButtonClicked() { const QList list = mThemeList->selectedItems(); if (list.isEmpty()) { return; } if (KMessageBox::Yes == KMessageBox::questionYesNo(q, list.count() > 1 ? i18n("Do you want to delete selected themes?") : i18n("Do you want to delete \"%1\"?", list.first()->text()), i18nc("@title:window", "Delete Theme"))) { mEditor->editTheme(nullptr); // forget it for (QListWidgetItem *it : list) { ThemeListWidgetItem *item = dynamic_cast< ThemeListWidgetItem * >(it); if (!item) { return; } if (!item->theme()->readOnly()) { delete item;// this will trigger themeListCurrentItemChanged() } if (mThemeList->count() < 2) { break; // no way: desperately try to keep at least one option set alive :) } } ThemeListWidgetItem *newItem = dynamic_cast< ThemeListWidgetItem * >(mThemeList->currentItem()); mDeleteThemeButton->setEnabled(newItem && !newItem->theme()->readOnly()); mExportThemeButton->setEnabled(newItem); const int numberOfSelectedItem(mThemeList->selectedItems().count()); mCloneThemeButton->setEnabled(numberOfSelectedItem == 1); } } void ConfigureThemesDialog::Private::importThemeButtonClicked() { const QString filename = QFileDialog::getOpenFileName(q, i18n("Import Theme")); if (!filename.isEmpty()) { KConfig config(filename); if (config.hasGroup(QStringLiteral("MessageListView::Themes"))) { KConfigGroup grp(&config, QStringLiteral("MessageListView::Themes")); const int cnt = grp.readEntry("Count", 0); int idx = 0; while (idx < cnt) { const QString data = grp.readEntry(QStringLiteral("Set%1").arg(idx), QString()); if (!data.isEmpty()) { Theme *set = new Theme(); if (set->loadFromString(data)) { set->setReadOnly(false); set->detach(); // detach shared data set->generateUniqueId(); // regenerate id so it becomes different set->setName(uniqueNameForTheme(set->name())); (void)new ThemeListWidgetItem(mThemeList, *set); } else { delete set; } } ++idx; } } } } void ConfigureThemesDialog::Private::exportThemeButtonClicked() { const QList list = mThemeList->selectedItems(); if (list.isEmpty()) { return; } const QString filename = QFileDialog::getSaveFileName(q, i18n("Export Theme"), QString(), i18n("All Files (*)")); if (!filename.isEmpty()) { KConfig config(filename); KConfigGroup grp(&config, QStringLiteral("MessageListView::Themes")); grp.writeEntry("Count", list.count()); int idx = 0; for (QListWidgetItem *item : list) { ThemeListWidgetItem *themeItem = static_cast< ThemeListWidgetItem * >(item); grp.writeEntry(QStringLiteral("Set%1").arg(idx), themeItem->theme()->saveToString()); ++idx; } } } #include "moc_configurethemesdialog.cpp" diff --git a/messagelist/src/widget.cpp b/messagelist/src/widget.cpp index 0e5cfbf6..e786add9 100644 --- a/messagelist/src/widget.cpp +++ b/messagelist/src/widget.cpp @@ -1,762 +1,761 @@ /* Copyright (c) 2009 Kevin Ottens 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 "widget.h" #include #include #include #include #include "storagemodel.h" #include "core/messageitem.h" #include "core/view.h" #include #include #include #include #include #include #include #include #include "messagelist_debug.h" #include -#include #include #include #include #include #include #include "core/groupheaderitem.h" #include #include #include #include #include namespace MessageList { class Q_DECL_HIDDEN Widget::Private { public: Private(Widget *owner) : q(owner) , mLastSelectedMessage(-1) , mXmlGuiClient(nullptr) , mMonitor(nullptr) { } Akonadi::Item::List selectionAsItems() const; Akonadi::Item itemForRow(int row) const; KMime::Message::Ptr messageForRow(int row) const; Widget *const q; int mLastSelectedMessage; KXMLGUIClient *mXmlGuiClient = nullptr; QModelIndex mGroupHeaderItemIndex; Akonadi::Monitor *mMonitor = nullptr; }; } // namespace MessageList using namespace MessageList; using namespace Akonadi; Widget::Widget(QWidget *parent) : Core::Widget(parent) , d(new Private(this)) { populateStatusFilterCombo(); d->mMonitor = new Akonadi::Monitor(this); d->mMonitor->setObjectName(QStringLiteral("MessageListTagMonitor")); d->mMonitor->setTypeMonitored(Akonadi::Monitor::Tags); connect(d->mMonitor, &Akonadi::Monitor::tagAdded, this, &Widget::populateStatusFilterCombo); connect(d->mMonitor, &Akonadi::Monitor::tagRemoved, this, &Widget::populateStatusFilterCombo); connect(d->mMonitor, &Akonadi::Monitor::tagChanged, this, &Widget::populateStatusFilterCombo); } Widget::~Widget() { delete d; } void Widget::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) { d->mXmlGuiClient = xmlGuiClient; } bool Widget::canAcceptDrag(const QDropEvent *e) { if (e->source() == view()->viewport()) { return false; } Collection::List collections = static_cast(storageModel())->displayedCollections(); if (collections.size() != 1) { return false; // no folder here or too many (in case we can't decide where the drop will end) } const Collection target = collections.first(); if ((target.rights() & Collection::CanCreateItem) == 0) { return false; // no way to drag into } const QList urls = e->mimeData()->urls(); for (const QUrl &url : urls) { const Collection collection = Collection::fromUrl(url); if (collection.isValid()) { // You're not supposed to drop collections here return false; } else { // Yay, this is an item! QUrlQuery query(url); const QString type = query.queryItemValue(QStringLiteral("type")); if (!target.contentMimeTypes().contains(type)) { return false; } } } return true; } bool Widget::selectNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop) { return view()->selectNextMessageItem(messageTypeFilter, existingSelectionBehaviour, centerItem, loop); } bool Widget::selectPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop) { return view()->selectPreviousMessageItem(messageTypeFilter, existingSelectionBehaviour, centerItem, loop); } bool Widget::focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop) { return view()->focusNextMessageItem(messageTypeFilter, centerItem, loop); } bool Widget::focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop) { return view()->focusPreviousMessageItem(messageTypeFilter, centerItem, loop); } void Widget::selectFocusedMessageItem(bool centerItem) { view()->selectFocusedMessageItem(centerItem); } bool Widget::selectFirstMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem) { return view()->selectFirstMessageItem(messageTypeFilter, centerItem); } bool Widget::selectLastMessageItem(Core::MessageTypeFilter messageTypeFilter, bool centerItem) { return view()->selectLastMessageItem(messageTypeFilter, centerItem); } void Widget::selectAll() { view()->setAllGroupsExpanded(true); view()->selectAll(); } void Widget::setCurrentThreadExpanded(bool expand) { view()->setCurrentThreadExpanded(expand); } void Widget::setAllThreadsExpanded(bool expand) { view()->setAllThreadsExpanded(expand); } void Widget::setAllGroupsExpanded(bool expand) { view()->setAllGroupsExpanded(expand); } void Widget::focusQuickSearch(const QString &selectedText) { view()->focusQuickSearch(selectedText); } void Widget::setQuickSearchClickMessage(const QString &msg) { view()->setQuickSearchClickMessage(msg); } void Widget::fillMessageTagCombo() { Akonadi::TagFetchJob *fetchJob = new Akonadi::TagFetchJob(this); fetchJob->fetchScope().fetchAttribute(); connect(fetchJob, &Akonadi::TagFetchJob::result, this, &Widget::slotTagsFetched); } void Widget::slotTagsFetched(KJob *job) { if (job->error()) { qCWarning(MESSAGELIST_LOG) << "Failed to load tags " << job->errorString(); return; } Akonadi::TagFetchJob *fetchJob = static_cast(job); KConfigGroup conf(MessageList::MessageListSettings::self()->config(), "MessageListView"); const QString tagSelected = conf.readEntry(QStringLiteral("TagSelected")); if (tagSelected.isEmpty()) { setCurrentStatusFilterItem(); return; } const QStringList tagSelectedLst = tagSelected.split(QLatin1Char(',')); addMessageTagItem(QIcon::fromTheme(QStringLiteral("mail-flag")).pixmap(16, 16), i18nc("Item in list of Akonadi tags, to show all e-mails", "All"), QString()); QStringList tagFound; foreach (const Akonadi::Tag &akonadiTag, fetchJob->tags()) { if (tagSelectedLst.contains(akonadiTag.url().url())) { tagFound.append(akonadiTag.url().url()); QString iconName = QStringLiteral("mail-tagged"); const QString label = akonadiTag.name(); const QString id = akonadiTag.url().url(); const Akonadi::TagAttribute *attr = akonadiTag.attribute(); if (attr) { iconName = attr->iconName(); } addMessageTagItem(QIcon::fromTheme(iconName).pixmap(16, 16), label, QVariant(id)); } } conf.writeEntry(QStringLiteral("TagSelected"), tagFound); conf.sync(); setCurrentStatusFilterItem(); } void Widget::viewMessageSelected(MessageList::Core::MessageItem *msg) { int row = -1; if (msg) { row = msg->currentModelIndexRow(); } if (!msg || !msg->isValid() || !storageModel()) { d->mLastSelectedMessage = -1; Q_EMIT messageSelected(Item()); return; } Q_ASSERT(row >= 0); d->mLastSelectedMessage = row; Q_EMIT messageSelected(d->itemForRow(row)); // this MAY be null } void Widget::viewMessageActivated(MessageList::Core::MessageItem *msg) { Q_ASSERT(msg); // must not be null Q_ASSERT(storageModel()); if (!msg->isValid()) { return; } int row = msg->currentModelIndexRow(); Q_ASSERT(row >= 0); // The assert below may fail when quickly opening and closing a non-selected thread. // This will actually activate the item without selecting it... //Q_ASSERT( d->mLastSelectedMessage == row ); if (d->mLastSelectedMessage != row) { // Very ugly. We are activating a non selected message. // This is very likely a double click on the plus sign near a thread leader. // Dealing with mLastSelectedMessage here would be expensive: it would involve releasing the last selected, // emitting signals, handling recursion... ugly. // We choose a very simple solution: double clicking on the plus sign near a thread leader does // NOT activate the message (i.e open it in a toplevel window) if it isn't previously selected. return; } Q_EMIT messageActivated(d->itemForRow(row)); // this MAY be null } void Widget::viewSelectionChanged() { Q_EMIT selectionChanged(); if (!currentMessageItem()) { Q_EMIT messageSelected(Item()); } } void Widget::viewMessageListContextPopupRequest(const QList< MessageList::Core::MessageItem * > &selectedItems, const QPoint &globalPos) { Q_UNUSED(selectedItems); if (!d->mXmlGuiClient) { return; } QMenu *popup = static_cast(d->mXmlGuiClient->factory()->container( QStringLiteral("akonadi_messagelist_contextmenu"), d->mXmlGuiClient)); if (popup) { popup->exec(globalPos); } } void Widget::viewMessageStatusChangeRequest(MessageList::Core::MessageItem *msg, Akonadi::MessageStatus set, Akonadi::MessageStatus clear) { Q_ASSERT(msg); // must not be null Q_ASSERT(storageModel()); if (!msg->isValid()) { return; } int row = msg->currentModelIndexRow(); Q_ASSERT(row >= 0); Item item = d->itemForRow(row); Q_ASSERT(item.isValid()); Q_EMIT messageStatusChangeRequest(item, set, clear); } void Widget::viewGroupHeaderContextPopupRequest(MessageList::Core::GroupHeaderItem *ghi, const QPoint &globalPos) { Q_UNUSED(ghi); QMenu menu(this); QAction *act = nullptr; QModelIndex index = view()->model()->index(ghi, 0); d->mGroupHeaderItemIndex = index; if (view()->isExpanded(index)) { act = menu.addAction(i18n("Collapse Group")); connect(act, &QAction::triggered, this, &Widget::slotCollapseItem); } else { act = menu.addAction(i18n("Expand Group")); connect(act, &QAction::triggered, this, &Widget::slotExpandItem); } menu.addSeparator(); act = menu.addAction(i18n("Expand All Groups")); connect(act, &QAction::triggered, view(), &Core::View::slotExpandAllGroups); act = menu.addAction(i18n("Collapse All Groups")); connect(act, &QAction::triggered, view(), &Core::View::slotCollapseAllGroups); menu.exec(globalPos); } void Widget::viewDragEnterEvent(QDragEnterEvent *e) { if (!canAcceptDrag(e)) { e->ignore(); return; } e->accept(); } void Widget::viewDragMoveEvent(QDragMoveEvent *e) { if (!canAcceptDrag(e)) { e->ignore(); return; } e->accept(); } enum DragMode { DragCopy, DragMove, DragCancel }; void Widget::viewDropEvent(QDropEvent *e) { if (!canAcceptDrag(e)) { e->ignore(); return; } const QList urls = e->mimeData()->urls(); if (urls.isEmpty()) { qCWarning(MESSAGELIST_LOG) << "Could not decode drag data!"; e->ignore(); return; } e->accept(); int action; if ((e->possibleActions() & Qt::MoveAction) == 0) { // We can't move anyway action = DragCopy; } else { action = DragCancel; const auto keybstate = QApplication::keyboardModifiers(); if (keybstate & Qt::CTRL) { action = DragCopy; } else if (keybstate & Qt::SHIFT) { action = DragMove; } else { QMenu menu; QAction *moveAction = menu.addAction(QIcon::fromTheme(QStringLiteral("go-jump")), i18n("&Move Here")); QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy Here")); menu.addSeparator(); menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-cancel")), i18n("C&ancel")); QAction *menuChoice = menu.exec(QCursor::pos()); if (menuChoice == moveAction) { action = DragMove; } else if (menuChoice == copyAction) { action = DragCopy; } else { action = DragCancel; } } } if (action == DragCancel) { return; } Collection::List collections = static_cast(storageModel())->displayedCollections(); Collection target = collections.at(0); Item::List items; items.reserve(urls.count()); for (const QUrl &url : qAsConst(urls)) { items << Item::fromUrl(url); } if (action == DragCopy) { new ItemCopyJob(items, target, this); } else if (action == DragMove) { new ItemMoveJob(items, target, this); } } void Widget::viewStartDragRequest() { Collection::List collections = static_cast(storageModel())->displayedCollections(); if (collections.isEmpty()) { return; // no folder here } const QList selection = view()->selectionAsMessageItemList(); if (selection.isEmpty()) { return; } bool readOnly = false; for (const Collection &c : qAsConst(collections)) { // We won't be able to remove items from this collection if ((c.rights() & Collection::CanDeleteItem) == 0) { // So the drag will be read-only readOnly = true; break; } } QList urls; urls.reserve(selection.count()); for (Core::MessageItem *mi : selection) { const Item i = d->itemForRow(mi->currentModelIndexRow()); QUrl url = i.url(Item::Item::Item::UrlWithMimeType); QUrlQuery query(url); query.addQueryItem(QStringLiteral("parent"), QString::number(mi->parentCollectionId())); url.setQuery(query); urls << url; } QMimeData *mimeData = new QMimeData; mimeData->setUrls(urls); QDrag *drag = new QDrag(view()->viewport()); drag->setMimeData(mimeData); // Set pixmap QPixmap pixmap; if (selection.size() == 1) { - pixmap = QPixmap(DesktopIcon(QStringLiteral("mail-message"), KIconLoader::SizeSmall)); + pixmap = QIcon::fromTheme(QStringLiteral("mail-message")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)); } else { - pixmap = QPixmap(DesktopIcon(QStringLiteral("document-multiple"), KIconLoader::SizeSmall)); + pixmap = QIcon::fromTheme(QStringLiteral("document-multiple")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)); } // Calculate hotspot (as in Konqueror) if (!pixmap.isNull()) { drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2)); drag->setPixmap(pixmap); } if (readOnly) { drag->exec(Qt::CopyAction); } else { drag->exec(Qt::CopyAction | Qt::MoveAction); } } Item::List Widget::Private::selectionAsItems() const { Item::List res; const QList selection = q->view()->selectionAsMessageItemList(); res.reserve(selection.count()); for (Core::MessageItem *mi : qAsConst(selection)) { Item i = itemForRow(mi->currentModelIndexRow()); Q_ASSERT(i.isValid()); res << i; } return res; } Item Widget::Private::itemForRow(int row) const { return static_cast(q->storageModel())->itemForRow(row); } KMime::Message::Ptr Widget::Private::messageForRow(int row) const { return static_cast(q->storageModel())->messageForRow(row); } Item Widget::currentItem() const { Core::MessageItem *mi = view()->currentMessageItem(); if (mi == nullptr) { return Item(); } return d->itemForRow(mi->currentModelIndexRow()); } KMime::Message::Ptr Widget::currentMessage() const { Core::MessageItem *mi = view()->currentMessageItem(); if (mi == nullptr) { return KMime::Message::Ptr(); } return d->messageForRow(mi->currentModelIndexRow()); } QList Widget::selectionAsMessageList(bool includeCollapsedChildren) const { QList lstMiPtr; const QList lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren); if (lstMi.isEmpty()) { return lstMiPtr; } lstMiPtr.reserve(lstMi.count()); for (Core::MessageItem *it : qAsConst(lstMi)) { lstMiPtr.append(d->messageForRow(it->currentModelIndexRow())); } return lstMiPtr; } Akonadi::Item::List Widget::selectionAsMessageItemList(bool includeCollapsedChildren) const { Akonadi::Item::List lstMiPtr; const QList lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren); if (lstMi.isEmpty()) { return lstMiPtr; } lstMiPtr.reserve(lstMi.count()); for (Core::MessageItem *it : qAsConst(lstMi)) { lstMiPtr.append(d->itemForRow(it->currentModelIndexRow())); } return lstMiPtr; } QVector Widget::selectionAsMessageItemListId(bool includeCollapsedChildren) const { QVector lstMiPtr; const QList lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren); if (lstMi.isEmpty()) { return lstMiPtr; } lstMiPtr.reserve(lstMi.count()); for (Core::MessageItem *it : qAsConst(lstMi)) { lstMiPtr.append(d->itemForRow(it->currentModelIndexRow()).id()); } return lstMiPtr; } QList Widget::selectionAsListMessageId(bool includeCollapsedChildren) const { QList lstMiPtr; const QList lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren); if (lstMi.isEmpty()) { return lstMiPtr; } lstMiPtr.reserve(lstMi.count()); for (Core::MessageItem *it : qAsConst(lstMi)) { lstMiPtr.append(d->itemForRow(it->currentModelIndexRow()).id()); } return lstMiPtr; } Akonadi::Item::List Widget::currentThreadAsMessageList() const { Akonadi::Item::List lstMiPtr; const QList lstMi = view()->currentThreadAsMessageItemList(); if (lstMi.isEmpty()) { return lstMiPtr; } lstMiPtr.reserve(lstMi.count()); for (Core::MessageItem *it : qAsConst(lstMi)) { lstMiPtr.append(d->itemForRow(it->currentModelIndexRow())); } return lstMiPtr; } MessageList::Core::QuickSearchLine::SearchOptions Widget::currentOptions() const { return view()->currentOptions(); } QList Widget::currentFilterStatus() const { return view()->currentFilterStatus(); } QString Widget::currentFilterSearchString() const { return view()->currentFilterSearchString(); } bool Widget::isThreaded() const { return view()->isThreaded(); } bool Widget::selectionEmpty() const { return view()->selectionEmpty(); } bool Widget::getSelectionStats( Akonadi::Item::List &selectedItems, Akonadi::Item::List &selectedVisibleItems, bool *allSelectedBelongToSameThread, bool includeCollapsedChildren) const { if (!storageModel()) { return false; } selectedItems.clear(); selectedVisibleItems.clear(); const QList< Core::MessageItem * > selected = view()->selectionAsMessageItemList(includeCollapsedChildren); Core::MessageItem *topmost = nullptr; *allSelectedBelongToSameThread = true; for (Core::MessageItem *it : qAsConst(selected)) { const Item item = d->itemForRow(it->currentModelIndexRow()); selectedItems.append(item); if (view()->isDisplayedWithParentsExpanded(it)) { selectedVisibleItems.append(item); } if (topmost == nullptr) { topmost = (*it).topmostMessage(); } else { if (topmost != (*it).topmostMessage()) { *allSelectedBelongToSameThread = false; } } } return true; } void Widget::deletePersistentSet(MessageList::Core::MessageItemSetReference ref) { view()->deletePersistentSet(ref); } void Widget::markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark) { QList< Core::MessageItem * > lstPersistent = view()->persistentSetCurrentMessageItemList(ref); if (!lstPersistent.isEmpty()) { view()->markMessageItemsAsAboutToBeRemoved(lstPersistent, bMark); } } Akonadi::Item::List Widget::itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref) { Akonadi::Item::List lstItem; const QList< Core::MessageItem * > refList = view()->persistentSetCurrentMessageItemList(ref); if (!refList.isEmpty()) { lstItem.reserve(refList.count()); for (Core::MessageItem *it : qAsConst(refList)) { lstItem.append(d->itemForRow(it->currentModelIndexRow())); } } return lstItem; } MessageList::Core::MessageItemSetReference Widget::selectionAsPersistentSet(bool includeCollapsedChildren) const { QList lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren); if (lstMi.isEmpty()) { return -1; } return view()->createPersistentSet(lstMi); } MessageList::Core::MessageItemSetReference Widget::currentThreadAsPersistentSet() const { QList lstMi = view()->currentThreadAsMessageItemList(); if (lstMi.isEmpty()) { return -1; } return view()->createPersistentSet(lstMi); } Akonadi::Collection Widget::currentCollection() const { Collection::List collections = static_cast(storageModel())->displayedCollections(); if (collections.size() != 1) { return Akonadi::Collection(); // no folder here or too many (in case we can't decide where the drop will end) } return collections.first(); } void Widget::slotCollapseItem() { view()->setCollapseItem(d->mGroupHeaderItemIndex); } void Widget::slotExpandItem() { view()->setExpandItem(d->mGroupHeaderItemIndex); } diff --git a/messageviewer/src/widgets/mailsourceviewtextbrowserwidget.cpp b/messageviewer/src/widgets/mailsourceviewtextbrowserwidget.cpp index fb6f5cb5..5a5bda58 100644 --- a/messageviewer/src/widgets/mailsourceviewtextbrowserwidget.cpp +++ b/messageviewer/src/widgets/mailsourceviewtextbrowserwidget.cpp @@ -1,183 +1,179 @@ /* * * This file is part of KMail, the KDE mail client. * * Copyright (c) 2002-2003 Carsten Pfeiffer * Copyright (c) 2003 Zack Rusin * * KMail 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. * * KMail 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 * * In addition, as a special exception, the copyright holders give * permission to link the code of this program with any edition of * the Qt library by Trolltech AS, Norway (or with modified versions * of Qt that use the same license as Qt), and distribute linked * combinations including the two. You must obey the GNU General * Public License in all respects for all of the code used other than * Qt. If you modify this file, you may extend this exception to * your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from * your version. */ #include "messageviewer_debug.h" #include "mailsourceviewtextbrowserwidget.h" #include "messageviewer/messageviewerutil.h" #include "findbar/findbarsourceview.h" #include "kpimtextedit/slidecontainer.h" #include "PimCommon/PimUtil" #include "kpimtextedit/texttospeechwidget.h" #include "kpimtextedit/texttospeechinterface.h" #include #include #include #include #include #include #include -#include #include #include #include #include #include #include using namespace MessageViewer; MailSourceViewTextBrowserWidget::MailSourceViewTextBrowserWidget(const QString &syntax, QWidget *parent) : QWidget(parent) { QVBoxLayout *lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); mTextToSpeechWidget = new KPIMTextEdit::TextToSpeechWidget; mTextToSpeechWidget->setObjectName(QStringLiteral("texttospeech")); lay->addWidget(mTextToSpeechWidget); KPIMTextEdit::TextToSpeechInterface *textToSpeechInterface = new KPIMTextEdit::TextToSpeechInterface(mTextToSpeechWidget, this); mTextBrowser = new MailSourceViewTextBrowser(textToSpeechInterface); mTextBrowser->setObjectName(QStringLiteral("textbrowser")); mTextBrowser->setLineWrapMode(QPlainTextEdit::NoWrap); mTextBrowser->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); const KSyntaxHighlighting::Definition def = mRepo.definitionForName(syntax); if (!def.isValid()) { qCWarning(MESSAGEVIEWER_LOG) << "Invalid definition name"; } KSyntaxHighlighting::SyntaxHighlighter *hl = new KSyntaxHighlighting::SyntaxHighlighter( mTextBrowser->document()); hl->setTheme((palette().color(QPalette::Base).lightness() < 128) ? mRepo.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : mRepo.defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); hl->setDefinition(def); connect(mTextBrowser, &MailSourceViewTextBrowser::findText, this, &MailSourceViewTextBrowserWidget::slotFind); lay->addWidget(mTextBrowser); mSliderContainer = new KPIMTextEdit::SlideContainer(this); mFindBar = new FindBarSourceView(mTextBrowser, this); mFindBar->setObjectName(QStringLiteral("findbar")); connect(mFindBar, &FindBarSourceView::hideFindBar, mSliderContainer, &KPIMTextEdit::SlideContainer::slideOut); mSliderContainer->setContent(mFindBar); lay->addWidget(mSliderContainer); QShortcut *shortcut = new QShortcut(this); shortcut->setKey(Qt::Key_F + Qt::CTRL); connect(shortcut, &QShortcut::activated, this, &MailSourceViewTextBrowserWidget::slotFind); } void MailSourceViewTextBrowserWidget::slotFind() { if (mTextBrowser->textCursor().hasSelection()) { mFindBar->setText(mTextBrowser->textCursor().selectedText()); } mSliderContainer->slideIn(); mFindBar->focusAndSetCursor(); } void MailSourceViewTextBrowserWidget::setText(const QString &text) { mTextBrowser->setPlainText(text); } void MailSourceViewTextBrowserWidget::setPlainText(const QString &text) { mTextBrowser->setPlainText(text); } void MailSourceViewTextBrowserWidget::setFixedFont() { mTextBrowser->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); } MessageViewer::MailSourceViewTextBrowser *MailSourceViewTextBrowserWidget::textBrowser() const { return mTextBrowser; } MailSourceViewTextBrowser::MailSourceViewTextBrowser( KPIMTextEdit::TextToSpeechInterface *textToSpeechInterface, QWidget *parent) : QPlainTextEdit(parent) , mTextToSpeechInterface(textToSpeechInterface) { } void MailSourceViewTextBrowser::contextMenuEvent(QContextMenuEvent *event) { QMenu *popup = createStandardContextMenu(); if (popup) { popup->addSeparator(); popup->addAction(KStandardAction::find(this, &MailSourceViewTextBrowser::findText, this)); //Code from KTextBrowser - KIconTheme::assignIconsToContextMenu(isReadOnly() ? KIconTheme::ReadOnlyText - : KIconTheme::TextEditor, - popup->actions()); if (mTextToSpeechInterface->isReady()) { popup->addSeparator(); popup->addAction(QIcon::fromTheme(QStringLiteral( "preferences-desktop-text-to-speech")), i18n( "Speak Text"), this, &MailSourceViewTextBrowser::slotSpeakText); } popup->addSeparator(); popup->addAction(KStandardAction::saveAs(this, &MailSourceViewTextBrowser::slotSaveAs, this)); popup->exec(event->globalPos()); delete popup; } } void MailSourceViewTextBrowser::slotSaveAs() { PimCommon::Util::saveTextAs(toPlainText(), QString(), this); } void MailSourceViewTextBrowser::slotSpeakText() { QString text; if (textCursor().hasSelection()) { text = textCursor().selectedText(); } else { text = toPlainText(); } mTextToSpeechInterface->say(text); } diff --git a/templateparser/src/CMakeLists.txt b/templateparser/src/CMakeLists.txt index fb3053b7..04bd53c7 100644 --- a/templateparser/src/CMakeLists.txt +++ b/templateparser/src/CMakeLists.txt @@ -1,126 +1,124 @@ add_definitions(-DTRANSLATION_DOMAIN=\"libtemplateparser\") set(templateparser_LIB_SRCS templateparserjob.cpp defaulttemplates.cpp templatesutil.cpp customtemplates.cpp customtemplatesmenu.cpp templatesconfiguration.cpp templatesinsertcommandpushbutton.cpp templatescommandmenu.cpp templatesinsertcommandaction.cpp templatestextedit.cpp templatestexteditor.cpp templateextracttextfrommail.cpp templatewebenginepage.cpp templateextracthtmlelementfrommail.cpp templateparserextracthtmlinfo.cpp templateparserextracthtmlinforesult.cpp templateparseremailaddressrequesterbase.cpp templateparseremailaddressrequesterlineedit.cpp templateparseremailaddressrequesterinterfacewidget.cpp templateconvertcommandjob.cpp ) ecm_qt_declare_logging_category(templateparser_LIB_SRCS HEADER templateparser_debug.h IDENTIFIER TEMPLATEPARSER_LOG CATEGORY_NAME org.kde.pim.templateparser) kconfig_add_kcfg_files(templateparser_LIB_SRCS settings/templatesconfiguration_kfg.kcfgc) kconfig_add_kcfg_files(templateparser_LIB_SRCS settings/customtemplates_kfg.kcfgc settings/globalsettings_templateparser.kcfgc ) ki18n_wrap_ui(templateparser_LIB_SRCS ui/templatesconfiguration_base.ui ui/customtemplates_base.ui ) add_library(KF5TemplateParser ${templateparser_LIB_SRCS}) generate_export_header(KF5TemplateParser BASE_NAME templateparser) add_library(KF5::TemplateParser ALIAS KF5TemplateParser) target_link_libraries(KF5TemplateParser PRIVATE KF5::Mime KF5::MimeTreeParser KF5::IdentityManagement KF5::MessageCore KF5::XmlGui - KF5::IconThemes KF5::Completion - KF5::Libkleo KF5::I18n KF5::SonnetUi KF5::SyntaxHighlighting Qt5::WebEngineWidgets ) set_target_properties(KF5TemplateParser PROPERTIES VERSION ${TEMPLATEPARSER_VERSION_STRING} SOVERSION ${TEMPLATEPARSER_SOVERSION} EXPORT_NAME TemplateParser ) target_include_directories(KF5TemplateParser INTERFACE "$") install(FILES settings/customtemplates_kfg.kcfg settings/templatesconfiguration_kfg.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR} ) ecm_generate_headers(TemplateParser_CamelCase_HEADERS HEADER_NAMES CustomTemplates CustomTemplatesMenu DefaultTemplates TemplatesConfiguration TemplatesTextEdit TemplatesUtil TemplatesInsertCommandPushButton TemplatesCommandMenu TemplatesInsertCommandAction TemplateParserJob TemplateParserExtractHtmlInfoResult TemplateParserEmailAddressRequesterBase TemplateConvertCommandJob REQUIRED_HEADERS TemplateParser_HEADERS PREFIX TemplateParser ) ecm_generate_pri_file(BASE_NAME TemplateParser LIB_NAME KF5TemplateParser DEPS "" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/TemplateParser ) install(TARGETS KF5TemplateParser EXPORT KF5TemplateParserTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK}) install(FILES ${TemplateParser_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/TemplateParser COMPONENT Devel ) install(FILES ${TemplateParser_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/templateparser_export.h ${CMAKE_CURRENT_BINARY_DIR}/globalsettings_templateparser.h ${CMAKE_CURRENT_BINARY_DIR}/customtemplates_kfg.h ${CMAKE_CURRENT_BINARY_DIR}/ui_templatesconfiguration_base.h ${CMAKE_CURRENT_BINARY_DIR}/templatesconfiguration_kfg.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/templateparser COMPONENT Devel ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) install(FILES syntax/kmail-template.xml DESTINATION ${KDE_INSTALL_DATADIR}/org.kde.syntax-highlighting/syntax)