File indexing completed on 2024-12-22 05:01:01
0001 /* 0002 SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-only 0005 */ 0006 0007 #include "configureappearancepage.h" 0008 #include <PimCommon/ConfigureImmutableWidgetUtils> 0009 using namespace PimCommon::ConfigureImmutableWidgetUtils; 0010 #include "configuredialog/colorlistbox.h" 0011 #include "kmkernel.h" 0012 #include <KLineEditEventHandler> 0013 #include <MailCommon/TagWidget> 0014 #include <MessageList/AggregationComboBox> 0015 #include <MessageList/AggregationConfigButton> 0016 #include <MessageList/ThemeComboBox> 0017 #include <MessageList/ThemeConfigButton> 0018 #include <messagelist/messagelistsettings.h> 0019 0020 #include <MailCommon/FolderTreeWidget> 0021 0022 #include "kmmainwidget.h" 0023 0024 #include <mailcommon/mailcommonsettings_base.h> 0025 0026 #include <MessageViewer/ConfigureWidget> 0027 #include <MessageViewer/MessageViewerSettings> 0028 0029 #include "settings/kmailsettings.h" 0030 #include <MessageCore/ColorUtil> 0031 #include <MessageCore/MessageCoreSettings> 0032 #include <MessageList/MessageListUtil> 0033 0034 #include <MailCommon/MailUtil> 0035 0036 #include <Akonadi/Tag> 0037 #include <Akonadi/TagAttribute> 0038 #include <Akonadi/TagCreateJob> 0039 #include <Akonadi/TagDeleteJob> 0040 #include <Akonadi/TagFetchJob> 0041 #include <Akonadi/TagFetchScope> 0042 #include <Akonadi/TagModifyJob> 0043 0044 #include "kmail_debug.h" 0045 #include <KFontChooser> 0046 #include <KIconButton> 0047 #include <KKeySequenceWidget> 0048 #include <KLocalizedString> 0049 #include <KMessageBox> 0050 #include <KSeparator> 0051 #include <QHBoxLayout> 0052 #include <QIcon> 0053 #include <QLineEdit> 0054 0055 #include <KMime/DateFormatter> 0056 0057 #include <KLazyLocalizedString> 0058 #include <QButtonGroup> 0059 #include <QCheckBox> 0060 #include <QFontDatabase> 0061 #include <QFormLayout> 0062 #include <QGroupBox> 0063 #include <QLabel> 0064 #include <QMenu> 0065 #include <QRadioButton> 0066 #include <QSpinBox> 0067 #include <QVBoxLayout> 0068 #include <QWhatsThis> 0069 0070 using KMime::DateFormatter; 0071 using namespace MailCommon; 0072 0073 QString AppearancePage::helpAnchor() const 0074 { 0075 return QStringLiteral("configure-appearance"); 0076 } 0077 0078 AppearancePage::AppearancePage(QObject *parent, const KPluginMetaData &data) 0079 : ConfigModuleWithTabs(parent, data) 0080 { 0081 // 0082 // "General" tab: 0083 // 0084 auto readerTab = new AppearancePageGeneralTab(); 0085 addTab(readerTab, i18n("General")); 0086 addConfig(MessageViewer::MessageViewerSettings::self(), readerTab); 0087 0088 // 0089 // "Fonts" tab: 0090 // 0091 auto fontsTab = new AppearancePageFontsTab(); 0092 addTab(fontsTab, i18n("Fonts")); 0093 0094 // 0095 // "Colors" tab: 0096 // 0097 auto colorsTab = new AppearancePageColorsTab(); 0098 addTab(colorsTab, i18n("Colors")); 0099 0100 // 0101 // "Layout" tab: 0102 // 0103 auto layoutTab = new AppearancePageLayoutTab(); 0104 addTab(layoutTab, i18n("Layout")); 0105 0106 // 0107 // "Headers" tab: 0108 // 0109 auto headersTab = new AppearancePageHeadersTab(); 0110 addTab(headersTab, i18n("Message List")); 0111 0112 // 0113 // "Message Tag" tab: 0114 // 0115 auto messageTagTab = new AppearancePageMessageTagTab(); 0116 addTab(messageTagTab, i18n("Message Tags")); 0117 } 0118 0119 QString AppearancePageFontsTab::helpAnchor() const 0120 { 0121 return QStringLiteral("configure-appearance-fonts"); 0122 } 0123 0124 static const struct { 0125 const char *configName; 0126 const KLazyLocalizedString displayName; 0127 bool enableFamilyAndSize; 0128 bool onlyFixed; 0129 } fontNames[] = { 0130 {"body-font", kli18n("Message Body"), true, false}, 0131 {"MessageListFont", kli18n("Message List"), true, false}, 0132 {"UnreadMessageFont", kli18n("Message List - Unread Messages"), false, false}, 0133 {"ImportantMessageFont", kli18n("Message List - Important Messages"), false, false}, 0134 {"TodoMessageFont", kli18n("Message List - Action Item Messages"), false, false}, 0135 {"fixed-font", kli18n("Fixed Width Font"), true, true}, 0136 {"composer-font", kli18n("Composer"), true, false}, 0137 {"print-font", kli18n("Printing Output"), true, false}, 0138 }; 0139 static const int numFontNames = sizeof fontNames / sizeof *fontNames; 0140 0141 AppearancePageFontsTab::AppearancePageFontsTab(QWidget *parent) 0142 : ConfigModuleTab(parent) 0143 { 0144 assert(numFontNames == sizeof mFont / sizeof *mFont); 0145 0146 // "Use custom fonts" checkbox, followed by <hr> 0147 auto vlay = new QVBoxLayout(this); 0148 mCustomFontCheck = new QCheckBox(i18n("&Use custom fonts"), this); 0149 vlay->addWidget(mCustomFontCheck); 0150 vlay->addWidget(new KSeparator(Qt::Horizontal, this)); 0151 connect(mCustomFontCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0152 0153 // "font location" combo box and label: 0154 auto hlay = new QHBoxLayout(); // inherites spacing 0155 vlay->addLayout(hlay); 0156 mFontLocationCombo = new QComboBox(this); 0157 mFontLocationCombo->setEnabled(false); // !mCustomFontCheck->isChecked() 0158 0159 QStringList fontDescriptions; 0160 fontDescriptions.reserve(numFontNames); 0161 for (int i = 0; i < numFontNames; ++i) { 0162 fontDescriptions << fontNames[i].displayName.toString(); 0163 } 0164 mFontLocationCombo->addItems(fontDescriptions); 0165 0166 auto label = new QLabel(i18n("Apply &to:"), this); 0167 label->setBuddy(mFontLocationCombo); 0168 label->setEnabled(false); // since !mCustomFontCheck->isChecked() 0169 hlay->addWidget(label); 0170 0171 hlay->addWidget(mFontLocationCombo); 0172 hlay->addStretch(10); 0173 mFontChooser = new KFontChooser(KFontChooser::DisplayFrame, this); 0174 mFontChooser->setMinVisibleItems(4); 0175 mFontChooser->setEnabled(false); // since !mCustomFontCheck->isChecked() 0176 vlay->addWidget(mFontChooser); 0177 connect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged); 0178 0179 // {en,dis}able widgets depending on the state of mCustomFontCheck: 0180 connect(mCustomFontCheck, &QAbstractButton::toggled, label, &QWidget::setEnabled); 0181 connect(mCustomFontCheck, &QAbstractButton::toggled, mFontLocationCombo, &QWidget::setEnabled); 0182 connect(mCustomFontCheck, &QAbstractButton::toggled, mFontChooser, &QWidget::setEnabled); 0183 // load the right font settings into mFontChooser: 0184 connect(mFontLocationCombo, &QComboBox::activated, this, &AppearancePageFontsTab::slotFontSelectorChanged); 0185 } 0186 0187 void AppearancePageFontsTab::slotFontSelectorChanged(int index) 0188 { 0189 qCDebug(KMAIL_LOG) << "slotFontSelectorChanged() called"; 0190 if (index < 0 || index >= mFontLocationCombo->count()) { 0191 return; // Should never happen, but it is better to check. 0192 } 0193 0194 // Save current fontselector setting before we install the new: 0195 if (mActiveFontIndex == 0) { 0196 mFont[0] = mFontChooser->font(); 0197 // hardcode the family and size of "message body" dependent fonts: 0198 for (int i = 0; i < numFontNames; ++i) { 0199 if (!fontNames[i].enableFamilyAndSize) { 0200 // ### shall we copy the font and set the save and re-set 0201 // {regular,italic,bold,bold italic} property or should we 0202 // copy only family and pointSize? 0203 mFont[i].setFamily(mFont[0].family()); 0204 mFont[i].setPointSize /*Float?*/ (mFont[0].pointSize /*Float?*/ ()); 0205 } 0206 } 0207 } else if (mActiveFontIndex > 0) { 0208 mFont[mActiveFontIndex] = mFontChooser->font(); 0209 } 0210 mActiveFontIndex = index; 0211 0212 // Disonnect so the "Apply" button is not activated by the change 0213 disconnect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged); 0214 0215 // Display the new setting: 0216 mFontChooser->setFont(mFont[index], fontNames[index].onlyFixed); 0217 0218 connect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged); 0219 0220 // Disable Family and Size list if we have selected a quote font: 0221 mFontChooser->enableColumn(KFontChooser::FamilyList | KFontChooser::SizeList, fontNames[index].enableFamilyAndSize); 0222 } 0223 0224 void AppearancePageFontsTab::doLoadOther() 0225 { 0226 if (KMKernel::self()) { 0227 KConfigGroup fonts(KMKernel::self()->config(), QStringLiteral("Fonts")); 0228 0229 mFont[0] = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 0230 QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); 0231 0232 for (int i = 0; i < numFontNames; ++i) { 0233 const QString configName = QLatin1StringView(fontNames[i].configName); 0234 if (configName == QLatin1StringView("MessageListFont")) { 0235 mFont[i] = MessageList::MessageListSettings::self()->messageListFont(); 0236 } else if (configName == QLatin1StringView("UnreadMessageFont")) { 0237 mFont[i] = MessageList::MessageListSettings::self()->unreadMessageFont(); 0238 } else if (configName == QLatin1StringView("ImportantMessageFont")) { 0239 mFont[i] = MessageList::MessageListSettings::self()->importantMessageFont(); 0240 } else if (configName == QLatin1StringView("TodoMessageFont")) { 0241 mFont[i] = MessageList::MessageListSettings::self()->todoMessageFont(); 0242 } else { 0243 mFont[i] = fonts.readEntry(configName, (fontNames[i].onlyFixed) ? fixedFont : mFont[0]); 0244 } 0245 } 0246 mCustomFontCheck->setChecked(!MessageCore::MessageCoreSettings::self()->useDefaultFonts()); 0247 mFontLocationCombo->setCurrentIndex(0); 0248 slotFontSelectorChanged(0); 0249 } else { 0250 setEnabled(false); 0251 } 0252 } 0253 0254 void AppearancePageFontsTab::save() 0255 { 0256 if (KMKernel::self()) { 0257 KConfigGroup fonts(KMKernel::self()->config(), QStringLiteral("Fonts")); 0258 0259 // read the current font (might have been modified) 0260 if (mActiveFontIndex >= 0) { 0261 mFont[mActiveFontIndex] = mFontChooser->font(); 0262 } 0263 0264 const bool customFonts = mCustomFontCheck->isChecked(); 0265 MessageCore::MessageCoreSettings::self()->setUseDefaultFonts(!customFonts); 0266 0267 for (int i = 0; i < numFontNames; ++i) { 0268 const QString configName = QLatin1StringView(fontNames[i].configName); 0269 if (customFonts && configName == QLatin1StringView("MessageListFont")) { 0270 MessageList::MessageListSettings::self()->setMessageListFont(mFont[i]); 0271 } else if (customFonts && configName == QLatin1StringView("UnreadMessageFont")) { 0272 MessageList::MessageListSettings::self()->setUnreadMessageFont(mFont[i]); 0273 } else if (customFonts && configName == QLatin1StringView("ImportantMessageFont")) { 0274 MessageList::MessageListSettings::self()->setImportantMessageFont(mFont[i]); 0275 } else if (customFonts && configName == QLatin1StringView("TodoMessageFont")) { 0276 MessageList::MessageListSettings::self()->setTodoMessageFont(mFont[i]); 0277 } else { 0278 if (customFonts || fonts.hasKey(configName)) { 0279 // Don't write font info when we use default fonts, but write 0280 // if it's already there: 0281 fonts.writeEntry(configName, mFont[i]); 0282 } 0283 } 0284 } 0285 } 0286 } 0287 0288 void AppearancePageFontsTab::doResetToDefaultsOther() 0289 { 0290 mCustomFontCheck->setChecked(false); 0291 } 0292 0293 QString AppearancePageColorsTab::helpAnchor() const 0294 { 0295 return QStringLiteral("configure-appearance-colors"); 0296 } 0297 0298 static const struct { 0299 const char *configName; 0300 const KLazyLocalizedString displayName; 0301 } colorNames[] = { // adjust doLoadOther if you change this: 0302 {"QuotedText1", kli18n("Quoted Text - First Level")}, 0303 {"QuotedText2", kli18n("Quoted Text - Second Level")}, 0304 {"QuotedText3", kli18n("Quoted Text - Third Level")}, 0305 {"LinkColor", kli18n("Link")}, 0306 {"UnreadMessageColor", kli18n("Unread Message")}, 0307 {"ImportantMessageColor", kli18n("Important Message")}, 0308 {"TodoMessageColor", kli18n("Action Item Message")}, 0309 {"ColorbarBackgroundPlain", kli18n("HTML Status Bar Background - No HTML Message")}, 0310 {"ColorbarForegroundPlain", kli18n("HTML Status Bar Foreground - No HTML Message")}, 0311 {"ColorbarBackgroundHTML", kli18n("HTML Status Bar Background - HTML Message")}, 0312 {"ColorbarForegroundHTML", kli18n("HTML Status Bar Foreground - HTML Message")}}; 0313 static const int numColorNames = sizeof colorNames / sizeof *colorNames; 0314 0315 AppearancePageColorsTab::AppearancePageColorsTab(QWidget *parent) 0316 : ConfigModuleTab(parent) 0317 { 0318 // "use custom colors" check box 0319 auto vlay = new QVBoxLayout(this); 0320 mCustomColorCheck = new QCheckBox(i18n("&Use custom colors"), this); 0321 vlay->addWidget(mCustomColorCheck); 0322 connect(mCustomColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0323 0324 mUseInlineStyle = new QCheckBox(i18n("&Do not change color from original HTML mail"), this); 0325 vlay->addWidget(mUseInlineStyle); 0326 connect(mUseInlineStyle, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0327 0328 // color list box: 0329 mColorList = new ColorListBox(this); 0330 mColorList->setEnabled(false); // since !mCustomColorCheck->isChecked() 0331 for (int i = 0; i < numColorNames; ++i) { 0332 mColorList->addColor(colorNames[i].displayName.toString()); 0333 } 0334 vlay->addWidget(mColorList, 1); 0335 0336 // "recycle colors" check box: 0337 mRecycleColorCheck = new QCheckBox(i18n("Recycle colors on deep "ing"), this); 0338 mRecycleColorCheck->setEnabled(false); 0339 vlay->addWidget(mRecycleColorCheck); 0340 connect(mRecycleColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0341 0342 // close to quota threshold 0343 auto hbox = new QHBoxLayout(); 0344 vlay->addLayout(hbox); 0345 auto l = new QLabel(i18n("Close to quota threshold:"), this); 0346 hbox->addWidget(l); 0347 mCloseToQuotaThreshold = new QSpinBox(this); 0348 mCloseToQuotaThreshold->setRange(0, 100); 0349 mCloseToQuotaThreshold->setSingleStep(1); 0350 connect(mCloseToQuotaThreshold, &QSpinBox::valueChanged, this, &ConfigModuleTab::slotEmitChanged); 0351 mCloseToQuotaThreshold->setSuffix(i18n("%")); 0352 0353 hbox->addWidget(mCloseToQuotaThreshold); 0354 hbox->addWidget(new QWidget(this), 2); 0355 0356 // {en,dir}able widgets depending on the state of mCustomColorCheck: 0357 connect(mCustomColorCheck, &QAbstractButton::toggled, mColorList, &QWidget::setEnabled); 0358 connect(mCustomColorCheck, &QAbstractButton::toggled, mRecycleColorCheck, &QWidget::setEnabled); 0359 connect(mCustomColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0360 connect(mColorList, &ColorListBox::changed, this, &ConfigModuleTab::slotEmitChanged); 0361 } 0362 0363 void AppearancePageColorsTab::doLoadOther() 0364 { 0365 mCustomColorCheck->setChecked(!MessageCore::MessageCoreSettings::self()->useDefaultColors()); 0366 mUseInlineStyle->setChecked(MessageCore::MessageCoreSettings::self()->useRealHtmlMailColor()); 0367 mRecycleColorCheck->setChecked(MessageViewer::MessageViewerSettings::self()->recycleQuoteColors()); 0368 mCloseToQuotaThreshold->setValue(KMailSettings::self()->closeToQuotaThreshold()); 0369 loadColor(true); 0370 } 0371 0372 void AppearancePageColorsTab::loadColor(bool loadFromConfig) 0373 { 0374 if (KMKernel::self()) { 0375 KConfigGroup reader(KMKernel::self()->config(), QStringLiteral("Reader")); 0376 0377 static const QColor defaultColor[numColorNames] = { 0378 MessageCore::ColorUtil::self()->quoteLevel1DefaultTextColor(), 0379 MessageCore::ColorUtil::self()->quoteLevel2DefaultTextColor(), 0380 MessageCore::ColorUtil::self()->quoteLevel3DefaultTextColor(), 0381 MessageCore::ColorUtil::self()->linkColor(), // link 0382 MessageList::Util::unreadDefaultMessageColor(), // unread mgs 0383 MessageList::Util::importantDefaultMessageColor(), // important msg 0384 MessageList::Util::todoDefaultMessageColor(), // action item mgs 0385 Qt::lightGray, // colorbar plain bg 0386 Qt::black, // colorbar plain fg 0387 Qt::black, // colorbar html bg 0388 Qt::white // colorbar html fg 0389 }; 0390 0391 for (int i = 0; i < numColorNames; ++i) { 0392 if (loadFromConfig) { 0393 const QString configName = QLatin1StringView(colorNames[i].configName); 0394 if (configName == QLatin1StringView("UnreadMessageColor")) { 0395 mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->unreadMessageColor()); 0396 } else if (configName == QLatin1StringView("ImportantMessageColor")) { 0397 mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->importantMessageColor()); 0398 } else if (configName == QLatin1StringView("TodoMessageColor")) { 0399 mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->todoMessageColor()); 0400 } else { 0401 mColorList->setColorSilently(i, reader.readEntry(configName, defaultColor[i])); 0402 } 0403 } else { 0404 mColorList->setColorSilently(i, defaultColor[i]); 0405 } 0406 } 0407 } else { 0408 setEnabled(false); 0409 } 0410 } 0411 0412 void AppearancePageColorsTab::doResetToDefaultsOther() 0413 { 0414 mCustomColorCheck->setChecked(false); 0415 mUseInlineStyle->setChecked(false); 0416 mRecycleColorCheck->setChecked(false); 0417 mCloseToQuotaThreshold->setValue(80); 0418 loadColor(false); 0419 } 0420 0421 void AppearancePageColorsTab::save() 0422 { 0423 if (!KMKernel::self()) { 0424 return; 0425 } 0426 KConfigGroup reader(KMKernel::self()->config(), QStringLiteral("Reader")); 0427 bool customColors = mCustomColorCheck->isChecked(); 0428 MessageCore::MessageCoreSettings::self()->setUseDefaultColors(!customColors); 0429 MessageCore::MessageCoreSettings::self()->setUseRealHtmlMailColor(mUseInlineStyle->isChecked()); 0430 0431 for (int i = 0; i < numColorNames; ++i) { 0432 const QString configName = QLatin1StringView(colorNames[i].configName); 0433 if (customColors && configName == QLatin1StringView("UnreadMessageColor")) { 0434 MessageList::MessageListSettings::self()->setUnreadMessageColor(mColorList->color(i)); 0435 } else if (customColors && configName == QLatin1StringView("ImportantMessageColor")) { 0436 MessageList::MessageListSettings::self()->setImportantMessageColor(mColorList->color(i)); 0437 } else if (customColors && configName == QLatin1StringView("TodoMessageColor")) { 0438 MessageList::MessageListSettings::self()->setTodoMessageColor(mColorList->color(i)); 0439 } else { 0440 if (customColors || reader.hasKey(configName)) { 0441 reader.writeEntry(configName, mColorList->color(i)); 0442 } 0443 } 0444 } 0445 MessageViewer::MessageViewerSettings::self()->setRecycleQuoteColors(mRecycleColorCheck->isChecked()); 0446 KMailSettings::self()->setCloseToQuotaThreshold(mCloseToQuotaThreshold->value()); 0447 } 0448 0449 QString AppearancePageLayoutTab::helpAnchor() const 0450 { 0451 return QStringLiteral("configure-appearance-layout"); 0452 } 0453 0454 AppearancePageLayoutTab::AppearancePageLayoutTab(QWidget *parent) 0455 : ConfigModuleTab(parent) 0456 { 0457 auto formLayout = new QFormLayout(this); 0458 0459 // "folder list" radio buttons: 0460 const auto folderListItem = KMailSettings::self()->folderListItem(); 0461 mFolderListGroup = new QButtonGroup(this); 0462 { 0463 const int numberChoices(folderListItem->choices().size()); 0464 for (int i = 0; i < numberChoices; ++i) { 0465 auto button = new QRadioButton(folderListItem->choices().at(i).label, this); 0466 mFolderListGroup->addButton(button, i); 0467 checkLockDown(button, folderListItem); 0468 if (i == 0) { 0469 formLayout->addRow(folderListItem->label(), button); 0470 } else { 0471 formLayout->addRow(nullptr, button); 0472 } 0473 } 0474 } 0475 0476 connect(mFolderListGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged); 0477 0478 mFolderQuickSearchCB = new QCheckBox(i18n("Show folder quick search field"), this); 0479 connect(mFolderQuickSearchCB, &QAbstractButton::toggled, this, &ConfigModuleTab::slotEmitChanged); 0480 formLayout->addRow(nullptr, mFolderQuickSearchCB); 0481 0482 // "favorite folders view mode" radio buttons: 0483 mFavoriteFoldersViewGroup = new QButtonGroup(this); 0484 connect(mFavoriteFoldersViewGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged); 0485 0486 auto favoriteFoldersViewHiddenRadio = new QRadioButton(i18n("Never"), this); 0487 mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewHiddenRadio, 0488 static_cast<int>(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode)); 0489 formLayout->addRow(i18nc("@label", "Show favorite folders view:"), favoriteFoldersViewHiddenRadio); 0490 0491 auto favoriteFoldersViewIconsRadio = new QRadioButton(i18n("As icons"), this); 0492 mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewIconsRadio, 0493 static_cast<int>(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode)); 0494 formLayout->addRow(nullptr, favoriteFoldersViewIconsRadio); 0495 0496 auto favoriteFoldersViewListRadio = new QRadioButton(i18n("As list"), this); 0497 mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewListRadio, 0498 static_cast<int>(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode)); 0499 formLayout->addRow(nullptr, favoriteFoldersViewListRadio); 0500 0501 // "folder tooltips" radio buttons: 0502 mFolderToolTipsGroup = new QButtonGroup(this); 0503 connect(mFolderToolTipsGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged); 0504 0505 auto folderToolTipsAlwaysRadio = new QRadioButton(i18n("Always"), this); 0506 mFolderToolTipsGroup->addButton(folderToolTipsAlwaysRadio, static_cast<int>(FolderTreeWidget::DisplayAlways)); 0507 formLayout->addRow(i18nc("@label", "Folder tooltips:"), folderToolTipsAlwaysRadio); 0508 0509 auto folderToolTipsNeverRadio = new QRadioButton(i18n("Never"), this); 0510 mFolderToolTipsGroup->addButton(folderToolTipsNeverRadio, static_cast<int>(FolderTreeWidget::DisplayNever)); 0511 formLayout->addRow(nullptr, folderToolTipsNeverRadio); 0512 0513 // "show reader window" radio buttons: 0514 mReaderWindowModeGroup = new QButtonGroup(this); 0515 const auto readerWindowModeItem = KMailSettings::self()->readerWindowModeItem(); 0516 const auto readerWindowModeLayout = new QVBoxLayout; 0517 mReaderWindowModeGroup = new QButtonGroup(this); 0518 { 0519 const int numberChoices(readerWindowModeItem->choices().size()); 0520 for (int i = 0; i < numberChoices; ++i) { 0521 auto button = new QRadioButton(readerWindowModeItem->choices().at(i).label, this); 0522 mReaderWindowModeGroup->addButton(button, i); 0523 readerWindowModeLayout->addWidget(button); 0524 checkLockDown(button, readerWindowModeItem); 0525 0526 if (i == 0) { 0527 formLayout->addRow(readerWindowModeItem->label(), button); 0528 } else { 0529 formLayout->addRow(nullptr, button); 0530 } 0531 } 0532 } 0533 0534 connect(mReaderWindowModeGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged); 0535 } 0536 0537 void AppearancePageLayoutTab::doLoadOther() 0538 { 0539 mFolderListGroup->buttons().at(KMailSettings::self()->folderListItem()->value())->setChecked(true); 0540 mReaderWindowModeGroup->buttons().at(KMailSettings::self()->readerWindowModeItem()->value())->setChecked(true); 0541 0542 if (KMKernel::self()) { 0543 const auto item = KMKernel::self()->mailCommonSettings()->favoriteCollectionViewModeItem(); 0544 mFavoriteFoldersViewGroup->buttons().at(item->value())->setChecked(true); 0545 if (item->isImmutable()) { 0546 for (int i = 0, count = mFavoriteFoldersViewGroup->buttons().count(); i < count; i++) { 0547 checkLockDown(mFavoriteFoldersViewGroup->buttons().at(i), item); 0548 } 0549 } 0550 } 0551 loadWidget(mFolderQuickSearchCB, KMailSettings::self()->enableFolderQuickSearchItem()); 0552 const int checkedFolderToolTipsPolicy = KMailSettings::self()->toolTipDisplayPolicy(); 0553 if (checkedFolderToolTipsPolicy >= 0) { 0554 mFolderToolTipsGroup->button(checkedFolderToolTipsPolicy)->setChecked(true); 0555 } 0556 } 0557 0558 void AppearancePageLayoutTab::save() 0559 { 0560 saveButtonGroup(mFolderListGroup, KMailSettings::self()->folderListItem()); 0561 saveButtonGroup(mReaderWindowModeGroup, KMailSettings::self()->readerWindowModeItem()); 0562 if (KMKernel::self()) { 0563 saveButtonGroup(mFavoriteFoldersViewGroup, KMKernel::self()->mailCommonSettings()->favoriteCollectionViewModeItem()); 0564 } 0565 saveCheckBox(mFolderQuickSearchCB, KMailSettings::self()->enableFolderQuickSearchItem()); 0566 KMailSettings::self()->setToolTipDisplayPolicy(mFolderToolTipsGroup->checkedId()); 0567 } 0568 0569 // 0570 // Appearance Message List 0571 // 0572 0573 QString AppearancePageHeadersTab::helpAnchor() const 0574 { 0575 return QStringLiteral("configure-appearance-headers"); 0576 } 0577 0578 static const struct { 0579 const KLazyLocalizedString displayName; 0580 DateFormatter::FormatType dateDisplay; 0581 } dateDisplayConfig[] = {{kli18n("Sta&ndard format (%1)"), KMime::DateFormatter::CTime}, 0582 {kli18n("Locali&zed format (%1)"), KMime::DateFormatter::Localized}, 0583 {kli18n("Smart for&mat (%1)"), KMime::DateFormatter::Fancy}, 0584 {kli18n("C&ustom format:"), KMime::DateFormatter::Custom}}; 0585 static const int numDateDisplayConfig = sizeof dateDisplayConfig / sizeof *dateDisplayConfig; 0586 0587 AppearancePageHeadersTab::AppearancePageHeadersTab(QWidget *parent) 0588 : ConfigModuleTab(parent) 0589 { 0590 auto formLayout = new QFormLayout(this); 0591 0592 mDisplayMessageToolTips = new QCheckBox(MessageList::MessageListSettings::self()->messageToolTipEnabledItem()->label(), this); 0593 formLayout->addRow(QString(), mDisplayMessageToolTips); 0594 0595 connect(mDisplayMessageToolTips, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0596 0597 // "Aggregation" 0598 using MessageList::Utils::AggregationComboBox; 0599 mAggregationComboBox = new AggregationComboBox(this); 0600 0601 using MessageList::Utils::AggregationConfigButton; 0602 auto aggregationConfigButton = new AggregationConfigButton(this, mAggregationComboBox); 0603 0604 auto aggregationLayout = new QHBoxLayout(); 0605 aggregationLayout->addWidget(mAggregationComboBox, 1); 0606 aggregationLayout->addWidget(aggregationConfigButton, 0); 0607 formLayout->addRow(i18n("Default aggregation:"), aggregationLayout); 0608 0609 connect(aggregationConfigButton, 0610 &MessageList::Utils::AggregationConfigButton::configureDialogCompleted, 0611 this, 0612 &AppearancePageHeadersTab::slotSelectDefaultAggregation); 0613 connect(mAggregationComboBox, &MessageList::Utils::AggregationComboBox::activated, this, &ConfigModuleTab::slotEmitChanged); 0614 0615 // "Theme" 0616 using MessageList::Utils::ThemeComboBox; 0617 mThemeComboBox = new ThemeComboBox(this); 0618 0619 using MessageList::Utils::ThemeConfigButton; 0620 auto themeConfigButton = new ThemeConfigButton(this, mThemeComboBox); 0621 0622 auto themeLayout = new QHBoxLayout(); 0623 themeLayout->addWidget(mThemeComboBox, 1); 0624 themeLayout->addWidget(themeConfigButton, 0); 0625 formLayout->addRow(i18n("Default theme:"), themeLayout); 0626 0627 connect(themeConfigButton, &MessageList::Utils::ThemeConfigButton::configureDialogCompleted, this, &AppearancePageHeadersTab::slotSelectDefaultTheme); 0628 connect(mThemeComboBox, &MessageList::Utils::ThemeComboBox::activated, this, &ConfigModuleTab::slotEmitChanged); 0629 0630 // "Date Display" group: 0631 mDateDisplay = new QButtonGroup(this); 0632 mDateDisplay->setExclusive(true); 0633 0634 for (int i = 0; i < numDateDisplayConfig; ++i) { 0635 const auto label = KLocalizedString(dateDisplayConfig[i].displayName).untranslatedText(); 0636 0637 QString buttonLabel; 0638 if (label.contains(QLatin1StringView("%1"))) { 0639 buttonLabel = KLocalizedString(dateDisplayConfig[i].displayName) 0640 .subs(DateFormatter::formatCurrentDate(dateDisplayConfig[i].dateDisplay)) 0641 .toString(); // i18n(label, DateFormatter::formatCurrentDate(dateDisplayConfig[i].dateDisplay)); 0642 } else { 0643 buttonLabel = KLocalizedString(dateDisplayConfig[i].displayName).toString(); 0644 } 0645 if (dateDisplayConfig[i].dateDisplay == DateFormatter::Custom) { 0646 auto hbox = new QWidget(this); 0647 auto hboxHBoxLayout = new QHBoxLayout(hbox); 0648 hboxHBoxLayout->setContentsMargins({}); 0649 auto radio = new QRadioButton(buttonLabel, hbox); 0650 hboxHBoxLayout->addWidget(radio); 0651 mDateDisplay->addButton(radio, dateDisplayConfig[i].dateDisplay); 0652 0653 mCustomDateFormatEdit = new QLineEdit(hbox); 0654 KLineEditEventHandler::catchReturnKey(mCustomDateFormatEdit); 0655 hboxHBoxLayout->addWidget(mCustomDateFormatEdit); 0656 mCustomDateFormatEdit->setEnabled(false); 0657 hboxHBoxLayout->setStretchFactor(mCustomDateFormatEdit, 1); 0658 0659 connect(radio, &QAbstractButton::toggled, mCustomDateFormatEdit, &QWidget::setEnabled); 0660 connect(mCustomDateFormatEdit, &QLineEdit::textChanged, this, &ConfigModuleTab::slotEmitChanged); 0661 0662 auto formatHelp = new QLabel(i18n("<qt><a href=\"whatsthis1\">Custom format information...</a></qt>"), hbox); 0663 formatHelp->setContextMenuPolicy(Qt::NoContextMenu); 0664 connect(formatHelp, &QLabel::linkActivated, this, &AppearancePageHeadersTab::slotLinkClicked); 0665 hboxHBoxLayout->addWidget(formatHelp); 0666 0667 mCustomDateWhatsThis = i18n( 0668 "<qt><p><strong>These expressions may be used for the date:" 0669 "</strong></p>" 0670 "<ul>" 0671 "<li>d - the day as a number without a leading zero (1-31)</li>" 0672 "<li>dd - the day as a number with a leading zero (01-31)</li>" 0673 "<li>ddd - the abbreviated day name (Mon - Sun)</li>" 0674 "<li>dddd - the long day name (Monday - Sunday)</li>" 0675 "<li>M - the month as a number without a leading zero (1-12)</li>" 0676 "<li>MM - the month as a number with a leading zero (01-12)</li>" 0677 "<li>MMM - the abbreviated month name (Jan - Dec)</li>" 0678 "<li>MMMM - the long month name (January - December)</li>" 0679 "<li>yy - the year as a two digit number (00-99)</li>" 0680 "<li>yyyy - the year as a four digit number (0000-9999)</li>" 0681 "</ul>" 0682 "<p><strong>These expressions may be used for the time:" 0683 "</strong></p> " 0684 "<ul>" 0685 "<li>h - the hour without a leading zero (0-23 or 1-12 if AM/PM display)</li>" 0686 "<li>hh - the hour with a leading zero (00-23 or 01-12 if AM/PM display)</li>" 0687 "<li>m - the minutes without a leading zero (0-59)</li>" 0688 "<li>mm - the minutes with a leading zero (00-59)</li>" 0689 "<li>s - the seconds without a leading zero (0-59)</li>" 0690 "<li>ss - the seconds with a leading zero (00-59)</li>" 0691 "<li>z - the milliseconds without leading zeroes (0-999)</li>" 0692 "<li>zzz - the milliseconds with leading zeroes (000-999)</li>" 0693 "<li>AP - switch to AM/PM display. AP will be replaced by either \"AM\" or \"PM\".</li>" 0694 "<li>ap - switch to AM/PM display. ap will be replaced by either \"am\" or \"pm\".</li>" 0695 "<li>Z - time zone in numeric form (-0500)</li>" 0696 "</ul>" 0697 "<p><strong>All other input characters will be ignored." 0698 "</strong></p></qt>"); 0699 mCustomDateFormatEdit->setWhatsThis(mCustomDateWhatsThis); 0700 radio->setWhatsThis(mCustomDateWhatsThis); 0701 formLayout->addRow(QString(), hbox); 0702 } else { 0703 auto radio = new QRadioButton(buttonLabel, mDateDisplayBox); 0704 if (i == 0) { 0705 formLayout->addRow(i18n("Date Display:"), radio); 0706 } else { 0707 formLayout->addRow(QString(), radio); 0708 } 0709 mDateDisplay->addButton(radio, dateDisplayConfig[i].dateDisplay); 0710 } 0711 } // end for loop populating mDateDisplay 0712 0713 connect(mDateDisplay, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged); 0714 } 0715 0716 void AppearancePageHeadersTab::slotLinkClicked(const QString &link) 0717 { 0718 if (link == QLatin1StringView("whatsthis1")) { 0719 QWhatsThis::showText(QCursor::pos(), mCustomDateWhatsThis); 0720 } 0721 } 0722 0723 void AppearancePageHeadersTab::slotSelectDefaultAggregation() 0724 { 0725 // Select current default aggregation. 0726 mAggregationComboBox->selectDefault(); 0727 } 0728 0729 void AppearancePageHeadersTab::slotSelectDefaultTheme() 0730 { 0731 // Select current default theme. 0732 mThemeComboBox->selectDefault(); 0733 } 0734 0735 void AppearancePageHeadersTab::doLoadOther() 0736 { 0737 // "General Options": 0738 loadWidget(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem()); 0739 0740 // "Aggregation": 0741 slotSelectDefaultAggregation(); 0742 0743 // "Theme": 0744 slotSelectDefaultTheme(); 0745 0746 // "Date Display": 0747 setDateDisplay(MessageCore::MessageCoreSettings::self()->dateFormat(), MessageCore::MessageCoreSettings::self()->customDateFormat()); 0748 } 0749 0750 void AppearancePageHeadersTab::doLoadFromGlobalSettings() 0751 { 0752 loadWidget(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem()); 0753 // "Aggregation": 0754 slotSelectDefaultAggregation(); 0755 0756 // "Theme": 0757 slotSelectDefaultTheme(); 0758 0759 setDateDisplay(MessageCore::MessageCoreSettings::self()->dateFormat(), MessageCore::MessageCoreSettings::self()->customDateFormat()); 0760 } 0761 0762 void AppearancePageHeadersTab::setDateDisplay(int num, const QString &format) 0763 { 0764 auto dateDisplay = static_cast<DateFormatter::FormatType>(num); 0765 0766 // special case: needs text for the line edit: 0767 if (dateDisplay == DateFormatter::Custom) { 0768 mCustomDateFormatEdit->setText(format); 0769 } 0770 0771 for (int i = 0; i < numDateDisplayConfig; ++i) { 0772 if (dateDisplay == dateDisplayConfig[i].dateDisplay) { 0773 mDateDisplay->button(dateDisplay)->setChecked(true); 0774 return; 0775 } 0776 } 0777 // fell through since none found: 0778 mDateDisplay->button(numDateDisplayConfig - 2)->setChecked(true); // default 0779 } 0780 0781 void AppearancePageHeadersTab::save() 0782 { 0783 saveCheckBox(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem()); 0784 0785 if (KMKernel::self()) { 0786 KMKernel::self()->savePaneSelection(); 0787 } 0788 // "Aggregation" 0789 mAggregationComboBox->writeDefaultConfig(); 0790 0791 // "Theme" 0792 mThemeComboBox->writeDefaultConfig(); 0793 0794 const int dateDisplayID = mDateDisplay->checkedId(); 0795 MessageCore::MessageCoreSettings::self()->setDateFormat(dateDisplayID); 0796 MessageCore::MessageCoreSettings::self()->setCustomDateFormat(mCustomDateFormatEdit->text()); 0797 } 0798 0799 // 0800 // Message Window 0801 // 0802 0803 QString AppearancePageGeneralTab::helpAnchor() const 0804 { 0805 return QStringLiteral("configure-appearance-reader"); 0806 } 0807 0808 AppearancePageGeneralTab::AppearancePageGeneralTab(QWidget *parent) 0809 : ConfigModuleTab(parent) 0810 { 0811 auto topLayout = new QVBoxLayout(this); 0812 0813 auto readerBox = new QGroupBox(i18n("Message Window"), this); 0814 topLayout->addWidget(readerBox); 0815 0816 auto readerBoxLayout = new QVBoxLayout(readerBox); 0817 0818 // "Close message window after replying or forwarding" check box: 0819 populateCheckBox(mCloseAfterReplyOrForwardCheck = new QCheckBox(this), MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem()); 0820 mCloseAfterReplyOrForwardCheck->setToolTip(i18n("Close the standalone message window after replying or forwarding the message")); 0821 readerBoxLayout->addWidget(mCloseAfterReplyOrForwardCheck); 0822 connect(mCloseAfterReplyOrForwardCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0823 0824 mViewerSettings = new MessageViewer::ConfigureWidget; 0825 connect(mViewerSettings, &MessageViewer::ConfigureWidget::settingsChanged, this, &ConfigModuleTab::slotEmitChanged); 0826 readerBoxLayout->addWidget(mViewerSettings); 0827 0828 auto systrayBox = new QGroupBox(i18n("System Tray"), this); 0829 topLayout->addWidget(systrayBox); 0830 0831 auto systrayBoxlayout = new QVBoxLayout(systrayBox); 0832 0833 // "Enable system tray applet" check box 0834 mSystemTrayCheck = new QCheckBox(i18n("Enable system tray icon"), this); 0835 systrayBoxlayout->addWidget(mSystemTrayCheck); 0836 0837 // "Enable start in system tray" check box 0838 mStartInTrayCheck = new QCheckBox(i18n("Start minimized to tray")); 0839 systrayBoxlayout->addWidget(mStartInTrayCheck); 0840 0841 // Dependencies between the two checkboxes 0842 connect(mStartInTrayCheck, &QCheckBox::stateChanged, this, [this](int state) { 0843 if (state == Qt::Checked) { 0844 mSystemTrayCheck->setCheckState(Qt::Checked); 0845 } 0846 slotEmitChanged(); 0847 }); 0848 connect(mSystemTrayCheck, &QCheckBox::stateChanged, this, [this](int state) { 0849 if (state == Qt::Unchecked) { 0850 mStartInTrayCheck->setCheckState(Qt::Unchecked); 0851 } 0852 slotEmitChanged(); 0853 }); 0854 0855 // "Enable system tray applet" check box 0856 mShowNumberInTaskBar = new QCheckBox(i18n("Show unread email in Taskbar"), this); 0857 systrayBoxlayout->addWidget(mShowNumberInTaskBar); 0858 connect(mShowNumberInTaskBar, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged); 0859 0860 topLayout->addStretch(100); // spacer 0861 } 0862 0863 void AppearancePageGeneralTab::doResetToDefaultsOther() 0864 { 0865 } 0866 0867 void AppearancePageGeneralTab::doLoadOther() 0868 { 0869 loadWidget(mSystemTrayCheck, KMailSettings::self()->systemTrayEnabledItem()); 0870 loadWidget(mStartInTrayCheck, KMailSettings::self()->startInTrayItem()); 0871 loadWidget(mShowNumberInTaskBar, KMailSettings::self()->showUnreadInTaskbarItem()); 0872 loadWidget(mCloseAfterReplyOrForwardCheck, MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem()); 0873 mViewerSettings->readConfig(); 0874 } 0875 0876 void AppearancePageGeneralTab::save() 0877 { 0878 saveCheckBox(mSystemTrayCheck, KMailSettings::self()->systemTrayEnabledItem()); 0879 saveCheckBox(mStartInTrayCheck, KMailSettings::self()->startInTrayItem()); 0880 saveCheckBox(mShowNumberInTaskBar, KMailSettings::self()->showUnreadInTaskbarItem()); 0881 KMailSettings::self()->save(); 0882 saveCheckBox(mCloseAfterReplyOrForwardCheck, MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem()); 0883 mViewerSettings->writeConfig(); 0884 } 0885 0886 QString AppearancePageMessageTagTab::helpAnchor() const 0887 { 0888 return QStringLiteral("configure-appearance-messagetag"); 0889 } 0890 0891 TagListWidgetItem::TagListWidgetItem(QListWidget *parent) 0892 : QListWidgetItem(parent) 0893 , mTag(nullptr) 0894 { 0895 } 0896 0897 TagListWidgetItem::TagListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent) 0898 : QListWidgetItem(icon, text, parent) 0899 , mTag(nullptr) 0900 { 0901 } 0902 0903 TagListWidgetItem::~TagListWidgetItem() = default; 0904 0905 void TagListWidgetItem::setKMailTag(const MailCommon::Tag::Ptr &tag) 0906 { 0907 mTag = tag; 0908 } 0909 0910 MailCommon::Tag::Ptr TagListWidgetItem::kmailTag() const 0911 { 0912 return mTag; 0913 } 0914 0915 AppearancePageMessageTagTab::AppearancePageMessageTagTab(QWidget *parent) 0916 : ConfigModuleTab(parent) 0917 { 0918 mPreviousTag = -1; 0919 auto maingrid = new QHBoxLayout(this); 0920 0921 // Lefthand side Listbox and friends 0922 0923 // Groupbox frame 0924 mTagsGroupBox = new QGroupBox(i18n("A&vailable Tags"), this); 0925 maingrid->addWidget(mTagsGroupBox); 0926 auto tageditgrid = new QVBoxLayout(mTagsGroupBox); 0927 0928 // Listbox, add, remove row 0929 auto addremovegrid = new QHBoxLayout(); 0930 tageditgrid->addLayout(addremovegrid); 0931 0932 mTagAddLineEdit = new QLineEdit(mTagsGroupBox); 0933 KLineEditEventHandler::catchReturnKey(mTagAddLineEdit); 0934 addremovegrid->addWidget(mTagAddLineEdit); 0935 0936 mTagAddButton = new QPushButton(mTagsGroupBox); 0937 mTagAddButton->setToolTip(i18n("Add new tag")); 0938 mTagAddButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0939 addremovegrid->addWidget(mTagAddButton); 0940 0941 mTagRemoveButton = new QPushButton(mTagsGroupBox); 0942 mTagRemoveButton->setToolTip(i18n("Remove selected tag")); 0943 mTagRemoveButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0944 addremovegrid->addWidget(mTagRemoveButton); 0945 0946 // Up and down buttons 0947 auto updowngrid = new QHBoxLayout(); 0948 tageditgrid->addLayout(updowngrid); 0949 0950 mTagUpButton = new QPushButton(mTagsGroupBox); 0951 mTagUpButton->setToolTip(i18n("Increase tag priority")); 0952 mTagUpButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); 0953 mTagUpButton->setAutoRepeat(true); 0954 updowngrid->addWidget(mTagUpButton); 0955 0956 mTagDownButton = new QPushButton(mTagsGroupBox); 0957 mTagDownButton->setToolTip(i18n("Decrease tag priority")); 0958 mTagDownButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); 0959 mTagDownButton->setAutoRepeat(true); 0960 updowngrid->addWidget(mTagDownButton); 0961 0962 // Listbox for tag names 0963 auto listboxgrid = new QHBoxLayout(); 0964 tageditgrid->addLayout(listboxgrid); 0965 mTagListBox = new QListWidget(mTagsGroupBox); 0966 mTagListBox->setDragDropMode(QAbstractItemView::InternalMove); 0967 connect(mTagListBox->model(), &QAbstractItemModel::rowsMoved, this, &AppearancePageMessageTagTab::slotRowsMoved); 0968 connect(mTagListBox, &QListWidget::customContextMenuRequested, this, &AppearancePageMessageTagTab::slotCustomMenuRequested); 0969 0970 mTagListBox->setMinimumWidth(150); 0971 listboxgrid->addWidget(mTagListBox); 0972 mTagListBox->setContextMenuPolicy(Qt::CustomContextMenu); 0973 0974 // RHS for individual tag settings 0975 0976 // Extra VBoxLayout for stretchers around settings 0977 auto tagsettinggrid = new QVBoxLayout(); 0978 maingrid->addLayout(tagsettinggrid); 0979 0980 // Groupbox frame 0981 mTagSettingGroupBox = new QGroupBox(i18n("Ta&g Settings"), this); 0982 tagsettinggrid->addWidget(mTagSettingGroupBox); 0983 QList<KActionCollection *> actionCollections; 0984 if (kmkernel->getKMMainWidget()) { 0985 actionCollections = kmkernel->getKMMainWidget()->actionCollections(); 0986 } 0987 0988 auto lay = new QHBoxLayout(mTagSettingGroupBox); 0989 mTagWidget = new MailCommon::TagWidget(actionCollections, this); 0990 lay->addWidget(mTagWidget); 0991 0992 connect(mTagWidget, &TagWidget::changed, this, &AppearancePageMessageTagTab::slotEmitChangeCheck); 0993 0994 // For enabling the add button in case box is non-empty 0995 connect(mTagAddLineEdit, &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotAddLineTextChanged); 0996 0997 // For on-the-fly updating of tag name in editbox 0998 connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); 0999 1000 connect(mTagWidget, &TagWidget::iconNameChanged, this, &AppearancePageMessageTagTab::slotIconNameChanged); 1001 1002 connect(mTagAddLineEdit, &QLineEdit::returnPressed, this, &AppearancePageMessageTagTab::slotAddNewTag); 1003 1004 connect(mTagAddButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotAddNewTag); 1005 1006 connect(mTagRemoveButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotRemoveTag); 1007 1008 connect(mTagUpButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotMoveTagUp); 1009 1010 connect(mTagDownButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotMoveTagDown); 1011 1012 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1013 // Adjust widths for columns 1014 maingrid->setStretchFactor(mTagsGroupBox, 1); 1015 maingrid->setStretchFactor(lay, 1); 1016 tagsettinggrid->addStretch(10); 1017 } 1018 1019 AppearancePageMessageTagTab::~AppearancePageMessageTagTab() = default; 1020 1021 void AppearancePageMessageTagTab::slotEmitChangeCheck() 1022 { 1023 slotEmitChanged(); 1024 } 1025 1026 void AppearancePageMessageTagTab::slotCustomMenuRequested(const QPoint &) 1027 { 1028 const int currentIndex = mTagListBox->currentRow(); 1029 if (currentIndex >= 0) { 1030 QMenu menu(this); 1031 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete"), this, &AppearancePageMessageTagTab::slotRemoveTag); 1032 menu.exec(QCursor::pos()); 1033 } 1034 } 1035 1036 void AppearancePageMessageTagTab::slotRowsMoved(const QModelIndex &, int sourcestart, int sourceEnd, const QModelIndex &, int destinationRow) 1037 { 1038 Q_UNUSED(sourceEnd) 1039 Q_UNUSED(sourcestart) 1040 Q_UNUSED(destinationRow) 1041 updateButtons(); 1042 slotEmitChangeCheck(); 1043 } 1044 1045 void AppearancePageMessageTagTab::updateButtons() 1046 { 1047 const int currentIndex = mTagListBox->currentRow(); 1048 1049 const bool theFirst = (currentIndex == 0); 1050 const bool theLast = (currentIndex >= (int)mTagListBox->count() - 1); 1051 const bool aFilterIsSelected = (currentIndex >= 0); 1052 1053 mTagUpButton->setEnabled(aFilterIsSelected && !theFirst); 1054 mTagDownButton->setEnabled(aFilterIsSelected && !theLast); 1055 } 1056 1057 void AppearancePageMessageTagTab::slotMoveTagUp() 1058 { 1059 const int tmp_index = mTagListBox->currentRow(); 1060 if (tmp_index <= 0) { 1061 return; 1062 } 1063 swapTagsInListBox(tmp_index, tmp_index - 1); 1064 updateButtons(); 1065 } 1066 1067 void AppearancePageMessageTagTab::slotMoveTagDown() 1068 { 1069 const int tmp_index = mTagListBox->currentRow(); 1070 if ((tmp_index < 0) || (tmp_index >= int(mTagListBox->count()) - 1)) { 1071 return; 1072 } 1073 swapTagsInListBox(tmp_index, tmp_index + 1); 1074 updateButtons(); 1075 } 1076 1077 void AppearancePageMessageTagTab::swapTagsInListBox(const int first, const int second) 1078 { 1079 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1080 QListWidgetItem *item = mTagListBox->takeItem(first); 1081 // now selected item is at idx(idx-1), so 1082 // insert the other item at idx, ie. above(below). 1083 mPreviousTag = second; 1084 mTagListBox->insertItem(second, item); 1085 mTagListBox->setCurrentRow(second); 1086 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1087 slotEmitChangeCheck(); 1088 } 1089 1090 void AppearancePageMessageTagTab::slotRecordTagSettings(int aIndex) 1091 { 1092 if ((aIndex < 0) || (aIndex >= int(mTagListBox->count()))) { 1093 return; 1094 } 1095 QListWidgetItem *item = mTagListBox->item(aIndex); 1096 auto tagItem = static_cast<TagListWidgetItem *>(item); 1097 1098 MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag(); 1099 1100 tmp_desc->tagName = tagItem->text(); 1101 mTagWidget->recordTagSettings(tmp_desc); 1102 } 1103 1104 void AppearancePageMessageTagTab::slotUpdateTagSettingWidgets(int aIndex) 1105 { 1106 // Check if selection is valid 1107 if ((aIndex < 0) || (mTagListBox->currentRow() < 0)) { 1108 mTagRemoveButton->setEnabled(false); 1109 mTagUpButton->setEnabled(false); 1110 mTagDownButton->setEnabled(false); 1111 1112 mTagWidget->setEnabled(false); 1113 return; 1114 } 1115 mTagWidget->setEnabled(true); 1116 1117 mTagRemoveButton->setEnabled(true); 1118 mTagUpButton->setEnabled((0 != aIndex)); 1119 mTagDownButton->setEnabled(((int(mTagListBox->count()) - 1) != aIndex)); 1120 QListWidgetItem *item = mTagListBox->currentItem(); 1121 auto tagItem = static_cast<TagListWidgetItem *>(item); 1122 MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag(); 1123 1124 disconnect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); 1125 1126 mTagWidget->tagNameLineEdit()->setEnabled(!tmp_desc->isImmutable); 1127 mTagWidget->tagNameLineEdit()->setText(tmp_desc->tagName); 1128 connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); 1129 1130 mTagWidget->setTagTextColor(tmp_desc->textColor); 1131 1132 mTagWidget->setTagBackgroundColor(tmp_desc->backgroundColor); 1133 1134 mTagWidget->setTagTextFormat(tmp_desc->isBold, tmp_desc->isItalic); 1135 1136 mTagWidget->iconButton()->setEnabled(!tmp_desc->isImmutable); 1137 mTagWidget->iconButton()->setIcon(tmp_desc->iconName); 1138 1139 mTagWidget->keySequenceWidget()->setEnabled(true); 1140 mTagWidget->keySequenceWidget()->setKeySequence(tmp_desc->shortcut, KKeySequenceWidget::NoValidate); 1141 1142 mTagWidget->inToolBarCheck()->setEnabled(true); 1143 mTagWidget->inToolBarCheck()->setChecked(tmp_desc->inToolbar); 1144 } 1145 1146 void AppearancePageMessageTagTab::slotSelectionChanged() 1147 { 1148 mEmitChanges = false; 1149 slotRecordTagSettings(mPreviousTag); 1150 slotUpdateTagSettingWidgets(mTagListBox->currentRow()); 1151 mPreviousTag = mTagListBox->currentRow(); 1152 mEmitChanges = true; 1153 } 1154 1155 void AppearancePageMessageTagTab::slotRemoveTag() 1156 { 1157 const int tmp_index = mTagListBox->currentRow(); 1158 if (tmp_index >= 0) { 1159 if (KMessageBox::ButtonCode::PrimaryAction 1160 == KMessageBox::questionTwoActions(this, 1161 i18n("Do you want to remove tag \'%1\'?", mTagListBox->item(mTagListBox->currentRow())->text()), 1162 i18nc("@title:window", "Remove Tag"), 1163 KStandardGuiItem::remove(), 1164 KStandardGuiItem::cancel())) { 1165 QListWidgetItem *item = mTagListBox->takeItem(mTagListBox->currentRow()); 1166 auto tagItem = static_cast<TagListWidgetItem *>(item); 1167 MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag(); 1168 if (tmp_desc->tag().isValid()) { 1169 new Akonadi::TagDeleteJob(tmp_desc->tag()); 1170 } else { 1171 qCWarning(KMAIL_LOG) << "Can't remove tag with invalid akonadi tag"; 1172 } 1173 mPreviousTag = -1; 1174 1175 // Before deleting the current item, make sure the selectionChanged signal 1176 // is disconnected, so that the widgets will not get updated while the 1177 // deletion takes place. 1178 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1179 1180 delete item; 1181 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1182 1183 slotSelectionChanged(); 1184 slotEmitChangeCheck(); 1185 } 1186 } 1187 } 1188 1189 void AppearancePageMessageTagTab::slotDeleteTagJob(KJob *job) 1190 { 1191 if (job->error()) { 1192 qCWarning(KMAIL_LOG) << "Failed to delete tag " << job->errorString(); 1193 } 1194 } 1195 1196 void AppearancePageMessageTagTab::slotNameLineTextChanged(const QString &aText) 1197 { 1198 // If deleted all, leave the first character for the sake of not having an 1199 // empty tag name 1200 if (aText.isEmpty() && !mTagListBox->currentItem()) { 1201 return; 1202 } 1203 1204 const int count = mTagListBox->count(); 1205 for (int i = 0; i < count; ++i) { 1206 if (mTagListBox->item(i)->text() == aText) { 1207 KMessageBox::error(this, i18n("We cannot create tag. A tag with same name already exists.")); 1208 disconnect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); 1209 mTagWidget->tagNameLineEdit()->setText(mTagListBox->currentItem()->text()); 1210 connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged); 1211 return; 1212 } 1213 } 1214 1215 // Disconnect so the tag information is not saved and reloaded with every 1216 // letter 1217 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1218 1219 mTagListBox->currentItem()->setText(aText); 1220 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1221 } 1222 1223 void AppearancePageMessageTagTab::slotIconNameChanged(const QString &iconName) 1224 { 1225 mTagListBox->currentItem()->setIcon(QIcon::fromTheme(iconName)); 1226 } 1227 1228 void AppearancePageMessageTagTab::slotAddLineTextChanged(const QString &aText) 1229 { 1230 mTagAddButton->setEnabled(!aText.trimmed().isEmpty()); 1231 } 1232 1233 void AppearancePageMessageTagTab::slotAddNewTag() 1234 { 1235 const QString newTagName = mTagAddLineEdit->text().trimmed(); 1236 if (newTagName.isEmpty()) { 1237 return; 1238 } 1239 const int count = mTagListBox->count(); 1240 for (int i = 0; i < count; ++i) { 1241 if (mTagListBox->item(i)->text() == newTagName) { 1242 KMessageBox::error(this, i18n("We cannot create tag. A tag with same name already exists.")); 1243 return; 1244 } 1245 } 1246 1247 const int tmp_priority = mTagListBox->count(); 1248 1249 MailCommon::Tag::Ptr tag(Tag::createDefaultTag(newTagName)); 1250 tag->priority = tmp_priority; 1251 1252 slotEmitChangeCheck(); 1253 auto newItem = new TagListWidgetItem(QIcon::fromTheme(tag->iconName), newTagName, mTagListBox); 1254 newItem->setKMailTag(tag); 1255 mTagListBox->addItem(newItem); 1256 mTagListBox->setCurrentItem(newItem); 1257 mTagAddLineEdit->clear(); 1258 } 1259 1260 void AppearancePageMessageTagTab::doLoadFromGlobalSettings() 1261 { 1262 mTagListBox->clear(); 1263 1264 auto fetchJob = new Akonadi::TagFetchJob(this); 1265 fetchJob->fetchScope().fetchAttribute<Akonadi::TagAttribute>(); 1266 connect(fetchJob, &KJob::result, this, &AppearancePageMessageTagTab::slotTagsFetched); 1267 } 1268 1269 void AppearancePageMessageTagTab::slotTagsFetched(KJob *job) 1270 { 1271 if (job->error()) { 1272 qCWarning(KMAIL_LOG) << "Failed to load tags " << job->errorString(); 1273 return; 1274 } 1275 auto fetchJob = static_cast<Akonadi::TagFetchJob *>(job); 1276 1277 QList<MailCommon::TagPtr> msgTagList; 1278 const Akonadi::Tag::List tagList = fetchJob->tags(); 1279 msgTagList.reserve(tagList.count()); 1280 for (const Akonadi::Tag &akonadiTag : tagList) { 1281 MailCommon::Tag::Ptr tag = MailCommon::Tag::fromAkonadi(akonadiTag); 1282 msgTagList.append(tag); 1283 } 1284 1285 std::sort(msgTagList.begin(), msgTagList.end(), MailCommon::Tag::compare); 1286 1287 for (const MailCommon::Tag::Ptr &tag : std::as_const(msgTagList)) { 1288 auto newItem = new TagListWidgetItem(QIcon::fromTheme(tag->iconName), tag->tagName, mTagListBox); 1289 newItem->setKMailTag(tag); 1290 if (tag->priority == -1) { 1291 tag->priority = mTagListBox->count() - 1; 1292 } 1293 } 1294 1295 // Disconnect so that insertItem's do not trigger an update procedure 1296 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1297 1298 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged); 1299 1300 slotUpdateTagSettingWidgets(-1); 1301 // Needed since the previous function doesn't affect add button 1302 mTagAddButton->setEnabled(false); 1303 1304 // Save the original list 1305 mOriginalMsgTagList.clear(); 1306 for (const MailCommon::TagPtr &tag : std::as_const(msgTagList)) { 1307 mOriginalMsgTagList.append(MailCommon::TagPtr(new MailCommon::Tag(*tag))); 1308 } 1309 } 1310 1311 void AppearancePageMessageTagTab::save() 1312 { 1313 const int currentRow = mTagListBox->currentRow(); 1314 if (currentRow < 0) { 1315 return; 1316 } 1317 1318 const int count = mTagListBox->count(); 1319 if (!count) { 1320 return; 1321 } 1322 1323 QListWidgetItem *item = mTagListBox->currentItem(); 1324 if (!item) { 1325 return; 1326 } 1327 slotRecordTagSettings(currentRow); 1328 const int numberOfMsgTagList = count; 1329 for (int i = 0; i < numberOfMsgTagList; ++i) { 1330 auto tagItem = static_cast<TagListWidgetItem *>(mTagListBox->item(i)); 1331 if ((i >= mOriginalMsgTagList.count()) || *(tagItem->kmailTag()) != *(mOriginalMsgTagList[i])) { 1332 MailCommon::Tag::Ptr tag = tagItem->kmailTag(); 1333 tag->priority = i; 1334 Akonadi::Tag akonadiTag = tag->saveToAkonadi(); 1335 if ((*tag).id() > 0) { 1336 akonadiTag.setId((*tag).id()); 1337 } 1338 if (akonadiTag.isValid()) { 1339 new Akonadi::TagModifyJob(akonadiTag); 1340 } else { 1341 new Akonadi::TagCreateJob(akonadiTag); 1342 } 1343 } 1344 } 1345 } 1346 1347 #include "moc_configureappearancepage.cpp"