1 /*
2 SPDX-FileCopyrightText: 2013-2021 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include "configureappearancepage.h"
8 #include <PimCommon/ConfigureImmutableWidgetUtils>
9 using namespace PimCommon::ConfigureImmutableWidgetUtils;
10 #include "configuredialog/colorlistbox.h"
11 #include "kmkernel.h"
12 #include "messagelistsettings.h"
13 #include <Libkdepim/LineEditCatchReturnKey>
14 #include <MailCommon/TagWidget>
15 #include <MessageList/AggregationComboBox>
16 #include <MessageList/AggregationConfigButton>
17 #include <MessageList/ThemeComboBox>
18 #include <MessageList/ThemeConfigButton>
19
20 #include "util.h"
21 #include <MailCommon/FolderTreeWidget>
22
23 #include "kmmainwidget.h"
24
25 #include "mailcommonsettings_base.h"
26
27 #include <MessageViewer/ConfigureWidget>
28 #include <MessageViewer/MessageViewerSettings>
29
30 #include "settings/kmailsettings.h"
31 #include <MessageCore/ColorUtil>
32 #include <MessageCore/MessageCoreSettings>
33 #include <MessageList/MessageListUtil>
34
35 #include <MailCommon/MailUtil>
36
37 #include <Akonadi/Tag>
38 #include <Akonadi/TagAttribute>
39 #include <Akonadi/TagCreateJob>
40 #include <Akonadi/TagDeleteJob>
41 #include <Akonadi/TagFetchJob>
42 #include <Akonadi/TagFetchScope>
43 #include <Akonadi/TagModifyJob>
44
45 #include "kmail_debug.h"
46 #include <KColorScheme>
47 #include <KFontChooser>
48 #include <KIconButton>
49 #include <KKeySequenceWidget>
50 #include <KLocalizedString>
51 #include <KMessageBox>
52 #include <KSeparator>
53 #include <QHBoxLayout>
54 #include <QIcon>
55 #include <QLineEdit>
56
57 #include <KMime/DateFormatter>
58
59 #include <QButtonGroup>
60 #include <QCheckBox>
61 #include <QFontDatabase>
62 #include <QFormLayout>
63 #include <QGroupBox>
64 #include <QLabel>
65 #include <QRadioButton>
66 #include <QSpinBox>
67 #include <QVBoxLayout>
68 #include <QWhatsThis>
69
70 using KMime::DateFormatter;
71 using namespace MailCommon;
72
helpAnchor() const73 QString AppearancePage::helpAnchor() const
74 {
75 return QStringLiteral("configure-appearance");
76 }
77
AppearancePage(QWidget * parent,const QVariantList & args)78 AppearancePage::AppearancePage(QWidget *parent, const QVariantList &args)
79 : ConfigModuleWithTabs(parent, args)
80 {
81 //
82 // "General" tab:
83 //
84 auto readerTab = new AppearancePageGeneralTab();
85 addTab(readerTab, i18n("General"));
86 addConfig(MessageViewer::MessageViewerSettings::self(), readerTab);
87
88 //
89 // "Fonts" tab:
90 //
91 auto fontsTab = new AppearancePageFontsTab();
92 addTab(fontsTab, i18n("Fonts"));
93
94 //
95 // "Colors" tab:
96 //
97 auto colorsTab = new AppearancePageColorsTab();
98 addTab(colorsTab, i18n("Colors"));
99
100 //
101 // "Layout" tab:
102 //
103 auto layoutTab = new AppearancePageLayoutTab();
104 addTab(layoutTab, i18n("Layout"));
105
106 //
107 // "Headers" tab:
108 //
109 auto headersTab = new AppearancePageHeadersTab();
110 addTab(headersTab, i18n("Message List"));
111
112 //
113 // "Message Tag" tab:
114 //
115 auto messageTagTab = new AppearancePageMessageTagTab();
116 addTab(messageTagTab, i18n("Message Tags"));
117 }
118
helpAnchor() const119 QString AppearancePageFontsTab::helpAnchor() const
120 {
121 return QStringLiteral("configure-appearance-fonts");
122 }
123
124 static const struct {
125 const char *configName;
126 const char *displayName;
127 bool enableFamilyAndSize;
128 bool onlyFixed;
129 } fontNames[] = {
130 {"body-font", I18N_NOOP("Message Body"), true, false},
131 {"MessageListFont", I18N_NOOP("Message List"), true, false},
132 {"UnreadMessageFont", I18N_NOOP("Message List - Unread Messages"), false, false},
133 {"ImportantMessageFont", I18N_NOOP("Message List - Important Messages"), false, false},
134 {"TodoMessageFont", I18N_NOOP("Message List - Action Item Messages"), false, false},
135 {"fixed-font", I18N_NOOP("Fixed Width Font"), true, true},
136 {"composer-font", I18N_NOOP("Composer"), true, false},
137 {"print-font", I18N_NOOP("Printing Output"), true, false},
138 };
139 static const int numFontNames = sizeof fontNames / sizeof *fontNames;
140
AppearancePageFontsTab(QWidget * parent)141 AppearancePageFontsTab::AppearancePageFontsTab(QWidget *parent)
142 : ConfigModuleTab(parent)
143 {
144 assert(numFontNames == sizeof mFont / sizeof *mFont);
145
146 // "Use custom fonts" checkbox, followed by <hr>
147 auto vlay = new QVBoxLayout(this);
148 mCustomFontCheck = new QCheckBox(i18n("&Use custom fonts"), this);
149 vlay->addWidget(mCustomFontCheck);
150 vlay->addWidget(new KSeparator(Qt::Horizontal, this));
151 connect(mCustomFontCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
152
153 // "font location" combo box and label:
154 auto hlay = new QHBoxLayout(); // inherites spacing
155 vlay->addLayout(hlay);
156 mFontLocationCombo = new QComboBox(this);
157 mFontLocationCombo->setEnabled(false); // !mCustomFontCheck->isChecked()
158
159 QStringList fontDescriptions;
160 fontDescriptions.reserve(numFontNames);
161 for (int i = 0; i < numFontNames; ++i) {
162 fontDescriptions << i18n(fontNames[i].displayName);
163 }
164 mFontLocationCombo->addItems(fontDescriptions);
165
166 auto label = new QLabel(i18n("Apply &to:"), this);
167 label->setBuddy(mFontLocationCombo);
168 label->setEnabled(false); // since !mCustomFontCheck->isChecked()
169 hlay->addWidget(label);
170
171 hlay->addWidget(mFontLocationCombo);
172 hlay->addStretch(10);
173 mFontChooser = new KFontChooser(KFontChooser::DisplayFrame, this);
174 mFontChooser->setMinVisibleItems(4);
175 mFontChooser->setEnabled(false); // since !mCustomFontCheck->isChecked()
176 vlay->addWidget(mFontChooser);
177 connect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged);
178
179 // {en,dis}able widgets depending on the state of mCustomFontCheck:
180 connect(mCustomFontCheck, &QAbstractButton::toggled, label, &QWidget::setEnabled);
181 connect(mCustomFontCheck, &QAbstractButton::toggled, mFontLocationCombo, &QWidget::setEnabled);
182 connect(mCustomFontCheck, &QAbstractButton::toggled, mFontChooser, &QWidget::setEnabled);
183 // load the right font settings into mFontChooser:
184 connect(mFontLocationCombo, &QComboBox::activated, this, &AppearancePageFontsTab::slotFontSelectorChanged);
185 }
186
slotFontSelectorChanged(int index)187 void AppearancePageFontsTab::slotFontSelectorChanged(int index)
188 {
189 qCDebug(KMAIL_LOG) << "slotFontSelectorChanged() called";
190 if (index < 0 || index >= mFontLocationCombo->count()) {
191 return; // Should never happen, but it is better to check.
192 }
193
194 // Save current fontselector setting before we install the new:
195 if (mActiveFontIndex == 0) {
196 mFont[0] = mFontChooser->font();
197 // hardcode the family and size of "message body" dependent fonts:
198 for (int i = 0; i < numFontNames; ++i) {
199 if (!fontNames[i].enableFamilyAndSize) {
200 // ### shall we copy the font and set the save and re-set
201 // {regular,italic,bold,bold italic} property or should we
202 // copy only family and pointSize?
203 mFont[i].setFamily(mFont[0].family());
204 mFont[i].setPointSize /*Float?*/ (mFont[0].pointSize /*Float?*/ ());
205 }
206 }
207 } else if (mActiveFontIndex > 0) {
208 mFont[mActiveFontIndex] = mFontChooser->font();
209 }
210 mActiveFontIndex = index;
211
212 // Disonnect so the "Apply" button is not activated by the change
213 disconnect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged);
214
215 // Display the new setting:
216 mFontChooser->setFont(mFont[index], fontNames[index].onlyFixed);
217
218 connect(mFontChooser, &KFontChooser::fontSelected, this, &ConfigModuleTab::slotEmitChanged);
219
220 // Disable Family and Size list if we have selected a quote font:
221 mFontChooser->enableColumn(KFontChooser::FamilyList | KFontChooser::SizeList, fontNames[index].enableFamilyAndSize);
222 }
223
doLoadOther()224 void AppearancePageFontsTab::doLoadOther()
225 {
226 if (KMKernel::self()) {
227 KConfigGroup fonts(KMKernel::self()->config(), "Fonts");
228
229 mFont[0] = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
230 QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
231
232 for (int i = 0; i < numFontNames; ++i) {
233 const QString configName = QLatin1String(fontNames[i].configName);
234 if (configName == QLatin1String("MessageListFont")) {
235 mFont[i] = MessageList::MessageListSettings::self()->messageListFont();
236 } else if (configName == QLatin1String("UnreadMessageFont")) {
237 mFont[i] = MessageList::MessageListSettings::self()->unreadMessageFont();
238 } else if (configName == QLatin1String("ImportantMessageFont")) {
239 mFont[i] = MessageList::MessageListSettings::self()->importantMessageFont();
240 } else if (configName == QLatin1String("TodoMessageFont")) {
241 mFont[i] = MessageList::MessageListSettings::self()->todoMessageFont();
242 } else {
243 mFont[i] = fonts.readEntry(configName, (fontNames[i].onlyFixed) ? fixedFont : mFont[0]);
244 }
245 }
246 mCustomFontCheck->setChecked(!MessageCore::MessageCoreSettings::self()->useDefaultFonts());
247 mFontLocationCombo->setCurrentIndex(0);
248 slotFontSelectorChanged(0);
249 } else {
250 setEnabled(false);
251 }
252 }
253
save()254 void AppearancePageFontsTab::save()
255 {
256 if (KMKernel::self()) {
257 KConfigGroup fonts(KMKernel::self()->config(), "Fonts");
258
259 // read the current font (might have been modified)
260 if (mActiveFontIndex >= 0) {
261 mFont[mActiveFontIndex] = mFontChooser->font();
262 }
263
264 const bool customFonts = mCustomFontCheck->isChecked();
265 MessageCore::MessageCoreSettings::self()->setUseDefaultFonts(!customFonts);
266
267 for (int i = 0; i < numFontNames; ++i) {
268 const QString configName = QLatin1String(fontNames[i].configName);
269 if (customFonts && configName == QLatin1String("MessageListFont")) {
270 MessageList::MessageListSettings::self()->setMessageListFont(mFont[i]);
271 } else if (customFonts && configName == QLatin1String("UnreadMessageFont")) {
272 MessageList::MessageListSettings::self()->setUnreadMessageFont(mFont[i]);
273 } else if (customFonts && configName == QLatin1String("ImportantMessageFont")) {
274 MessageList::MessageListSettings::self()->setImportantMessageFont(mFont[i]);
275 } else if (customFonts && configName == QLatin1String("TodoMessageFont")) {
276 MessageList::MessageListSettings::self()->setTodoMessageFont(mFont[i]);
277 } else {
278 if (customFonts || fonts.hasKey(configName)) {
279 // Don't write font info when we use default fonts, but write
280 // if it's already there:
281 fonts.writeEntry(configName, mFont[i]);
282 }
283 }
284 }
285 }
286 }
287
doResetToDefaultsOther()288 void AppearancePageFontsTab::doResetToDefaultsOther()
289 {
290 mCustomFontCheck->setChecked(false);
291 }
292
helpAnchor() const293 QString AppearancePageColorsTab::helpAnchor() const
294 {
295 return QStringLiteral("configure-appearance-colors");
296 }
297
298 static const struct {
299 const char *configName;
300 const char *displayName;
301 } colorNames[] = { // adjust doLoadOther if you change this:
302 {"QuotedText1", I18N_NOOP("Quoted Text - First Level")},
303 {"QuotedText2", I18N_NOOP("Quoted Text - Second Level")},
304 {"QuotedText3", I18N_NOOP("Quoted Text - Third Level")},
305 {"LinkColor", I18N_NOOP("Link")},
306 {"UnreadMessageColor", I18N_NOOP("Unread Message")},
307 {"ImportantMessageColor", I18N_NOOP("Important Message")},
308 {"TodoMessageColor", I18N_NOOP("Action Item Message")},
309 {"ColorbarBackgroundPlain", I18N_NOOP("HTML Status Bar Background - No HTML Message")},
310 {"ColorbarForegroundPlain", I18N_NOOP("HTML Status Bar Foreground - No HTML Message")},
311 {"ColorbarBackgroundHTML", I18N_NOOP("HTML Status Bar Background - HTML Message")},
312 {"ColorbarForegroundHTML", I18N_NOOP("HTML Status Bar Foreground - HTML Message")}};
313 static const int numColorNames = sizeof colorNames / sizeof *colorNames;
314
AppearancePageColorsTab(QWidget * parent)315 AppearancePageColorsTab::AppearancePageColorsTab(QWidget *parent)
316 : ConfigModuleTab(parent)
317 {
318 // "use custom colors" check box
319 auto vlay = new QVBoxLayout(this);
320 mCustomColorCheck = new QCheckBox(i18n("&Use custom colors"), this);
321 vlay->addWidget(mCustomColorCheck);
322 connect(mCustomColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
323
324 mUseInlineStyle = new QCheckBox(i18n("&Do not change color from original HTML mail"), this);
325 vlay->addWidget(mUseInlineStyle);
326 connect(mUseInlineStyle, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
327
328 // color list box:
329 mColorList = new ColorListBox(this);
330 mColorList->setEnabled(false); // since !mCustomColorCheck->isChecked()
331 for (int i = 0; i < numColorNames; ++i) {
332 mColorList->addColor(i18n(colorNames[i].displayName));
333 }
334 vlay->addWidget(mColorList, 1);
335
336 // "recycle colors" check box:
337 mRecycleColorCheck = new QCheckBox(i18n("Recycle colors on deep "ing"), this);
338 mRecycleColorCheck->setEnabled(false);
339 vlay->addWidget(mRecycleColorCheck);
340 connect(mRecycleColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
341
342 // close to quota threshold
343 auto hbox = new QHBoxLayout();
344 vlay->addLayout(hbox);
345 auto l = new QLabel(i18n("Close to quota threshold:"), this);
346 hbox->addWidget(l);
347 mCloseToQuotaThreshold = new QSpinBox(this);
348 mCloseToQuotaThreshold->setRange(0, 100);
349 mCloseToQuotaThreshold->setSingleStep(1);
350 connect(mCloseToQuotaThreshold, &QSpinBox::valueChanged, this, &ConfigModuleTab::slotEmitChanged);
351 mCloseToQuotaThreshold->setSuffix(i18n("%"));
352
353 hbox->addWidget(mCloseToQuotaThreshold);
354 hbox->addWidget(new QWidget(this), 2);
355
356 // {en,dir}able widgets depending on the state of mCustomColorCheck:
357 connect(mCustomColorCheck, &QAbstractButton::toggled, mColorList, &QWidget::setEnabled);
358 connect(mCustomColorCheck, &QAbstractButton::toggled, mRecycleColorCheck, &QWidget::setEnabled);
359 connect(mCustomColorCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
360 connect(mColorList, &ColorListBox::changed, this, &ConfigModuleTab::slotEmitChanged);
361 }
362
doLoadOther()363 void AppearancePageColorsTab::doLoadOther()
364 {
365 mCustomColorCheck->setChecked(!MessageCore::MessageCoreSettings::self()->useDefaultColors());
366 mUseInlineStyle->setChecked(MessageCore::MessageCoreSettings::self()->useRealHtmlMailColor());
367 mRecycleColorCheck->setChecked(MessageViewer::MessageViewerSettings::self()->recycleQuoteColors());
368 mCloseToQuotaThreshold->setValue(KMailSettings::self()->closeToQuotaThreshold());
369 loadColor(true);
370 }
371
loadColor(bool loadFromConfig)372 void AppearancePageColorsTab::loadColor(bool loadFromConfig)
373 {
374 if (KMKernel::self()) {
375 KConfigGroup reader(KMKernel::self()->config(), "Reader");
376
377 static const QColor defaultColor[numColorNames] = {
378 MessageCore::ColorUtil::self()->quoteLevel1DefaultTextColor(),
379 MessageCore::ColorUtil::self()->quoteLevel2DefaultTextColor(),
380 MessageCore::ColorUtil::self()->quoteLevel3DefaultTextColor(),
381 MessageCore::ColorUtil::self()->linkColor(), // link
382 MessageList::Util::unreadDefaultMessageColor(), // unread mgs
383 MessageList::Util::importantDefaultMessageColor(), // important msg
384 MessageList::Util::todoDefaultMessageColor(), // action item mgs
385 Qt::lightGray, // colorbar plain bg
386 Qt::black, // colorbar plain fg
387 Qt::black, // colorbar html bg
388 Qt::white // colorbar html fg
389 };
390
391 for (int i = 0; i < numColorNames; ++i) {
392 if (loadFromConfig) {
393 const QString configName = QLatin1String(colorNames[i].configName);
394 if (configName == QLatin1String("UnreadMessageColor")) {
395 mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->unreadMessageColor());
396 } else if (configName == QLatin1String("ImportantMessageColor")) {
397 mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->importantMessageColor());
398 } else if (configName == QLatin1String("TodoMessageColor")) {
399 mColorList->setColorSilently(i, MessageList::MessageListSettings::self()->todoMessageColor());
400 } else {
401 mColorList->setColorSilently(i, reader.readEntry(configName, defaultColor[i]));
402 }
403 } else {
404 mColorList->setColorSilently(i, defaultColor[i]);
405 }
406 }
407 } else {
408 setEnabled(false);
409 }
410 }
411
doResetToDefaultsOther()412 void AppearancePageColorsTab::doResetToDefaultsOther()
413 {
414 mCustomColorCheck->setChecked(false);
415 mUseInlineStyle->setChecked(false);
416 mRecycleColorCheck->setChecked(false);
417 mCloseToQuotaThreshold->setValue(80);
418 loadColor(false);
419 }
420
save()421 void AppearancePageColorsTab::save()
422 {
423 if (!KMKernel::self()) {
424 return;
425 }
426 KConfigGroup reader(KMKernel::self()->config(), "Reader");
427 bool customColors = mCustomColorCheck->isChecked();
428 MessageCore::MessageCoreSettings::self()->setUseDefaultColors(!customColors);
429 MessageCore::MessageCoreSettings::self()->setUseRealHtmlMailColor(mUseInlineStyle->isChecked());
430
431 for (int i = 0; i < numColorNames; ++i) {
432 const QString configName = QLatin1String(colorNames[i].configName);
433 if (customColors && configName == QLatin1String("UnreadMessageColor")) {
434 MessageList::MessageListSettings::self()->setUnreadMessageColor(mColorList->color(i));
435 } else if (customColors && configName == QLatin1String("ImportantMessageColor")) {
436 MessageList::MessageListSettings::self()->setImportantMessageColor(mColorList->color(i));
437 } else if (customColors && configName == QLatin1String("TodoMessageColor")) {
438 MessageList::MessageListSettings::self()->setTodoMessageColor(mColorList->color(i));
439 } else {
440 if (customColors || reader.hasKey(configName)) {
441 reader.writeEntry(configName, mColorList->color(i));
442 }
443 }
444 }
445 MessageViewer::MessageViewerSettings::self()->setRecycleQuoteColors(mRecycleColorCheck->isChecked());
446 KMailSettings::self()->setCloseToQuotaThreshold(mCloseToQuotaThreshold->value());
447 }
448
helpAnchor() const449 QString AppearancePageLayoutTab::helpAnchor() const
450 {
451 return QStringLiteral("configure-appearance-layout");
452 }
453
AppearancePageLayoutTab(QWidget * parent)454 AppearancePageLayoutTab::AppearancePageLayoutTab(QWidget *parent)
455 : ConfigModuleTab(parent)
456 {
457 auto vlay = new QVBoxLayout(this);
458
459 // "folder list" radio buttons:
460 populateButtonGroup(mFolderListGroupBox = new QGroupBox(this),
461 mFolderListGroup = new QButtonGroup(this),
462 Qt::Vertical,
463 KMailSettings::self()->folderListItem());
464 vlay->addWidget(mFolderListGroupBox);
465 connect(mFolderListGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged);
466
467 auto folderCBHLayout = new QHBoxLayout;
468 mFolderQuickSearchCB = new QCheckBox(i18n("Show folder quick search field"), this);
469 connect(mFolderQuickSearchCB, &QAbstractButton::toggled, this, &ConfigModuleTab::slotEmitChanged);
470 folderCBHLayout->addWidget(mFolderQuickSearchCB);
471 vlay->addLayout(folderCBHLayout);
472
473 // "favorite folders view mode" radio buttons:
474 mFavoriteFoldersViewGroupBox = new QGroupBox(this);
475 mFavoriteFoldersViewGroupBox->setTitle(i18n("Show Favorite Folders View"));
476 mFavoriteFoldersViewGroupBox->setLayout(new QVBoxLayout());
477 mFavoriteFoldersViewGroup = new QButtonGroup(this);
478 connect(mFavoriteFoldersViewGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged);
479
480 auto favoriteFoldersViewHiddenRadio = new QRadioButton(i18n("Never"), mFavoriteFoldersViewGroupBox);
481 mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewHiddenRadio,
482 static_cast<int>(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode));
483 mFavoriteFoldersViewGroupBox->layout()->addWidget(favoriteFoldersViewHiddenRadio);
484
485 auto favoriteFoldersViewIconsRadio = new QRadioButton(i18n("As icons"), mFavoriteFoldersViewGroupBox);
486 mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewIconsRadio,
487 static_cast<int>(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode));
488 mFavoriteFoldersViewGroupBox->layout()->addWidget(favoriteFoldersViewIconsRadio);
489
490 auto favoriteFoldersViewListRadio = new QRadioButton(i18n("As list"), mFavoriteFoldersViewGroupBox);
491 mFavoriteFoldersViewGroup->addButton(favoriteFoldersViewListRadio,
492 static_cast<int>(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode));
493 mFavoriteFoldersViewGroupBox->layout()->addWidget(favoriteFoldersViewListRadio);
494
495 vlay->addWidget(mFavoriteFoldersViewGroupBox);
496
497 // "folder tooltips" radio buttons:
498 mFolderToolTipsGroupBox = new QGroupBox(this);
499 mFolderToolTipsGroupBox->setTitle(i18n("Folder Tooltips"));
500 mFolderToolTipsGroupBox->setLayout(new QVBoxLayout());
501 mFolderToolTipsGroup = new QButtonGroup(this);
502 connect(mFolderToolTipsGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged);
503
504 auto folderToolTipsAlwaysRadio = new QRadioButton(i18n("Always"), mFolderToolTipsGroupBox);
505 mFolderToolTipsGroup->addButton(folderToolTipsAlwaysRadio, static_cast<int>(FolderTreeWidget::DisplayAlways));
506 mFolderToolTipsGroupBox->layout()->addWidget(folderToolTipsAlwaysRadio);
507
508 auto folderToolTipsNeverRadio = new QRadioButton(i18n("Never"), mFolderToolTipsGroupBox);
509 mFolderToolTipsGroup->addButton(folderToolTipsNeverRadio, static_cast<int>(FolderTreeWidget::DisplayNever));
510 mFolderToolTipsGroupBox->layout()->addWidget(folderToolTipsNeverRadio);
511
512 vlay->addWidget(mFolderToolTipsGroupBox);
513
514 // "show reader window" radio buttons:
515 populateButtonGroup(mReaderWindowModeGroupBox = new QGroupBox(this),
516 mReaderWindowModeGroup = new QButtonGroup(this),
517 Qt::Vertical,
518 KMailSettings::self()->readerWindowModeItem());
519 vlay->addWidget(mReaderWindowModeGroupBox);
520
521 connect(mReaderWindowModeGroup, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged);
522
523 vlay->addStretch(10); // spacer
524 }
525
doLoadOther()526 void AppearancePageLayoutTab::doLoadOther()
527 {
528 loadWidget(mFolderListGroupBox, mFolderListGroup, KMailSettings::self()->folderListItem());
529 loadWidget(mReaderWindowModeGroupBox, mReaderWindowModeGroup, KMailSettings::self()->readerWindowModeItem());
530 if (KMKernel::self()) {
531 loadWidget(mFavoriteFoldersViewGroupBox, mFavoriteFoldersViewGroup, KMKernel::self()->mailCommonSettings()->favoriteCollectionViewModeItem());
532 }
533 loadWidget(mFolderQuickSearchCB, KMailSettings::self()->enableFolderQuickSearchItem());
534 const int checkedFolderToolTipsPolicy = KMailSettings::self()->toolTipDisplayPolicy();
535 if (checkedFolderToolTipsPolicy >= 0) {
536 mFolderToolTipsGroup->button(checkedFolderToolTipsPolicy)->setChecked(true);
537 }
538 }
539
save()540 void AppearancePageLayoutTab::save()
541 {
542 saveButtonGroup(mFolderListGroup, KMailSettings::self()->folderListItem());
543 saveButtonGroup(mReaderWindowModeGroup, KMailSettings::self()->readerWindowModeItem());
544 if (KMKernel::self()) {
545 saveButtonGroup(mFavoriteFoldersViewGroup, KMKernel::self()->mailCommonSettings()->favoriteCollectionViewModeItem());
546 }
547 saveCheckBox(mFolderQuickSearchCB, KMailSettings::self()->enableFolderQuickSearchItem());
548 KMailSettings::self()->setToolTipDisplayPolicy(mFolderToolTipsGroup->checkedId());
549 }
550
551 //
552 // Appearance Message List
553 //
554
helpAnchor() const555 QString AppearancePageHeadersTab::helpAnchor() const
556 {
557 return QStringLiteral("configure-appearance-headers");
558 }
559
560 static const struct {
561 const char *displayName;
562 DateFormatter::FormatType dateDisplay;
563 } dateDisplayConfig[] = {{I18N_NOOP("Sta&ndard format (%1)"), KMime::DateFormatter::CTime},
564 {I18N_NOOP("Locali&zed format (%1)"), KMime::DateFormatter::Localized},
565 {I18N_NOOP("Smart for&mat (%1)"), KMime::DateFormatter::Fancy},
566 {I18N_NOOP("C&ustom format:"), KMime::DateFormatter::Custom}};
567 static const int numDateDisplayConfig = sizeof dateDisplayConfig / sizeof *dateDisplayConfig;
568
AppearancePageHeadersTab(QWidget * parent)569 AppearancePageHeadersTab::AppearancePageHeadersTab(QWidget *parent)
570 : ConfigModuleTab(parent)
571 {
572 auto formLayout = new QFormLayout(this);
573
574 mDisplayMessageToolTips = new QCheckBox(MessageList::MessageListSettings::self()->messageToolTipEnabledItem()->label(), this);
575 formLayout->addRow(QString(), mDisplayMessageToolTips);
576
577 connect(mDisplayMessageToolTips, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
578
579 // "Aggregation"
580 using MessageList::Utils::AggregationComboBox;
581 mAggregationComboBox = new AggregationComboBox(this);
582
583 using MessageList::Utils::AggregationConfigButton;
584 auto aggregationConfigButton = new AggregationConfigButton(this, mAggregationComboBox);
585
586 auto aggregationLayout = new QHBoxLayout();
587 aggregationLayout->addWidget(mAggregationComboBox, 1);
588 aggregationLayout->addWidget(aggregationConfigButton, 0);
589 formLayout->addRow(i18n("Default aggregation:"), aggregationLayout);
590
591 connect(aggregationConfigButton,
592 &MessageList::Utils::AggregationConfigButton::configureDialogCompleted,
593 this,
594 &AppearancePageHeadersTab::slotSelectDefaultAggregation);
595 connect(mAggregationComboBox, &MessageList::Utils::AggregationComboBox::activated, this, &ConfigModuleTab::slotEmitChanged);
596
597 // "Theme"
598 using MessageList::Utils::ThemeComboBox;
599 mThemeComboBox = new ThemeComboBox(this);
600
601 using MessageList::Utils::ThemeConfigButton;
602 auto themeConfigButton = new ThemeConfigButton(this, mThemeComboBox);
603
604 auto themeLayout = new QHBoxLayout();
605 themeLayout->addWidget(mThemeComboBox, 1);
606 themeLayout->addWidget(themeConfigButton, 0);
607 formLayout->addRow(i18n("Default theme:"), themeLayout);
608
609 connect(themeConfigButton, &MessageList::Utils::ThemeConfigButton::configureDialogCompleted, this, &AppearancePageHeadersTab::slotSelectDefaultTheme);
610 connect(mThemeComboBox, &MessageList::Utils::ThemeComboBox::activated, this, &ConfigModuleTab::slotEmitChanged);
611
612 // "Date Display" group:
613 mDateDisplay = new QButtonGroup(this);
614 mDateDisplay->setExclusive(true);
615
616 for (int i = 0; i < numDateDisplayConfig; ++i) {
617 const char *label = dateDisplayConfig[i].displayName;
618 QString buttonLabel;
619 if (QString::fromLatin1(label).contains(QLatin1String("%1"))) {
620 buttonLabel = i18n(label, DateFormatter::formatCurrentDate(dateDisplayConfig[i].dateDisplay));
621 } else {
622 buttonLabel = i18n(label);
623 }
624 if (dateDisplayConfig[i].dateDisplay == DateFormatter::Custom) {
625 auto hbox = new QWidget(this);
626 auto hboxHBoxLayout = new QHBoxLayout(hbox);
627 hboxHBoxLayout->setContentsMargins({});
628 auto radio = new QRadioButton(buttonLabel, hbox);
629 hboxHBoxLayout->addWidget(radio);
630 mDateDisplay->addButton(radio, dateDisplayConfig[i].dateDisplay);
631
632 mCustomDateFormatEdit = new QLineEdit(hbox);
633 new KPIM::LineEditCatchReturnKey(mCustomDateFormatEdit, this);
634 hboxHBoxLayout->addWidget(mCustomDateFormatEdit);
635 mCustomDateFormatEdit->setEnabled(false);
636 hboxHBoxLayout->setStretchFactor(mCustomDateFormatEdit, 1);
637
638 connect(radio, &QAbstractButton::toggled, mCustomDateFormatEdit, &QWidget::setEnabled);
639 connect(mCustomDateFormatEdit, &QLineEdit::textChanged, this, &ConfigModuleTab::slotEmitChanged);
640
641 auto formatHelp = new QLabel(i18n("<qt><a href=\"whatsthis1\">Custom format information...</a></qt>"), hbox);
642 formatHelp->setContextMenuPolicy(Qt::NoContextMenu);
643 connect(formatHelp, &QLabel::linkActivated, this, &AppearancePageHeadersTab::slotLinkClicked);
644 hboxHBoxLayout->addWidget(formatHelp);
645
646 mCustomDateWhatsThis = i18n(
647 "<qt><p><strong>These expressions may be used for the date:"
648 "</strong></p>"
649 "<ul>"
650 "<li>d - the day as a number without a leading zero (1-31)</li>"
651 "<li>dd - the day as a number with a leading zero (01-31)</li>"
652 "<li>ddd - the abbreviated day name (Mon - Sun)</li>"
653 "<li>dddd - the long day name (Monday - Sunday)</li>"
654 "<li>M - the month as a number without a leading zero (1-12)</li>"
655 "<li>MM - the month as a number with a leading zero (01-12)</li>"
656 "<li>MMM - the abbreviated month name (Jan - Dec)</li>"
657 "<li>MMMM - the long month name (January - December)</li>"
658 "<li>yy - the year as a two digit number (00-99)</li>"
659 "<li>yyyy - the year as a four digit number (0000-9999)</li>"
660 "</ul>"
661 "<p><strong>These expressions may be used for the time:"
662 "</strong></p> "
663 "<ul>"
664 "<li>h - the hour without a leading zero (0-23 or 1-12 if AM/PM display)</li>"
665 "<li>hh - the hour with a leading zero (00-23 or 01-12 if AM/PM display)</li>"
666 "<li>m - the minutes without a leading zero (0-59)</li>"
667 "<li>mm - the minutes with a leading zero (00-59)</li>"
668 "<li>s - the seconds without a leading zero (0-59)</li>"
669 "<li>ss - the seconds with a leading zero (00-59)</li>"
670 "<li>z - the milliseconds without leading zeroes (0-999)</li>"
671 "<li>zzz - the milliseconds with leading zeroes (000-999)</li>"
672 "<li>AP - switch to AM/PM display. AP will be replaced by either \"AM\" or \"PM\".</li>"
673 "<li>ap - switch to AM/PM display. ap will be replaced by either \"am\" or \"pm\".</li>"
674 "<li>Z - time zone in numeric form (-0500)</li>"
675 "</ul>"
676 "<p><strong>All other input characters will be ignored."
677 "</strong></p></qt>");
678 mCustomDateFormatEdit->setWhatsThis(mCustomDateWhatsThis);
679 radio->setWhatsThis(mCustomDateWhatsThis);
680 formLayout->addRow(QString(), hbox);
681 } else {
682 auto radio = new QRadioButton(buttonLabel, mDateDisplayBox);
683 if (i == 0) {
684 formLayout->addRow(i18n("Date Display:"), radio);
685 } else {
686 formLayout->addRow(QString(), radio);
687 }
688 mDateDisplay->addButton(radio, dateDisplayConfig[i].dateDisplay);
689 }
690 } // end for loop populating mDateDisplay
691
692 connect(mDateDisplay, &QButtonGroup::buttonClicked, this, &ConfigModuleTab::slotEmitChanged);
693 }
694
slotLinkClicked(const QString & link)695 void AppearancePageHeadersTab::slotLinkClicked(const QString &link)
696 {
697 if (link == QLatin1String("whatsthis1")) {
698 QWhatsThis::showText(QCursor::pos(), mCustomDateWhatsThis);
699 }
700 }
701
slotSelectDefaultAggregation()702 void AppearancePageHeadersTab::slotSelectDefaultAggregation()
703 {
704 // Select current default aggregation.
705 mAggregationComboBox->selectDefault();
706 }
707
slotSelectDefaultTheme()708 void AppearancePageHeadersTab::slotSelectDefaultTheme()
709 {
710 // Select current default theme.
711 mThemeComboBox->selectDefault();
712 }
713
doLoadOther()714 void AppearancePageHeadersTab::doLoadOther()
715 {
716 // "General Options":
717 loadWidget(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem());
718
719 // "Aggregation":
720 slotSelectDefaultAggregation();
721
722 // "Theme":
723 slotSelectDefaultTheme();
724
725 // "Date Display":
726 setDateDisplay(MessageCore::MessageCoreSettings::self()->dateFormat(), MessageCore::MessageCoreSettings::self()->customDateFormat());
727 }
728
doLoadFromGlobalSettings()729 void AppearancePageHeadersTab::doLoadFromGlobalSettings()
730 {
731 loadWidget(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem());
732 // "Aggregation":
733 slotSelectDefaultAggregation();
734
735 // "Theme":
736 slotSelectDefaultTheme();
737
738 setDateDisplay(MessageCore::MessageCoreSettings::self()->dateFormat(), MessageCore::MessageCoreSettings::self()->customDateFormat());
739 }
740
setDateDisplay(int num,const QString & format)741 void AppearancePageHeadersTab::setDateDisplay(int num, const QString &format)
742 {
743 auto dateDisplay = static_cast<DateFormatter::FormatType>(num);
744
745 // special case: needs text for the line edit:
746 if (dateDisplay == DateFormatter::Custom) {
747 mCustomDateFormatEdit->setText(format);
748 }
749
750 for (int i = 0; i < numDateDisplayConfig; ++i) {
751 if (dateDisplay == dateDisplayConfig[i].dateDisplay) {
752 mDateDisplay->button(dateDisplay)->setChecked(true);
753 return;
754 }
755 }
756 // fell through since none found:
757 mDateDisplay->button(numDateDisplayConfig - 2)->setChecked(true); // default
758 }
759
save()760 void AppearancePageHeadersTab::save()
761 {
762 saveCheckBox(mDisplayMessageToolTips, MessageList::MessageListSettings::self()->messageToolTipEnabledItem());
763
764 if (KMKernel::self()) {
765 KMKernel::self()->savePaneSelection();
766 }
767 // "Aggregation"
768 mAggregationComboBox->writeDefaultConfig();
769
770 // "Theme"
771 mThemeComboBox->writeDefaultConfig();
772
773 const int dateDisplayID = mDateDisplay->checkedId();
774 MessageCore::MessageCoreSettings::self()->setDateFormat(dateDisplayID);
775 MessageCore::MessageCoreSettings::self()->setCustomDateFormat(mCustomDateFormatEdit->text());
776 }
777
778 //
779 // Message Window
780 //
781
helpAnchor() const782 QString AppearancePageGeneralTab::helpAnchor() const
783 {
784 return QStringLiteral("configure-appearance-reader");
785 }
786
AppearancePageGeneralTab(QWidget * parent)787 AppearancePageGeneralTab::AppearancePageGeneralTab(QWidget *parent)
788 : ConfigModuleTab(parent)
789 {
790 auto topLayout = new QVBoxLayout(this);
791
792 auto readerBox = new QGroupBox(i18n("Message Window"), this);
793 topLayout->addWidget(readerBox);
794
795 auto readerBoxLayout = new QVBoxLayout(readerBox);
796
797 // "Close message window after replying or forwarding" check box:
798 populateCheckBox(mCloseAfterReplyOrForwardCheck = new QCheckBox(this), MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem());
799 mCloseAfterReplyOrForwardCheck->setToolTip(i18n("Close the standalone message window after replying or forwarding the message"));
800 readerBoxLayout->addWidget(mCloseAfterReplyOrForwardCheck);
801 connect(mCloseAfterReplyOrForwardCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
802
803 mViewerSettings = new MessageViewer::ConfigureWidget;
804 connect(mViewerSettings, &MessageViewer::ConfigureWidget::settingsChanged, this, &ConfigModuleTab::slotEmitChanged);
805 readerBoxLayout->addWidget(mViewerSettings);
806
807 auto systrayBox = new QGroupBox(i18n("System Tray"), this);
808 topLayout->addWidget(systrayBox);
809
810 auto systrayBoxlayout = new QVBoxLayout(systrayBox);
811
812 // "Enable system tray applet" check box
813 mSystemTrayCheck = new QCheckBox(i18n("Enable system tray icon"), this);
814 systrayBoxlayout->addWidget(mSystemTrayCheck);
815
816 // "Enable start in system tray" check box
817 mStartInTrayCheck = new QCheckBox(i18n("Start minimized to tray"));
818 systrayBoxlayout->addWidget(mStartInTrayCheck);
819
820 // Dependencies between the two checkboxes
821 connect(mStartInTrayCheck, &QCheckBox::stateChanged, this, [this](int state) {
822 if (state == Qt::Checked) {
823 mSystemTrayCheck->setCheckState(Qt::Checked);
824 }
825 slotEmitChanged();
826 });
827 connect(mSystemTrayCheck, &QCheckBox::stateChanged, this, [this](int state) {
828 if (state == Qt::Unchecked) {
829 mStartInTrayCheck->setCheckState(Qt::Unchecked);
830 }
831 slotEmitChanged();
832 });
833
834 // "Enable system tray applet" check box
835 mShowNumberInTaskBar = new QCheckBox(i18n("Show unread email in Taskbar"), this);
836 systrayBoxlayout->addWidget(mShowNumberInTaskBar);
837 connect(mShowNumberInTaskBar, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
838
839 topLayout->addStretch(100); // spacer
840 }
841
doResetToDefaultsOther()842 void AppearancePageGeneralTab::doResetToDefaultsOther()
843 {
844 }
845
doLoadOther()846 void AppearancePageGeneralTab::doLoadOther()
847 {
848 loadWidget(mSystemTrayCheck, KMailSettings::self()->systemTrayEnabledItem());
849 loadWidget(mStartInTrayCheck, KMailSettings::self()->startInTrayItem());
850 loadWidget(mShowNumberInTaskBar, KMailSettings::self()->showUnreadInTaskbarItem());
851 loadWidget(mCloseAfterReplyOrForwardCheck, MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem());
852 mViewerSettings->readConfig();
853 }
854
save()855 void AppearancePageGeneralTab::save()
856 {
857 saveCheckBox(mSystemTrayCheck, KMailSettings::self()->systemTrayEnabledItem());
858 saveCheckBox(mStartInTrayCheck, KMailSettings::self()->startInTrayItem());
859 saveCheckBox(mShowNumberInTaskBar, KMailSettings::self()->showUnreadInTaskbarItem());
860 KMailSettings::self()->save();
861 saveCheckBox(mCloseAfterReplyOrForwardCheck, MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForwardItem());
862 mViewerSettings->writeConfig();
863 }
864
helpAnchor() const865 QString AppearancePageMessageTagTab::helpAnchor() const
866 {
867 return QStringLiteral("configure-appearance-messagetag");
868 }
869
TagListWidgetItem(QListWidget * parent)870 TagListWidgetItem::TagListWidgetItem(QListWidget *parent)
871 : QListWidgetItem(parent)
872 , mTag(nullptr)
873 {
874 }
875
TagListWidgetItem(const QIcon & icon,const QString & text,QListWidget * parent)876 TagListWidgetItem::TagListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent)
877 : QListWidgetItem(icon, text, parent)
878 , mTag(nullptr)
879 {
880 }
881
882 TagListWidgetItem::~TagListWidgetItem() = default;
883
setKMailTag(const MailCommon::Tag::Ptr & tag)884 void TagListWidgetItem::setKMailTag(const MailCommon::Tag::Ptr &tag)
885 {
886 mTag = tag;
887 }
888
kmailTag() const889 MailCommon::Tag::Ptr TagListWidgetItem::kmailTag() const
890 {
891 return mTag;
892 }
893
AppearancePageMessageTagTab(QWidget * parent)894 AppearancePageMessageTagTab::AppearancePageMessageTagTab(QWidget *parent)
895 : ConfigModuleTab(parent)
896 {
897 mPreviousTag = -1;
898 auto maingrid = new QHBoxLayout(this);
899
900 // Lefthand side Listbox and friends
901
902 // Groupbox frame
903 mTagsGroupBox = new QGroupBox(i18n("A&vailable Tags"), this);
904 maingrid->addWidget(mTagsGroupBox);
905 auto tageditgrid = new QVBoxLayout(mTagsGroupBox);
906
907 // Listbox, add, remove row
908 auto addremovegrid = new QHBoxLayout();
909 tageditgrid->addLayout(addremovegrid);
910
911 mTagAddLineEdit = new QLineEdit(mTagsGroupBox);
912 new KPIM::LineEditCatchReturnKey(mTagAddLineEdit, this);
913 addremovegrid->addWidget(mTagAddLineEdit);
914
915 mTagAddButton = new QPushButton(mTagsGroupBox);
916 mTagAddButton->setToolTip(i18n("Add new tag"));
917 mTagAddButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
918 addremovegrid->addWidget(mTagAddButton);
919
920 mTagRemoveButton = new QPushButton(mTagsGroupBox);
921 mTagRemoveButton->setToolTip(i18n("Remove selected tag"));
922 mTagRemoveButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
923 addremovegrid->addWidget(mTagRemoveButton);
924
925 // Up and down buttons
926 auto updowngrid = new QHBoxLayout();
927 tageditgrid->addLayout(updowngrid);
928
929 mTagUpButton = new QPushButton(mTagsGroupBox);
930 mTagUpButton->setToolTip(i18n("Increase tag priority"));
931 mTagUpButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
932 mTagUpButton->setAutoRepeat(true);
933 updowngrid->addWidget(mTagUpButton);
934
935 mTagDownButton = new QPushButton(mTagsGroupBox);
936 mTagDownButton->setToolTip(i18n("Decrease tag priority"));
937 mTagDownButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
938 mTagDownButton->setAutoRepeat(true);
939 updowngrid->addWidget(mTagDownButton);
940
941 // Listbox for tag names
942 auto listboxgrid = new QHBoxLayout();
943 tageditgrid->addLayout(listboxgrid);
944 mTagListBox = new QListWidget(mTagsGroupBox);
945 mTagListBox->setDragDropMode(QAbstractItemView::InternalMove);
946 connect(mTagListBox->model(), &QAbstractItemModel::rowsMoved, this, &AppearancePageMessageTagTab::slotRowsMoved);
947
948 mTagListBox->setMinimumWidth(150);
949 listboxgrid->addWidget(mTagListBox);
950
951 // RHS for individual tag settings
952
953 // Extra VBoxLayout for stretchers around settings
954 auto tagsettinggrid = new QVBoxLayout();
955 maingrid->addLayout(tagsettinggrid);
956
957 // Groupbox frame
958 mTagSettingGroupBox = new QGroupBox(i18n("Ta&g Settings"), this);
959 tagsettinggrid->addWidget(mTagSettingGroupBox);
960 QList<KActionCollection *> actionCollections;
961 if (kmkernel->getKMMainWidget()) {
962 actionCollections = kmkernel->getKMMainWidget()->actionCollections();
963 }
964
965 auto lay = new QHBoxLayout(mTagSettingGroupBox);
966 mTagWidget = new MailCommon::TagWidget(actionCollections, this);
967 lay->addWidget(mTagWidget);
968
969 connect(mTagWidget, &TagWidget::changed, this, &AppearancePageMessageTagTab::slotEmitChangeCheck);
970
971 // For enabling the add button in case box is non-empty
972 connect(mTagAddLineEdit, &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotAddLineTextChanged);
973
974 // For on-the-fly updating of tag name in editbox
975 connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged);
976
977 connect(mTagWidget, &TagWidget::iconNameChanged, this, &AppearancePageMessageTagTab::slotIconNameChanged);
978
979 connect(mTagAddLineEdit, &QLineEdit::returnPressed, this, &AppearancePageMessageTagTab::slotAddNewTag);
980
981 connect(mTagAddButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotAddNewTag);
982
983 connect(mTagRemoveButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotRemoveTag);
984
985 connect(mTagUpButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotMoveTagUp);
986
987 connect(mTagDownButton, &QAbstractButton::clicked, this, &AppearancePageMessageTagTab::slotMoveTagDown);
988
989 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
990 // Adjust widths for columns
991 maingrid->setStretchFactor(mTagsGroupBox, 1);
992 maingrid->setStretchFactor(lay, 1);
993 tagsettinggrid->addStretch(10);
994 }
995
996 AppearancePageMessageTagTab::~AppearancePageMessageTagTab() = default;
997
slotEmitChangeCheck()998 void AppearancePageMessageTagTab::slotEmitChangeCheck()
999 {
1000 slotEmitChanged();
1001 }
1002
slotRowsMoved(const QModelIndex &,int sourcestart,int sourceEnd,const QModelIndex &,int destinationRow)1003 void AppearancePageMessageTagTab::slotRowsMoved(const QModelIndex &, int sourcestart, int sourceEnd, const QModelIndex &, int destinationRow)
1004 {
1005 Q_UNUSED(sourceEnd)
1006 Q_UNUSED(sourcestart)
1007 Q_UNUSED(destinationRow)
1008 updateButtons();
1009 slotEmitChangeCheck();
1010 }
1011
updateButtons()1012 void AppearancePageMessageTagTab::updateButtons()
1013 {
1014 const int currentIndex = mTagListBox->currentRow();
1015
1016 const bool theFirst = (currentIndex == 0);
1017 const bool theLast = (currentIndex >= (int)mTagListBox->count() - 1);
1018 const bool aFilterIsSelected = (currentIndex >= 0);
1019
1020 mTagUpButton->setEnabled(aFilterIsSelected && !theFirst);
1021 mTagDownButton->setEnabled(aFilterIsSelected && !theLast);
1022 }
1023
slotMoveTagUp()1024 void AppearancePageMessageTagTab::slotMoveTagUp()
1025 {
1026 const int tmp_index = mTagListBox->currentRow();
1027 if (tmp_index <= 0) {
1028 return;
1029 }
1030 swapTagsInListBox(tmp_index, tmp_index - 1);
1031 updateButtons();
1032 }
1033
slotMoveTagDown()1034 void AppearancePageMessageTagTab::slotMoveTagDown()
1035 {
1036 const int tmp_index = mTagListBox->currentRow();
1037 if ((tmp_index < 0) || (tmp_index >= int(mTagListBox->count()) - 1)) {
1038 return;
1039 }
1040 swapTagsInListBox(tmp_index, tmp_index + 1);
1041 updateButtons();
1042 }
1043
swapTagsInListBox(const int first,const int second)1044 void AppearancePageMessageTagTab::swapTagsInListBox(const int first, const int second)
1045 {
1046 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1047 QListWidgetItem *item = mTagListBox->takeItem(first);
1048 // now selected item is at idx(idx-1), so
1049 // insert the other item at idx, ie. above(below).
1050 mPreviousTag = second;
1051 mTagListBox->insertItem(second, item);
1052 mTagListBox->setCurrentRow(second);
1053 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1054 slotEmitChangeCheck();
1055 }
1056
slotRecordTagSettings(int aIndex)1057 void AppearancePageMessageTagTab::slotRecordTagSettings(int aIndex)
1058 {
1059 if ((aIndex < 0) || (aIndex >= int(mTagListBox->count()))) {
1060 return;
1061 }
1062 QListWidgetItem *item = mTagListBox->item(aIndex);
1063 auto tagItem = static_cast<TagListWidgetItem *>(item);
1064
1065 MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag();
1066
1067 tmp_desc->tagName = tagItem->text();
1068 mTagWidget->recordTagSettings(tmp_desc);
1069 }
1070
slotUpdateTagSettingWidgets(int aIndex)1071 void AppearancePageMessageTagTab::slotUpdateTagSettingWidgets(int aIndex)
1072 {
1073 // Check if selection is valid
1074 if ((aIndex < 0) || (mTagListBox->currentRow() < 0)) {
1075 mTagRemoveButton->setEnabled(false);
1076 mTagUpButton->setEnabled(false);
1077 mTagDownButton->setEnabled(false);
1078
1079 mTagWidget->setEnabled(false);
1080 return;
1081 }
1082 mTagWidget->setEnabled(true);
1083
1084 mTagRemoveButton->setEnabled(true);
1085 mTagUpButton->setEnabled((0 != aIndex));
1086 mTagDownButton->setEnabled(((int(mTagListBox->count()) - 1) != aIndex));
1087 QListWidgetItem *item = mTagListBox->currentItem();
1088 auto tagItem = static_cast<TagListWidgetItem *>(item);
1089 MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag();
1090
1091 disconnect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged);
1092
1093 mTagWidget->tagNameLineEdit()->setEnabled(!tmp_desc->isImmutable);
1094 mTagWidget->tagNameLineEdit()->setText(tmp_desc->tagName);
1095 connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged);
1096
1097 mTagWidget->setTagTextColor(tmp_desc->textColor);
1098
1099 mTagWidget->setTagBackgroundColor(tmp_desc->backgroundColor);
1100
1101 mTagWidget->setTagTextFormat(tmp_desc->isBold, tmp_desc->isItalic);
1102
1103 mTagWidget->iconButton()->setEnabled(!tmp_desc->isImmutable);
1104 mTagWidget->iconButton()->setIcon(tmp_desc->iconName);
1105
1106 mTagWidget->keySequenceWidget()->setEnabled(true);
1107 mTagWidget->keySequenceWidget()->setKeySequence(tmp_desc->shortcut, KKeySequenceWidget::NoValidate);
1108
1109 mTagWidget->inToolBarCheck()->setEnabled(true);
1110 mTagWidget->inToolBarCheck()->setChecked(tmp_desc->inToolbar);
1111 }
1112
slotSelectionChanged()1113 void AppearancePageMessageTagTab::slotSelectionChanged()
1114 {
1115 mEmitChanges = false;
1116 slotRecordTagSettings(mPreviousTag);
1117 slotUpdateTagSettingWidgets(mTagListBox->currentRow());
1118 mPreviousTag = mTagListBox->currentRow();
1119 mEmitChanges = true;
1120 }
1121
slotRemoveTag()1122 void AppearancePageMessageTagTab::slotRemoveTag()
1123 {
1124 const int tmp_index = mTagListBox->currentRow();
1125 if (tmp_index >= 0) {
1126 if (KMessageBox::Yes
1127 == KMessageBox::questionYesNo(this,
1128 i18n("Do you want to remove tag \'%1\'?", mTagListBox->item(mTagListBox->currentRow())->text()),
1129 i18nc("@title:window", "Remove Tag"),
1130 KStandardGuiItem::remove(),
1131 KStandardGuiItem::cancel())) {
1132 QListWidgetItem *item = mTagListBox->takeItem(mTagListBox->currentRow());
1133 auto tagItem = static_cast<TagListWidgetItem *>(item);
1134 MailCommon::Tag::Ptr tmp_desc = tagItem->kmailTag();
1135 if (tmp_desc->tag().isValid()) {
1136 new Akonadi::TagDeleteJob(tmp_desc->tag());
1137 } else {
1138 qCWarning(KMAIL_LOG) << "Can't remove tag with invalid akonadi tag";
1139 }
1140 mPreviousTag = -1;
1141
1142 // Before deleting the current item, make sure the selectionChanged signal
1143 // is disconnected, so that the widgets will not get updated while the
1144 // deletion takes place.
1145 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1146
1147 delete item;
1148 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1149
1150 slotSelectionChanged();
1151 slotEmitChangeCheck();
1152 }
1153 }
1154 }
1155
slotDeleteTagJob(KJob * job)1156 void AppearancePageMessageTagTab::slotDeleteTagJob(KJob *job)
1157 {
1158 if (job->error()) {
1159 qCWarning(KMAIL_LOG) << "Failed to delete tag " << job->errorString();
1160 }
1161 }
1162
slotNameLineTextChanged(const QString & aText)1163 void AppearancePageMessageTagTab::slotNameLineTextChanged(const QString &aText)
1164 {
1165 // If deleted all, leave the first character for the sake of not having an
1166 // empty tag name
1167 if (aText.isEmpty() && !mTagListBox->currentItem()) {
1168 return;
1169 }
1170
1171 const int count = mTagListBox->count();
1172 for (int i = 0; i < count; ++i) {
1173 if (mTagListBox->item(i)->text() == aText) {
1174 KMessageBox::error(this, i18n("We cannot create tag. A tag with same name already exists."));
1175 disconnect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged);
1176 mTagWidget->tagNameLineEdit()->setText(mTagListBox->currentItem()->text());
1177 connect(mTagWidget->tagNameLineEdit(), &QLineEdit::textChanged, this, &AppearancePageMessageTagTab::slotNameLineTextChanged);
1178 return;
1179 }
1180 }
1181
1182 // Disconnect so the tag information is not saved and reloaded with every
1183 // letter
1184 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1185
1186 mTagListBox->currentItem()->setText(aText);
1187 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1188 }
1189
slotIconNameChanged(const QString & iconName)1190 void AppearancePageMessageTagTab::slotIconNameChanged(const QString &iconName)
1191 {
1192 mTagListBox->currentItem()->setIcon(QIcon::fromTheme(iconName));
1193 }
1194
slotAddLineTextChanged(const QString & aText)1195 void AppearancePageMessageTagTab::slotAddLineTextChanged(const QString &aText)
1196 {
1197 mTagAddButton->setEnabled(!aText.trimmed().isEmpty());
1198 }
1199
slotAddNewTag()1200 void AppearancePageMessageTagTab::slotAddNewTag()
1201 {
1202 const QString newTagName = mTagAddLineEdit->text().trimmed();
1203 if (newTagName.isEmpty()) {
1204 return;
1205 }
1206 const int count = mTagListBox->count();
1207 for (int i = 0; i < count; ++i) {
1208 if (mTagListBox->item(i)->text() == newTagName) {
1209 KMessageBox::error(this, i18n("We cannot create tag. A tag with same name already exists."));
1210 return;
1211 }
1212 }
1213
1214 const int tmp_priority = mTagListBox->count();
1215
1216 MailCommon::Tag::Ptr tag(Tag::createDefaultTag(newTagName));
1217 tag->priority = tmp_priority;
1218
1219 slotEmitChangeCheck();
1220 auto newItem = new TagListWidgetItem(QIcon::fromTheme(tag->iconName), newTagName, mTagListBox);
1221 newItem->setKMailTag(tag);
1222 mTagListBox->addItem(newItem);
1223 mTagListBox->setCurrentItem(newItem);
1224 mTagAddLineEdit->clear();
1225 }
1226
doLoadFromGlobalSettings()1227 void AppearancePageMessageTagTab::doLoadFromGlobalSettings()
1228 {
1229 mTagListBox->clear();
1230
1231 auto fetchJob = new Akonadi::TagFetchJob(this);
1232 fetchJob->fetchScope().fetchAttribute<Akonadi::TagAttribute>();
1233 connect(fetchJob, &KJob::result, this, &AppearancePageMessageTagTab::slotTagsFetched);
1234 }
1235
slotTagsFetched(KJob * job)1236 void AppearancePageMessageTagTab::slotTagsFetched(KJob *job)
1237 {
1238 if (job->error()) {
1239 qCWarning(KMAIL_LOG) << "Failed to load tags " << job->errorString();
1240 return;
1241 }
1242 auto fetchJob = static_cast<Akonadi::TagFetchJob *>(job);
1243
1244 QList<MailCommon::TagPtr> msgTagList;
1245 const Akonadi::Tag::List tagList = fetchJob->tags();
1246 msgTagList.reserve(tagList.count());
1247 for (const Akonadi::Tag &akonadiTag : tagList) {
1248 MailCommon::Tag::Ptr tag = MailCommon::Tag::fromAkonadi(akonadiTag);
1249 msgTagList.append(tag);
1250 }
1251
1252 std::sort(msgTagList.begin(), msgTagList.end(), MailCommon::Tag::compare);
1253
1254 for (const MailCommon::Tag::Ptr &tag : std::as_const(msgTagList)) {
1255 auto newItem = new TagListWidgetItem(QIcon::fromTheme(tag->iconName), tag->tagName, mTagListBox);
1256 newItem->setKMailTag(tag);
1257 if (tag->priority == -1) {
1258 tag->priority = mTagListBox->count() - 1;
1259 }
1260 }
1261
1262 // Disconnect so that insertItem's do not trigger an update procedure
1263 disconnect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1264
1265 connect(mTagListBox, &QListWidget::currentItemChanged, this, &AppearancePageMessageTagTab::slotSelectionChanged);
1266
1267 slotUpdateTagSettingWidgets(-1);
1268 // Needed since the previous function doesn't affect add button
1269 mTagAddButton->setEnabled(false);
1270
1271 // Save the original list
1272 mOriginalMsgTagList.clear();
1273 for (const MailCommon::TagPtr &tag : std::as_const(msgTagList)) {
1274 mOriginalMsgTagList.append(MailCommon::TagPtr(new MailCommon::Tag(*tag)));
1275 }
1276 }
1277
save()1278 void AppearancePageMessageTagTab::save()
1279 {
1280 const int currentRow = mTagListBox->currentRow();
1281 if (currentRow < 0) {
1282 return;
1283 }
1284
1285 const int count = mTagListBox->count();
1286 if (!count) {
1287 return;
1288 }
1289
1290 QListWidgetItem *item = mTagListBox->currentItem();
1291 if (!item) {
1292 return;
1293 }
1294 slotRecordTagSettings(currentRow);
1295 const int numberOfMsgTagList = count;
1296 for (int i = 0; i < numberOfMsgTagList; ++i) {
1297 auto tagItem = static_cast<TagListWidgetItem *>(mTagListBox->item(i));
1298 if ((i >= mOriginalMsgTagList.count()) || *(tagItem->kmailTag()) != *(mOriginalMsgTagList[i])) {
1299 MailCommon::Tag::Ptr tag = tagItem->kmailTag();
1300 tag->priority = i;
1301 Akonadi::Tag akonadiTag = tag->saveToAkonadi();
1302 if ((*tag).id() > 0) {
1303 akonadiTag.setId((*tag).id());
1304 }
1305 if (akonadiTag.isValid()) {
1306 new Akonadi::TagModifyJob(akonadiTag);
1307 } else {
1308 new Akonadi::TagCreateJob(akonadiTag);
1309 }
1310 }
1311 }
1312 }
1313