diff --git a/src/configuredialog/configureappearancepage.cpp b/src/configuredialog/configureappearancepage.cpp index 970f2d89c..b7e134800 100644 --- a/src/configuredialog/configureappearancepage.cpp +++ b/src/configuredialog/configureappearancepage.cpp @@ -1,1376 +1,1384 @@ /* Copyright (c) 2013-2019 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "configureappearancepage.h" #include "PimCommon/ConfigureImmutableWidgetUtils" using namespace PimCommon::ConfigureImmutableWidgetUtils; #include "configuredialog/colorlistbox.h" #include "messagelist/aggregationcombobox.h" #include "messagelist/aggregationconfigbutton.h" #include "messagelist/themecombobox.h" #include "messagelist/themeconfigbutton.h" #include "messagelistsettings.h" #include "MailCommon/TagWidget" #include "MailCommon/Tag" #include "kmkernel.h" #include "util.h" #include "MailCommon/FolderTreeWidget" #include "kmmainwidget.h" #include "mailcommonsettings_base.h" #include "MessageViewer/ConfigureWidget" #include "messageviewer/messageviewersettings.h" #include "messagelist/messagelistutil.h" #include #include "MessageCore/MessageCoreUtil" #include "settings/kmailsettings.h" #include "MailCommon/MailUtil" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kmail_debug.h" #include using KMime::DateFormatter; #include #include #include #include #include #include #include #include #include #include using namespace MailCommon; QString AppearancePage::helpAnchor() const { return QStringLiteral("configure-appearance"); } AppearancePage::AppearancePage(QWidget *parent) : ConfigModuleWithTabs(parent) { // // "General" tab: // ReaderTab *readerTab = new ReaderTab(); addTab(readerTab, i18n("General")); addConfig(MessageViewer::MessageViewerSettings::self(), readerTab); // // "Fonts" tab: // FontsTab *fontsTab = new FontsTab(); addTab(fontsTab, i18n("Fonts")); // // "Colors" tab: // ColorsTab *colorsTab = new ColorsTab(); addTab(colorsTab, i18n("Colors")); // // "Layout" tab: // LayoutTab *layoutTab = new LayoutTab(); addTab(layoutTab, i18n("Layout")); // // "Headers" tab: // HeadersTab *headersTab = new HeadersTab(); addTab(headersTab, i18n("Message List")); // // "Message Tag" tab: // MessageTagTab *messageTagTab = new MessageTagTab(); addTab(messageTagTab, i18n("Message Tags")); } QString AppearancePage::FontsTab::helpAnchor() const { return QStringLiteral("configure-appearance-fonts"); } static const struct { const char *configName; const char *displayName; bool enableFamilyAndSize; bool onlyFixed; } fontNames[] = { { "body-font", I18N_NOOP("Message Body"), true, false }, { "MessageListFont", I18N_NOOP("Message List"), true, false }, { "UnreadMessageFont", I18N_NOOP("Message List - Unread Messages"), false, false }, { "ImportantMessageFont", I18N_NOOP("Message List - Important Messages"), false, false }, { "TodoMessageFont", I18N_NOOP("Message List - Action Item Messages"), false, false }, { "fixed-font", I18N_NOOP("Fixed Width Font"), true, true }, { "composer-font", I18N_NOOP("Composer"), true, false }, { "print-font", I18N_NOOP("Printing Output"), true, false }, }; static const int numFontNames = sizeof fontNames / sizeof *fontNames; AppearancePageFontsTab::AppearancePageFontsTab(QWidget *parent) : ConfigModuleTab(parent) , mActiveFontIndex(-1) { assert(numFontNames == sizeof mFont / sizeof *mFont); // "Use custom fonts" checkbox, followed by
QVBoxLayout *vlay = new QVBoxLayout(this); mCustomFontCheck = new QCheckBox(i18n("&Use custom fonts"), this); vlay->addWidget(mCustomFontCheck); vlay->addWidget(new KSeparator(Qt::Horizontal, this)); connect(mCustomFontCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); // "font location" combo box and label: QHBoxLayout *hlay = new QHBoxLayout(); // inherites spacing vlay->addLayout(hlay); mFontLocationCombo = new KComboBox(this); mFontLocationCombo->setEditable(false); mFontLocationCombo->setEnabled(false); // !mCustomFontCheck->isChecked() QStringList fontDescriptions; fontDescriptions.reserve(numFontNames); for (int i = 0; i < numFontNames; ++i) { fontDescriptions << i18n(fontNames[i].displayName); } mFontLocationCombo->addItems(fontDescriptions); QLabel *label = new QLabel(i18n("Apply &to:"), this); label->setBuddy(mFontLocationCombo); label->setEnabled(false); // since !mCustomFontCheck->isChecked() hlay->addWidget(label); hlay->addWidget(mFontLocationCombo); hlay->addStretch(10); mFontChooser = new KFontChooser(this, KFontChooser::DisplayFrame, QStringList(), 4); mFontChooser->setEnabled(false); // since !mCustomFontCheck->isChecked() vlay->addWidget(mFontChooser); connect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged); // {en,dis}able widgets depending on the state of mCustomFontCheck: connect(mCustomFontCheck, &QAbstractButton::toggled, label, &QWidget::setEnabled); connect(mCustomFontCheck, &QAbstractButton::toggled, mFontLocationCombo, &QWidget::setEnabled); connect(mCustomFontCheck, &QAbstractButton::toggled, mFontChooser, &QWidget::setEnabled); // load the right font settings into mFontChooser: connect(mFontLocationCombo, QOverload::of(&KComboBox::activated), this, &AppearancePage::FontsTab::slotFontSelectorChanged); } void AppearancePage::FontsTab::slotFontSelectorChanged(int index) { qCDebug(KMAIL_LOG) << "slotFontSelectorChanged() called"; if (index < 0 || index >= mFontLocationCombo->count()) { return; // Should never happen, but it is better to check. } // Save current fontselector setting before we install the new: if (mActiveFontIndex == 0) { mFont[0] = mFontChooser->font(); // hardcode the family and size of "message body" dependant fonts: for (int i = 0; i < numFontNames; ++i) { if (!fontNames[i].enableFamilyAndSize) { // ### shall we copy the font and set the save and re-set // {regular,italic,bold,bold italic} property or should we // copy only family and pointSize? mFont[i].setFamily(mFont[0].family()); mFont[i].setPointSize/*Float?*/ (mFont[0].pointSize/*Float?*/ ()); } } } else if (mActiveFontIndex > 0) { mFont[ mActiveFontIndex ] = mFontChooser->font(); } mActiveFontIndex = index; // Disonnect so the "Apply" button is not activated by the change disconnect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged); // Display the new setting: mFontChooser->setFont(mFont[index], fontNames[index].onlyFixed); connect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged); // Disable Family and Size list if we have selected a quote font: mFontChooser->enableColumn(KFontChooser::FamilyList | KFontChooser::SizeList, fontNames[ index ].enableFamilyAndSize); } void AppearancePage::FontsTab::doLoadOther() { if (KMKernel::self()) { KConfigGroup fonts(KMKernel::self()->config(), "Fonts"); mFont[0] = QFontDatabase::systemFont(QFontDatabase::GeneralFont); QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); for (int i = 0; i < numFontNames; ++i) { const QString configName = QLatin1String(fontNames[i].configName); if (configName == QLatin1String("MessageListFont")) { mFont[i] = MessageList::MessageListSettings::self()->messageListFont(); } else if (configName == QLatin1String("UnreadMessageFont")) { mFont[i] = MessageList::MessageListSettings::self()->unreadMessageFont(); } else if (configName == QLatin1String("ImportantMessageFont")) { mFont[i] = MessageList::MessageListSettings::self()->importantMessageFont(); } else if (configName == QLatin1String("TodoMessageFont")) { mFont[i] = MessageList::MessageListSettings::self()->todoMessageFont(); } else { mFont[i] = fonts.readEntry(configName, (fontNames[i].onlyFixed) ? fixedFont : mFont[0]); } } mCustomFontCheck->setChecked(!MessageCore::MessageCoreSettings::self()->useDefaultFonts()); mFontLocationCombo->setCurrentIndex(0); slotFontSelectorChanged(0); } else { setEnabled(false); } } void AppearancePage::FontsTab::save() { if (KMKernel::self()) { KConfigGroup fonts(KMKernel::self()->config(), "Fonts"); // read the current font (might have been modified) if (mActiveFontIndex >= 0) { mFont[ mActiveFontIndex ] = mFontChooser->font(); } const bool customFonts = mCustomFontCheck->isChecked(); MessageCore::MessageCoreSettings::self()->setUseDefaultFonts(!customFonts); for (int i = 0; i < numFontNames; ++i) { const QString configName = QLatin1String(fontNames[i].configName); if (customFonts && configName == QLatin1String("MessageListFont")) { MessageList::MessageListSettings::self()->setMessageListFont(mFont[i]); } else if (customFonts && configName == QLatin1String("UnreadMessageFont")) { MessageList::MessageListSettings::self()->setUnreadMessageFont(mFont[i]); } else if (customFonts && configName == QLatin1String("ImportantMessageFont")) { MessageList::MessageListSettings::self()->setImportantMessageFont(mFont[i]); } else if (customFonts && configName == QLatin1String("TodoMessageFont")) { MessageList::MessageListSettings::self()->setTodoMessageFont(mFont[i]); } else { if (customFonts || fonts.hasKey(configName)) { // Don't write font info when we use default fonts, but write // if it's already there: fonts.writeEntry(configName, mFont[i]); } } } } } void AppearancePage::FontsTab::doResetToDefaultsOther() { mCustomFontCheck->setChecked(false); } QString AppearancePage::ColorsTab::helpAnchor() const { return QStringLiteral("configure-appearance-colors"); } static const struct { const char *configName; const char *displayName; } colorNames[] = { // adjust doLoadOther if you change this: { "QuotedText1", I18N_NOOP("Quoted Text - First Level") }, { "QuotedText2", I18N_NOOP("Quoted Text - Second Level") }, { "QuotedText3", I18N_NOOP("Quoted Text - Third Level") }, { "LinkColor", I18N_NOOP("Link") }, { "UnreadMessageColor", I18N_NOOP("Unread Message") }, { "ImportantMessageColor", I18N_NOOP("Important Message") }, { "TodoMessageColor", I18N_NOOP("Action Item Message") }, { "ColorbarBackgroundPlain", I18N_NOOP("HTML Status Bar Background - No HTML Message") }, { "ColorbarForegroundPlain", I18N_NOOP("HTML Status Bar Foreground - No HTML Message") }, { "ColorbarBackgroundHTML", I18N_NOOP("HTML Status Bar Background - HTML Message") }, { "ColorbarForegroundHTML", I18N_NOOP("HTML Status Bar Foreground - HTML Message") } }; static const int numColorNames = sizeof colorNames / sizeof *colorNames; AppearancePageColorsTab::AppearancePageColorsTab(QWidget *parent) : ConfigModuleTab(parent) { // "use custom colors" check box QVBoxLayout *vlay = new QVBoxLayout(this); mCustomColorCheck = new QCheckBox(i18n("&Use custom colors"), this); vlay->addWidget(mCustomColorCheck); connect(mCustomColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); mUseInlineStyle = new QCheckBox(i18n("&Do not change color from original HTML mail"), this); vlay->addWidget(mUseInlineStyle); connect(mUseInlineStyle, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); // color list box: mColorList = new ColorListBox(this); mColorList->setEnabled(false); // since !mCustomColorCheck->isChecked() for (int i = 0; i < numColorNames; ++i) { mColorList->addColor(i18n(colorNames[i].displayName)); } vlay->addWidget(mColorList, 1); // "recycle colors" check box: mRecycleColorCheck = new QCheckBox(i18n("Recycle colors on deep "ing"), this); mRecycleColorCheck->setEnabled(false); vlay->addWidget(mRecycleColorCheck); connect(mRecycleColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); // close to quota threshold QHBoxLayout *hbox = new QHBoxLayout(); vlay->addLayout(hbox); QLabel *l = new QLabel(i18n("Close to quota threshold:"), this); hbox->addWidget(l); mCloseToQuotaThreshold = new QSpinBox(this); mCloseToQuotaThreshold->setRange(0, 100); mCloseToQuotaThreshold->setSingleStep(1); connect(mCloseToQuotaThreshold, QOverload::of(&QSpinBox::valueChanged), this, &ConfigModuleTab::slotEmitChanged); mCloseToQuotaThreshold->setSuffix(i18n("%")); hbox->addWidget(mCloseToQuotaThreshold); hbox->addWidget(new QWidget(this), 2); // {en,dir}able widgets depending on the state of mCustomColorCheck: connect(mCustomColorCheck, &QAbstractButton::toggled, mColorList, &QWidget::setEnabled); connect(mCustomColorCheck, &QAbstractButton::toggled, mRecycleColorCheck, &QWidget::setEnabled); connect(mCustomColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); connect(mColorList, &ColorListBox::changed, this, &ConfigModuleTab::slotEmitChanged); } void AppearancePage::ColorsTab::doLoadOther() { mCustomColorCheck->setChecked(!MessageCore::MessageCoreSettings::self()->useDefaultColors()); mUseInlineStyle->setChecked(MessageCore::MessageCoreSettings::self()->useRealHtmlMailColor()); mRecycleColorCheck->setChecked(MessageViewer::MessageViewerSettings::self()->recycleQuoteColors()); mCloseToQuotaThreshold->setValue(KMailSettings::self()->closeToQuotaThreshold()); loadColor(true); } void AppearancePage::ColorsTab::loadColor(bool loadFromConfig) { if (KMKernel::self()) { KConfigGroup reader(KMKernel::self()->config(), "Reader"); static const QColor defaultColor[ numColorNames ] = { MessageCore::ColorUtil::self()->quoteLevel1DefaultTextColor(), MessageCore::ColorUtil::self()->quoteLevel2DefaultTextColor(), MessageCore::ColorUtil::self()->quoteLevel3DefaultTextColor(), MessageCore::ColorUtil::self()->linkColor(), // link MessageList::Util::unreadDefaultMessageColor(), // unread mgs MessageList::Util::importantDefaultMessageColor(), // important msg MessageList::Util::todoDefaultMessageColor(), // action item mgs Qt::lightGray, // colorbar plain bg Qt::black, // colorbar plain fg Qt::black, // colorbar html bg Qt::white // colorbar html fg }; for (int i = 0; i < numColorNames; ++i) { if (loadFromConfig) { const QString configName = QLatin1String(colorNames[i].configName); if (configName == QLatin1String("UnreadMessageColor")) { mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->unreadMessageColor()); } else if (configName == QLatin1String("ImportantMessageColor")) { mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->importantMessageColor()); } else if (configName == QLatin1String("TodoMessageColor")) { mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->todoMessageColor()); } else { mColorList->setColorSilently(i, reader.readEntry(configName, defaultColor[i])); } } else { mColorList->setColorSilently(i, defaultColor[i]); } } } else { setEnabled(false); } } void AppearancePage::ColorsTab::doResetToDefaultsOther() { mCustomColorCheck->setChecked(false); mUseInlineStyle->setChecked(false); mRecycleColorCheck->setChecked(false); mCloseToQuotaThreshold->setValue(80); loadColor(false); } void AppearancePage::ColorsTab::save() { if (!KMKernel::self()) { return; } KConfigGroup reader(KMKernel::self()->config(), "Reader"); bool customColors = mCustomColorCheck->isChecked(); MessageCore::MessageCoreSettings::self()->setUseDefaultColors(!customColors); MessageCore::MessageCoreSettings::self()->setUseRealHtmlMailColor(mUseInlineStyle->isChecked()); for (int i = 0; i < numColorNames; ++i) { const QString configName = QLatin1String(colorNames[i].configName); if (customColors && configName == QLatin1String("UnreadMessageColor")) { MessageList::MessageListSettings::self()->setUnreadMessageColor(mColorList->color(i)); } else if (customColors && configName == QLatin1String("ImportantMessageColor")) { MessageList::MessageListSettings::self()->setImportantMessageColor(mColorList->color(i)); } else if (customColors && configName == QLatin1String("TodoMessageColor")) { MessageList::MessageListSettings::self()->setTodoMessageColor(mColorList->color(i)); } else { if (customColors || reader.hasKey(configName)) { reader.writeEntry(configName, mColorList->color(i)); } } } MessageViewer::MessageViewerSettings::self()->setRecycleQuoteColors(mRecycleColorCheck->isChecked()); KMailSettings::self()->setCloseToQuotaThreshold(mCloseToQuotaThreshold->value()); } QString AppearancePage::LayoutTab::helpAnchor() const { return QStringLiteral("configure-appearance-layout"); } AppearancePageLayoutTab::AppearancePageLayoutTab(QWidget *parent) : ConfigModuleTab(parent) { QVBoxLayout *vlay = new QVBoxLayout(this); // "folder list" radio buttons: populateButtonGroup(mFolderListGroupBox = new QGroupBox(this), mFolderListGroup = new QButtonGroup(this), Qt::Vertical, KMailSettings::self()->folderListItem()); vlay->addWidget(mFolderListGroupBox); connect(mFolderListGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ConfigModuleTab::slotEmitChanged); QHBoxLayout *folderCBHLayout = new QHBoxLayout; mFolderQuickSearchCB = new QCheckBox(i18n("Show folder quick search field"), this); connect(mFolderQuickSearchCB, &QAbstractButton::toggled, this, &ConfigModuleTab::slotEmitChanged); folderCBHLayout->addWidget(mFolderQuickSearchCB); vlay->addLayout(folderCBHLayout); // "favorite folders view mode" radio buttons: mFavoriteFoldersViewGroupBox = new QGroupBox(this); mFavoriteFoldersViewGroupBox->setTitle(i18n("Show Favorite Folders View")); mFavoriteFoldersViewGroupBox->setLayout(new QVBoxLayout()); mFavoriteFoldersViewGroup = new QButtonGroup(this); connect(mFavoriteFoldersViewGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ConfigModuleTab::slotEmitChanged); QRadioButton *favoriteFoldersViewHiddenRadio = new QRadioButton(i18n("Never"), mFavoriteFoldersViewGroupBox); mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewHiddenRadio, static_cast(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode)); mFavoriteFoldersViewGroupBox->layout()->addWidget(favoriteFoldersViewHiddenRadio); QRadioButton *favoriteFoldersViewIconsRadio = new QRadioButton(i18n("As icons"), mFavoriteFoldersViewGroupBox); mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewIconsRadio, static_cast(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode)); mFavoriteFoldersViewGroupBox->layout()->addWidget(favoriteFoldersViewIconsRadio); QRadioButton *favoriteFoldersViewListRadio = new QRadioButton(i18n("As list"), mFavoriteFoldersViewGroupBox); mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewListRadio, static_cast(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode)); mFavoriteFoldersViewGroupBox->layout()->addWidget(favoriteFoldersViewListRadio); vlay->addWidget(mFavoriteFoldersViewGroupBox); // "folder tooltips" radio buttons: mFolderToolTipsGroupBox = new QGroupBox(this); mFolderToolTipsGroupBox->setTitle(i18n("Folder Tooltips")); mFolderToolTipsGroupBox->setLayout(new QVBoxLayout()); mFolderToolTipsGroup = new QButtonGroup(this); connect(mFolderToolTipsGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ConfigModuleTab::slotEmitChanged); QRadioButton *folderToolTipsAlwaysRadio = new QRadioButton(i18n("Always"), mFolderToolTipsGroupBox); mFolderToolTipsGroup->addButton(folderToolTipsAlwaysRadio, static_cast< int >(FolderTreeWidget::DisplayAlways)); mFolderToolTipsGroupBox->layout()->addWidget(folderToolTipsAlwaysRadio); QRadioButton *folderToolTipsNeverRadio = new QRadioButton(i18n("Never"), mFolderToolTipsGroupBox); mFolderToolTipsGroup->addButton(folderToolTipsNeverRadio, static_cast< int >(FolderTreeWidget::DisplayNever)); mFolderToolTipsGroupBox->layout()->addWidget(folderToolTipsNeverRadio); vlay->addWidget(mFolderToolTipsGroupBox); // "show reader window" radio buttons: populateButtonGroup(mReaderWindowModeGroupBox = new QGroupBox(this), mReaderWindowModeGroup = new QButtonGroup(this), Qt::Vertical, KMailSettings::self()->readerWindowModeItem()); vlay->addWidget(mReaderWindowModeGroupBox); connect(mReaderWindowModeGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ConfigModuleTab::slotEmitChanged); vlay->addStretch(10); // spacer } void AppearancePage::LayoutTab::doLoadOther() { loadWidget(mFolderListGroupBox, mFolderListGroup, KMailSettings::self()->folderListItem()); loadWidget(mReaderWindowModeGroupBox, mReaderWindowModeGroup, KMailSettings::self()->readerWindowModeItem()); if (KMKernel::self()) { loadWidget(mFavoriteFoldersViewGroupBox, mFavoriteFoldersViewGroup, KMKernel::self()->mailCommonSettings()->favoriteCollectionViewModeItem()); } loadWidget(mFolderQuickSearchCB, KMailSettings::self()->enableFolderQuickSearchItem()); const int checkedFolderToolTipsPolicy = KMailSettings::self()->toolTipDisplayPolicy(); if (checkedFolderToolTipsPolicy >= 0) { mFolderToolTipsGroup->button(checkedFolderToolTipsPolicy)->setChecked(true); } } void AppearancePage::LayoutTab::save() { saveButtonGroup(mFolderListGroup, KMailSettings::self()->folderListItem()); saveButtonGroup(mReaderWindowModeGroup, KMailSettings::self()->readerWindowModeItem()); if (KMKernel::self()) { saveButtonGroup(mFavoriteFoldersViewGroup, KMKernel::self()->mailCommonSettings()->favoriteCollectionViewModeItem()); } saveCheckBox(mFolderQuickSearchCB, KMailSettings::self()->enableFolderQuickSearchItem()); KMailSettings::self()->setToolTipDisplayPolicy(mFolderToolTipsGroup->checkedId()); } // // Appearance Message List // QString AppearancePage::HeadersTab::helpAnchor() const { return QStringLiteral("configure-appearance-headers"); } static const struct { const char *displayName; DateFormatter::FormatType dateDisplay; } dateDisplayConfig[] = { { I18N_NOOP("Sta&ndard format (%1)"), KMime::DateFormatter::CTime }, { I18N_NOOP("Locali&zed format (%1)"), KMime::DateFormatter::Localized }, { I18N_NOOP("Smart for&mat (%1)"), KMime::DateFormatter::Fancy }, { I18N_NOOP("C&ustom format:"), KMime::DateFormatter::Custom } }; static const int numDateDisplayConfig = sizeof dateDisplayConfig / sizeof *dateDisplayConfig; AppearancePageHeadersTab::AppearancePageHeadersTab(QWidget *parent) : ConfigModuleTab(parent) , mCustomDateFormatEdit(nullptr) { QVBoxLayout *vlay = new QVBoxLayout(this); // "General Options" group: QGroupBox *group = new QGroupBox(i18nc("General options for the message list.", "General"), this); QVBoxLayout *gvlay = new QVBoxLayout(group); mDisplayMessageToolTips = new QCheckBox( MessageList::MessageListSettings::self()->messageToolTipEnabledItem()->label(), group); gvlay->addWidget(mDisplayMessageToolTips); connect(mDisplayMessageToolTips, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); // "Aggregation" using MessageList::Utils::AggregationComboBox; mAggregationComboBox = new AggregationComboBox(group); QLabel *aggregationLabel = new QLabel(i18n("Default aggregation:"), group); aggregationLabel->setBuddy(mAggregationComboBox); using MessageList::Utils::AggregationConfigButton; AggregationConfigButton *aggregationConfigButton = new AggregationConfigButton(group, mAggregationComboBox); QHBoxLayout *aggregationLayout = new QHBoxLayout(); aggregationLayout->addWidget(aggregationLabel, 1); aggregationLayout->addWidget(mAggregationComboBox, 1); aggregationLayout->addWidget(aggregationConfigButton, 0); gvlay->addLayout(aggregationLayout); connect(aggregationConfigButton, &MessageList::Utils::AggregationConfigButton::configureDialogCompleted, this, &AppearancePageHeadersTab::slotSelectDefaultAggregation); connect(mAggregationComboBox, QOverload::of(&MessageList::Utils::AggregationComboBox::activated), this, &ConfigModuleTab::slotEmitChanged); // "Theme" using MessageList::Utils::ThemeComboBox; mThemeComboBox = new ThemeComboBox(group); QLabel *themeLabel = new QLabel(i18n("Default theme:"), group); themeLabel->setBuddy(mThemeComboBox); using MessageList::Utils::ThemeConfigButton; ThemeConfigButton *themeConfigButton = new ThemeConfigButton(group, mThemeComboBox); QHBoxLayout *themeLayout = new QHBoxLayout(); themeLayout->addWidget(themeLabel, 1); themeLayout->addWidget(mThemeComboBox, 1); themeLayout->addWidget(themeConfigButton, 0); gvlay->addLayout(themeLayout); connect(themeConfigButton, &MessageList::Utils::ThemeConfigButton::configureDialogCompleted, this, &AppearancePageHeadersTab::slotSelectDefaultTheme); connect(mThemeComboBox, QOverload::of(&MessageList::Utils::ThemeComboBox::activated), this, &ConfigModuleTab::slotEmitChanged); vlay->addWidget(group); // "Date Display" group: mDateDisplayBox = new QGroupBox(this); mDateDisplayBox->setTitle(i18n("Date Display")); mDateDisplay = new QButtonGroup(this); mDateDisplay->setExclusive(true); gvlay = new QVBoxLayout(mDateDisplayBox); for (int i = 0; i < numDateDisplayConfig; ++i) { const char *label = dateDisplayConfig[i].displayName; QString buttonLabel; if (QString::fromLatin1(label).contains(QLatin1String("%1"))) { buttonLabel = i18n(label, DateFormatter::formatCurrentDate(dateDisplayConfig[i].dateDisplay)); } else { buttonLabel = i18n(label); } if (dateDisplayConfig[i].dateDisplay == DateFormatter::Custom) { QWidget *hbox = new QWidget(mDateDisplayBox); QHBoxLayout *hboxHBoxLayout = new QHBoxLayout(hbox); hboxHBoxLayout->setMargin(0); QRadioButton *radio = new QRadioButton(buttonLabel, hbox); hboxHBoxLayout->addWidget(radio); mDateDisplay->addButton(radio, dateDisplayConfig[i].dateDisplay); mCustomDateFormatEdit = new KLineEdit(hbox); hboxHBoxLayout->addWidget(mCustomDateFormatEdit); mCustomDateFormatEdit->setEnabled(false); hboxHBoxLayout->setStretchFactor(mCustomDateFormatEdit, 1); connect(radio, &QAbstractButton::toggled, mCustomDateFormatEdit, &QWidget::setEnabled); connect(mCustomDateFormatEdit, &QLineEdit::textChanged, this, &ConfigModuleTab::slotEmitChanged); QLabel *formatHelp = new QLabel( i18n("Custom format information..."), hbox); formatHelp->setContextMenuPolicy(Qt::NoContextMenu); connect(formatHelp, &QLabel::linkActivated, this, &AppearancePageHeadersTab::slotLinkClicked); hboxHBoxLayout->addWidget(formatHelp); mCustomDateWhatsThis = i18n("

These expressions may be used for the date:" "

" "
    " "
  • d - the day as a number without a leading zero (1-31)
  • " "
  • dd - the day as a number with a leading zero (01-31)
  • " "
  • ddd - the abbreviated day name (Mon - Sun)
  • " "
  • dddd - the long day name (Monday - Sunday)
  • " "
  • M - the month as a number without a leading zero (1-12)
  • " "
  • MM - the month as a number with a leading zero (01-12)
  • " "
  • MMM - the abbreviated month name (Jan - Dec)
  • " "
  • MMMM - the long month name (January - December)
  • " "
  • yy - the year as a two digit number (00-99)
  • " "
  • yyyy - the year as a four digit number (0000-9999)
  • " "
" "

These expressions may be used for the time:" "

" "
    " "
  • h - the hour without a leading zero (0-23 or 1-12 if AM/PM display)
  • " "
  • hh - the hour with a leading zero (00-23 or 01-12 if AM/PM display)
  • " "
  • m - the minutes without a leading zero (0-59)
  • " "
  • mm - the minutes with a leading zero (00-59)
  • " "
  • s - the seconds without a leading zero (0-59)
  • " "
  • ss - the seconds with a leading zero (00-59)
  • " "
  • z - the milliseconds without leading zeroes (0-999)
  • " "
  • zzz - the milliseconds with leading zeroes (000-999)
  • " "
  • AP - switch to AM/PM display. AP will be replaced by either \"AM\" or \"PM\".
  • " "
  • ap - switch to AM/PM display. ap will be replaced by either \"am\" or \"pm\".
  • " "
  • Z - time zone in numeric form (-0500)
  • " "
" "

All other input characters will be ignored." "

"); mCustomDateFormatEdit->setWhatsThis(mCustomDateWhatsThis); radio->setWhatsThis(mCustomDateWhatsThis); gvlay->addWidget(hbox); } else { QRadioButton *radio = new QRadioButton(buttonLabel, mDateDisplayBox); gvlay->addWidget(radio); mDateDisplay->addButton(radio, dateDisplayConfig[i].dateDisplay); } } // end for loop populating mDateDisplay vlay->addWidget(mDateDisplayBox); connect(mDateDisplay, QOverload::of(&QButtonGroup::buttonClicked), this, &ConfigModuleTab::slotEmitChanged); vlay->addStretch(10); // spacer } void AppearancePageHeadersTab::slotLinkClicked(const QString &link) { if (link == QLatin1String("whatsthis1")) { QWhatsThis::showText(QCursor::pos(), mCustomDateWhatsThis); } } void AppearancePage::HeadersTab::slotSelectDefaultAggregation() { // Select current default aggregation. mAggregationComboBox->selectDefault(); } void AppearancePage::HeadersTab::slotSelectDefaultTheme() { // Select current default theme. mThemeComboBox->selectDefault(); } void AppearancePage::HeadersTab::doLoadOther() { // "General Options": loadWidget(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem()); // "Aggregation": slotSelectDefaultAggregation(); // "Theme": slotSelectDefaultTheme(); // "Date Display": setDateDisplay(MessageCore::MessageCoreSettings::self()->dateFormat(), MessageCore::MessageCoreSettings::self()->customDateFormat()); } void AppearancePage::HeadersTab::doLoadFromGlobalSettings() { loadWidget(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem()); // "Aggregation": slotSelectDefaultAggregation(); // "Theme": slotSelectDefaultTheme(); setDateDisplay(MessageCore::MessageCoreSettings::self()->dateFormat(), MessageCore::MessageCoreSettings::self()->customDateFormat()); } void AppearancePage::HeadersTab::setDateDisplay(int num, const QString &format) { DateFormatter::FormatType dateDisplay = static_cast(num); // special case: needs text for the line edit: if (dateDisplay == DateFormatter::Custom) { mCustomDateFormatEdit->setText(format); } for (int i = 0; i < numDateDisplayConfig; ++i) { if (dateDisplay == dateDisplayConfig[i].dateDisplay) { mDateDisplay->button(dateDisplay)->setChecked(true); return; } } // fell through since none found: mDateDisplay->button(numDateDisplayConfig - 2)->setChecked(true); // default } void AppearancePage::HeadersTab::save() { saveCheckBox(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem()); if (KMKernel::self()) { KMKernel::self()->savePaneSelection(); } // "Aggregation" mAggregationComboBox->writeDefaultConfig(); // "Theme" mThemeComboBox->writeDefaultConfig(); const int dateDisplayID = mDateDisplay->checkedId(); MessageCore::MessageCoreSettings::self()->setDateFormat(dateDisplayID); MessageCore::MessageCoreSettings::self()->setCustomDateFormat(mCustomDateFormatEdit->text()); } // // Message Window // QString AppearancePage::ReaderTab::helpAnchor() const { return QStringLiteral("configure-appearance-reader"); } AppearancePageGeneralTab::AppearancePageGeneralTab(QWidget *parent) : ConfigModuleTab(parent) { QVBoxLayout *topLayout = new QVBoxLayout(this); QGroupBox *readerBox = new QGroupBox(i18n("Message Window"), this); topLayout->addWidget(readerBox); QVBoxLayout *readerBoxLayout = new QVBoxLayout(readerBox); // "Close message window after replying or forwarding" check box: populateCheckBox(mCloseAfterReplyOrForwardCheck = new QCheckBox(this), MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem()); mCloseAfterReplyOrForwardCheck->setToolTip( i18n("Close the standalone message window after replying or forwarding the message")); readerBoxLayout->addWidget(mCloseAfterReplyOrForwardCheck); connect(mCloseAfterReplyOrForwardCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); mViewerSettings = new MessageViewer::ConfigureWidget; connect(mViewerSettings, &MessageViewer::ConfigureWidget::settingsChanged, this, &ConfigModuleTab::slotEmitChanged); readerBoxLayout->addWidget(mViewerSettings); mGravatarConfigWidget = new Gravatar::GravatarConfigWidget; connect(mGravatarConfigWidget, &Gravatar::GravatarConfigWidget::configChanged, this, &ConfigModuleTab::slotEmitChanged); readerBoxLayout->addWidget(mGravatarConfigWidget); QGroupBox *systrayBox = new QGroupBox(i18n("System Tray"), this); topLayout->addWidget(systrayBox); QVBoxLayout *systrayBoxlayout = new QVBoxLayout(systrayBox); // "Enable system tray applet" check box mSystemTrayCheck = new QCheckBox(i18n("Enable system tray icon"), this); systrayBoxlayout->addWidget(mSystemTrayCheck); connect(mSystemTrayCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); + // "Enable system tray applet" check box + mShowNumberInTaskBar = new QCheckBox(i18n("Show unread email in Taskbar"), this); + systrayBoxlayout->addWidget(mShowNumberInTaskBar); + connect(mShowNumberInTaskBar, &QCheckBox::stateChanged, + this, &ConfigModuleTab::slotEmitChanged); + topLayout->addStretch(100); // spacer } void AppearancePage::ReaderTab::doResetToDefaultsOther() { mGravatarConfigWidget->doResetToDefaultsOther(); } void AppearancePage::ReaderTab::doLoadOther() { loadWidget(mSystemTrayCheck, KMailSettings::self()->systemTrayEnabledItem()); + loadWidget(mShowNumberInTaskBar, KMailSettings::self()->showUnreadInTaskbarItem()); loadWidget(mCloseAfterReplyOrForwardCheck, MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem()); mViewerSettings->readConfig(); mGravatarConfigWidget->doLoadFromGlobalSettings(); } void AppearancePage::ReaderTab::save() { saveCheckBox(mSystemTrayCheck, KMailSettings::self()->systemTrayEnabledItem()); + saveCheckBox(mShowNumberInTaskBar, KMailSettings::self()->showUnreadInTaskbarItem()); KMailSettings::self()->save(); saveCheckBox(mCloseAfterReplyOrForwardCheck, MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem()); mViewerSettings->writeConfig(); mGravatarConfigWidget->save(); } QString AppearancePage::MessageTagTab::helpAnchor() const { return QStringLiteral("configure-appearance-messagetag"); } TagListWidgetItem::TagListWidgetItem(QListWidget *parent) : QListWidgetItem(parent) , mTag(nullptr) { } TagListWidgetItem::TagListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent) : QListWidgetItem(icon, text, parent) , mTag(nullptr) { } TagListWidgetItem::~TagListWidgetItem() { } void TagListWidgetItem::setKMailTag(const MailCommon::Tag::Ptr &tag) { mTag = tag; } MailCommon::Tag::Ptr TagListWidgetItem::kmailTag() const { return mTag; } AppearancePageMessageTagTab::AppearancePageMessageTagTab(QWidget *parent) : ConfigModuleTab(parent) { mPreviousTag = -1; QHBoxLayout *maingrid = new QHBoxLayout(this); //Lefthand side Listbox and friends //Groupbox frame mTagsGroupBox = new QGroupBox(i18n("A&vailable Tags"), this); maingrid->addWidget(mTagsGroupBox); QVBoxLayout *tageditgrid = new QVBoxLayout(mTagsGroupBox); //Listbox, add, remove row QHBoxLayout *addremovegrid = new QHBoxLayout(); tageditgrid->addLayout(addremovegrid); mTagAddLineEdit = new KLineEdit(mTagsGroupBox); mTagAddLineEdit->setTrapReturnKey(true); addremovegrid->addWidget(mTagAddLineEdit); mTagAddButton = new QPushButton(mTagsGroupBox); mTagAddButton->setToolTip(i18n("Add new tag")); mTagAddButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); addremovegrid->addWidget(mTagAddButton); mTagRemoveButton = new QPushButton(mTagsGroupBox); mTagRemoveButton->setToolTip(i18n("Remove selected tag")); mTagRemoveButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); addremovegrid->addWidget(mTagRemoveButton); //Up and down buttons QHBoxLayout *updowngrid = new QHBoxLayout(); tageditgrid->addLayout(updowngrid); mTagUpButton = new QPushButton(mTagsGroupBox); mTagUpButton->setToolTip(i18n("Increase tag priority")); mTagUpButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); mTagUpButton->setAutoRepeat(true); updowngrid->addWidget(mTagUpButton); mTagDownButton = new QPushButton(mTagsGroupBox); mTagDownButton->setToolTip(i18n("Decrease tag priority")); mTagDownButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); mTagDownButton->setAutoRepeat(true); updowngrid->addWidget(mTagDownButton); //Listbox for tag names QHBoxLayout *listboxgrid = new QHBoxLayout(); tageditgrid->addLayout(listboxgrid); mTagListBox = new QListWidget(mTagsGroupBox); mTagListBox->setDragDropMode(QAbstractItemView::InternalMove); connect(mTagListBox->model(), &QAbstractItemModel::rowsMoved, this, &AppearancePageMessageTagTab::slotRowsMoved); mTagListBox->setMinimumWidth(150); listboxgrid->addWidget(mTagListBox); //RHS for individual tag settings //Extra VBoxLayout for stretchers around settings QVBoxLayout *tagsettinggrid = new QVBoxLayout(); maingrid->addLayout(tagsettinggrid); //Groupbox frame mTagSettingGroupBox = new QGroupBox(i18n("Ta&g Settings"), this); tagsettinggrid->addWidget(mTagSettingGroupBox); QList actionCollections; if (kmkernel->getKMMainWidget()) { actionCollections = kmkernel->getKMMainWidget()->actionCollections(); } QHBoxLayout *lay = new QHBoxLayout(mTagSettingGroupBox); mTagWidget = new MailCommon::TagWidget(actionCollections, this); lay->addWidget(mTagWidget); connect(mTagWidget, &TagWidget::changed, this, &AppearancePageMessageTagTab::slotEmitChangeCheck); //For enabling the add button in case box is non-empty connect(mTagAddLineEdit, &KLineEdit::textChanged, this, &AppearancePage::MessageTagTab::slotAddLineTextChanged); //For on-the-fly updating of tag name in editbox connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); connect(mTagWidget, &TagWidget::iconNameChanged, this, &AppearancePageMessageTagTab::slotIconNameChanged); connect(mTagAddLineEdit, &KLineEdit::returnPressed, this, &AppearancePageMessageTagTab::slotAddNewTag); connect(mTagAddButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotAddNewTag); connect(mTagRemoveButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotRemoveTag); connect(mTagUpButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotMoveTagUp); connect(mTagDownButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotMoveTagDown); connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); //Adjust widths for columns maingrid->setStretchFactor(mTagsGroupBox, 1); maingrid->setStretchFactor(lay, 1); tagsettinggrid->addStretch(10); } AppearancePageMessageTagTab::~AppearancePageMessageTagTab() { } void AppearancePage::MessageTagTab::slotEmitChangeCheck() { slotEmitChanged(); } void AppearancePage::MessageTagTab::slotRowsMoved(const QModelIndex &, int sourcestart, int sourceEnd, const QModelIndex &, int destinationRow) { Q_UNUSED(sourceEnd); Q_UNUSED(sourcestart); Q_UNUSED(destinationRow); updateButtons(); slotEmitChangeCheck(); } void AppearancePage::MessageTagTab::updateButtons() { const int currentIndex = mTagListBox->currentRow(); const bool theFirst = (currentIndex == 0); const bool theLast = (currentIndex >= (int)mTagListBox->count() - 1); const bool aFilterIsSelected = (currentIndex >= 0); mTagUpButton->setEnabled(aFilterIsSelected && !theFirst); mTagDownButton->setEnabled(aFilterIsSelected && !theLast); } void AppearancePage::MessageTagTab::slotMoveTagUp() { const int tmp_index = mTagListBox->currentRow(); if (tmp_index <= 0) { return; } swapTagsInListBox(tmp_index, tmp_index - 1); updateButtons(); } void AppearancePage::MessageTagTab::slotMoveTagDown() { const int tmp_index = mTagListBox->currentRow(); if ((tmp_index < 0) || (tmp_index >= int(mTagListBox->count()) - 1)) { return; } swapTagsInListBox(tmp_index, tmp_index + 1); updateButtons(); } void AppearancePage::MessageTagTab::swapTagsInListBox(const int first, const int second) { disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); QListWidgetItem *item = mTagListBox->takeItem(first); // now selected item is at idx(idx-1), so // insert the other item at idx, ie. above(below). mPreviousTag = second; mTagListBox->insertItem(second, item); mTagListBox->setCurrentRow(second); connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); slotEmitChangeCheck(); } void AppearancePage::MessageTagTab::slotRecordTagSettings(int aIndex) { if ((aIndex < 0) || (aIndex >= int(mTagListBox->count()))) { return; } QListWidgetItem *item = mTagListBox->item(aIndex); TagListWidgetItem *tagItem = static_cast(item); MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag(); tmp_desc->tagName = tagItem->text(); mTagWidget->recordTagSettings(tmp_desc); } void AppearancePage::MessageTagTab::slotUpdateTagSettingWidgets(int aIndex) { //Check if selection is valid if ((aIndex < 0) || (mTagListBox->currentRow() < 0)) { mTagRemoveButton->setEnabled(false); mTagUpButton->setEnabled(false); mTagDownButton->setEnabled(false); mTagWidget->setEnabled(false); return; } mTagWidget->setEnabled(true); mTagRemoveButton->setEnabled(true); mTagUpButton->setEnabled((0 != aIndex)); mTagDownButton->setEnabled(((int(mTagListBox->count()) - 1) != aIndex)); QListWidgetItem *item = mTagListBox->currentItem(); TagListWidgetItem *tagItem = static_cast(item); MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag(); disconnect(mTagWidget->tagNameLineEdit(), &KLineEdit::textChanged, this, &AppearancePage::MessageTagTab::slotNameLineTextChanged); mTagWidget->tagNameLineEdit()->setEnabled(!tmp_desc->isImmutable); mTagWidget->tagNameLineEdit()->setText(tmp_desc->tagName); connect(mTagWidget->tagNameLineEdit(), &KLineEdit::textChanged, this, &AppearancePage::MessageTagTab::slotNameLineTextChanged); mTagWidget->setTagTextColor(tmp_desc->textColor); mTagWidget->setTagBackgroundColor(tmp_desc->backgroundColor); mTagWidget->setTagTextFormat(tmp_desc->isBold, tmp_desc->isItalic); mTagWidget->iconButton()->setEnabled(!tmp_desc->isImmutable); mTagWidget->iconButton()->setIcon(tmp_desc->iconName); mTagWidget->keySequenceWidget()->setEnabled(true); mTagWidget->keySequenceWidget()->setKeySequence(tmp_desc->shortcut, KKeySequenceWidget::NoValidate); mTagWidget->inToolBarCheck()->setEnabled(true); mTagWidget->inToolBarCheck()->setChecked(tmp_desc->inToolbar); } void AppearancePage::MessageTagTab::slotSelectionChanged() { mEmitChanges = false; slotRecordTagSettings(mPreviousTag); slotUpdateTagSettingWidgets(mTagListBox->currentRow()); mPreviousTag = mTagListBox->currentRow(); mEmitChanges = true; } void AppearancePage::MessageTagTab::slotRemoveTag() { const int tmp_index = mTagListBox->currentRow(); if (tmp_index >= 0) { if (KMessageBox::Yes == KMessageBox::questionYesNo(this, i18n("Do you want to remove tag \'%1\'?", mTagListBox->item(mTagListBox->currentRow())->text()))) { QListWidgetItem *item = mTagListBox->takeItem(mTagListBox->currentRow()); TagListWidgetItem *tagItem = static_cast(item); MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag(); if (tmp_desc->tag().isValid()) { new Akonadi::TagDeleteJob(tmp_desc->tag()); } else { qCWarning(KMAIL_LOG) << "Can't remove tag with invalid akonadi tag"; } mPreviousTag = -1; //Before deleting the current item, make sure the selectionChanged signal //is disconnected, so that the widgets will not get updated while the //deletion takes place. disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); delete item; connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); slotSelectionChanged(); slotEmitChangeCheck(); } } } void AppearancePage::MessageTagTab::slotDeleteTagJob(KJob *job) { if (job->error()) { qCWarning(KMAIL_LOG) << "Failed to delete tag " << job->errorString(); } } void AppearancePage::MessageTagTab::slotNameLineTextChanged(const QString &aText) { //If deleted all, leave the first character for the sake of not having an //empty tag name if (aText.isEmpty() && !mTagListBox->currentItem()) { return; } const int count = mTagListBox->count(); for (int i = 0; i < count; ++i) { if (mTagListBox->item(i)->text() == aText) { KMessageBox::error(this, i18n("We cannot create tag. A tag with same name already exists.")); disconnect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); mTagWidget->tagNameLineEdit()->setText(mTagListBox->currentItem()->text()); connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); return; } } //Disconnect so the tag information is not saved and reloaded with every //letter disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); mTagListBox->currentItem()->setText(aText); connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); } void AppearancePage::MessageTagTab::slotIconNameChanged(const QString &iconName) { mTagListBox->currentItem()->setIcon(QIcon::fromTheme(iconName)); } void AppearancePage::MessageTagTab::slotAddLineTextChanged(const QString &aText) { mTagAddButton->setEnabled(!aText.trimmed().isEmpty()); } void AppearancePage::MessageTagTab::slotAddNewTag() { const QString newTagName = mTagAddLineEdit->text().trimmed(); if (newTagName.isEmpty()) { return; } const int count = mTagListBox->count(); for (int i = 0; i < count; ++i) { if (mTagListBox->item(i)->text() == newTagName) { KMessageBox::error(this, i18n("We cannot create tag. A tag with same name already exists.")); return; } } const int tmp_priority = mTagListBox->count(); MailCommon::Tag::Ptr tag(Tag::createDefaultTag(newTagName)); tag->priority = tmp_priority; slotEmitChangeCheck(); TagListWidgetItem *newItem = new TagListWidgetItem(QIcon::fromTheme(tag->iconName), newTagName, mTagListBox); newItem->setKMailTag(tag); mTagListBox->addItem(newItem); mTagListBox->setCurrentItem(newItem); mTagAddLineEdit->clear(); } void AppearancePage::MessageTagTab::doLoadFromGlobalSettings() { mTagListBox->clear(); Akonadi::TagFetchJob *fetchJob = new Akonadi::TagFetchJob(this); fetchJob->fetchScope().fetchAttribute(); connect(fetchJob, &KJob::result, this, &AppearancePageMessageTagTab::slotTagsFetched); } void AppearancePage::MessageTagTab::slotTagsFetched(KJob *job) { if (job->error()) { qCWarning(KMAIL_LOG) << "Failed to load tags " << job->errorString(); return; } Akonadi::TagFetchJob *fetchJob = static_cast(job); QList msgTagList; const Akonadi::Tag::List tagList = fetchJob->tags(); msgTagList.reserve(tagList.count()); for (const Akonadi::Tag &akonadiTag : tagList) { MailCommon::Tag::Ptr tag = MailCommon::Tag::fromAkonadi(akonadiTag); msgTagList.append(tag); } std::sort(msgTagList.begin(), msgTagList.end(), MailCommon::Tag::compare); for (const MailCommon::Tag::Ptr &tag : qAsConst(msgTagList)) { TagListWidgetItem *newItem = new TagListWidgetItem(QIcon::fromTheme(tag->iconName), tag->tagName, mTagListBox); newItem->setKMailTag(tag); if (tag->priority == -1) { tag->priority = mTagListBox->count() - 1; } } //Disconnect so that insertItem's do not trigger an update procedure disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); slotUpdateTagSettingWidgets(-1); //Needed since the previous function doesn't affect add button mTagAddButton->setEnabled(false); // Save the original list mOriginalMsgTagList.clear(); for (const MailCommon::TagPtr &tag : qAsConst(msgTagList)) { mOriginalMsgTagList.append(MailCommon::TagPtr(new MailCommon::Tag(*tag))); } } void AppearancePage::MessageTagTab::save() { const int currentRow = mTagListBox->currentRow(); if (currentRow < 0) { return; } const int count = mTagListBox->count(); if (!count) { return; } QListWidgetItem *item = mTagListBox->currentItem(); if (!item) { return; } slotRecordTagSettings(currentRow); const int numberOfMsgTagList = count; for (int i = 0; i < numberOfMsgTagList; ++i) { TagListWidgetItem *tagItem = static_cast(mTagListBox->item(i)); if ((i >= mOriginalMsgTagList.count()) || *(tagItem->kmailTag()) != *(mOriginalMsgTagList[i])) { MailCommon::Tag::Ptr tag = tagItem->kmailTag(); tag->priority = i; Akonadi::Tag akonadiTag = tag->saveToAkonadi(); if ((*tag).id() > 0) { akonadiTag.setId((*tag).id()); } if (akonadiTag.isValid()) { new Akonadi::TagModifyJob(akonadiTag); } else { new Akonadi::TagCreateJob(akonadiTag); } } } } diff --git a/src/configuredialog/configureappearancepage.h b/src/configuredialog/configureappearancepage.h index de8e517f9..1e10c9261 100644 --- a/src/configuredialog/configureappearancepage.h +++ b/src/configuredialog/configureappearancepage.h @@ -1,289 +1,290 @@ /* Copyright (c) 2013-2019 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CONFIGUREAPPEARANCEPAGE_H #define CONFIGUREAPPEARANCEPAGE_H #include "kmail_export.h" #include "configuredialog_p.h" #include "MailCommon/Tag" #include class QPushButton; class QCheckBox; class KComboBox; class KFontChooser; class ColorListBox; class QButtonGroup; class QGroupBox; class QSpinBox; class KLineEdit; class QModelIndex; class KJob; namespace MessageViewer { class ConfigureWidget; } namespace Gravatar { class GravatarConfigWidget; } namespace MessageList { namespace Utils { class AggregationComboBox; class ThemeComboBox; } } namespace MailCommon { class Tag; typedef QSharedPointer TagPtr; } namespace MailCommon { class TagWidget; } class AppearancePageFontsTab : public ConfigModuleTab { Q_OBJECT public: explicit AppearancePageFontsTab(QWidget *parent = nullptr); QString helpAnchor() const; void save() override; private: void slotFontSelectorChanged(int); void doLoadOther() override; void updateFontSelector(); void doResetToDefaultsOther() override; private: QCheckBox *mCustomFontCheck = nullptr; KComboBox *mFontLocationCombo = nullptr; KFontChooser *mFontChooser = nullptr; int mActiveFontIndex; QFont mFont[8]; }; class AppearancePageColorsTab : public ConfigModuleTab { Q_OBJECT public: explicit AppearancePageColorsTab(QWidget *parent = nullptr); QString helpAnchor() const; void save() override; private: void doLoadOther() override; void doResetToDefaultsOther() override; void loadColor(bool loadFromConfig); private: QCheckBox *mCustomColorCheck = nullptr; ColorListBox *mColorList = nullptr; QCheckBox *mRecycleColorCheck = nullptr; QSpinBox *mCloseToQuotaThreshold = nullptr; QCheckBox *mUseInlineStyle = nullptr; }; class AppearancePageLayoutTab : public ConfigModuleTab { Q_OBJECT public: explicit AppearancePageLayoutTab(QWidget *parent = nullptr); QString helpAnchor() const; void save() override; private: void doLoadOther() override; private: // data QButtonGroup *mFolderListGroup = nullptr; QGroupBox *mFolderListGroupBox = nullptr; QButtonGroup *mReaderWindowModeGroup = nullptr; QGroupBox *mReaderWindowModeGroupBox = nullptr; QButtonGroup *mFolderToolTipsGroup = nullptr; QGroupBox *mFolderToolTipsGroupBox = nullptr; QButtonGroup *mFavoriteFoldersViewGroup = nullptr; QGroupBox *mFavoriteFoldersViewGroupBox = nullptr; QCheckBox *mFolderQuickSearchCB = nullptr; }; class AppearancePageHeadersTab : public ConfigModuleTab { Q_OBJECT public: explicit AppearancePageHeadersTab(QWidget *parent = nullptr); QString helpAnchor() const; void save() override; private: // methods void doLoadFromGlobalSettings() override; void doLoadOther() override; // virtual void doResetToDefaultsOther(); void setDateDisplay(int id, const QString &format); void slotLinkClicked(const QString &link); void slotSelectDefaultAggregation(); void slotSelectDefaultTheme(); private: // data QCheckBox *mDisplayMessageToolTips = nullptr; MessageList::Utils::AggregationComboBox *mAggregationComboBox = nullptr; MessageList::Utils::ThemeComboBox *mThemeComboBox = nullptr; QButtonGroup *mDateDisplay = nullptr; QGroupBox *mDateDisplayBox = nullptr; KLineEdit *mCustomDateFormatEdit = nullptr; QString mCustomDateWhatsThis; }; class AppearancePageGeneralTab : public ConfigModuleTab { Q_OBJECT public: explicit AppearancePageGeneralTab(QWidget *parent = nullptr); QString helpAnchor() const; void save() override; private: void doLoadOther() override; void doResetToDefaultsOther() override; private: // data QCheckBox *mCloseAfterReplyOrForwardCheck = nullptr; MessageViewer::ConfigureWidget *mViewerSettings = nullptr; Gravatar::GravatarConfigWidget *mGravatarConfigWidget = nullptr; QCheckBox *mSystemTrayCheck = nullptr; + QCheckBox *mShowNumberInTaskBar = nullptr; }; class TagListWidgetItem : public QListWidgetItem { public: explicit TagListWidgetItem(QListWidget *parent = nullptr); explicit TagListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent = nullptr); ~TagListWidgetItem(); void setKMailTag(const MailCommon::Tag::Ptr &tag); MailCommon::Tag::Ptr kmailTag() const; private: MailCommon::Tag::Ptr mTag; }; /**Configuration tab in the appearance page for modifying the available set of +message tags*/ class AppearancePageMessageTagTab : public ConfigModuleTab { Q_OBJECT public: explicit AppearancePageMessageTagTab(QWidget *parent = nullptr); ~AppearancePageMessageTagTab() override; QString helpAnchor() const; void save() override; public Q_SLOTS: /**Enables/disables Add button according to whether @p aText is empty. Connected to signal of the line edit widget for adding tags @param aText String to change add button according to */ void slotAddLineTextChanged(const QString &aText); /**Creates a generic tag with the visible name from the line edit widget for adding tags. Adds it to the end of the list and selects. Empties the line edit widget*/ void slotAddNewTag(); /**Removes the currently selected text in the list box.*/ void slotRemoveTag(); /**Increases the currently selected tag's priority and handles related visual changes*/ void slotMoveTagUp(); /**Decreases the currently selected tag's priority and handles related visual changes*/ void slotMoveTagDown(); private: /*Handles necessary processing when the selection in the edit box changes. Records the unselected tag's information, and applies visual changes necessary depending on the description of the new tag. Private since doesn't change the selection of the edit box itself*/ void slotSelectionChanged(); /*This slot is necessary so that apply button is not activated when we are only applying visual changes after selecting a new tag in the list box*/ void slotEmitChangeCheck(); /*Transfers the tag settings from the widgets to the internal data structures. Private since passing a wrong parameter modifies another tag's data*/ void slotRecordTagSettings(int aIndex); /*Transfers the tag settings from the internal data structures to the widgets. Private since passing a wrong parameter visualizes another tag's data*/ void slotUpdateTagSettingWidgets(int aIndex); /*Transfers changes in the tag name edit box to the list box for tags. Private since calling externally decouples the name in the list box from name edit box*/ void slotNameLineTextChanged(const QString &); void slotIconNameChanged(const QString &iconName); void slotRowsMoved(const QModelIndex &, int sourcestart, int sourceEnd, const QModelIndex &, int destinationRow); void slotTagsFetched(KJob *job); void slotDeleteTagJob(KJob *job); void doLoadFromGlobalSettings() override; void swapTagsInListBox(const int first, const int second); void updateButtons(); private: // data KLineEdit *mTagAddLineEdit = nullptr; QPushButton *mTagAddButton = nullptr; QPushButton *mTagRemoveButton = nullptr; QPushButton *mTagUpButton = nullptr; QPushButton *mTagDownButton = nullptr; QListWidget *mTagListBox = nullptr; QGroupBox *mTagsGroupBox = nullptr; QGroupBox *mTagSettingGroupBox = nullptr; MailCommon::TagWidget *mTagWidget = nullptr; // So we can compare to mMsgTagList and see if the user changed tags QList mOriginalMsgTagList; /*Used to safely call slotRecordTagSettings when the selection in list box changes*/ int mPreviousTag; }; class KMAIL_EXPORT AppearancePage : public ConfigModuleWithTabs { Q_OBJECT public: explicit AppearancePage(QWidget *parent = nullptr); QString helpAnchor() const override; // hrmpf. moc doesn't like nested classes with slots/signals...: typedef AppearancePageFontsTab FontsTab; typedef AppearancePageColorsTab ColorsTab; typedef AppearancePageLayoutTab LayoutTab; typedef AppearancePageHeadersTab HeadersTab; typedef AppearancePageGeneralTab ReaderTab; typedef AppearancePageMessageTagTab MessageTagTab; }; #endif // CONFIGUREAPPEARANCEPAGE_H diff --git a/src/kmkernel.cpp b/src/kmkernel.cpp index 88fbc907b..a6cfae03e 100644 --- a/src/kmkernel.cpp +++ b/src/kmkernel.cpp @@ -1,1938 +1,1939 @@ /* */ #include "kmkernel.h" #include "settings/kmailsettings.h" #include "libkdepim/broadcaststatus.h" #include "job/opencomposerjob.h" #include "job/newmessagejob.h" #include "job/opencomposerhiddenjob.h" #include "job/fillcomposerjob.h" #include #include using KPIM::BroadcastStatus; #include "kmstartup.h" #include "kmmainwin.h" #include "editor/composer.h" #include "kmreadermainwin.h" #include "undostack.h" #include "kmmainwidget.h" #include "search/checkindexingmanager.h" #include "libkdepim/recentaddresses.h" using KPIM::RecentAddresses; #include "configuredialog/configuredialog.h" #include "kmcommands.h" #include "unityservicemanager.h" #include #include "mailcommon/mailutil.h" #include "pop3settings.h" #include "MailCommon/FolderTreeView" #include "MailCommon/KMFilterDialog" #include "mailcommonsettings_base.h" #include "mailfilteragentinterface.h" #include "PimCommon/PimUtil" #include "folderarchive/folderarchivemanager.h" #include "sieveimapinterface/kmailsieveimapinstanceinterface.h" // kdepim includes #include "kmail-version.h" // kdepimlibs includes #include #include #include #include #include #include #include "mailserviceimpl.h" using KMail::MailServiceImpl; #include "mailcommon/jobscheduler.h" #include #include "messagelistsettings.h" #include "gravatarsettings.h" #include "messagelist/messagelistutil.h" #include "messageviewer/messageviewersettings.h" #include "MessageComposer/AkonadiSender" #include "messagecomposer/messagecomposersettings.h" #include "MessageComposer/MessageHelper" #include "MessageComposer/MessageComposerSettings" #include "PimCommon/PimCommonSettings" #include "PimCommon/AutoCorrection" #include #include "globalsettings_templateparser.h" #include "TemplateParser/TemplatesUtil" #include "mailcommon/foldersettings.h" #include "editor/codec/codecmanager.h" #include #include #include #include #include #include #include "kmail_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kmailinterface.h" #include "mailcommon/foldercollectionmonitor.h" #include "imapresourcesettings.h" #include "util.h" #include "MailCommon/MailKernel" #include "searchdialog/searchdescriptionattribute.h" #include "kmail_options.h" using namespace MailCommon; static KMKernel *mySelf = nullptr; static bool s_askingToGoOnline = false; /********************************************************************/ /* Constructor and destructor */ /********************************************************************/ KMKernel::KMKernel(QObject *parent) : QObject(parent) { //Initialize kmail sieveimap interface KSieveUi::SieveImapInstanceInterfaceManager::self()->setSieveImapInstanceInterface(new KMailSieveImapInstanceInterface); mDebug = !qEnvironmentVariableIsEmpty("KDEPIM_DEBUGGING"); mSystemNetworkStatus = PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline(); Akonadi::AttributeFactory::registerAttribute(); QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kmail")); qCDebug(KMAIL_LOG) << "Starting up..."; mySelf = this; the_firstInstance = true; the_undoStack = nullptr; the_msgSender = nullptr; mFilterEditDialog = nullptr; // make sure that we check for config updates before doing anything else KMKernel::config(); // this shares the kmailrc parsing too (via KSharedConfig), and reads values from it // so better do it here, than in some code where changing the group of config() // would be unexpected KMailSettings::self(); mJobScheduler = new JobScheduler(this); mAutoCorrection = new PimCommon::AutoCorrection(); KMime::setUseOutlookAttachmentEncoding(MessageComposer::MessageComposerSettings::self()->outlookCompatibleAttachments()); // cberzan: this crap moved to CodecManager ====================== mNetCodec = QTextCodec::codecForLocale(); // In the case of Japan. Japanese locale name is "eucjp" but // The Japanese mail systems normally used "iso-2022-jp" of locale name. // We want to change locale name from eucjp to iso-2022-jp at KMail only. // (Introduction to i18n, 6.6 Limit of Locale technology): // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP // is the standard for Internet, and Shift-JIS is the encoding // for Windows and Macintosh. const QByteArray netCodecLower = mNetCodec->name().toLower(); if (netCodecLower == "eucjp" #if defined Q_OS_WIN || defined Q_OS_MACX || netCodecLower == "shift-jis" // OK? #endif ) { mNetCodec = QTextCodec::codecForName("jis7"); } // until here ================================================ Akonadi::Session *session = new Akonadi::Session("KMail Kernel ETM", this); mFolderCollectionMonitor = new FolderCollectionMonitor(session, this); connect(mFolderCollectionMonitor->monitor(), &Akonadi::Monitor::collectionRemoved, this, &KMKernel::slotCollectionRemoved); mEntityTreeModel = new Akonadi::EntityTreeModel(folderCollectionMonitor(), this); mEntityTreeModel->setListFilter(Akonadi::CollectionFetchScope::Enabled); mEntityTreeModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation); mCollectionModel = new Akonadi::EntityMimeTypeFilterModel(this); mCollectionModel->setSourceModel(mEntityTreeModel); mCollectionModel->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType()); mCollectionModel->setHeaderGroup(Akonadi::EntityTreeModel::CollectionTreeHeaders); mCollectionModel->setDynamicSortFilter(true); mCollectionModel->setSortCaseSensitivity(Qt::CaseInsensitive); connect(folderCollectionMonitor(), QOverload &>::of(&Akonadi::ChangeRecorder::collectionChanged), this, &KMKernel::slotCollectionChanged); connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRemoved, this, &KMKernel::transportRemoved); connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRenamed, this, &KMKernel::transportRenamed); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/MailDispatcherAgent"), QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent"), QStringLiteral( "itemDispatchStarted"), this, SLOT(itemDispatchStarted())); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceStatusChanged, this, &KMKernel::instanceStatusChanged); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceError, this, &KMKernel::slotInstanceError); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceWarning, this, &KMKernel::slotInstanceWarning); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &KMKernel::slotInstanceRemoved); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceAdded, this, &KMKernel::slotInstanceAdded); connect(PimCommon::NetworkManager::self()->networkConfigureManager(), &QNetworkConfigurationManager::onlineStateChanged, this, &KMKernel::slotSystemNetworkStatusChanged); connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCompleted, this, &KMKernel::slotProgressItemCompletedOrCanceled); connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCanceled, this, &KMKernel::slotProgressItemCompletedOrCanceled); connect(identityManager(), &KIdentityManagement::IdentityManager::deleted, this, &KMKernel::slotDeleteIdentity); CommonKernel->registerKernelIf(this); CommonKernel->registerSettingsIf(this); CommonKernel->registerFilterIf(this); mFolderArchiveManager = new FolderArchiveManager(this); mIndexedItems = new Akonadi::Search::PIM::IndexedItems(this); mCheckIndexingManager = new CheckIndexingManager(mIndexedItems, this); mUnityServiceManager = new KMail::UnityServiceManager(this); } KMKernel::~KMKernel() { delete mMailService; mMailService = nullptr; stopAgentInstance(); saveConfig(); delete mAutoCorrection; delete mMailCommonSettings; mySelf = nullptr; } Akonadi::ChangeRecorder *KMKernel::folderCollectionMonitor() const { return mFolderCollectionMonitor->monitor(); } Akonadi::EntityTreeModel *KMKernel::entityTreeModel() const { return mEntityTreeModel; } Akonadi::EntityMimeTypeFilterModel *KMKernel::collectionModel() const { return mCollectionModel; } void KMKernel::setupDBus() { (void)new KmailAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/KMail"), this); mMailService = new MailServiceImpl(); } static QUrl makeAbsoluteUrl(const QString &str, const QString &cwd) { return QUrl::fromUserInput(str, cwd, QUrl::AssumeLocalFile); } bool KMKernel::handleCommandLine(bool noArgsOpensReader, const QStringList &args, const QString &workingDir) { QString to, cc, bcc, subj, body, inReplyTo, replyTo; QStringList customHeaders; QUrl messageFile; QList attachURLs; QString identity; bool mailto = false; bool checkMail = false; bool viewOnly = false; bool calledWithSession = false; // for ignoring '-session foo' // process args: QCommandLineParser parser; kmail_options(&parser); QStringList newargs; bool addAttachmentAttribute = false; for (const QString &argument : qAsConst(args)) { if (argument == QLatin1String("--attach")) { addAttachmentAttribute = true; } else { if (argument.startsWith(QLatin1String("--"))) { addAttachmentAttribute = false; } if (argument.contains(QLatin1Char('@')) || argument.startsWith(QLatin1String("mailto:"))) { //address mustn't be trade as a attachment addAttachmentAttribute = false; } if (addAttachmentAttribute) { newargs.append(QStringLiteral("--attach")); newargs.append(argument); } else { newargs.append(argument); } } } parser.process(newargs); if (parser.isSet(QStringLiteral("subject"))) { subj = parser.value(QStringLiteral("subject")); // if kmail is called with 'kmail -session abc' then this doesn't mean // that the user wants to send a message with subject "ession" but // (most likely) that the user clicked on KMail's system tray applet // which results in KMKernel::raise() calling "kmail kmail newInstance" // via D-Bus which apparently executes the application with the original // command line arguments and those include "-session ..." if // kmail/kontact was restored by session management if (subj == QLatin1String("ession")) { subj.clear(); calledWithSession = true; } else { mailto = true; } } if (parser.isSet(QStringLiteral("cc"))) { mailto = true; cc = parser.value(QStringLiteral("cc")); } if (parser.isSet(QStringLiteral("bcc"))) { mailto = true; bcc = parser.value(QStringLiteral("bcc")); } if (parser.isSet(QStringLiteral("replyTo"))) { mailto = true; replyTo = parser.value(QStringLiteral("replyTo")); } if (parser.isSet(QStringLiteral("msg"))) { mailto = true; const QString file = parser.value(QStringLiteral("msg")); messageFile = makeAbsoluteUrl(file, workingDir); } if (parser.isSet(QStringLiteral("body"))) { mailto = true; body = parser.value(QStringLiteral("body")); } const QStringList attachList = parser.values(QStringLiteral("attach")); if (!attachList.isEmpty()) { mailto = true; QStringList::ConstIterator end = attachList.constEnd(); for (QStringList::ConstIterator it = attachList.constBegin(); it != end; ++it) { if (!(*it).isEmpty()) { if ((*it) != QLatin1String("--")) { attachURLs.append(makeAbsoluteUrl(*it, workingDir)); } } } } customHeaders = parser.values(QStringLiteral("header")); if (parser.isSet(QStringLiteral("composer"))) { mailto = true; } if (parser.isSet(QStringLiteral("check"))) { checkMail = true; } if (parser.isSet(QStringLiteral("identity"))) { identity = parser.value(QStringLiteral("identity")); } if (parser.isSet(QStringLiteral("view"))) { viewOnly = true; const QString filename = parser.value(QStringLiteral("view")); messageFile = QUrl::fromUserInput(filename, workingDir); } if (!calledWithSession) { // only read additional command line arguments if kmail/kontact is // not called with "-session foo" for (const QString &arg : parser.positionalArguments()) { if (arg.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { const QUrl urlDecoded(QUrl::fromPercentEncoding(arg.toUtf8())); const QList > values = MessageCore::StringUtil::parseMailtoUrl(urlDecoded); QString previousKey; for (int i = 0; i < values.count(); ++i) { const QPair element = values.at(i); const QString key = element.first.toLower(); if (key == QLatin1Literal("to")) { if (!element.second.isEmpty()) { to += element.second + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1Literal("cc")) { if (!element.second.isEmpty()) { cc += element.second + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1Literal("bcc")) { if (!element.second.isEmpty()) { bcc += element.second + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1Literal("subject")) { subj = element.second; previousKey.clear(); } else if (key == QLatin1Literal("body")) { body = element.second; previousKey = key; } else if (key == QLatin1Literal("in-reply-to")) { inReplyTo = element.second; previousKey.clear(); } else if (key == QLatin1Literal("attachment") || key == QLatin1Literal("attach")) { if (!element.second.isEmpty()) { attachURLs << makeAbsoluteUrl(element.second, workingDir); } previousKey.clear(); } else { qCWarning(KMAIL_LOG) << "unknown key" << key; //Workaround: https://bugs.kde.org/show_bug.cgi?id=390939 //QMap parseMailtoUrl(const QUrl &url) parses correctly url //But if we have a "&" unknown key we lost it. if (previousKey == QLatin1Literal("body")) { body += QLatin1Char('&') + key + QLatin1Char('=') + element.second; } //Don't clear previous key. } } } else { QUrl url(arg); if (url.isValid() && !url.scheme().isEmpty()) { attachURLs += url; } else { to += arg + QStringLiteral(", "); } } mailto = true; } if (!to.isEmpty()) { // cut off the superfluous trailing ", " to.truncate(to.length() - 2); } } if (!noArgsOpensReader && !mailto && !checkMail && !viewOnly) { return false; } if (viewOnly) { viewMessage(messageFile); } else { action(mailto, checkMail, to, cc, bcc, subj, body, messageFile, attachURLs, customHeaders, replyTo, inReplyTo, identity); } return true; } /********************************************************************/ /* D-Bus-callable, and command line actions */ /********************************************************************/ void KMKernel::checkMail() //might create a new reader but won't show!! { if (!kmkernel->askToGoOnline()) { return; } const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { const QString id = type.identifier(); KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(id)); if (group.readEntry("IncludeInManualChecks", true)) { if (!type.isOnline()) { type.setIsOnline(true); } if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "Starting manual mail check"; Q_EMIT startCheckMail(); } if (!mResourcesBeingChecked.contains(id)) { mResourcesBeingChecked.append(id); } type.synchronize(); } } } void KMKernel::openReader() { openReader(false); } QStringList KMKernel::accounts() const { QStringList accountLst; const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); accountLst.reserve(lst.count()); for (const Akonadi::AgentInstance &type : lst) { // Explicitly make a copy, as we're not changing values of the list but only // the local copy which is passed to action. accountLst << type.identifier(); } return accountLst; } void KMKernel::checkAccount(const QString &account) //might create a new reader but won't show!! { if (account.isEmpty()) { checkMail(); } else { Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(account); if (agent.isValid()) { agent.synchronize(); } else { qCDebug(KMAIL_LOG) << "- account with name '" << account << "' not found"; } } } void KMKernel::openReader(bool onlyCheck) { KMainWindow *ktmw = nullptr; foreach (KMainWindow *window, KMainWindow::memberList()) { if (::qobject_cast(window)) { ktmw = window; break; } } bool activate; if (ktmw) { KMMainWin *win = static_cast(ktmw); activate = !onlyCheck; // existing window: only activate if not --check if (activate) { win->show(); } } else { KMMainWin *win = new KMMainWin; win->show(); activate = false; // new window: no explicit activation (#73591) } } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden, const QString &messageFile, const QStringList &attachmentPaths, const QStringList &customHeaders, const QString &replyTo, const QString &inReplyTo, const QString &identity) { const OpenComposerSettings settings(to, cc, bcc, subject, body, hidden, messageFile, attachmentPaths, customHeaders, replyTo, inReplyTo, identity); OpenComposerJob *job = new OpenComposerJob(this); job->setOpenComposerSettings(settings); job->start(); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity) { fillComposer(hidden, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, false); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity) { fillComposer(false, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, true); } void KMKernel::fillComposer(bool hidden, const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity, bool forceShowWindow) { const FillComposerJobSettings settings(hidden, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, forceShowWindow); FillComposerJob *job = new FillComposerJob; job->setSettings(settings); job->start(); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden) { const OpenComposerHiddenJobSettings settings(to, cc, bcc, subject, body, hidden); OpenComposerHiddenJob *job = new OpenComposerHiddenJob(this); job->setSettings(settings); job->start(); } void KMKernel::newMessage(const QString &to, const QString &cc, const QString &bcc, bool hidden, bool useFolderId, const QString & /*messageFile*/, const QString &_attachURL) { QSharedPointer folder; Akonadi::Collection col; uint id = 0; if (useFolderId) { //create message with required folder identity folder = currentFolderCollection(); id = folder ? folder->identity() : 0; col = currentCollection(); } const NewMessageJobSettings settings(to, cc, bcc, hidden, _attachURL, folder, id, col); NewMessageJob *job = new NewMessageJob(this); job->setNewMessageJobSettings(settings); job->start(); } void KMKernel::viewMessage(const QUrl &url) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(nullptr, url); openCommand->start(); } int KMKernel::viewMessage(const QString &messageFile) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(nullptr, QUrl::fromLocalFile(messageFile)); openCommand->start(); return 1; } void KMKernel::raise() { QDBusInterface iface(QStringLiteral("org.kde.kmail"), QStringLiteral("/MainApplication"), QStringLiteral("org.kde.PIMUniqueApplication"), QDBusConnection::sessionBus()); QDBusReply reply; if (!iface.isValid() || !(reply = iface.call(QStringLiteral("newInstance"))).isValid()) { QDBusError err = iface.lastError(); qCritical() << "Communication problem with KMail. " << "Error message was:" << err.name() << ": \"" << err.message() << "\""; } } bool KMKernel::showMail(qint64 serialNumber) { KMMainWidget *mainWidget = nullptr; // First look for a KMainWindow. foreach (KMainWindow *window, KMainWindow::memberList()) { // Then look for a KMMainWidget. QList l = window->findChildren(); if (!l.isEmpty() && l.first()) { mainWidget = l.first(); if (window->isActiveWindow()) { break; } } } if (mainWidget) { Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(Akonadi::Item(serialNumber), this); job->fetchScope().fetchFullPayload(); job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); if (job->exec()) { if (job->items().count() >= 1) { KMReaderMainWin *win = new KMReaderMainWin(MessageViewer::Viewer::UseGlobalSetting, false); const auto item = job->items().at(0); win->showMessage(MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(), item, item.parentCollection()); win->show(); return true; } } } return false; } void KMKernel::pauseBackgroundJobs() { mBackgroundTasksTimer->stop(); mJobScheduler->pause(); } void KMKernel::resumeBackgroundJobs() { mJobScheduler->resume(); mBackgroundTasksTimer->start(4 * 60 * 60 * 1000); } void KMKernel::stopNetworkJobs() { if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { return; } setAccountStatus(false); KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Offline); BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be offline; all network jobs are suspended")); Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState()); } void KMKernel::setAccountStatus(bool goOnline) { const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(false); for (Akonadi::AgentInstance type : lst) { const QString identifier(type.identifier()); if (PimCommon::Util::isImapResource(identifier) || identifier.contains(POP3_RESOURCE_IDENTIFIER) || identifier.contains(QLatin1String("akonadi_maildispatcher_agent")) || type.type().capabilities().contains(QLatin1String("NeedsNetwork"))) { type.setIsOnline(goOnline); } } if (goOnline && MessageComposer::MessageComposerSettings::self()->sendImmediate()) { const auto col = CommonKernel->collectionFromId(CommonKernel->outboxCollectionFolder().id()); const qint64 nbMsgOutboxCollection = col.statistics().count(); if (nbMsgOutboxCollection > 0) { if (!kmkernel->msgSender()->sendQueued()) { KMessageBox::error(KMKernel::self()->mainWin(), i18n("Impossible to send email"), i18n("Send Email")); } } } } const QString KMKernel::xmlGuiInstanceName() const { return mXmlGuiInstance; } void KMKernel::setXmlGuiInstanceName(const QString &instance) { mXmlGuiInstance = instance; } KMail::UndoStack *KMKernel::undoStack() const { return the_undoStack; } void KMKernel::resumeNetworkJobs() { if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) { return; } if (mSystemNetworkStatus) { setAccountStatus(true); BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs resumed")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs will resume when a network connection is detected")); } KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Online); Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState()); KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->refreshMessageListSelection(); } } bool KMKernel::isOffline() { if ((KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) || !PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline()) { return true; } else { return false; } } void KMKernel::verifyAccount() { const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(type.identifier())); if (group.readEntry("CheckOnStartup", false)) { if (!type.isOnline()) { type.setIsOnline(true); } type.synchronize(); } // "false" is also hardcoded in ConfigureDialog, don't forget to change there. if (group.readEntry("OfflineOnShutdown", false)) { if (!type.isOnline()) { type.setIsOnline(true); } } } } void KMKernel::slotCheckAccount(Akonadi::ServerManager::State state) { if (state == Akonadi::ServerManager::Running) { disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State))); verifyAccount(); } } void KMKernel::checkMailOnStartup() { if (!kmkernel->askToGoOnline()) { return; } if (Akonadi::ServerManager::state() != Akonadi::ServerManager::Running) { connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::slotCheckAccount); } else { verifyAccount(); } } bool KMKernel::askToGoOnline() { // already asking means we are offline and need to wait anyhow if (s_askingToGoOnline) { return false; } if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { s_askingToGoOnline = true; int rc = KMessageBox::questionYesNo(KMKernel::self()->mainWin(), i18n("KMail is currently in offline mode. " "How do you want to proceed?"), i18n("Online/Offline"), KGuiItem(i18n("Work Online")), KGuiItem(i18n("Work Offline"))); s_askingToGoOnline = false; if (rc == KMessageBox::No) { return false; } else { kmkernel->resumeNetworkJobs(); } } if (kmkernel->isOffline()) { return false; } return true; } void KMKernel::slotSystemNetworkStatusChanged(bool isOnline) { mSystemNetworkStatus = isOnline; if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { return; } if (isOnline) { BroadcastStatus::instance()->setStatusMsg(i18n( "Network connection detected, all network jobs resumed")); kmkernel->setAccountStatus(true); } else { BroadcastStatus::instance()->setStatusMsg(i18n( "No network connection detected, all network jobs are suspended")); kmkernel->setAccountStatus(false); } } /********************************************************************/ /* Kernel methods */ /********************************************************************/ void KMKernel::quit() { // Called when all windows are closed. Will take care of compacting, // sending... should handle session management too!! } /* TODO later: Asuming that: - msgsender is nonblocking (our own, QSocketNotifier based. Pops up errors and sends signal senderFinished when done) o If we are getting mail, stop it (but don't lose something!) [Done already, see mailCheckAborted] o If we are sending mail, go on UNLESS this was called by SM, in which case stop ASAP that too (can we warn? should we continue on next start?) o If we are compacting, or expunging, go on UNLESS this was SM call. In that case stop compacting ASAP and continue on next start, before touching any folders. [Not needed anymore with CompactionJob] KMKernel::quit () { SM call? if compacting, stop; if sending, stop; if receiving, stop; Windows will take care of themselves (composer should dump its messages, if any but not in deadMail) declare us ready for the End of the Session No, normal quit call All windows are off. Anything to do, should compact or sender sends? Yes, maybe put an icon in panel as a sign of life if sender sending, connect us to his finished slot, declare us ready for quit and wait for senderFinished if not, Folder manager, go compact sent-mail and outbox } (= call slotFinished()) void KMKernel::slotSenderFinished() { good, Folder manager go compact sent-mail and outbox clean up stage1 (release folders and config, unregister from dcop) -- another kmail may start now --- qApp->quit(); } */ /********************************************************************/ /* Init, Exit, and handler methods */ /********************************************************************/ //----------------------------------------------------------------------------- // Open a composer for each message found in the dead.letter folder void KMKernel::recoverDeadLetters() { const QString pathName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kmail2/"); QDir dir(pathName); if (!dir.exists(QStringLiteral("autosave"))) { return; } dir.cd(pathName + QLatin1String("autosave")); const QFileInfoList autoSaveFiles = dir.entryInfoList(); for (const QFileInfo &file : autoSaveFiles) { // Disregard the '.' and '..' folders const QString filename = file.fileName(); if (filename == QLatin1String(".") || filename == QLatin1String("..") || file.isDir()) { continue; } qCDebug(KMAIL_LOG) << "Opening autosave file:" << file.absoluteFilePath(); QFile autoSaveFile(file.absoluteFilePath()); if (autoSaveFile.open(QIODevice::ReadOnly)) { const KMime::Message::Ptr autoSaveMessage(new KMime::Message()); const QByteArray msgData = autoSaveFile.readAll(); autoSaveMessage->setContent(msgData); autoSaveMessage->parse(); // Show the a new composer dialog for the message KMail::Composer *autoSaveWin = KMail::makeComposer(); autoSaveWin->setMessage(autoSaveMessage, false, false, false); autoSaveWin->setAutoSaveFileName(filename); autoSaveWin->show(); autoSaveFile.close(); } else { KMessageBox::sorry(nullptr, i18n("Failed to open autosave file at %1.\nReason: %2", file.absoluteFilePath(), autoSaveFile.errorString()), i18n("Opening Autosave File Failed")); } } } void KMKernel::akonadiStateChanged(Akonadi::ServerManager::State state) { qCDebug(KMAIL_LOG) << "KMKernel has akonadi state changed to:" << int(state); if (state == Akonadi::ServerManager::Running) { CommonKernel->initFolders(); } } static void kmCrashHandler(int sigId) { fprintf(stderr, "*** KMail got signal %d (Exiting)\n", sigId); // try to cleanup all windows if (kmkernel) { kmkernel->dumpDeadLetters(); fprintf(stderr, "*** Dead letters dumped.\n"); kmkernel->stopAgentInstance(); kmkernel->cleanupTemporaryFiles(); } } void KMKernel::init() { the_shuttingDown = false; the_firstStart = KMailSettings::self()->firstStart(); KMailSettings::self()->setFirstStart(false); the_undoStack = new KMail::UndoStack(20); the_msgSender = new MessageComposer::AkonadiSender; // filterMgr->dump(); mBackgroundTasksTimer = new QTimer(this); mBackgroundTasksTimer->setSingleShot(true); connect(mBackgroundTasksTimer, &QTimer::timeout, this, &KMKernel::slotRunBackgroundTasks); #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h mBackgroundTasksTimer->start(10000); // 10s, singleshot #else mBackgroundTasksTimer->start(5 * 60000); // 5 minutes, singleshot #endif KCrash::setEmergencySaveFunction(kmCrashHandler); qCDebug(KMAIL_LOG) << "KMail init with akonadi server state:" << int(Akonadi::ServerManager::state()); if (Akonadi::ServerManager::state() == Akonadi::ServerManager::Running) { CommonKernel->initFolders(); } connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::akonadiStateChanged); } bool KMKernel::doSessionManagement() { // Do session management if (qApp->isSessionRestored()) { int n = 1; while (KMMainWin::canBeRestored(n)) { //only restore main windows! (Matthias); if (KMMainWin::classNameOfToplevel(n) == QLatin1String("KMMainWin")) { (new KMMainWin)->restoreDockedState(n); } ++n; } return true; // we were restored by SM } return false; // no, we were not restored } bool KMKernel::firstInstance() const { return the_firstInstance; } void KMKernel::setFirstInstance(bool value) { the_firstInstance = value; } void KMKernel::closeAllKMailWindows() { foreach (KMainWindow *window, KMainWindow::memberList()) { if (::qobject_cast(window) || ::qobject_cast(window)) { // close and delete the window window->setAttribute(Qt::WA_DeleteOnClose); window->close(); } } } void KMKernel::cleanup() { disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceStatusChanged(Akonadi::AgentInstance))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceError(Akonadi::AgentInstance,QString))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceWarning(Akonadi::AgentInstance,QString))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance))); disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCompleted(KPIM::ProgressItem*))); disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCanceled(KPIM::ProgressItem*))); dumpDeadLetters(); the_shuttingDown = true; closeAllKMailWindows(); // Flush the cache of foldercollection objects. This results // in configuration writes, so we need to do it early enough. MailCommon::FolderSettings::clearCache(); // Write the config while all other managers are alive delete the_msgSender; the_msgSender = nullptr; delete the_undoStack; the_undoStack = nullptr; delete mConfigureDialog; mConfigureDialog = nullptr; KSharedConfig::Ptr config = KMKernel::config(); if (RecentAddresses::exists()) { RecentAddresses::self(config.data())->save(config.data()); } Akonadi::Collection trashCollection = CommonKernel->trashCollectionFolder(); if (trashCollection.isValid()) { if (KMailSettings::self()->emptyTrashOnExit()) { const auto service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_mailfilter_agent")); OrgFreedesktopAkonadiMailFilterAgentInterface mailFilterInterface(service, QStringLiteral("/MailFilterAgent"), QDBusConnection::sessionBus(), this); if (mailFilterInterface.isValid()) { mailFilterInterface.expunge(static_cast(trashCollection.id())); } else { qCWarning(KMAIL_LOG) << "Mailfilter is not active"; } } } } void KMKernel::dumpDeadLetters() { if (shuttingDown()) { return; //All documents should be saved before shutting down is set! } // make all composer windows autosave their contents foreach (KMainWindow *window, KMainWindow::memberList()) { if (KMail::Composer *win = ::qobject_cast(window)) { win->autoSaveMessage(true); while (win->isComposing()) { qCWarning(KMAIL_LOG) << "Danger, using an event loop, this should no longer be happening!"; qApp->processEvents(); } } } } void KMKernel::action(bool mailto, bool check, const QString &to, const QString &cc, const QString &bcc, const QString &subj, const QString &body, const QUrl &messageFile, const QList &attachURLs, const QStringList &customHeaders, const QString &replyTo, const QString &inReplyTo, const QString &identity) { if (mailto) { openComposer(to, cc, bcc, subj, body, 0, messageFile.toLocalFile(), QUrl::toStringList(attachURLs), customHeaders, replyTo, inReplyTo, identity); } else { openReader(check); } if (check) { checkMail(); } //Anything else? } void KMKernel::slotRequestConfigSync() { // ### FIXME: delay as promised in the kdoc of this function ;-) slotSyncConfig(); } void KMKernel::slotSyncConfig() { saveConfig(); //Laurent investigate why we need to reload them. PimCommon::PimCommonSettings::self()->load(); MessageCore::MessageCoreSettings::self()->load(); MessageViewer::MessageViewerSettings::self()->load(); MessageComposer::MessageComposerSettings::self()->load(); TemplateParser::TemplateParserSettings::self()->load(); MessageList::MessageListSettings::self()->load(); mMailCommonSettings->load(); Gravatar::GravatarSettings::self()->load(); KMailSettings::self()->load(); KMKernel::config()->reparseConfiguration(); + mUnityServiceManager->updateCount(); } void KMKernel::saveConfig() { PimCommon::PimCommonSettings::self()->save(); MessageCore::MessageCoreSettings::self()->save(); MessageViewer::MessageViewerSettings::self()->save(); MessageComposer::MessageComposerSettings::self()->save(); TemplateParser::TemplateParserSettings::self()->save(); MessageList::MessageListSettings::self()->save(); mMailCommonSettings->save(); Gravatar::GravatarSettings::self()->save(); KMailSettings::self()->save(); } void KMKernel::updateConfig() { slotConfigChanged(); } void KMKernel::slotShowConfigurationDialog() { if (KMKernel::getKMMainWidget() == nullptr) { // ensure that there is a main widget available // as parts of the configure dialog (identity) rely on this // and this slot can be called when there is only a KMComposeWin showing KMMainWin *win = new KMMainWin; win->show(); } if (!mConfigureDialog) { mConfigureDialog = new ConfigureDialog(nullptr, false); mConfigureDialog->setObjectName(QStringLiteral("configure")); connect(mConfigureDialog, &ConfigureDialog::configChanged, this, &KMKernel::slotConfigChanged); } // Save all current settings. if (getKMMainWidget()) { getKMMainWidget()->writeReaderConfig(); } if (mConfigureDialog->isHidden()) { mConfigureDialog->show(); } else { mConfigureDialog->raise(); } } void KMKernel::slotConfigChanged() { CodecManager::self()->updatePreferredCharsets(); Q_EMIT configChanged(); } //------------------------------------------------------------------------------- bool KMKernel::haveSystemTrayApplet() const { return mUnityServiceManager->haveSystemTrayApplet(); } QTextCodec *KMKernel::networkCodec() const { return mNetCodec; } void KMKernel::updateSystemTray() { if (!the_shuttingDown) { mUnityServiceManager->initListOfCollection(); } } KIdentityManagement::IdentityManager *KMKernel::identityManager() { return KIdentityManagement::IdentityManager::self(); } JobScheduler *KMKernel::jobScheduler() const { return mJobScheduler; } KMainWindow *KMKernel::mainWin() { // First look for a KMMainWin. foreach (KMainWindow *window, KMainWindow::memberList()) { if (::qobject_cast(window)) { return window; } } // There is no KMMainWin. Use any other KMainWindow instead (e.g. in // case we are running inside Kontact) because we anyway only need // it for modal message boxes and for KNotify events. if (!KMainWindow::memberList().isEmpty()) { KMainWindow *kmWin = KMainWindow::memberList().constFirst(); if (kmWin) { return kmWin; } } // There's not a single KMainWindow. Create a KMMainWin. // This could happen if we want to pop up an error message // while we are still doing the startup wizard and no other // KMainWindow is running. return new KMMainWin; } KMKernel *KMKernel::self() { return mySelf; } KSharedConfig::Ptr KMKernel::config() { assert(mySelf); if (!mySelf->mConfig) { mySelf->mConfig = KSharedConfig::openConfig(QStringLiteral("kmail2rc")); // Check that all updates have been run on the config file: MessageList::MessageListSettings::self()->setSharedConfig(mySelf->mConfig); MessageList::MessageListSettings::self()->load(); TemplateParser::TemplateParserSettings::self()->setSharedConfig(mySelf->mConfig); TemplateParser::TemplateParserSettings::self()->load(); MessageComposer::MessageComposerSettings::self()->setSharedConfig(mySelf->mConfig); MessageComposer::MessageComposerSettings::self()->load(); MessageCore::MessageCoreSettings::self()->setSharedConfig(mySelf->mConfig); MessageCore::MessageCoreSettings::self()->load(); MessageViewer::MessageViewerSettings::self()->setSharedConfig(mySelf->mConfig); MessageViewer::MessageViewerSettings::self()->load(); mMailCommonSettings = new MailCommon::MailCommonSettings; mMailCommonSettings->setSharedConfig(mySelf->mConfig); mMailCommonSettings->load(); PimCommon::PimCommonSettings::self()->setSharedConfig(mySelf->mConfig); PimCommon::PimCommonSettings::self()->load(); Gravatar::GravatarSettings::self()->setSharedConfig(mySelf->mConfig); Gravatar::GravatarSettings::self()->load(); } return mySelf->mConfig; } void KMKernel::syncConfig() { slotRequestConfigSync(); } void KMKernel::selectCollectionFromId(Akonadi::Collection::Id id) { KMMainWidget *widget = getKMMainWidget(); Q_ASSERT(widget); if (!widget) { return; } Akonadi::Collection colFolder = CommonKernel->collectionFromId(id); if (colFolder.isValid()) { widget->slotSelectCollectionFolder(colFolder); } } bool KMKernel::selectFolder(const QString &folder) { KMMainWidget *widget = getKMMainWidget(); Q_ASSERT(widget); if (!widget) { return false; } const Akonadi::Collection colFolder = CommonKernel->collectionFromId(folder.toLongLong()); if (colFolder.isValid()) { widget->slotSelectCollectionFolder(colFolder); return true; } return false; } KMMainWidget *KMKernel::getKMMainWidget() { //This could definitely use a speadup const QWidgetList l = QApplication::topLevelWidgets(); for (QWidget *wid : l) { QList l2 = wid->window()->findChildren(); if (!l2.isEmpty() && l2.first()) { return l2.first(); } } return nullptr; } void KMKernel::slotRunBackgroundTasks() // called regularly by timer { // Hidden KConfig keys. Not meant to be used, but a nice fallback in case // a stable kmail release goes out with a nasty bug in CompactionJob... if (KMailSettings::self()->autoExpiring()) { mFolderCollectionMonitor->expireAllFolders(false /*scheduled, not immediate*/, entityTreeModel()); } if (KMailSettings::self()->checkCollectionsIndexing()) { mCheckIndexingManager->start(entityTreeModel()); } #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h mBackgroundTasksTimer->start(60 * 1000); // check again in 1 minute #else mBackgroundTasksTimer->start(4 * 60 * 60 * 1000); // check again in 4 hours #endif } static Akonadi::Collection::List collect_collections(const QAbstractItemModel *model, const QModelIndex &parent) { Akonadi::Collection::List collections; QStack stack; stack.push(parent); while (!stack.isEmpty()) { const QModelIndex idx = stack.pop(); if (idx.isValid()) { collections << model->data(idx, Akonadi::EntityTreeModel::CollectionRole).value(); for (int i = model->rowCount(idx) - 1; i >= 0; --i) { stack.push(model->index(i, 0, idx)); } } } return collections; } Akonadi::Collection::List KMKernel::allFolders() const { return collect_collections(collectionModel(), QModelIndex()); } Akonadi::Collection::List KMKernel::subfolders(const Akonadi::Collection &col) const { const auto idx = collectionModel()->match({}, Akonadi::EntityTreeModel::CollectionRole, QVariant::fromValue(col), 1, Qt::MatchExactly); if (!idx.isEmpty()) { return collect_collections(collectionModel(), idx[0]); } return {}; } void KMKernel::expireAllFoldersNow() // called by the GUI { mFolderCollectionMonitor->expireAllFolders(true /*immediate*/, entityTreeModel()); } bool KMKernel::canQueryClose() { if (KMMainWidget::mainWidgetList() && KMMainWidget::mainWidgetList()->count() > 1) { return true; } return mUnityServiceManager->canQueryClose(); } Akonadi::Collection KMKernel::currentCollection() { KMMainWidget *widget = getKMMainWidget(); Akonadi::Collection col; if (widget) { col = widget->currentCollection(); } return col; } QSharedPointer KMKernel::currentFolderCollection() { KMMainWidget *widget = getKMMainWidget(); QSharedPointer folder; if (widget) { folder = widget->currentFolder(); } return folder; } MailCommon::MailCommonSettings *KMKernel::mailCommonSettings() const { return mMailCommonSettings; } Akonadi::Search::PIM::IndexedItems *KMKernel::indexedItems() const { return mIndexedItems; } // can't be inline, since KMSender isn't known to implement // KMail::MessageSender outside this .cpp file MessageComposer::MessageSender *KMKernel::msgSender() { return the_msgSender; } void KMKernel::transportRemoved(int id, const QString &name) { Q_UNUSED(id); // reset all identities using the deleted transport QStringList changedIdents; KIdentityManagement::IdentityManager *im = identityManager(); KIdentityManagement::IdentityManager::Iterator end = im->modifyEnd(); for (KIdentityManagement::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) { if (name == (*it).transport()) { (*it).setTransport(QString()); changedIdents += (*it).identityName(); } } // if the deleted transport is the currently used transport reset it to default const QString ¤tTransport = KMailSettings::self()->currentTransport(); if (name == currentTransport) { KMailSettings::self()->setCurrentTransport(QString()); } if (!changedIdents.isEmpty()) { QString information = i18np("This identity has been changed to use the default transport:", "These %1 identities have been changed to use the default transport:", changedIdents.count()); //Don't set parent otherwise we will swith to current KMail and we configure it. So not good KMessageBox::informationList(nullptr, information, changedIdents); im->commit(); } } void KMKernel::transportRenamed(int id, const QString &oldName, const QString &newName) { Q_UNUSED(id); QStringList changedIdents; KIdentityManagement::IdentityManager *im = identityManager(); KIdentityManagement::IdentityManager::Iterator end = im->modifyEnd(); for (KIdentityManagement::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) { if (oldName == (*it).transport()) { (*it).setTransport(newName); changedIdents << (*it).identityName(); } } if (!changedIdents.isEmpty()) { const QString information = i18np("This identity has been changed to use the modified transport:", "These %1 identities have been changed to use the modified transport:", changedIdents.count()); //Don't set parent otherwise we will swith to current KMail and we configure it. So not good KMessageBox::informationList(nullptr, information, changedIdents); im->commit(); } } void KMKernel::itemDispatchStarted() { // Watch progress of the MDA. KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, MailTransport::DispatcherInterface().dispatcherInstance(), QStringLiteral("Sender"), i18n("Sending messages"), i18n("Initiating sending process..."), true, KPIM::ProgressItem::Unknown); } void KMKernel::instanceStatusChanged(const Akonadi::AgentInstance &instance) { if (instance.identifier() == QLatin1String("akonadi_mailfilter_agent")) { // Creating a progress item twice is ok, it will simply return the already existing // item KPIM::ProgressItem *progress = KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, instance, instance.identifier(), instance.name(), instance.statusMessage(), false, KPIM::ProgressItem::Encrypted); progress->setProperty("AgentIdentifier", instance.identifier()); return; } if (MailCommon::Util::agentInstances(true).contains(instance)) { if (instance.status() == Akonadi::AgentInstance::Running) { if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "A Resource started to synchronize, starting a mail check."; Q_EMIT startCheckMail(); } const QString identifier(instance.identifier()); if (!mResourcesBeingChecked.contains(identifier)) { mResourcesBeingChecked.append(identifier); } KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted; if (mResourceCryptoSettingCache.contains(identifier)) { cryptoStatus = mResourceCryptoSettingCache.value(identifier); } else { if (PimCommon::Util::isImapResource(identifier)) { MailCommon::ResourceReadConfigFile resourceFile(identifier); const KConfigGroup grp = resourceFile.group(QStringLiteral("network")); if (grp.isValid()) { const QString imapSafety = grp.readEntry(QStringLiteral("Safety")); if (imapSafety == QLatin1String("None")) { cryptoStatus = KPIM::ProgressItem::Unencrypted; } else { cryptoStatus = KPIM::ProgressItem::Encrypted; } mResourceCryptoSettingCache.insert(identifier, cryptoStatus); } } else if (identifier.contains(POP3_RESOURCE_IDENTIFIER)) { MailCommon::ResourceReadConfigFile resourceFile(identifier); const KConfigGroup grp = resourceFile.group(QStringLiteral("General")); if (grp.isValid()) { if (grp.readEntry(QStringLiteral("useSSL"), false) || grp.readEntry(QStringLiteral("useTLS"), false)) { cryptoStatus = KPIM::ProgressItem::Encrypted; } mResourceCryptoSettingCache.insert(identifier, cryptoStatus); } } } // Creating a progress item twice is ok, it will simply return the already existing // item KPIM::ProgressItem *progress = KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, instance, instance.identifier(), instance.name(), instance.statusMessage(), true, cryptoStatus); progress->setProperty("AgentIdentifier", instance.identifier()); } else if (instance.status() == Akonadi::AgentInstance::Broken) { agentInstanceBroken(instance); } } } void KMKernel::agentInstanceBroken(const Akonadi::AgentInstance &instance) { const QString summary = i18n("Resource %1 is broken.", instance.name()); KNotification::event(QStringLiteral("akonadi-resource-broken"), QString(), summary, QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotProgressItemCompletedOrCanceled(KPIM::ProgressItem *item) { const QString identifier = item->property("AgentIdentifier").toString(); const Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(identifier); if (agent.isValid()) { mResourcesBeingChecked.removeAll(identifier); if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "Last resource finished syncing, mail check done"; Q_EMIT endCheckMail(); } } } void KMKernel::updatedTemplates() { Q_EMIT customTemplatesChanged(); } void KMKernel::cleanupTemporaryFiles() { QDir dir(QDir::tempPath()); const QStringList lst = dir.entryList(QStringList{QStringLiteral("messageviewer_*")}); qCDebug(KMAIL_LOG) << " list file to delete " << lst; for (const QString &file : lst) { QDir tempDir(QDir::tempPath() + QLatin1Char('/') + file); if (!tempDir.removeRecursively()) { fprintf(stderr, "%s was not removed .\n", qPrintable(tempDir.absolutePath())); } else { fprintf(stderr, "%s was removed .\n", qPrintable(tempDir.absolutePath())); } } } void KMKernel::stopAgentInstance() { const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { const QString identifier = type.identifier(); KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(identifier)); // Keep sync in ConfigureDialog, don't forget to change there. if (group.readEntry("OfflineOnShutdown", identifier.startsWith(QLatin1String("akonadi_pop3_resource")) ? true : false)) { type.setIsOnline(false); } } } void KMKernel::slotCollectionRemoved(const Akonadi::Collection &col) { KConfigGroup group(KMKernel::config(), MailCommon::FolderSettings::configGroupName(col)); group.deleteGroup(); group.sync(); const QString colStr = QString::number(col.id()); TemplateParser::Util::deleteTemplate(colStr); MessageList::Util::deleteConfig(colStr); } void KMKernel::slotDeleteIdentity(uint identity) { TemplateParser::Util::deleteTemplate(QStringLiteral("IDENTITY_%1").arg(identity)); } bool KMKernel::showPopupAfterDnD() { return KMailSettings::self()->showPopupAfterDnD(); } bool KMKernel::excludeImportantMailFromExpiry() { return KMailSettings::self()->excludeImportantMailFromExpiry(); } qreal KMKernel::closeToQuotaThreshold() { return KMailSettings::self()->closeToQuotaThreshold(); } Akonadi::Collection::Id KMKernel::lastSelectedFolder() { return KMailSettings::self()->lastSelectedFolder(); } void KMKernel::setLastSelectedFolder(Akonadi::Collection::Id col) { KMailSettings::self()->setLastSelectedFolder(col); } QStringList KMKernel::customTemplates() { return GlobalSettingsBase::self()->customTemplates(); } void KMKernel::openFilterDialog(bool createDummyFilter) { if (!mFilterEditDialog) { mFilterEditDialog = new MailCommon::KMFilterDialog(getKMMainWidget()->actionCollections(), nullptr, createDummyFilter); mFilterEditDialog->setObjectName(QStringLiteral("filterdialog")); } mFilterEditDialog->show(); mFilterEditDialog->raise(); mFilterEditDialog->activateWindow(); } void KMKernel::createFilter(const QByteArray &field, const QString &value) { mFilterEditDialog->createFilter(field, value); } void KMKernel::checkFolderFromResources(const Akonadi::Collection::List &collectionList) { const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (const Akonadi::AgentInstance &type : lst) { if (type.status() == Akonadi::AgentInstance::Broken) { continue; } const QString typeIdentifier(type.identifier()); if (PimCommon::Util::isImapResource(typeIdentifier)) { OrgKdeAkonadiImapSettingsInterface *iface = PimCommon::Util::createImapSettingsInterface(typeIdentifier); if (iface && iface->isValid()) { const Akonadi::Collection::Id imapTrashId = iface->trashCollection(); for (const Akonadi::Collection &collection : collectionList) { const Akonadi::Collection::Id collectionId = collection.id(); if (imapTrashId == collectionId) { //Use default trash iface->setTrashCollection(CommonKernel->trashCollectionFolder().id()); iface->save(); break; } } } delete iface; } else if (typeIdentifier.contains(POP3_RESOURCE_IDENTIFIER)) { OrgKdeAkonadiPOP3SettingsInterface *iface = MailCommon::Util::createPop3SettingsInterface(typeIdentifier); if (iface->isValid()) { for (const Akonadi::Collection &collection : qAsConst(collectionList)) { const Akonadi::Collection::Id collectionId = collection.id(); if (iface->targetCollection() == collectionId) { //Use default inbox iface->setTargetCollection(CommonKernel->inboxCollectionFolder().id()); iface->save(); break; } } } delete iface; } } } const QAbstractItemModel *KMKernel::treeviewModelSelection() { if (getKMMainWidget()) { return getKMMainWidget()->folderTreeView()->selectionModel()->model(); } else { return entityTreeModel(); } } void KMKernel::slotInstanceWarning(const Akonadi::AgentInstance &instance, const QString &message) { const QString summary = i18nc(": ", "%1: %2", instance.name(), message); KNotification::event(QStringLiteral("akonadi-instance-warning"), QString(), summary, QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotInstanceError(const Akonadi::AgentInstance &instance, const QString &message) { const QString summary = i18nc(": ", "%1: %2", instance.name(), message); KNotification::event(QStringLiteral("akonadi-instance-error"), QString(), summary, QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotInstanceRemoved(const Akonadi::AgentInstance &instance) { const QString identifier(instance.identifier()); const QString resourceGroup = QStringLiteral("Resource %1").arg(identifier); if (KMKernel::config()->hasGroup(resourceGroup)) { KConfigGroup group(KMKernel::config(), resourceGroup); group.deleteGroup(); group.sync(); } if (mResourceCryptoSettingCache.contains(identifier)) { mResourceCryptoSettingCache.remove(identifier); } mFolderArchiveManager->slotInstanceRemoved(instance); if (MailCommon::Util::isMailAgent(instance)) { Q_EMIT incomingAccountsChanged(); } } void KMKernel::slotInstanceAdded(const Akonadi::AgentInstance &instance) { if (MailCommon::Util::isMailAgent(instance)) { Q_EMIT incomingAccountsChanged(); } } void KMKernel::savePaneSelection() { KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->savePaneSelection(); } } void KMKernel::updatePaneTagComboBox() { KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->updatePaneTagComboBox(); } } void KMKernel::resourceGoOnLine() { KMMainWidget *widget = getKMMainWidget(); if (widget) { if (widget->currentCollection().isValid()) { Akonadi::Collection collection = widget->currentCollection(); Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(collection.resource()); instance.setIsOnline(true); widget->refreshMessageListSelection(); } } } void KMKernel::makeResourceOnline(MessageViewer::Viewer::ResourceOnlineMode mode) { switch (mode) { case MessageViewer::Viewer::AllResources: resumeNetworkJobs(); break; case MessageViewer::Viewer::SelectedResource: resourceGoOnLine(); break; } } PimCommon::AutoCorrection *KMKernel::composerAutoCorrection() { return mAutoCorrection; } void KMKernel::toggleSystemTray() { KMMainWidget *widget = getKMMainWidget(); if (widget) { mUnityServiceManager->toggleSystemTray(widget); } } void KMKernel::showFolder(const QString &collectionId) { if (!collectionId.isEmpty()) { const Akonadi::Collection::Id id = collectionId.toLongLong(); selectCollectionFromId(id); } } void KMKernel::reloadFolderArchiveConfig() { mFolderArchiveManager->reloadConfig(); } void KMKernel::slotCollectionChanged(const Akonadi::Collection &, const QSet &set) { if (set.contains("newmailnotifierattribute")) { mUnityServiceManager->initListOfCollection(); } } FolderArchiveManager *KMKernel::folderArchiveManager() const { return mFolderArchiveManager; } bool KMKernel::allowToDebug() const { return mDebug; } bool KMKernel::firstStart() const { return the_firstStart; } bool KMKernel::shuttingDown() const { return the_shuttingDown; } void KMKernel::setShuttingDown(bool flag) { the_shuttingDown = flag; } void KMKernel::expunge(Akonadi::Collection::Id col, bool sync) { Q_UNUSED(col); Q_UNUSED(sync); } diff --git a/src/settings/kmail.kcfg.cmake b/src/settings/kmail.kcfg.cmake index 49cceaa19..5b2eb519e 100644 --- a/src/settings/kmail.kcfg.cmake +++ b/src/settings/kmail.kcfg.cmake @@ -1,417 +1,421 @@ qtextcodec.h QFontDatabase kcolorscheme.h editor/composer.h MessageComposer/Util KCompletion SelectLastSelected Online DontLoop true This prevents the automatic expiry of old messages in a folder from deleting (or moving to an archive folder) the messages that are marked 'Important' or 'Action Item' true false <qt><p>Select whether you want KMail to send all messages in the outbox on manual or all mail checks, or whether you do not want messages to be sent automatically at all. </p></qt> DontSendOnCheck The most recently selected folder in the folder selection dialog. -1 true false + + + true + kwrite %f false 80 false false true -1 true true true true true true false false ${WARN_TOOMANY_RECIPIENTS_DEFAULT} If the number of recipients is larger than this value, KMail will warn and ask for a confirmation before sending the mail. The warning can be turned off. 5 1 100 If the number of recipients is larger than this value, KMail will warn and ask for a confirmation before sending the mail. The warning can be turned off. false true false false false <qt><p>Enable this option if you want KMail to request Message Disposition Notifications (MDNs) for each of your outgoing messages.</p><p>This option only affects the default; you can still enable or disable MDN requesting on a per-message basis in the composer, menu item <em>Options</em>-><em>Request Disposition Notification</em>.</p></qt> false KMail::Composer::HDR_SUBJECT|KMail::Composer::HDR_FROM KCompletion::CompletionPopup true true MessageComposer::Util::AttachmentKeywords() A backup copy of the text in the composer window can be created regularly. The interval used to create the backups is set here. You can disable autosaving by setting it to the value 0. 2 false true QStringList() QStringList() true false QFontDatabase::systemFont(QFontDatabase::GeneralFont) QSize(800,600) QSize() 0 0 0 0 250 500 400 180 450 200 320 below longlist TemplateParser::DefaultTemplates::defaultNewMessage() TemplateParser::DefaultTemplates::defaultReply() TemplateParser::DefaultTemplates::defaultReplyAll() TemplateParser::DefaultTemplates::defaultForward() TemplateParser::DefaultTemplates::defaultQuoteString() -1 0 150 150 120 120 120 100 diff --git a/src/unityservicemanager.cpp b/src/unityservicemanager.cpp index d9da17fd9..15e35080d 100644 --- a/src/unityservicemanager.cpp +++ b/src/unityservicemanager.cpp @@ -1,250 +1,251 @@ /* Copyright (C) 2018-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "unityservicemanager.h" #include "kmkernel.h" #include "kmsystemtray.h" #include "settings/kmailsettings.h" #include "kmail_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KMail; UnityServiceManager::UnityServiceManager(QObject *parent) : QObject(parent) , mUnityServiceWatcher(new QDBusServiceWatcher(this)) { connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionStatisticsChanged, this, &UnityServiceManager::slotCollectionStatisticsChanged); connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionAdded, this, &UnityServiceManager::initListOfCollection); connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionRemoved, this, &UnityServiceManager::initListOfCollection); connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionSubscribed, this, &UnityServiceManager::initListOfCollection); connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionUnsubscribed, this, &UnityServiceManager::initListOfCollection); initListOfCollection(); initUnity(); } UnityServiceManager::~UnityServiceManager() { mSystemTray = nullptr; } bool UnityServiceManager::excludeFolder(const Akonadi::Collection &collection) const { if (!collection.isValid() || !collection.contentMimeTypes().contains(KMime::Message::mimeType())) { return true; } if (CommonKernel->outboxCollectionFolder() == collection || CommonKernel->sentCollectionFolder() == collection || CommonKernel->templatesCollectionFolder() == collection || CommonKernel->trashCollectionFolder() == collection || CommonKernel->draftsCollectionFolder() == collection) { return true; } if (MailCommon::Util::isVirtualCollection(collection)) { return true; } return false; } void UnityServiceManager::unreadMail(const QAbstractItemModel *model, const QModelIndex &parentIndex) { const int rowCount = model->rowCount(parentIndex); for (int row = 0; row < rowCount; ++row) { const QModelIndex index = model->index(row, 0, parentIndex); const Akonadi::Collection collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value(); if (!excludeFolder(collection)) { const Akonadi::CollectionStatistics statistics = collection.statistics(); const qint64 count = qMax(0LL, statistics.unreadCount()); if (count > 0) { if (!ignoreNewMailInFolder(collection)) { mCount += count; } } } if (model->rowCount(index) > 0) { unreadMail(model, index); } } if (mSystemTray) { // Update tooltip to reflect count of unread messages mSystemTray->updateToolTip(mCount); } } void UnityServiceManager::updateSystemTray() { initListOfCollection(); } void UnityServiceManager::initListOfCollection() { mCount = 0; const QAbstractItemModel *model = kmkernel->collectionModel(); if (model->rowCount() == 0) { QTimer::singleShot(1000, this, &UnityServiceManager::initListOfCollection); return; } unreadMail(model); if (mSystemTray) { mSystemTray->updateStatus(mCount); } //qCDebug(KMAIL_LOG)<<" mCount :"<outboxCollectionFolder().id() == id || CommonKernel->sentCollectionFolder().id() == id || CommonKernel->templatesCollectionFolder().id() == id || CommonKernel->trashCollectionFolder().id() == id || CommonKernel->draftsCollectionFolder().id() == id) { return; } initListOfCollection(); } void UnityServiceManager::updateCount() { if (mSystemTray) { mSystemTray->updateCount(mCount); } + if (mUnityServiceAvailable) { const QString launcherId = qApp->desktopFileName() + QLatin1String(".desktop"); - + const int unreadEmail = KMailSettings::self()->showUnreadInTaskbar() ? mCount : 0; const QVariantMap properties{ - {QStringLiteral("count-visible"), mCount > 0}, - {QStringLiteral("count"), mCount} + {QStringLiteral("count-visible"), unreadEmail > 0}, + {QStringLiteral("count"), unreadEmail} }; QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/org/kmail2/UnityLauncher"), QStringLiteral("com.canonical.Unity.LauncherEntry"), QStringLiteral("Update")); message.setArguments({launcherId, properties}); QDBusConnection::sessionBus().send(message); } } void UnityServiceManager::initUnity() { mUnityServiceWatcher->setConnection(QDBusConnection::sessionBus()); mUnityServiceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration); mUnityServiceWatcher->addWatchedService(QStringLiteral("com.canonical.Unity")); connect(mUnityServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &service) { Q_UNUSED(service); mUnityServiceAvailable = true; updateCount(); }); connect(mUnityServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &service) { Q_UNUSED(service); mUnityServiceAvailable = false; }); // QDBusConnectionInterface::isServiceRegistered blocks QDBusPendingCall listNamesCall = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("ListNames")); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(listNamesCall, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qCWarning(KMAIL_LOG) << "DBus reported an error " << reply.error().message(); return; } const QStringList &services = reply.value(); mUnityServiceAvailable = services.contains(QLatin1String("com.canonical.Unity")); if (mUnityServiceAvailable) { updateCount(); } }); } bool UnityServiceManager::ignoreNewMailInFolder(const Akonadi::Collection &collection) { if (collection.hasAttribute()) { if (collection.attribute()->ignoreNewMail()) { return true; } } return false; } bool UnityServiceManager::haveSystemTrayApplet() const { return mSystemTray != nullptr; } bool UnityServiceManager::hasUnreadMail() const { return mCount != 0; } bool UnityServiceManager::canQueryClose() { if (!mSystemTray) { return true; } if (hasUnreadMail()) { mSystemTray->setStatus(KStatusNotifierItem::Active); } mSystemTray->hideKMail(); return false; } void UnityServiceManager::toggleSystemTray(QWidget *widget) { if (widget) { if (!mSystemTray && KMailSettings::self()->systemTrayEnabled()) { mSystemTray = new KMail::KMSystemTray(widget); mSystemTray->setUnityServiceManager(this); mSystemTray->initialize(mCount); } else if (mSystemTray && !KMailSettings::self()->systemTrayEnabled()) { // Get rid of system tray on user's request qCDebug(KMAIL_LOG) << "deleting systray"; delete mSystemTray; mSystemTray = nullptr; } } } diff --git a/src/unityservicemanager.h b/src/unityservicemanager.h index f8344eb6d..f58eeebd6 100644 --- a/src/unityservicemanager.h +++ b/src/unityservicemanager.h @@ -1,58 +1,58 @@ /* Copyright (C) 2018-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UNITYSERVICEMANAGER_H #define UNITYSERVICEMANAGER_H #include #include #include class QDBusServiceWatcher; class QAbstractItemModel; namespace KMail { class KMSystemTray; class UnityServiceManager : public QObject { Q_OBJECT public: explicit UnityServiceManager(QObject *parent = nullptr); ~UnityServiceManager(); void updateSystemTray(); bool haveSystemTrayApplet() const; bool canQueryClose(); void toggleSystemTray(QWidget *parent); void initListOfCollection(); bool excludeFolder(const Akonadi::Collection &collection) const; bool ignoreNewMailInFolder(const Akonadi::Collection &collection); + void updateCount(); private: Q_DISABLE_COPY(UnityServiceManager) void unreadMail(const QAbstractItemModel *model, const QModelIndex &parentIndex = {}); void slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &); - void updateCount(); void initUnity(); bool hasUnreadMail() const; QDBusServiceWatcher *mUnityServiceWatcher = nullptr; KMail::KMSystemTray *mSystemTray = nullptr; int mCount = 0; bool mUnityServiceAvailable = false; }; } #endif // UNITYSERVICEMANAGER_H