diff --git a/messagelist/src/core/manager.cpp b/messagelist/src/core/manager.cpp index ca7a216d..03281dbc 100644 --- a/messagelist/src/core/manager.cpp +++ b/messagelist/src/core/manager.cpp @@ -1,975 +1,975 @@ /****************************************************************************** * * 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 "core/manager.h" #include "core/aggregation.h" #include "core/theme.h" #include "core/view.h" #include "core/widgetbase.h" #include "core/storagemodelbase.h" #include "core/model.h" #include "core/model_p.h" #include "messagelistsettings.h" #include "utils/configureaggregationsdialog.h" #include "utils/configureaggregationsdialog_p.h" #include "utils/configurethemesdialog.h" #include "utils/configurethemesdialog_p.h" #include "MessageCore/MessageCoreSettings" #include "messagelistutil.h" #include "messagelistutil_p.h" #include // kdepimlibs #include #include "messagelist_debug.h" #include using namespace MessageList::Core; Manager *Manager::mInstance = nullptr; Manager::Manager() : QObject() { mInstance = this; mDateFormatter = new KMime::DateFormatter(); mCachedLocalizedUnknownText = i18nc("Unknown date", "Unknown"); loadConfiguration(); connect(MessageListSettings::self(), &MessageListSettings::configChanged, this, &Manager::reloadGlobalConfiguration); connect(MessageCore::MessageCoreSettings::self(), &MessageCore::MessageCoreSettings::configChanged, this, &Manager::reloadGlobalConfiguration); } Manager::~Manager() { saveConfiguration(); removeAllAggregations(); removeAllThemes(); delete mDateFormatter; mInstance = nullptr; } void Manager::registerWidget(Widget *pWidget) { if (!mInstance) { mInstance = new Manager(); } mInstance->mWidgetList.append(pWidget); } void Manager::unregisterWidget(Widget *pWidget) { if (!mInstance) { qCWarning(MESSAGELIST_LOG) << ("ERROR: MessageList::Manager::unregisterWidget() called when Manager::mInstance is 0"); return; } mInstance->mWidgetList.removeAll(pWidget); if (mInstance->mWidgetList.isEmpty()) { delete mInstance; mInstance = nullptr; } } const Aggregation *Manager::aggregation(const QString &id) { Aggregation *opt = mAggregations.value(id); if (opt) { return opt; } return defaultAggregation(); } const Aggregation *Manager::defaultAggregation() { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup()); const QString aggregationId = conf.readEntry(QStringLiteral("DefaultSet"), ""); Aggregation *opt = nullptr; if (!aggregationId.isEmpty()) { opt = mAggregations.value(aggregationId); } if (opt) { return opt; } // try just the first one QMap< QString, Aggregation * >::ConstIterator it = mAggregations.constBegin(); if (it != mAggregations.constEnd()) { return *it; } // aargh createDefaultAggregations(); return *(mAggregations.constBegin()); } void Manager::saveAggregationForStorageModel(const Akonadi::Collection &col, const QString &id, bool storageUsesPrivateAggregation) { if (!col.isValid()) { return; } saveAggregationForStorageModel(QString::number(col.id()), id, storageUsesPrivateAggregation); } void Manager::saveAggregationForStorageModel(const StorageModel *storageModel, const QString &id, bool storageUsesPrivateAggregation) { saveAggregationForStorageModel(storageModel->id(), id, storageUsesPrivateAggregation); } void Manager::saveAggregationForStorageModel(const QString &modelId, const QString &id, bool storageUsesPrivateAggregation) { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup()); if (storageUsesPrivateAggregation) { conf.writeEntry(MessageList::Util::setForStorageModelConfigName().arg(modelId), id); } else { conf.deleteEntry(MessageList::Util::setForStorageModelConfigName().arg(modelId)); } if (!storageUsesPrivateAggregation) { conf.writeEntry(QStringLiteral("DefaultSet"), id); } } const Aggregation *Manager::aggregationForStorageModel(const Akonadi::Collection &col, bool *storageUsesPrivateAggregation) { Q_ASSERT(storageUsesPrivateAggregation); *storageUsesPrivateAggregation = false; // this is by default if (!col.isValid()) { return defaultAggregation(); } return Manager::aggregationForStorageModel(QString::number(col.id()), storageUsesPrivateAggregation); } const Aggregation *Manager::aggregationForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateAggregation) { Q_ASSERT(storageUsesPrivateAggregation); *storageUsesPrivateAggregation = false; // this is by default if (!storageModel) { return defaultAggregation(); } return Manager::aggregationForStorageModel(storageModel->id(), storageUsesPrivateAggregation); } const Aggregation *Manager::aggregationForStorageModel(const QString &storageId, bool *storageUsesPrivateAggregation) { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup()); const QString aggregationId = conf.readEntry(MessageList::Util::setForStorageModelConfigName().arg(storageId), ""); Aggregation *opt = nullptr; if (!aggregationId.isEmpty()) { // a private aggregation was stored opt = mAggregations.value(aggregationId); *storageUsesPrivateAggregation = (opt != nullptr); } if (opt) { return opt; } // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset... // We could even try to guess if the storageModel is a mailing list return defaultAggregation(); } void Manager::addAggregation(Aggregation *set) { Aggregation *old = mAggregations.value(set->id()); delete old; mAggregations.insert(set->id(), set); } void Manager::createDefaultAggregations() { addAggregation( new Aggregation( i18n("Current Activity, Threaded"), i18n("This view uses smart date range groups. " \ "Messages are threaded. " \ "So for example, in \"Today\" you will find all the messages arrived today " \ "and all the threads that have been active today." ), Aggregation::GroupByDateRange, Aggregation::ExpandRecentGroups, Aggregation::PerfectReferencesAndSubject, Aggregation::MostRecentMessage, Aggregation::ExpandThreadsWithUnreadOrImportantMessages, Aggregation::FavorInteractivity, true ) ); addAggregation( new Aggregation( i18n("Current Activity, Flat"), i18n("This view uses smart date range groups. " \ "Messages are not threaded. " \ "So for example, in \"Today\" you will simply find all the messages arrived today." ), Aggregation::GroupByDateRange, Aggregation::ExpandRecentGroups, Aggregation::NoThreading, Aggregation::MostRecentMessage, Aggregation::NeverExpandThreads, Aggregation::FavorInteractivity, true ) ); addAggregation( new Aggregation( i18n("Activity by Date, Threaded"), i18n("This view uses day-by-day groups. " \ "Messages are threaded. " \ "So for example, in \"Today\" you will find all the messages arrived today " \ "and all the threads that have been active today." ), Aggregation::GroupByDate, Aggregation::ExpandRecentGroups, Aggregation::PerfectReferencesAndSubject, Aggregation::MostRecentMessage, Aggregation::ExpandThreadsWithUnreadOrImportantMessages, Aggregation::FavorInteractivity, true ) ); addAggregation( new Aggregation( i18n("Activity by Date, Flat"), i18n("This view uses day-by-day groups. " \ "Messages are not threaded. " \ "So for example, in \"Today\" you will simply find all the messages arrived today." ), Aggregation::GroupByDate, Aggregation::ExpandRecentGroups, Aggregation::NoThreading, Aggregation::MostRecentMessage, Aggregation::NeverExpandThreads, Aggregation::FavorInteractivity, true ) ); addAggregation( new Aggregation( i18n("Standard Mailing List"), i18n("This is a plain and old mailing list view: no groups and heavy threading."), Aggregation::NoGrouping, Aggregation::NeverExpandGroups, Aggregation::PerfectReferencesAndSubject, Aggregation::TopmostMessage, Aggregation::ExpandThreadsWithUnreadOrImportantMessages, Aggregation::FavorInteractivity, true ) ); addAggregation( new Aggregation( i18n("Flat Date View"), i18n("This is a plain and old list of messages sorted by date: no groups and no threading." \ ), Aggregation::NoGrouping, Aggregation::NeverExpandGroups, Aggregation::NoThreading, Aggregation::TopmostMessage, Aggregation::NeverExpandThreads, Aggregation::FavorInteractivity, true ) ); addAggregation( new Aggregation( i18n("Senders/Receivers, Flat"), i18n("This view groups the messages by senders or receivers (depending on the folder " \ "type). " \ "Messages are not threaded." ), Aggregation::GroupBySenderOrReceiver, Aggregation::NeverExpandGroups, Aggregation::NoThreading, Aggregation::TopmostMessage, Aggregation::NeverExpandThreads, Aggregation::FavorSpeed, true ) ); addAggregation( new Aggregation( i18n("Thread Starters"), i18n("This view groups the messages in threads and then groups the threads by the starting user."), Aggregation::GroupBySenderOrReceiver, Aggregation::NeverExpandGroups, Aggregation::PerfectReferencesAndSubject, Aggregation::TopmostMessage, Aggregation::NeverExpandThreads, Aggregation::FavorSpeed, true ) ); /* FIX THIS addAggregation( new Aggregation( i18n( "Recent Thread Starters" ), i18n( "This view groups the messages in threads and then groups the threads by the starting user. " \ "Groups are sorted by the date of the first thread start. " ), Aggregation::GroupBySenderOrReceiver, Aggregation::SortGroupsByDateTimeOfMostRecent, Aggregation::Descending, Aggregation::PerfectReferencesAndSubject, Aggregation::TopmostMessage, Aggregation::SortMessagesByDateTime, Aggregation::Descending ) ); */ } void Manager::removeAllAggregations() { QMap< QString, Aggregation * >::ConstIterator end(mAggregations.constEnd()); for (QMap< QString, Aggregation * >::ConstIterator it = mAggregations.constBegin(); it != end; ++it) { delete(*it); } mAggregations.clear(); } void Manager::aggregationsConfigurationCompleted() { if (mAggregations.isEmpty()) { createDefaultAggregations(); // panic } saveConfiguration(); // just to be sure :) // notify all the widgets that they should reload the option set combos Q_EMIT aggregationsChanged(); } const SortOrder Manager::sortOrderForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateSortOrder) { Q_ASSERT(storageUsesPrivateSortOrder); *storageUsesPrivateSortOrder = false; // this is by default if (!storageModel) { return SortOrder(); } KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelSortOrderGroup()); SortOrder ret; ret.readConfig(conf, storageModel->id(), storageUsesPrivateSortOrder); return ret; } void Manager::saveSortOrderForStorageModel(const StorageModel *storageModel, const SortOrder &order, bool storageUsesPrivateSortOrder) { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelSortOrderGroup()); order.writeConfig(conf, storageModel->id(), storageUsesPrivateSortOrder); } const Theme *Manager::theme(const QString &id) { Theme *opt = mThemes.value(id); if (opt) { return opt; } return defaultTheme(); } const Theme *Manager::defaultTheme() { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup()); const QString themeId = conf.readEntry(QStringLiteral("DefaultSet"), ""); Theme *opt = nullptr; if (!themeId.isEmpty()) { opt = mThemes.value(themeId); } if (opt) { return opt; } // try just the first one QMap< QString, Theme * >::ConstIterator it = mThemes.constBegin(); if (it != mThemes.constEnd()) { return *it; } // aargh createDefaultThemes(); it = mThemes.constBegin(); Q_ASSERT(it != mThemes.constEnd()); return *it; } void Manager::saveThemeForStorageModel(int index, const QString &id, bool storageUsesPrivateTheme) { saveThemeForStorageModel(QString::number(index), id, storageUsesPrivateTheme); } void Manager::saveThemeForStorageModel(const StorageModel *storageModel, const QString &id, bool storageUsesPrivateTheme) { saveThemeForStorageModel(storageModel->id(), id, storageUsesPrivateTheme); } void Manager::saveThemeForStorageModel(const QString &storageModelIndex, const QString &id, bool storageUsesPrivateTheme) { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup()); if (storageUsesPrivateTheme) { conf.writeEntry(MessageList::Util::setForStorageModelConfigName().arg(storageModelIndex), id); } else { conf.deleteEntry(MessageList::Util::setForStorageModelConfigName().arg(storageModelIndex)); } if (!storageUsesPrivateTheme) { conf.writeEntry(QStringLiteral("DefaultSet"), id); } } const Theme *Manager::themeForStorageModel(const Akonadi::Collection &col, bool *storageUsesPrivateTheme) { Q_ASSERT(storageUsesPrivateTheme); *storageUsesPrivateTheme = false; // this is by default if (!col.isValid()) { return defaultTheme(); } return Manager::themeForStorageModel(QString::number(col.id()), storageUsesPrivateTheme); } const Theme *Manager::themeForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateTheme) { Q_ASSERT(storageUsesPrivateTheme); *storageUsesPrivateTheme = false; // this is by default if (!storageModel) { return defaultTheme(); } return Manager::themeForStorageModel(storageModel->id(), storageUsesPrivateTheme); } const Theme *Manager::themeForStorageModel(const QString &id, bool *storageUsesPrivateTheme) { KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup()); const QString themeId = conf.readEntry(MessageList::Util::setForStorageModelConfigName().arg(id), ""); Theme *opt = nullptr; if (!themeId.isEmpty()) { // a private theme was stored opt = mThemes.value(themeId); *storageUsesPrivateTheme = (opt != nullptr); } if (opt) { return opt; } // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset... // We could even try to guess if the storageModel is a mailing list // FIXME: Prefer right-to-left themes when application layout is RTL. return defaultTheme(); } void Manager::addTheme(Theme *set) { Theme *old = mThemes.value(set->id()); delete old; mThemes.insert(set->id(), set); } static Theme::Column *add_theme_simple_text_column(Theme *s, const QString &name, Theme::ContentItem::Type type, bool visibleByDefault, SortOrder::MessageSorting messageSorting, bool alignRight, bool addGroupHeaderItem) { Theme::Column *c = new Theme::Column(); c->setLabel(name); c->setVisibleByDefault(visibleByDefault); c->setMessageSorting(messageSorting); Theme::Row *r = new Theme::Row(); Theme::ContentItem *i = new Theme::ContentItem(type); if (alignRight) { r->addRightItem(i); } else { r->addLeftItem(i); } c->addMessageRow(r); if (addGroupHeaderItem) { Theme::Row *r = new Theme::Row(); Theme::ContentItem *i = new Theme::ContentItem(type); if (alignRight) { r->addRightItem(i); } else { r->addLeftItem(i); } c->addGroupHeaderRow(r); } s->addColumn(c); return c; } static Theme::Column *add_theme_simple_icon_column(Theme *s, const QString &name, const QString &pixmapName, Theme::ContentItem::Type type, bool visibleByDefault, SortOrder::MessageSorting messageSorting) { Theme::Column *c = new Theme::Column(); c->setLabel(name); c->setPixmapName(pixmapName); c->setVisibleByDefault(visibleByDefault); c->setMessageSorting(messageSorting); Theme::Row *r = new Theme::Row(); Theme::ContentItem *i = new Theme::ContentItem(type); i->setSoftenByBlendingWhenDisabled(true); r->addLeftItem(i); c->addMessageRow(r); s->addColumn(c); return c; } void Manager::createDefaultThemes() { Theme *s; Theme::Column *c; Theme::Row *r; Theme::ContentItem *i; // The "Classic" backward compatible theme s = new Theme( i18nc("Default theme name", "Classic"), i18n("A simple, backward compatible, single row theme"), true /*readOnly*/ ); c = new Theme::Column(); c->setLabel(i18nc("@title:column Subject of messages", "Subject")); c->setMessageSorting(SortOrder::SortMessagesBySubject); r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::ExpandedStateIcon); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::GroupHeaderLabel); i->setBold(true); r->addLeftItem(i); c->addGroupHeaderRow(r); r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::CombinedReadRepliedStateIcon); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::AttachmentStateIcon); i->setHideWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::AnnotationIcon); i->setHideWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::InvitationIcon); i->setHideWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::SignatureStateIcon); i->setHideWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::EncryptionStateIcon); i->setHideWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::Subject); r->addLeftItem(i); c->addMessageRow(r); s->addColumn(c); c = add_theme_simple_text_column(s, i18n("Sender/Receiver"), Theme::ContentItem::SenderOrReceiver, true, SortOrder::SortMessagesBySenderOrReceiver, false, false); c->setIsSenderOrReceiver(true); add_theme_simple_text_column(s, i18nc("Sender of a message", "Sender"), Theme::ContentItem::Sender, false, SortOrder::SortMessagesBySender, false, false); add_theme_simple_text_column(s, i18nc("Receiver of a message", "Receiver"), Theme::ContentItem::Receiver, false, SortOrder::SortMessagesByReceiver, false, false); add_theme_simple_text_column(s, i18nc("Date of a message", "Date"), Theme::ContentItem::Date, true, SortOrder::SortMessagesByDateTime, false, false); add_theme_simple_text_column(s, i18n("Most Recent Date"), Theme::ContentItem::MostRecentDate, false, SortOrder::SortMessagesByDateTimeOfMostRecent, false, true); add_theme_simple_text_column(s, i18nc("Size of a message", "Size"), Theme::ContentItem::Size, false, SortOrder::SortMessagesBySize, false, false); add_theme_simple_icon_column(s, i18nc("Attachment indication", "Attachment"), QStringLiteral( "mail-attachment"), Theme::ContentItem::AttachmentStateIcon, false, SortOrder::SortMessagesByAttachmentStatus); add_theme_simple_icon_column(s, i18n("Read/Unread"), QStringLiteral("mail-mark-unread-new"), Theme::ContentItem::ReadStateIcon, false, SortOrder::SortMessagesByUnreadStatus); add_theme_simple_icon_column(s, i18n("Replied"), QStringLiteral("mail-replied"), Theme::ContentItem::RepliedStateIcon, false, SortOrder::NoMessageSorting); add_theme_simple_icon_column(s, i18nc("Message importance indication", "Important"), QStringLiteral( - "emblem-important"), Theme::ContentItem::ImportantStateIcon, false, SortOrder::SortMessagesByImportantStatus); + "mail-mark-important"), Theme::ContentItem::ImportantStateIcon, false, SortOrder::SortMessagesByImportantStatus); add_theme_simple_icon_column(s, i18n("Action Item"), QStringLiteral("mail-task"), Theme::ContentItem::ActionItemStateIcon, false, SortOrder::SortMessagesByActionItemStatus); add_theme_simple_icon_column(s, i18n("Spam/Ham"), QStringLiteral("mail-mark-junk"), Theme::ContentItem::SpamHamStateIcon, false, SortOrder::NoMessageSorting); add_theme_simple_icon_column(s, i18n("Watched/Ignored"), QStringLiteral("mail-thread-watch"), Theme::ContentItem::WatchedIgnoredStateIcon, false, SortOrder::NoMessageSorting); add_theme_simple_icon_column(s, i18n("Encryption"), QStringLiteral("mail-encrypted-full"), Theme::ContentItem::EncryptionStateIcon, false, SortOrder::NoMessageSorting); add_theme_simple_icon_column(s, i18n("Signature"), QStringLiteral("mail-signed-verified"), Theme::ContentItem::SignatureStateIcon, false, SortOrder::NoMessageSorting); add_theme_simple_icon_column(s, i18n("Tag List"), QStringLiteral("feed-subscribe"), Theme::ContentItem::TagList, false, SortOrder::NoMessageSorting); s->resetColumnState(); // so it's initially set from defaults addTheme(s); // The Fancy theme s = new Theme( i18n("Smart"), i18n("A smart multiline and multi item theme"), true /*readOnly*/ ); c = new Theme::Column(); c->setLabel(i18n("Message")); r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::ExpandedStateIcon); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::GroupHeaderLabel); i->setBold(true); r->addLeftItem(i); c->addGroupHeaderRow(r); r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::Subject); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::ReadStateIcon); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::RepliedStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::AttachmentStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::AnnotationIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::InvitationIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::EncryptionStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::SignatureStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::TagList); i->setHideWhenDisabled(true); r->addRightItem(i); c->addMessageRow(r); Theme::Row *firstFancyRow = r; // save it so we can continue adding stuff below (after cloning the theme) r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::SenderOrReceiver); i->setSoftenByBlending(true); i->setItalic(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::Date); i->setSoftenByBlending(true); i->setItalic(true); r->addRightItem(i); c->addMessageRow(r); s->addColumn(c); // clone the "Fancy theme" here so we'll use it as starting point for the "Fancy with clickable status" Theme *fancyWithClickableStatus = new Theme(*s); fancyWithClickableStatus->detach(); fancyWithClickableStatus->generateUniqueId(); // and continue the "Fancy" specific settings r = firstFancyRow; i = new Theme::ContentItem(Theme::ContentItem::ActionItemStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::ImportantStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::SpamHamStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); i = new Theme::ContentItem(Theme::ContentItem::WatchedIgnoredStateIcon); i->setHideWhenDisabled(true); r->addRightItem(i); s->setViewHeaderPolicy(Theme::NeverShowHeader); s->resetColumnState(); // so it's initially set from defaults addTheme(s); // The "Fancy with Clickable Status" theme s = fancyWithClickableStatus; s->setName(i18n("Smart with Clickable Status")); s->setDescription(i18n("A smart multiline and multi item theme with a clickable status column")); s->setReadOnly(true); c = new Theme::Column(); c->setLabel(i18n("Status")); c->setVisibleByDefault(true); r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::ActionItemStateIcon); i->setSoftenByBlendingWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::ImportantStateIcon); i->setSoftenByBlendingWhenDisabled(true); r->addLeftItem(i); c->addMessageRow(r); r = new Theme::Row(); i = new Theme::ContentItem(Theme::ContentItem::SpamHamStateIcon); i->setSoftenByBlendingWhenDisabled(true); r->addLeftItem(i); i = new Theme::ContentItem(Theme::ContentItem::WatchedIgnoredStateIcon); i->setSoftenByBlendingWhenDisabled(true); r->addLeftItem(i); c->addMessageRow(r); s->addColumn(c); s->resetColumnState(); // so it's initially set from defaults addTheme(s); } void Manager::removeAllThemes() { QMap< QString, Theme * >::ConstIterator end(mThemes.constEnd()); for (QMap< QString, Theme * >::ConstIterator it = mThemes.constBegin(); it != end; ++it) { delete(*it); } mThemes.clear(); } void Manager::themesConfigurationCompleted() { if (mThemes.isEmpty()) { createDefaultThemes(); // panic } saveConfiguration(); // just to be sure :) // notify all the widgets that they should reload the option set combos Q_EMIT themesChanged(); } void Manager::reloadAllWidgets() { QList< Widget * >::ConstIterator end(mWidgetList.constEnd()); for (QList< Widget * >::ConstIterator it = mWidgetList.constBegin(); it != end; ++it) { if ((*it)->view()) { (*it)->view()->reload(); } } } void Manager::reloadGlobalConfiguration() { // This is called when configuration changes (probably edited by the options dialog) const int oldDateFormat = (int)mDateFormatter->format(); const QString oldDateCustomFormat = mDateFormatter->customFormat(); loadGlobalConfiguration(); if ( (oldDateFormat != (int)mDateFormatter->format()) || (oldDateCustomFormat != mDateFormatter->customFormat()) ) { reloadAllWidgets(); } } void Manager::loadGlobalConfiguration() { // Load the date format const KMime::DateFormatter::FormatType type = static_cast( MessageCore::MessageCoreSettings::self()->dateFormat()); mDateFormatter->setCustomFormat(MessageCore::MessageCoreSettings::self()->customDateFormat()); mDateFormatter->setFormat(type); } void Manager::loadConfiguration() { loadGlobalConfiguration(); { // load Aggregations KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Aggregations"); mAggregations.clear(); const int cnt = conf.readEntry("Count", 0); int idx = 0; while (idx < cnt) { const QString data = conf.readEntry(QStringLiteral("Set%1").arg(idx), QString()); if (!data.isEmpty()) { Aggregation *set = new Aggregation(); if (set->loadFromString(data)) { if (Aggregation *old = mAggregations.value(set->id())) { delete old; } mAggregations.insert(set->id(), set); } else { delete set; // b0rken } } idx++; } if (mAggregations.isEmpty()) { // don't allow zero configuration, create some presets createDefaultAggregations(); } } { // load Themes KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Themes"); mThemes.clear(); const int cnt = conf.readEntry("Count", 0); int idx = 0; while (idx < cnt) { const QString data = conf.readEntry(QStringLiteral("Set%1").arg(idx), QString()); if (!data.isEmpty()) { Theme *set = new Theme(); if (set->loadFromString(data)) { if (Theme *old = mThemes.value(set->id())) { delete old; } mThemes.insert(set->id(), set); } else { qCWarning(MESSAGELIST_LOG) << "Saved theme loading failed"; delete set; // b0rken } } ++idx; } if (mThemes.isEmpty()) { // don't allow zero configuration, create some presets createDefaultThemes(); } } } void Manager::saveGlobalConfiguration() { MessageListSettings::self()->save(); } void Manager::saveConfiguration() { saveGlobalConfiguration(); { // store aggregations KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Aggregations"); //conf.clear(); conf.writeEntry("Count", mAggregations.count()); int idx = 0; QMap< QString, Aggregation * >::ConstIterator end(mAggregations.end()); for (QMap< QString, Aggregation * >::ConstIterator it = mAggregations.constBegin(); it != end; ++it) { conf.writeEntry(QStringLiteral("Set%1").arg(idx), (*it)->saveToString()); ++idx; } } { // store themes KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Themes"); //conf.clear(); conf.writeEntry("Count", mThemes.count()); int idx = 0; QMap< QString, Theme * >::ConstIterator end(mThemes.constEnd()); for (QMap< QString, Theme * >::ConstIterator it = mThemes.constBegin(); it != end; ++it) { conf.writeEntry(QStringLiteral("Set%1").arg(idx), (*it)->saveToString()); ++idx; } } MessageListSettings::self()->config()->sync(); } diff --git a/messagelist/src/core/theme.cpp b/messagelist/src/core/theme.cpp index a21c6f1d..9bbc0114 100644 --- a/messagelist/src/core/theme.cpp +++ b/messagelist/src/core/theme.cpp @@ -1,1288 +1,1288 @@ /****************************************************************************** * * 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 "core/theme.h" #include #include #include #include #include #include #include "messagelist_debug.h" using namespace MessageList::Core; // // Theme versioning // // The themes simply have a DWORD version number attached. // The earliest version we're able to load is 0x1013. // // Theme revision history: // // Version Date introduced Description // -------------------------------------------------------------------------------------------------------------- // 0x1013 08.11.2008 Initial theme version, introduced when this piece of code has been moved into trunk. // 0x1014 12.11.2008 Added runtime column data: width and column visibility // 0x1015 03.03.2009 Added icon size // 0x1016 08.03.2009 Added support for sorting by New/Unread status // 0x1017 16.08.2009 Added support for column icon // 0x1018 17.01.2010 Added support for annotation icon // 0x1019 13.07.2010 Added support for invitation icon // static const int gThemeCurrentVersion = 0x1019; // increase if you add new fields or change the meaning of some // you don't need to change the values below, but you might want to add new ones static const int gThemeMinimumSupportedVersion = 0x1013; static const int gThemeMinimumVersionWithColumnRuntimeData = 0x1014; static const int gThemeMinimumVersionWithIconSizeField = 0x1015; static const int gThemeMinimumVersionWithSortingByUnreadStatusAllowed = 0x1016; static const int gThemeMinimumVersionWithColumnIcon = 0x1017; static const int gThemeMinimumVersionWithAnnotationIcon = 0x1018; static const int gThemeMinimumVersionWithInvitationIcon = 0x1019; // the default icon size static const int gThemeDefaultIconSize = 16; Theme::ContentItem::ContentItem(Type type) : mType(type) , mFlags(0) { } Theme::ContentItem::ContentItem(const ContentItem &src) : mType(src.mType) , mFlags(src.mFlags) , mCustomColor(src.mCustomColor) { } Theme::ContentItem::Type Theme::ContentItem::type() const { return mType; } bool Theme::ContentItem::canBeDisabled() const { return (static_cast< int >(mType) & CanBeDisabled) != 0; } bool Theme::ContentItem::canUseCustomColor() const { return (static_cast< int >(mType) & CanUseCustomColor) != 0; } bool Theme::ContentItem::displaysText() const { return (static_cast< int >(mType) & DisplaysText) != 0; } bool Theme::ContentItem::displaysLongText() const { return (static_cast< int >(mType) & LongText) != 0; } bool Theme::ContentItem::isIcon() const { return (static_cast< int >(mType) & IsIcon) != 0; } bool Theme::ContentItem::isClickable() const { return (static_cast< int >(mType) & IsClickable) != 0; } bool Theme::ContentItem::isSpacer() const { return (static_cast< int >(mType) & IsSpacer) != 0; } QString Theme::ContentItem::description(Type type) { switch (type) { case Subject: return i18nc("Description of Type Subject", "Subject"); break; case Date: return i18nc("Description of Type Date", "Date"); break; case SenderOrReceiver: return i18n("Sender/Receiver"); break; case Sender: return i18nc("Description of Type Sender", "Sender"); break; case Receiver: return i18nc("Description of Type Receiver", "Receiver"); break; case Size: return i18nc("Description of Type Size", "Size"); break; case ReadStateIcon: return i18n("Unread/Read Icon"); break; case AttachmentStateIcon: return i18n("Attachment Icon"); break; case RepliedStateIcon: return i18n("Replied/Forwarded Icon"); break; case CombinedReadRepliedStateIcon: return i18n("Combined New/Unread/Read/Replied/Forwarded Icon"); break; case ActionItemStateIcon: return i18n("Action Item Icon"); break; case ImportantStateIcon: return i18n("Important Icon"); break; case GroupHeaderLabel: return i18n("Group Header Label"); break; case SpamHamStateIcon: return i18n("Spam/Ham Icon"); break; case WatchedIgnoredStateIcon: return i18n("Watched/Ignored Icon"); break; case ExpandedStateIcon: return i18n("Group Header Expand/Collapse Icon"); break; case EncryptionStateIcon: return i18n("Encryption State Icon"); break; case SignatureStateIcon: return i18n("Signature State Icon"); break; case VerticalLine: return i18n("Vertical Separation Line"); break; case HorizontalSpacer: return i18n("Horizontal Spacer"); break; case MostRecentDate: return i18n("Max Date"); break; case TagList: return i18n("Message Tags"); break; case AnnotationIcon: return i18n("Note Icon"); case InvitationIcon: return i18n("Invitation Icon"); default: return i18nc("Description for an Unknown Type", "Unknown"); break; } } bool Theme::ContentItem::useCustomColor() const { return mFlags & UseCustomColor; } void Theme::ContentItem::setUseCustomColor(bool useCustomColor) { if (useCustomColor) { mFlags |= UseCustomColor; } else { mFlags &= ~UseCustomColor; } } bool Theme::ContentItem::isBold() const { return mFlags & IsBold; } void Theme::ContentItem::setBold(bool isBold) { if (isBold) { mFlags |= IsBold; } else { mFlags &= ~IsBold; } } bool Theme::ContentItem::isItalic() const { return mFlags & IsItalic; } void Theme::ContentItem::setItalic(bool isItalic) { if (isItalic) { mFlags |= IsItalic; } else { mFlags &= ~IsItalic; } } bool Theme::ContentItem::hideWhenDisabled() const { return mFlags & HideWhenDisabled; } void Theme::ContentItem::setHideWhenDisabled(bool hideWhenDisabled) { if (hideWhenDisabled) { mFlags |= HideWhenDisabled; } else { mFlags &= ~HideWhenDisabled; } } bool Theme::ContentItem::softenByBlendingWhenDisabled() const { return mFlags & SoftenByBlendingWhenDisabled; } void Theme::ContentItem::setSoftenByBlendingWhenDisabled(bool softenByBlendingWhenDisabled) { if (softenByBlendingWhenDisabled) { mFlags |= SoftenByBlendingWhenDisabled; } else { mFlags &= ~SoftenByBlendingWhenDisabled; } } bool Theme::ContentItem::softenByBlending() const { return mFlags & SoftenByBlending; } void Theme::ContentItem::setSoftenByBlending(bool softenByBlending) { if (softenByBlending) { mFlags |= SoftenByBlending; } else { mFlags &= ~SoftenByBlending; } } const QColor &Theme::ContentItem::customColor() const { return mCustomColor; } void Theme::ContentItem::setCustomColor(const QColor &clr) { mCustomColor = clr; } bool Theme::ContentItem::applicableToMessageItems(Type type) { return static_cast< int >(type) & ApplicableToMessageItems; } bool Theme::ContentItem::applicableToGroupHeaderItems(Type type) { return static_cast< int >(type) & ApplicableToGroupHeaderItems; } void Theme::ContentItem::save(QDataStream &stream) const { stream << (int)mType; stream << mFlags; stream << mCustomColor; } bool Theme::ContentItem::load(QDataStream &stream, int /*themeVersion*/) { int val; stream >> val; mType = static_cast< Type >(val); switch (mType) { case Subject: case Date: case SenderOrReceiver: case Sender: case Receiver: case Size: case ReadStateIcon: case AttachmentStateIcon: case RepliedStateIcon: case GroupHeaderLabel: case ActionItemStateIcon: case ImportantStateIcon: case SpamHamStateIcon: case WatchedIgnoredStateIcon: case ExpandedStateIcon: case EncryptionStateIcon: case SignatureStateIcon: case VerticalLine: case HorizontalSpacer: case MostRecentDate: case CombinedReadRepliedStateIcon: case TagList: case AnnotationIcon: case InvitationIcon: // ok break; default: qCDebug(MESSAGELIST_LOG) << "Invalid content item type"; return false; // b0rken break; } stream >> mFlags; stream >> mCustomColor; if (mFlags & UseCustomColor) { if (!mCustomColor.isValid()) { mFlags &= ~UseCustomColor; } } return true; } Theme::Row::Row() { } Theme::Row::Row(const Row &src) { for (const auto ci : qAsConst(src.mLeftItems)) { addLeftItem(new ContentItem(*ci)); } for (const auto ci : qAsConst(src.mRightItems)) { addRightItem(new ContentItem(*ci)); } } Theme::Row::~Row() { removeAllLeftItems(); removeAllRightItems(); } void Theme::Row::removeAllLeftItems() { while (!mLeftItems.isEmpty()) { delete mLeftItems.takeFirst(); } } void Theme::Row::addLeftItem(Theme::ContentItem *item) { mLeftItems.append(item); } void Theme::Row::removeAllRightItems() { while (!mRightItems.isEmpty()) { delete mRightItems.takeFirst(); } } void Theme::Row::addRightItem(Theme::ContentItem *item) { mRightItems.append(item); } void Theme::Row::insertLeftItem(int idx, ContentItem *item) { if (idx >= mLeftItems.count()) { mLeftItems.append(item); return; } mLeftItems.insert(idx, item); } void Theme::Row::removeLeftItem(Theme::ContentItem *item) { mLeftItems.removeAll(item); } const QList &Theme::Row::rightItems() const { return mRightItems; } void Theme::Row::insertRightItem(int idx, ContentItem *item) { if (idx >= mRightItems.count()) { mRightItems.append(item); return; } mRightItems.insert(idx, item); } void Theme::Row::removeRightItem(Theme::ContentItem *item) { mRightItems.removeAll(item); } bool Theme::Row::containsTextItems() const { for (const auto ci : qAsConst(mLeftItems)) { if (ci->displaysText()) { return true; } } for (const auto ci : qAsConst(mRightItems)) { if (ci->displaysText()) { return true; } } return false; } void Theme::Row::save(QDataStream &stream) const { stream << (int)mLeftItems.count(); int cnt = mLeftItems.count(); for (int i = 0; i < cnt; ++i) { ContentItem *ci = mLeftItems.at(i); ci->save(stream); } stream << (int)mRightItems.count(); cnt = mRightItems.count(); for (int i = 0; i < cnt; ++i) { ContentItem *ci = mRightItems.at(i); ci->save(stream); } } bool Theme::Row::LoadContentItem(int val, QDataStream &stream, int themeVersion, bool leftItem) { if ((val < 0) || (val > 50)) { return false; // senseless } // FIXME: Remove code duplication here for (int i = 0; i < val; ++i) { ContentItem *ci = new ContentItem(ContentItem::Subject); // dummy type if (!ci->load(stream, themeVersion)) { qCDebug(MESSAGELIST_LOG) << "Left content item loading failed"; delete ci; return false; } if (leftItem) { addLeftItem(ci); } else { addRightItem(ci); } // Add the annotation item next to the attachment icon, so that users upgrading from old // versions don't manually need to set this. // Don't do this for the stand-alone attachment column. if (ci->type() == ContentItem::AttachmentStateIcon && themeVersion < gThemeMinimumVersionWithAnnotationIcon && val > 1) { qCDebug(MESSAGELIST_LOG) << "Old theme version detected, adding annotation item next to attachment icon."; ContentItem *annotationItem = new ContentItem(ContentItem::AnnotationIcon); annotationItem->setHideWhenDisabled(true); if (leftItem) { addLeftItem(annotationItem); } else { addRightItem(annotationItem); } } // Same as above, for the invitation icon if (ci->type() == ContentItem::AttachmentStateIcon && themeVersion < gThemeMinimumVersionWithInvitationIcon && val > 1) { qCDebug(MESSAGELIST_LOG) << "Old theme version detected, adding invitation item next to attachment icon."; ContentItem *invitationItem = new ContentItem(ContentItem::InvitationIcon); invitationItem->setHideWhenDisabled(true); if (leftItem) { addLeftItem(invitationItem); } else { addRightItem(invitationItem); } } } return true; } const QList &Theme::Row::leftItems() const { return mLeftItems; } bool Theme::Row::load(QDataStream &stream, int themeVersion) { removeAllLeftItems(); removeAllRightItems(); int val; // left item count stream >> val; if (!LoadContentItem(val, stream, themeVersion, true)) { return false; } // right item count stream >> val; if (!LoadContentItem(val, stream, themeVersion, false)) { return false; } return true; } Theme::Column::SharedRuntimeData::SharedRuntimeData(bool currentlyVisible, double currentWidth) : mReferences(0) , mCurrentlyVisible(currentlyVisible) , mCurrentWidth(currentWidth) { } Theme::Column::SharedRuntimeData::~SharedRuntimeData() { } void Theme::Column::SharedRuntimeData::addReference() { mReferences++; } bool Theme::Column::SharedRuntimeData::deleteReference() { mReferences--; Q_ASSERT(mReferences >= 0); return mReferences > 0; } int Theme::Column::SharedRuntimeData::referenceCount() const { return mReferences; } bool Theme::Column::SharedRuntimeData::currentlyVisible() const { return mCurrentlyVisible; } void Theme::Column::SharedRuntimeData::setCurrentlyVisible(bool visible) { mCurrentlyVisible = visible; } double Theme::Column::SharedRuntimeData::currentWidth() const { return mCurrentWidth; } void Theme::Column::SharedRuntimeData::setCurrentWidth(double currentWidth) { mCurrentWidth = currentWidth; } void Theme::Column::SharedRuntimeData::save(QDataStream &stream) const { stream << mCurrentlyVisible; stream << mCurrentWidth; } bool Theme::Column::SharedRuntimeData::load(QDataStream &stream, int /* themeVersion */) { stream >> mCurrentlyVisible; stream >> mCurrentWidth; if (mCurrentWidth > 10000) { qCDebug(MESSAGELIST_LOG) << "Theme has insane column width " << mCurrentWidth << " chopping to 100"; mCurrentWidth = 100; // avoid really insane values } return mCurrentWidth >= -1; } Theme::Column::Column() : mVisibleByDefault(true) , mIsSenderOrReceiver(false) , mMessageSorting(SortOrder::NoMessageSorting) { mSharedRuntimeData = new SharedRuntimeData(true, -1); mSharedRuntimeData->addReference(); } Theme::Column::Column(const Column &src) { mLabel = src.mLabel; mPixmapName = src.mPixmapName; mVisibleByDefault = src.mVisibleByDefault; mIsSenderOrReceiver = src.mIsSenderOrReceiver; mMessageSorting = src.mMessageSorting; mSharedRuntimeData = src.mSharedRuntimeData; mSharedRuntimeData->addReference(); for (const auto row : qAsConst(src.mMessageRows)) { addMessageRow(new Row(*row)); } for (const auto row : qAsConst(src.mGroupHeaderRows)) { addGroupHeaderRow(new Row(*row)); } } Theme::Column::~Column() { removeAllMessageRows(); removeAllGroupHeaderRows(); if (!(mSharedRuntimeData->deleteReference())) { delete mSharedRuntimeData; } } const QString &Theme::Column::label() const { return mLabel; } void Theme::Column::setLabel(const QString &label) { mLabel = label; } const QString &Theme::Column::pixmapName() const { return mPixmapName; } void Theme::Column::setPixmapName(const QString &pixmapName) { mPixmapName = pixmapName; } bool Theme::Column::isSenderOrReceiver() const { return mIsSenderOrReceiver; } void Theme::Column::setIsSenderOrReceiver(bool sor) { mIsSenderOrReceiver = sor; } bool Theme::Column::visibleByDefault() const { return mVisibleByDefault; } void Theme::Column::setVisibleByDefault(bool vbd) { mVisibleByDefault = vbd; } void Theme::Column::detach() { if (mSharedRuntimeData->referenceCount() < 2) { return; // nothing to detach } mSharedRuntimeData->deleteReference(); mSharedRuntimeData = new SharedRuntimeData(mVisibleByDefault, -1); mSharedRuntimeData->addReference(); } SortOrder::MessageSorting Theme::Column::messageSorting() const { return mMessageSorting; } void Theme::Column::setMessageSorting(SortOrder::MessageSorting ms) { mMessageSorting = ms; } bool Theme::Column::currentlyVisible() const { return mSharedRuntimeData->currentlyVisible(); } void Theme::Column::setCurrentlyVisible(bool currentlyVisible) { mSharedRuntimeData->setCurrentlyVisible(currentlyVisible); } double Theme::Column::currentWidth() const { return mSharedRuntimeData->currentWidth(); } void Theme::Column::setCurrentWidth(double currentWidth) { mSharedRuntimeData->setCurrentWidth(currentWidth); } const QList &Theme::Column::messageRows() const { return mMessageRows; } void Theme::Column::removeAllMessageRows() { while (!mMessageRows.isEmpty()) { delete mMessageRows.takeFirst(); } } void Theme::Column::addMessageRow(Theme::Row *row) { mMessageRows.append(row); } void Theme::Column::removeAllGroupHeaderRows() { while (!mGroupHeaderRows.isEmpty()) { delete mGroupHeaderRows.takeFirst(); } } void Theme::Column::addGroupHeaderRow(Theme::Row *row) { mGroupHeaderRows.append(row); } void Theme::Column::insertMessageRow(int idx, Row *row) { if (idx >= mMessageRows.count()) { mMessageRows.append(row); return; } mMessageRows.insert(idx, row); } void Theme::Column::removeMessageRow(Theme::Row *row) { mMessageRows.removeAll(row); } const QList &Theme::Column::groupHeaderRows() const { return mGroupHeaderRows; } void Theme::Column::insertGroupHeaderRow(int idx, Row *row) { if (idx >= mGroupHeaderRows.count()) { mGroupHeaderRows.append(row); return; } mGroupHeaderRows.insert(idx, row); } void Theme::Column::removeGroupHeaderRow(Theme::Row *row) { mGroupHeaderRows.removeAll(row); } bool Theme::Column::containsTextItems() const { for (const auto row : qAsConst(mMessageRows)) { if (row->containsTextItems()) { return true; } } for (const auto row : qAsConst(mGroupHeaderRows)) { if (row->containsTextItems()) { return true; } } return false; } void Theme::Column::save(QDataStream &stream) const { stream << mLabel; stream << mPixmapName; stream << mVisibleByDefault; stream << mIsSenderOrReceiver; stream << static_cast(mMessageSorting); stream << static_cast(mGroupHeaderRows.count()); int cnt = mGroupHeaderRows.count(); for (int i = 0; i < cnt; ++i) { Row *row = mGroupHeaderRows.at(i); row->save(stream); } cnt = mMessageRows.count(); stream << static_cast(cnt); for (int i = 0; i < cnt; ++i) { Row *row = mMessageRows.at(i); row->save(stream); } // added in version 0x1014 mSharedRuntimeData->save(stream); } bool Theme::Column::load(QDataStream &stream, int themeVersion) { removeAllGroupHeaderRows(); removeAllMessageRows(); stream >> mLabel; if (themeVersion >= gThemeMinimumVersionWithColumnIcon) { stream >> mPixmapName; } stream >> mVisibleByDefault; stream >> mIsSenderOrReceiver; int val; stream >> val; mMessageSorting = static_cast< SortOrder::MessageSorting >(val); if (!SortOrder::isValidMessageSorting(mMessageSorting)) { qCDebug(MESSAGELIST_LOG) << "Invalid message sorting"; return false; } if (themeVersion < gThemeMinimumVersionWithSortingByUnreadStatusAllowed) { // The default "Classic" theme "Unread" column had sorting disabled here. // We want to be nice to the existing users and automatically set // the new sorting method for this column (so they don't have to make the // complex steps to set it by themselves). // This piece of code isn't strictly required: it's just a niceness :) if ((mMessageSorting == SortOrder::NoMessageSorting) && (mLabel == i18n("Unread"))) { mMessageSorting = SortOrder::SortMessagesByUnreadStatus; } } // group header row count stream >> val; if ((val < 0) || (val > 50)) { qCDebug(MESSAGELIST_LOG) << "Invalid group header row count"; return false; // senseless } for (int i = 0; i < val; ++i) { Row *row = new Row(); if (!row->load(stream, themeVersion)) { qCDebug(MESSAGELIST_LOG) << "Group header row loading failed"; delete row; return false; } addGroupHeaderRow(row); } // message row count stream >> val; if ((val < 0) || (val > 50)) { qCDebug(MESSAGELIST_LOG) << "Invalid message row count"; return false; // senseless } for (int i = 0; i < val; ++i) { Row *row = new Row(); if (!row->load(stream, themeVersion)) { qCDebug(MESSAGELIST_LOG) << "Message row loading failed"; delete row; return false; } addMessageRow(row); } if (themeVersion >= gThemeMinimumVersionWithColumnRuntimeData) { // starting with version 0x1014 we have runtime data too if (!mSharedRuntimeData->load(stream, themeVersion)) { qCDebug(MESSAGELIST_LOG) << "Shared runtime data loading failed"; return false; } } else { // assume default shared data mSharedRuntimeData->setCurrentlyVisible(mVisibleByDefault); mSharedRuntimeData->setCurrentWidth(-1); } return true; } Theme::Theme() : OptionSet() { mGroupHeaderBackgroundMode = AutoColor; mViewHeaderPolicy = ShowHeaderAlways; mIconSize = gThemeDefaultIconSize; mGroupHeaderBackgroundStyle = StyledJoinedRect; } Theme::Theme(const QString &name, const QString &description, bool readOnly) : OptionSet(name, description, readOnly) { mGroupHeaderBackgroundMode = AutoColor; mGroupHeaderBackgroundStyle = StyledJoinedRect; mViewHeaderPolicy = ShowHeaderAlways; mIconSize = gThemeDefaultIconSize; } Theme::Theme(const Theme &src) : OptionSet(src) { mGroupHeaderBackgroundMode = src.mGroupHeaderBackgroundMode; mGroupHeaderBackgroundColor = src.mGroupHeaderBackgroundColor; mGroupHeaderBackgroundStyle = src.mGroupHeaderBackgroundStyle; mViewHeaderPolicy = src.mViewHeaderPolicy; mIconSize = src.mIconSize; for (const auto col : qAsConst(src.mColumns)) { addColumn(new Column(*col)); } } Theme::~Theme() { clearPixmapCache(); removeAllColumns(); } void Theme::detach() { for (const auto col : qAsConst(mColumns)) { col->detach(); } } void Theme::resetColumnState() { for (const auto col : qAsConst(mColumns)) { col->setCurrentlyVisible(col->visibleByDefault()); col->setCurrentWidth(-1); } } void Theme::resetColumnSizes() { for (const auto col : qAsConst(mColumns)) { col->setCurrentWidth(-1); } } const QList &Theme::columns() const { return mColumns; } Theme::Column *Theme::column(int idx) const { return mColumns.count() > idx ? mColumns.at(idx) : nullptr; } void Theme::removeAllColumns() { while (!mColumns.isEmpty()) { delete mColumns.takeFirst(); } } void Theme::addColumn(Theme::Column *column) { mColumns.append(column); } void Theme::insertColumn(int idx, Column *column) { if (idx >= mColumns.count()) { mColumns.append(column); return; } mColumns.insert(idx, column); } void Theme::removeColumn(Theme::Column *col) { mColumns.removeAll(col); } Theme::GroupHeaderBackgroundMode Theme::groupHeaderBackgroundMode() const { return mGroupHeaderBackgroundMode; } void Theme::moveColumn(int idx, int newPosition) { if ((newPosition >= mColumns.count()) || newPosition < 0) { return; } mColumns.move(idx, newPosition); } void Theme::setGroupHeaderBackgroundMode(GroupHeaderBackgroundMode bm) { mGroupHeaderBackgroundMode = bm; if ((bm == CustomColor) && !mGroupHeaderBackgroundColor.isValid()) { mGroupHeaderBackgroundColor = QColor(127, 127, 127); // something neutral } } const QColor &Theme::groupHeaderBackgroundColor() const { return mGroupHeaderBackgroundColor; } void Theme::setGroupHeaderBackgroundColor(const QColor &clr) { mGroupHeaderBackgroundColor = clr; } Theme::GroupHeaderBackgroundStyle Theme::groupHeaderBackgroundStyle() const { return mGroupHeaderBackgroundStyle; } void Theme::setGroupHeaderBackgroundStyle(Theme::GroupHeaderBackgroundStyle groupHeaderBackgroundStyle) { mGroupHeaderBackgroundStyle = groupHeaderBackgroundStyle; } QVector > Theme::enumerateViewHeaderPolicyOptions() { return { { i18n("Never Show"), NeverShowHeader }, { i18n("Always Show"), ShowHeaderAlways } }; } QVector > Theme::enumerateGroupHeaderBackgroundStyles() { return { { i18n("Plain Rectangles"), PlainRect }, { i18n("Plain Joined Rectangle"), PlainJoinedRect }, { i18n("Rounded Rectangles"), RoundedRect }, { i18n("Rounded Joined Rectangle"), RoundedJoinedRect }, { i18n("Gradient Rectangles"), GradientRect }, { i18n("Gradient Joined Rectangle"), GradientJoinedRect }, { i18n("Styled Rectangles"), StyledRect }, { i18n("Styled Joined Rectangles"), StyledJoinedRect } }; } Theme::ViewHeaderPolicy Theme::viewHeaderPolicy() const { return mViewHeaderPolicy; } void Theme::setViewHeaderPolicy(Theme::ViewHeaderPolicy vhp) { mViewHeaderPolicy = vhp; } int Theme::iconSize() const { return mIconSize; } void Theme::setIconSize(int iconSize) { if (mIconSize != iconSize) { clearPixmapCache(); mIconSize = iconSize; if ((mIconSize < 8) || (mIconSize > 64)) { mIconSize = gThemeDefaultIconSize; } } } bool Theme::load(QDataStream &stream) { removeAllColumns(); int themeVersion; stream >> themeVersion; // We support themes starting at version gThemeMinimumSupportedVersion (0x1013 actually) if ( (themeVersion > gThemeCurrentVersion) || (themeVersion < gThemeMinimumSupportedVersion) ) { qCDebug(MESSAGELIST_LOG) << "Invalid theme version"; return false; // b0rken (invalid version) } int val; stream >> val; mGroupHeaderBackgroundMode = static_cast(val); switch (mGroupHeaderBackgroundMode) { case Transparent: case AutoColor: case CustomColor: // ok break; default: qCDebug(MESSAGELIST_LOG) << "Invalid theme group header background mode"; return false; // b0rken } stream >> mGroupHeaderBackgroundColor; stream >> val; mGroupHeaderBackgroundStyle = static_cast(val); switch (mGroupHeaderBackgroundStyle) { case PlainRect: case PlainJoinedRect: case RoundedRect: case RoundedJoinedRect: case GradientRect: case GradientJoinedRect: case StyledRect: case StyledJoinedRect: // ok break; default: qCDebug(MESSAGELIST_LOG) << "Invalid theme group header background style"; return false; // b0rken } stream >> val; mViewHeaderPolicy = (ViewHeaderPolicy)val; switch (mViewHeaderPolicy) { case ShowHeaderAlways: case NeverShowHeader: // ok break; default: qCDebug(MESSAGELIST_LOG) << "Invalid theme view header policy"; return false; // b0rken } if (themeVersion >= gThemeMinimumVersionWithIconSizeField) { // icon size parameter stream >> mIconSize; if ((mIconSize < 8) || (mIconSize > 64)) { mIconSize = gThemeDefaultIconSize; // limit insane values } } else { mIconSize = gThemeDefaultIconSize; } // column count stream >> val; if (val < 1 || val > 50) { return false; // plain b0rken ( negative, zero or more than 50 columns ) } for (int i = 0; i < val; ++i) { Column *col = new Column(); if (!col->load(stream, themeVersion)) { qCDebug(MESSAGELIST_LOG) << "Column loading failed"; delete col; return false; } addColumn(col); } return true; } void Theme::save(QDataStream &stream) const { stream << (int)gThemeCurrentVersion; stream << (int)mGroupHeaderBackgroundMode; stream << mGroupHeaderBackgroundColor; stream << (int)mGroupHeaderBackgroundStyle; stream << (int)mViewHeaderPolicy; stream << mIconSize; const int cnt = mColumns.count(); stream << (int)cnt; for (int i = 0; i < cnt; ++i) { Column *col = mColumns.at(i); col->save(stream); } } void Theme::clearPixmapCache() const { qDeleteAll(mPixmaps); mPixmaps.clear(); } void Theme::populatePixmapCache() const { clearPixmapCache(); mPixmaps.reserve(_IconCount); // WARNING: The order of those icons must be in sync with order of the // corresponding enum values in ThemeIcon! mPixmaps << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-unread-new")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-unread")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-read")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-deleted")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-replied")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-forwarded-replied")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-queued")).pixmap(mIconSize, mIconSize)) // mail-queue ? << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-task")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-sent")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-forwarded")).pixmap(mIconSize, mIconSize)) - << new QPixmap(QIcon::fromTheme(QStringLiteral("emblem-important")).pixmap(mIconSize, mIconSize)) // "flag" + << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-important")).pixmap(mIconSize, mIconSize)) // "flag" << new QPixmap(QIcon::fromTheme(QStringLiteral("messagelist/pics/mail-thread-watch.png")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("messagelist/pics/mail-thread-ignored.png")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-junk")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-notjunk")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-signed-verified")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-signed-part")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-signed")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("text-plain")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-encrypted-full")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-encrypted-part")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-encrypted")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("text-plain")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-attachment")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("view-pim-notes")).pixmap(mIconSize, mIconSize)) << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-invitation")).pixmap(mIconSize, mIconSize)) << ((QApplication::isRightToLeft()) ? new QPixmap(QIcon::fromTheme(QStringLiteral("arrow-left")).pixmap(mIconSize, mIconSize)) : new QPixmap(QIcon::fromTheme(QStringLiteral("arrow-right")).pixmap(mIconSize, mIconSize))) << new QPixmap(QIcon::fromTheme(QStringLiteral("arrow-down")).pixmap(mIconSize, mIconSize)) << new QPixmap(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("messagelist/pics/mail-vertical-separator-line.png"))) << new QPixmap(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("messagelist/pics/mail-horizontal-space.png"))); } diff --git a/messagelist/src/core/widgets/searchlinestatus.cpp b/messagelist/src/core/widgets/searchlinestatus.cpp index 5972206e..20517bfb 100644 --- a/messagelist/src/core/widgets/searchlinestatus.cpp +++ b/messagelist/src/core/widgets/searchlinestatus.cpp @@ -1,365 +1,365 @@ /* Copyright (c) 2016-2020 Laurent Montel 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 "searchlinestatus.h" #include "messagelist_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static const char qLineEditclearButtonActionNameC[] = "_q_qlineeditclearaction"; #define MAX_COMPLETION_ITEMS 20 using namespace MessageList::Core; SearchLineStatus::SearchLineStatus(QWidget *parent) : QLineEdit(parent) { mCompleter = new QCompleter(this); mCompleterListModel = new QStringListModel(this); mCompleter->setModel(mCompleterListModel); setCompleter(mCompleter); setClearButtonEnabled(true); initializeActions(); createMenuSearch(); QAction *act = findChild(QLatin1String(qLineEditclearButtonActionNameC)); if (act) { connect(act, &QAction::triggered, this, &SearchLineStatus::slotClear); } else { qCWarning(MESSAGELIST_LOG) << "Clear button name was changed ! Please verify qt code"; } } SearchLineStatus::~SearchLineStatus() { } void SearchLineStatus::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { if (mCompleter->popup()->isVisible()) { QLineEdit::keyPressEvent(e); } else { Q_EMIT forceLostFocus(); } } else if (e->key() == Qt::Key_Q && (e->modifiers().testFlag(Qt::ShiftModifier) && e->modifiers().testFlag(Qt::AltModifier))) { mLockAction->trigger(); } else { QLineEdit::keyPressEvent(e); } } void SearchLineStatus::slotClear() { Q_EMIT clearButtonClicked(); } void SearchLineStatus::updateLockAction() { if (mLocked) { mLockAction->setIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); mLockAction->setToolTip(i18nc("@info:tooltip", "Prevent the quick search field from being cleared when changing folders")); } else { mLockAction->setIcon(QIcon::fromTheme(QStringLiteral("object-unlocked"))); mLockAction->setToolTip(i18nc("@info:tooltip", "Clear the quick search field when changing folders")); } } void SearchLineStatus::setLocked(bool b) { if (mLocked != b) { slotToggledLockAction(); } } bool SearchLineStatus::locked() const { return mLocked; } void SearchLineStatus::initializeActions() { mLockAction = addAction(QIcon::fromTheme(QStringLiteral("object-locked")), QLineEdit::TrailingPosition); mLockAction->setWhatsThis( i18nc("@info:whatsthis", "Toggle this button if you want to keep your quick search " "locked when moving to other folders or when narrowing the search " "by message status.")); connect(mLockAction, &QAction::triggered, this, &SearchLineStatus::slotToggledLockAction); updateLockAction(); const QStringList overlays = QStringList() << QStringLiteral("list-add"); mWithFilter = QIcon(new KIconEngine(QStringLiteral("view-filter"), KIconLoader::global(), overlays)); mWithoutFilter = QIcon::fromTheme(QStringLiteral("view-filter")); mFiltersAction = addAction(mWithoutFilter, QLineEdit::LeadingPosition); mFiltersAction->setToolTip(i18n("Filter Mails by Status")); connect(mFiltersAction, &QAction::triggered, this, &SearchLineStatus::showMenu); } void SearchLineStatus::slotToggledLockAction() { mLocked = !mLocked; updateLockAction(); } void SearchLineStatus::updateFilters() { QVector lstStatus; for (QAction *act : qAsConst(mFilterListActions)) { if (act->isChecked()) { Akonadi::MessageStatus status; status.fromQInt32(static_cast< qint32 >(act->data().toInt())); lstStatus.append(status); } } mHasFilter = !lstStatus.isEmpty(); Q_EMIT filterActionChanged(lstStatus); updateFilterActionIcon(); } void SearchLineStatus::showMenu() { if (mFilterMenu->exec(mapToGlobal(QPoint(0, height())))) { updateFilters(); } } void SearchLineStatus::clearFilterAction() { for (QAction *act : qAsConst(mFilterListActions)) { act->setChecked(false); } mHasFilter = false; updateFilterActionIcon(); } void SearchLineStatus::createFilterAction(const QIcon &icon, const QString &text, int value) { QAction *act = new QAction(icon, text, this); act->setCheckable(true); act->setData(value); mFilterMenu->addAction(act); mFilterListActions.append(act); } void SearchLineStatus::updateFilterActionIcon() { mFiltersAction->setIcon(mHasFilter ? mWithFilter : mWithoutFilter); if (mColorName.isEmpty()) { const KColorScheme::BackgroundRole bgColorScheme(KColorScheme::PositiveBackground); KStatefulBrush bgBrush(KColorScheme::View, bgColorScheme); mColorName = bgBrush.brush(this).color().name(); } setStyleSheet(mHasFilter ? QStringLiteral("QLineEdit{ background-color:%1 }").arg(mColorName) : QString()); } void SearchLineStatus::clearFilterButtonClicked() { clearFilterAction(); clearFilterByAction(); updateFilters(); } void SearchLineStatus::createMenuSearch() { mFilterMenu = new QMenu(this); mFilterMenu->setObjectName(QStringLiteral("filtermenu")); QWidgetAction *clearWidgetAction = new QWidgetAction(mFilterMenu); QPushButton *clearFilterButton = new QPushButton(i18n("Clear Filter"), mFilterMenu); connect(clearFilterButton, &QPushButton::clicked, this, &SearchLineStatus::clearFilterButtonClicked); clearWidgetAction->setDefaultWidget(clearFilterButton); mFilterMenu->addAction(clearWidgetAction); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-unread")), i18nc("@action:inmenu Status of a message", "Unread"), Akonadi::MessageStatus::statusUnread().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-replied")), i18nc("@action:inmenu Status of a message", "Replied"), Akonadi::MessageStatus::statusReplied().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-forwarded")), i18nc("@action:inmenu Status of a message", "Forwarded"), Akonadi::MessageStatus::statusForwarded().toQInt32()); - createFilterAction(QIcon::fromTheme(QStringLiteral("emblem-important")), + createFilterAction(QIcon::fromTheme(QStringLiteral("mail-mark-important")), i18nc("@action:inmenu Status of a message", "Important"), Akonadi::MessageStatus::statusImportant().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-task")), i18nc("@action:inmenu Status of a message", "Action Item"), Akonadi::MessageStatus::statusToAct().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-thread-watch.png")), i18nc("@action:inmenu Status of a message", "Watched"), Akonadi::MessageStatus::statusWatched().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-thread-ignored.png")), i18nc("@action:inmenu Status of a message", "Ignored"), Akonadi::MessageStatus::statusIgnored().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18nc("@action:inmenu Status of a message", "Has Attachment"), Akonadi::MessageStatus::statusHasAttachment().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-invitation")), i18nc("@action:inmenu Status of a message", "Has Invitation"), Akonadi::MessageStatus::statusHasInvitation().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-mark-junk")), i18nc("@action:inmenu Status of a message", "Spam"), Akonadi::MessageStatus::statusSpam().toQInt32()); createFilterAction(QIcon::fromTheme(QStringLiteral("mail-mark-notjunk")), i18nc("@action:inmenu Status of a message", "Ham"), Akonadi::MessageStatus::statusHam().toQInt32()); createFilterByAction(); } void SearchLineStatus::createFilterByAction() { mFilterMenu->addSeparator(); QActionGroup *grp = new QActionGroup(mFilterMenu); mSearchEveryWhereAction = new QAction(i18n("Full Message"), mFilterMenu); mSearchEveryWhereAction->setCheckable(true); mSearchEveryWhereAction->setChecked(true); mFilterMenu->addAction(mSearchEveryWhereAction); grp->addAction(mSearchEveryWhereAction); mSearchAgainstBodyAction = new QAction(i18n("Body"), mFilterMenu); grp->addAction(mSearchAgainstBodyAction); mFilterMenu->addAction(mSearchAgainstBodyAction); mSearchAgainstBodyAction->setCheckable(true); mSearchAgainstSubjectAction = new QAction(i18n("Subject"), mFilterMenu); grp->addAction(mSearchAgainstSubjectAction); mFilterMenu->addAction(mSearchAgainstSubjectAction); mSearchAgainstSubjectAction->setCheckable(true); mSearchAgainstFromOrToAction = new QAction(mFilterMenu); changeSearchAgainstFromOrToText(); grp->addAction(mSearchAgainstFromOrToAction); mFilterMenu->addAction(mSearchAgainstFromOrToAction); mSearchAgainstFromOrToAction->setCheckable(true); mSearchAgainstBccAction = new QAction(i18n("BCC"), mFilterMenu); grp->addAction(mSearchAgainstBccAction); mFilterMenu->addAction(mSearchAgainstBccAction); mSearchAgainstBccAction->setCheckable(true); connect(grp, &QActionGroup::triggered, this, &SearchLineStatus::slotFilterActionClicked); } void SearchLineStatus::clearFilterByAction() { mSearchEveryWhereAction->setChecked(true); } bool SearchLineStatus::containsOutboundMessages() const { return mContainsOutboundMessages; } void SearchLineStatus::setContainsOutboundMessages(bool containsOutboundMessages) { if (mContainsOutboundMessages != containsOutboundMessages) { mContainsOutboundMessages = containsOutboundMessages; changeSearchAgainstFromOrToText(); } } void SearchLineStatus::changeSearchAgainstFromOrToText() { if (mContainsOutboundMessages) { mSearchAgainstFromOrToAction->setText(i18n("To")); } else { mSearchAgainstFromOrToAction->setText(i18n("From")); } } QuickSearchLine::SearchOptions SearchLineStatus::searchOptions() const { QuickSearchLine::SearchOptions searchOptions; if (mSearchEveryWhereAction->isChecked()) { searchOptions |= QuickSearchLine::SearchEveryWhere; } if (mSearchAgainstBodyAction->isChecked()) { searchOptions |= QuickSearchLine::SearchAgainstBody; } if (mSearchAgainstSubjectAction->isChecked()) { searchOptions |= QuickSearchLine::SearchAgainstSubject; } if (mSearchAgainstFromOrToAction->isChecked()) { if (mContainsOutboundMessages) { searchOptions |= QuickSearchLine::SearchAgainstTo; } else { searchOptions |= QuickSearchLine::SearchAgainstFrom; } } if (mSearchAgainstBccAction->isChecked()) { searchOptions |= QuickSearchLine::SearchAgainstBcc; } return searchOptions; } void SearchLineStatus::slotFilterActionClicked(QAction *act) { Q_UNUSED(act); Q_EMIT searchOptionChanged(); } void SearchLineStatus::addCompletionItem(const QString &str) { mListCompetion.removeAll(str); mListCompetion.prepend(str); while (mListCompetion.size() > MAX_COMPLETION_ITEMS) { mListCompetion.removeLast(); } mCompleterListModel->setStringList(mListCompetion); } void SearchLineStatus::contextMenuEvent(QContextMenuEvent *e) { QMenu *popup = QLineEdit::createStandardContextMenu(); if (popup) { popup->addSeparator(); popup->addAction(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-rtl")), i18n("Clear History"), this, &SearchLineStatus::slotClearHistory); popup->exec(e->globalPos()); delete popup; } } void SearchLineStatus::slotClearHistory() { mListCompetion.clear(); mCompleterListModel->setStringList(mListCompetion); }