Messagelib

manager.cpp
1/******************************************************************************
2 *
3 * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 *
7 *******************************************************************************/
8
9#include "core/manager.h"
10
11#include "core/aggregation.h"
12#include "core/storagemodelbase.h"
13#include "core/theme.h"
14#include "core/view.h"
15#include "core/widgetbase.h"
16#include "messagelistsettings.h"
17
18#include <MessageCore/DateFormatter>
19#include <MessageCore/MessageCoreSettings>
20
21#include "messagelistutil_p.h"
22
23#include "messagelist_debug.h"
24#include <KConfig>
25#include <KLocalizedString>
26
27using namespace MessageList::Core;
28
29Manager *Manager::mInstance = nullptr;
30
31Manager::Manager()
32 : mDateFormatter(new MessageCore::DateFormatter())
33 , mCachedLocalizedUnknownText(i18nc("Unknown date", "Unknown"))
34{
35 mInstance = this;
36
37 loadConfiguration();
38 connect(MessageListSettings::self(), &MessageListSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
39 connect(MessageCore::MessageCoreSettings::self(), &MessageCore::MessageCoreSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
40}
41
42Manager::~Manager()
43{
44 disconnect(MessageListSettings::self(), &MessageListSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
45 disconnect(MessageCore::MessageCoreSettings::self(), &MessageCore::MessageCoreSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
46
47 saveConfiguration();
48 removeAllAggregations();
49 removeAllThemes();
50
51 mInstance = nullptr;
52}
53
54void Manager::registerWidget(Widget *pWidget)
55{
56 if (!mInstance) {
57 mInstance = new Manager();
58 }
59
60 mInstance->mWidgetList.append(pWidget);
61}
62
63void Manager::unregisterWidget(Widget *pWidget)
64{
65 if (!mInstance) {
66 qCWarning(MESSAGELIST_LOG) << ("ERROR: MessageList::Manager::unregisterWidget() called when Manager::mInstance is null");
67 return;
68 }
69
70 mInstance->mWidgetList.removeAll(pWidget);
71
72 if (mInstance->mWidgetList.isEmpty()) {
73 delete mInstance;
74 mInstance = nullptr;
75 }
76}
77
78const Aggregation *Manager::aggregation(const QString &id)
79{
80 Aggregation *opt = mAggregations.value(id);
81 if (opt) {
82 return opt;
83 }
84
85 return defaultAggregation();
86}
87
88const Aggregation *Manager::defaultAggregation()
89{
90 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup());
91
92 const QString aggregationId = conf.readEntry(QStringLiteral("DefaultSet"), "");
93
94 Aggregation *opt = nullptr;
95
96 if (!aggregationId.isEmpty()) {
97 opt = mAggregations.value(aggregationId);
98 }
99
100 if (opt) {
101 return opt;
102 }
103
104 // try just the first one
105 QMap<QString, Aggregation *>::ConstIterator it = mAggregations.constBegin();
106 if (it != mAggregations.constEnd()) {
107 return *it;
108 }
109
110 // aargh
111 createDefaultAggregations();
112
113 return *(mAggregations.constBegin());
114}
115
116void Manager::saveAggregationForStorageModel(const Akonadi::Collection &col, const QString &id, bool storageUsesPrivateAggregation)
117{
118 if (!col.isValid()) {
119 return;
120 }
121 saveAggregationForStorageModel(QString::number(col.id()), id, storageUsesPrivateAggregation);
122}
123
124void Manager::saveAggregationForStorageModel(const StorageModel *storageModel, const QString &id, bool storageUsesPrivateAggregation)
125{
126 saveAggregationForStorageModel(storageModel->id(), id, storageUsesPrivateAggregation);
127}
128
129void Manager::saveAggregationForStorageModel(const QString &modelId, const QString &id, bool storageUsesPrivateAggregation)
130{
131 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup());
132
133 if (storageUsesPrivateAggregation) {
134 conf.writeEntry(MessageList::Util::setForStorageModelConfigName().arg(modelId), id);
135 } else {
136 conf.deleteEntry(MessageList::Util::setForStorageModelConfigName().arg(modelId));
137 }
138
139 if (!storageUsesPrivateAggregation) {
140 conf.writeEntry(QStringLiteral("DefaultSet"), id);
141 }
142 conf.sync();
143}
144
145const Aggregation *Manager::aggregationForStorageModel(const Akonadi::Collection &col, bool *storageUsesPrivateAggregation)
146{
147 Q_ASSERT(storageUsesPrivateAggregation);
148
149 *storageUsesPrivateAggregation = false; // this is by default
150
151 if (!col.isValid()) {
152 return defaultAggregation();
153 }
154 return Manager::aggregationForStorageModel(QString::number(col.id()), storageUsesPrivateAggregation);
155}
156
157const Aggregation *Manager::aggregationForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateAggregation)
158{
159 Q_ASSERT(storageUsesPrivateAggregation);
160
161 *storageUsesPrivateAggregation = false; // this is by default
162
163 if (!storageModel) {
164 return defaultAggregation();
165 }
166 return Manager::aggregationForStorageModel(storageModel->id(), storageUsesPrivateAggregation);
167}
168
169const Aggregation *Manager::aggregationForStorageModel(const QString &storageId, bool *storageUsesPrivateAggregation)
170{
171 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup());
172
173 const QString aggregationId = conf.readEntry(MessageList::Util::setForStorageModelConfigName().arg(storageId), "");
174
175 Aggregation *opt = nullptr;
176
177 if (!aggregationId.isEmpty()) {
178 // a private aggregation was stored
179 opt = mAggregations.value(aggregationId);
180 *storageUsesPrivateAggregation = (opt != nullptr);
181 }
182
183 if (opt) {
184 return opt;
185 }
186
187 // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset...
188 // We could even try to guess if the storageModel is a mailing list
189
190 return defaultAggregation();
191}
192
193void Manager::addAggregation(Aggregation *set)
194{
195 Aggregation *old = mAggregations.value(set->id());
196 delete old;
197 mAggregations.insert(set->id(), set);
198}
199
200void Manager::createDefaultAggregations()
201{
202 addAggregation(new Aggregation(i18n("Current Activity, Threaded"),
203 i18n("This view uses smart date range groups. "
204 "Messages are threaded. "
205 "So for example, in \"Today\" you will find all the messages arrived today "
206 "and all the threads that have been active today."),
213 true));
214
215 addAggregation(new Aggregation(i18n("Current Activity, Flat"),
216 i18n("This view uses smart date range groups. "
217 "Messages are not threaded. "
218 "So for example, in \"Today\" you will simply find all the messages arrived today."),
225 true));
226
227 addAggregation(new Aggregation(i18n("Activity by Date, Threaded"),
228 i18n("This view uses day-by-day groups. "
229 "Messages are threaded. "
230 "So for example, in \"Today\" you will find all the messages arrived today "
231 "and all the threads that have been active today."),
238 true));
239
240 addAggregation(new Aggregation(i18n("Activity by Date, Flat"),
241 i18n("This view uses day-by-day groups. "
242 "Messages are not threaded. "
243 "So for example, in \"Today\" you will simply find all the messages arrived today."),
250 true));
251
252 addAggregation(new Aggregation(i18n("Standard Mailing List"),
253 i18n("This is a plain and old mailing list view: no groups and heavy threading."),
260 true));
261
262 addAggregation(new Aggregation(i18n("Flat Date View"),
263 i18n("This is a plain and old list of messages sorted by date: no groups and no threading."),
270 true
271
272 ));
273
274 addAggregation(new Aggregation(i18n("Senders/Receivers, Flat"),
275 i18n("This view groups the messages by senders or receivers (depending on the folder "
276 "type). "
277 "Messages are not threaded."),
284 true));
285
286 addAggregation(new Aggregation(i18n("Thread Starters"),
287 i18n("This view groups the messages in threads and then groups the threads by the starting user."),
294 true));
295
296 /*
297 FIX THIS
298 addAggregation(
299 new Aggregation(
300 i18n( "Recent Thread Starters" ),
301 i18n( "This view groups the messages in threads and then groups the threads by the starting user. " \
302 "Groups are sorted by the date of the first thread start. "
303 ),
304 Aggregation::GroupBySenderOrReceiver,
305 Aggregation::SortGroupsByDateTimeOfMostRecent,
306 Aggregation::Descending,
307 Aggregation::PerfectReferencesAndSubject,
308 Aggregation::TopmostMessage,
309 Aggregation::SortMessagesByDateTime,
310 Aggregation::Descending
311 )
312 );
313 */
314}
315
316void Manager::removeAllAggregations()
317{
318 QMap<QString, Aggregation *>::ConstIterator end(mAggregations.constEnd());
319 for (QMap<QString, Aggregation *>::ConstIterator it = mAggregations.constBegin(); it != end; ++it) {
320 delete (*it);
321 }
322
323 mAggregations.clear();
324}
325
327{
328 if (mAggregations.isEmpty()) {
329 createDefaultAggregations(); // panic
330 }
331
332 saveConfiguration(); // just to be sure :)
333
334 // notify all the widgets that they should reload the option set combos
335 Q_EMIT aggregationsChanged();
336}
337
338const SortOrder Manager::sortOrderForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateSortOrder)
339{
340 Q_ASSERT(storageUsesPrivateSortOrder);
341
342 *storageUsesPrivateSortOrder = false; // this is by default
343
344 if (!storageModel) {
345 return SortOrder();
346 }
347
348 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelSortOrderGroup());
349 SortOrder ret;
350 ret.readConfig(conf, storageModel->id(), storageUsesPrivateSortOrder);
351 return ret;
352}
353
354void Manager::saveSortOrderForStorageModel(const StorageModel *storageModel, SortOrder order, bool storageUsesPrivateSortOrder)
355{
356 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelSortOrderGroup());
357 order.writeConfig(conf, storageModel->id(), storageUsesPrivateSortOrder);
358}
359
360const Theme *Manager::theme(const QString &id)
361{
362 Theme *opt = mThemes.value(id);
363 if (opt) {
364 return opt;
365 }
366
367 return defaultTheme();
368}
369
370const Theme *Manager::defaultTheme()
371{
372 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup());
373
374 const QString themeId = conf.readEntry(QStringLiteral("DefaultSet"), "");
375
376 Theme *opt = nullptr;
377
378 if (!themeId.isEmpty()) {
379 opt = mThemes.value(themeId);
380 }
381
382 if (opt) {
383 return opt;
384 }
385
386 // try just the first one
388 if (it != mThemes.constEnd()) {
389 return *it;
390 }
391
392 // aargh
393 createDefaultThemes();
394
395 it = mThemes.constBegin();
396
397 Q_ASSERT(it != mThemes.constEnd());
398
399 return *it;
400}
401
402void Manager::saveThemeForStorageModel(int index, const QString &id, bool storageUsesPrivateTheme)
403{
404 saveThemeForStorageModel(QString::number(index), id, storageUsesPrivateTheme);
405}
406
407void Manager::saveThemeForStorageModel(const StorageModel *storageModel, const QString &id, bool storageUsesPrivateTheme)
408{
409 saveThemeForStorageModel(storageModel->id(), id, storageUsesPrivateTheme);
410}
411
412void Manager::saveThemeForStorageModel(const QString &storageModelIndex, const QString &id, bool storageUsesPrivateTheme)
413{
414 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup());
415
416 if (storageUsesPrivateTheme) {
417 conf.writeEntry(MessageList::Util::setForStorageModelConfigName().arg(storageModelIndex), id);
418 } else {
419 conf.deleteEntry(MessageList::Util::setForStorageModelConfigName().arg(storageModelIndex));
420 }
421
422 if (!storageUsesPrivateTheme) {
423 conf.writeEntry(QStringLiteral("DefaultSet"), id);
424 }
425 conf.sync();
426}
427
428const Theme *Manager::themeForStorageModel(const Akonadi::Collection &col, bool *storageUsesPrivateTheme)
429{
430 Q_ASSERT(storageUsesPrivateTheme);
431
432 *storageUsesPrivateTheme = false; // this is by default
433
434 if (!col.isValid()) {
435 return defaultTheme();
436 }
437 return Manager::themeForStorageModel(QString::number(col.id()), storageUsesPrivateTheme);
438}
439
440const Theme *Manager::themeForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateTheme)
441{
442 Q_ASSERT(storageUsesPrivateTheme);
443
444 *storageUsesPrivateTheme = false; // this is by default
445
446 if (!storageModel) {
447 return defaultTheme();
448 }
449 return Manager::themeForStorageModel(storageModel->id(), storageUsesPrivateTheme);
450}
451
452const Theme *Manager::themeForStorageModel(const QString &id, bool *storageUsesPrivateTheme)
453{
454 KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup());
455 const QString themeId = conf.readEntry(MessageList::Util::setForStorageModelConfigName().arg(id), "");
456
457 Theme *opt = nullptr;
458
459 if (!themeId.isEmpty()) {
460 // a private theme was stored
461 opt = mThemes.value(themeId);
462 *storageUsesPrivateTheme = (opt != nullptr);
463 }
464
465 if (opt) {
466 return opt;
467 }
468
469 // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset...
470 // We could even try to guess if the storageModel is a mailing list
471
472 // FIXME: Prefer right-to-left themes when application layout is RTL.
473
474 return defaultTheme();
475}
476
477void Manager::addTheme(Theme *set)
478{
479 Theme *old = mThemes.value(set->id());
480 delete old;
481 mThemes.insert(set->id(), set);
482}
483
484static Theme::Column *add_theme_simple_text_column(Theme *s,
485 const QString &name,
487 bool visibleByDefault,
488 SortOrder::MessageSorting messageSorting,
489 bool alignRight,
490 bool addGroupHeaderItem)
491{
492 auto c = new Theme::Column();
493 c->setLabel(name);
494 c->setVisibleByDefault(visibleByDefault);
495 c->setMessageSorting(messageSorting);
496
497 auto r = new Theme::Row();
498
499 auto i = new Theme::ContentItem(type);
500
501 if (alignRight) {
502 r->addRightItem(i);
503 } else {
504 r->addLeftItem(i);
505 }
506
507 c->addMessageRow(r);
508
509 if (addGroupHeaderItem) {
510 auto row = new Theme::Row();
511
512 auto iRow = new Theme::ContentItem(type);
513
514 if (alignRight) {
515 row->addRightItem(iRow);
516 } else {
517 row->addLeftItem(iRow);
518 }
519
520 c->addGroupHeaderRow(row);
521 }
522
523 s->addColumn(c);
524
525 return c;
526}
527
528static Theme::Column *add_theme_simple_icon_column(Theme *s,
529 const QString &name,
530 const QString &pixmapName,
532 bool visibleByDefault,
533 SortOrder::MessageSorting messageSorting)
534{
535 auto c = new Theme::Column();
536 c->setLabel(name);
537 c->setPixmapName(pixmapName);
538 c->setVisibleByDefault(visibleByDefault);
539 c->setMessageSorting(messageSorting);
540
541 auto r = new Theme::Row();
542
543 auto i = new Theme::ContentItem(type);
544 i->setSoftenByBlendingWhenDisabled(true);
545
546 r->addLeftItem(i);
547
548 c->addMessageRow(r);
549
550 s->addColumn(c);
551
552 return c;
553}
554
555void Manager::createDefaultThemes()
556{
557 Theme *s;
558 Theme::Column *c;
559 Theme::Row *r;
560 Theme::ContentItem *i;
561
562 // The "Classic" backward compatible theme
563
564 s = new Theme(i18nc("Default theme name", "Classic"), i18n("A simple, backward compatible, single row theme"), true /*readOnly*/
565 );
566
567 c = new Theme::Column();
568 c->setLabel(i18nc("@title:column Subject of messages", "Subject"));
570
571 r = new Theme::Row();
572 i = new Theme::ContentItem(Theme::ContentItem::ExpandedStateIcon);
573 r->addLeftItem(i);
574 i = new Theme::ContentItem(Theme::ContentItem::GroupHeaderLabel);
575 i->setBold(true);
576 r->addLeftItem(i);
577 c->addGroupHeaderRow(r);
578
579 r = new Theme::Row();
580 i = new Theme::ContentItem(Theme::ContentItem::CombinedReadRepliedStateIcon);
581 r->addLeftItem(i);
582 i = new Theme::ContentItem(Theme::ContentItem::AttachmentStateIcon);
583 i->setHideWhenDisabled(true);
584 r->addLeftItem(i);
585 i = new Theme::ContentItem(Theme::ContentItem::InvitationIcon);
586 i->setHideWhenDisabled(true);
587 r->addLeftItem(i);
588 i = new Theme::ContentItem(Theme::ContentItem::SignatureStateIcon);
589 i->setHideWhenDisabled(true);
590 r->addLeftItem(i);
591 i = new Theme::ContentItem(Theme::ContentItem::EncryptionStateIcon);
592 i->setHideWhenDisabled(true);
593 r->addLeftItem(i);
594 i = new Theme::ContentItem(Theme::ContentItem::Subject);
595 r->addLeftItem(i);
596 c->addMessageRow(r);
597
598 s->addColumn(c);
599
600 c = add_theme_simple_text_column(s,
601 i18n("Sender/Receiver"),
603 true,
605 false,
606 false);
607 c->setIsSenderOrReceiver(true);
608 add_theme_simple_text_column(s, i18nc("Sender of a message", "Sender"), Theme::ContentItem::Sender, false, SortOrder::SortMessagesBySender, false, false);
609 add_theme_simple_text_column(s,
610 i18nc("Receiver of a message", "Receiver"),
612 false,
614 false,
615 false);
616 add_theme_simple_text_column(s, i18nc("Date of a message", "Date"), Theme::ContentItem::Date, true, SortOrder::SortMessagesByDateTime, false, false);
617 add_theme_simple_text_column(s,
618 i18n("Most Recent Date"),
620 false,
622 false,
623 true);
624 add_theme_simple_text_column(s, i18nc("Size of a message", "Size"), Theme::ContentItem::Size, false, SortOrder::SortMessagesBySize, false, false);
625 add_theme_simple_icon_column(s,
626 i18nc("Attachment indication", "Attachment"),
627 QStringLiteral("mail-attachment"),
629 false,
631 add_theme_simple_icon_column(s,
632 i18n("Read/Unread"),
633 QStringLiteral("mail-mark-unread-new"),
635 false,
637 add_theme_simple_icon_column(s, i18n("Replied"), QStringLiteral("mail-replied"), Theme::ContentItem::RepliedStateIcon, false, SortOrder::NoMessageSorting);
638 add_theme_simple_icon_column(s,
639 i18nc("Message importance indication", "Important"),
640 QStringLiteral("mail-mark-important"),
642 false,
643 SortOrder::SortMessagesByImportantStatus);
644 add_theme_simple_icon_column(s,
645 i18n("Action Item"),
646 QStringLiteral("mail-task"),
648 false,
650 add_theme_simple_icon_column(s,
651 i18n("Spam/Ham"),
652 QStringLiteral("mail-mark-junk"),
654 false,
656 add_theme_simple_icon_column(s,
657 i18n("Watched/Ignored"),
658 QStringLiteral("mail-thread-watch"),
660 false,
662 add_theme_simple_icon_column(s,
663 i18n("Encryption"),
664 QStringLiteral("mail-encrypted-full"),
666 false,
668 add_theme_simple_icon_column(s,
669 i18n("Signature"),
670 QStringLiteral("mail-signed-verified"),
672 false,
674 add_theme_simple_icon_column(s, i18n("Tag List"), QStringLiteral("feed-subscribe"), Theme::ContentItem::TagList, false, SortOrder::NoMessageSorting);
675
676 s->resetColumnState(); // so it's initially set from defaults
677
678 addTheme(s);
679
680 // The Fancy theme
681
682 s = new Theme(i18n("Smart"), i18n("A smart multiline and multi item theme"), true /*readOnly*/
683 );
684
685 c = new Theme::Column();
686 c->setLabel(i18n("Message"));
687
688 r = new Theme::Row();
689 i = new Theme::ContentItem(Theme::ContentItem::ExpandedStateIcon);
690 r->addLeftItem(i);
691 i = new Theme::ContentItem(Theme::ContentItem::GroupHeaderLabel);
692 i->setBold(true);
693 r->addLeftItem(i);
694 c->addGroupHeaderRow(r);
695
696 r = new Theme::Row();
697 i = new Theme::ContentItem(Theme::ContentItem::Subject);
698 r->addLeftItem(i);
699 i = new Theme::ContentItem(Theme::ContentItem::ReadStateIcon);
700 r->addRightItem(i);
701 i = new Theme::ContentItem(Theme::ContentItem::RepliedStateIcon);
702 i->setHideWhenDisabled(true);
703 r->addRightItem(i);
704 i = new Theme::ContentItem(Theme::ContentItem::AttachmentStateIcon);
705 i->setHideWhenDisabled(true);
706 r->addRightItem(i);
707 i = new Theme::ContentItem(Theme::ContentItem::InvitationIcon);
708 i->setHideWhenDisabled(true);
709 r->addRightItem(i);
710 i = new Theme::ContentItem(Theme::ContentItem::EncryptionStateIcon);
711 i->setHideWhenDisabled(true);
712 r->addRightItem(i);
713 i = new Theme::ContentItem(Theme::ContentItem::SignatureStateIcon);
714 i->setHideWhenDisabled(true);
715 r->addRightItem(i);
716 i = new Theme::ContentItem(Theme::ContentItem::TagList);
717 i->setHideWhenDisabled(true);
718 r->addRightItem(i);
719 c->addMessageRow(r);
720
721 Theme::Row *firstFancyRow = r; // save it so we can continue adding stuff below (after cloning the theme)
722
723 r = new Theme::Row();
724 i = new Theme::ContentItem(Theme::ContentItem::SenderOrReceiver);
725 i->setSoftenByBlending(true);
726 i->setItalic(true);
727 r->addLeftItem(i);
728 i = new Theme::ContentItem(Theme::ContentItem::Date);
729 i->setSoftenByBlending(true);
730 i->setItalic(true);
731 r->addRightItem(i);
732 c->addMessageRow(r);
733
734 s->addColumn(c);
735
736 // clone the "Fancy theme" here so we'll use it as starting point for the "Fancy with clickable status"
737 auto fancyWithClickableStatus = new Theme(*s);
738 fancyWithClickableStatus->detach();
739 fancyWithClickableStatus->generateUniqueId();
740
741 // and continue the "Fancy" specific settings
742 r = firstFancyRow;
743
744 i = new Theme::ContentItem(Theme::ContentItem::ActionItemStateIcon);
745 i->setHideWhenDisabled(true);
746 r->addRightItem(i);
747 i = new Theme::ContentItem(Theme::ContentItem::ImportantStateIcon);
748 i->setHideWhenDisabled(true);
749 r->addRightItem(i);
750 i = new Theme::ContentItem(Theme::ContentItem::SpamHamStateIcon);
751 i->setHideWhenDisabled(true);
752 r->addRightItem(i);
753 i = new Theme::ContentItem(Theme::ContentItem::WatchedIgnoredStateIcon);
754 i->setHideWhenDisabled(true);
755 r->addRightItem(i);
756
757 s->setViewHeaderPolicy(Theme::NeverShowHeader);
758
759 s->resetColumnState(); // so it's initially set from defaults
760
761 addTheme(s);
762
763 // The "Fancy with Clickable Status" theme
764
765 s = fancyWithClickableStatus;
766
767 s->setName(i18n("Smart with Clickable Status"));
768 s->setDescription(i18n("A smart multiline and multi item theme with a clickable status column"));
769 s->setReadOnly(true);
770
771 c = new Theme::Column();
772 c->setLabel(i18n("Status"));
773 c->setVisibleByDefault(true);
774
775 r = new Theme::Row();
776 i = new Theme::ContentItem(Theme::ContentItem::ActionItemStateIcon);
778 r->addLeftItem(i);
779 i = new Theme::ContentItem(Theme::ContentItem::ImportantStateIcon);
781 r->addLeftItem(i);
782 c->addMessageRow(r);
783
784 r = new Theme::Row();
785 i = new Theme::ContentItem(Theme::ContentItem::SpamHamStateIcon);
787 r->addLeftItem(i);
788 i = new Theme::ContentItem(Theme::ContentItem::WatchedIgnoredStateIcon);
790 r->addLeftItem(i);
791 c->addMessageRow(r);
792
793 s->addColumn(c);
794
795 s->resetColumnState(); // so it's initially set from defaults
796
797 addTheme(s);
798}
799
800void Manager::removeAllThemes()
801{
802 QMap<QString, Theme *>::ConstIterator end(mThemes.constEnd());
803 for (QMap<QString, Theme *>::ConstIterator it = mThemes.constBegin(); it != end; ++it) {
804 delete (*it);
805 }
806
807 mThemes.clear();
808}
809
811{
812 if (mThemes.isEmpty()) {
813 createDefaultThemes(); // panic
814 }
815
816 saveConfiguration(); // just to be sure :)
817
818 // notify all the widgets that they should reload the option set combos
819 Q_EMIT themesChanged();
820}
821
823{
824 QList<Widget *>::ConstIterator end(mWidgetList.constEnd());
825 for (QList<Widget *>::ConstIterator it = mWidgetList.constBegin(); it != end; ++it) {
826 if ((*it)->view()) {
827 (*it)->view()->reload();
828 }
829 }
830}
831
833{
834 // This is called when configuration changes (probably edited by the options dialog)
835 const int oldDateFormat = (int)mDateFormatter->format();
836 const QString oldDateCustomFormat = mDateFormatter->customFormat();
837
838 loadGlobalConfiguration();
839
840 if ((oldDateFormat != (int)mDateFormatter->format()) || (oldDateCustomFormat != mDateFormatter->customFormat())) {
842 }
843}
844
845void Manager::loadGlobalConfiguration()
846{
847 // Load the date format
848 const auto type = static_cast<MessageCore::DateFormatter::FormatType>(MessageCore::MessageCoreSettings::self()->dateFormat());
849 mDateFormatter->setCustomFormat(MessageCore::MessageCoreSettings::self()->customDateFormat());
850 mDateFormatter->setFormat(type);
851}
852
853void Manager::loadConfiguration()
854{
855 loadGlobalConfiguration();
856
857 {
858 // load Aggregations
859
860 KConfigGroup conf(MessageListSettings::self()->config(), QStringLiteral("MessageListView::Aggregations"));
861
862 mAggregations.clear();
863
864 const int cnt = conf.readEntry("Count", 0);
865
866 int idx = 0;
867 while (idx < cnt) {
868 const QString data = conf.readEntry(QStringLiteral("Set%1").arg(idx), QString());
869 if (!data.isEmpty()) {
870 auto set = new Aggregation();
871 if (set->loadFromString(data)) {
872 if (Aggregation *old = mAggregations.value(set->id())) {
873 delete old;
874 }
875 mAggregations.insert(set->id(), set);
876 } else {
877 delete set; // b0rken
878 }
879 }
880 idx++;
881 }
882
883 if (mAggregations.isEmpty()) {
884 // don't allow zero configuration, create some presets
885 createDefaultAggregations();
886 }
887 }
888
889 {
890 // load Themes
891
892 KConfigGroup conf(MessageListSettings::self()->config(), QStringLiteral("MessageListView::Themes"));
893
894 mThemes.clear();
895
896 const int cnt = conf.readEntry("Count", 0);
897
898 int idx = 0;
899 while (idx < cnt) {
900 const QString data = conf.readEntry(QStringLiteral("Set%1").arg(idx), QString());
901 if (!data.isEmpty()) {
902 auto set = new Theme();
903 if (set->loadFromString(data)) {
904 if (Theme *old = mThemes.value(set->id())) {
905 delete old;
906 }
907 mThemes.insert(set->id(), set);
908 } else {
909 qCWarning(MESSAGELIST_LOG) << "Saved theme loading failed";
910 delete set; // b0rken
911 }
912 }
913 ++idx;
914 }
915
916 if (mThemes.isEmpty()) {
917 // don't allow zero configuration, create some presets
918 createDefaultThemes();
919 }
920 }
921}
922
923void Manager::saveGlobalConfiguration()
924{
925 MessageListSettings::self()->save();
926}
927
928void Manager::saveConfiguration()
929{
930 saveGlobalConfiguration();
931
932 {
933 // store aggregations
934
935 KConfigGroup conf(MessageListSettings::self()->config(), QStringLiteral("MessageListView::Aggregations"));
936 // conf.clear();
937
938 conf.writeEntry("Count", mAggregations.count());
939
940 int idx = 0;
942 for (QMap<QString, Aggregation *>::ConstIterator it = mAggregations.constBegin(); it != end; ++it) {
943 conf.writeEntry(QStringLiteral("Set%1").arg(idx), (*it)->saveToString());
944 ++idx;
945 }
946 }
947
948 {
949 // store themes
950
951 KConfigGroup conf(MessageListSettings::self()->config(), QStringLiteral("MessageListView::Themes"));
952 // conf.clear();
953
954 conf.writeEntry("Count", mThemes.count());
955
956 int idx = 0;
957 QMap<QString, Theme *>::ConstIterator end(mThemes.constEnd());
958 for (QMap<QString, Theme *>::ConstIterator it = mThemes.constBegin(); it != end; ++it) {
959 conf.writeEntry(QStringLiteral("Set%1").arg(idx), (*it)->saveToString());
960 ++idx;
961 }
962 }
963
964 MessageListSettings::self()->config()->sync();
965}
966
967#include "moc_manager.cpp"
bool isValid() const
FormatType
The different types of date formats.
A set of aggregation options that can be applied to the MessageList::Model in a single shot.
Definition aggregation.h:29
@ NoGrouping
Don't group messages at all.
Definition aggregation.h:37
@ GroupByDateRange
Use smart (thread leader) date ranges ("Today","Yesterday","Last Week"...)
Definition aggregation.h:39
@ GroupBySenderOrReceiver
Group by sender (incoming) or receiver (outgoing) field.
Definition aggregation.h:40
@ GroupByDate
Group the messages by the date of the thread leader.
Definition aggregation.h:38
@ NeverExpandGroups
Never expand groups during a view fill algorithm.
Definition aggregation.h:54
@ ExpandRecentGroups
Makes sense only with GroupByDate or GroupByDateRange.
Definition aggregation.h:55
@ NoThreading
Perform no threading at all.
Definition aggregation.h:66
@ PerfectReferencesAndSubject
Thread by all of the above and try to match subjects too.
Definition aggregation.h:69
@ ExpandThreadsWithUnreadOrImportantMessages
Expand threads with "hot" messages (this includes new, unread, important, todo)
Definition aggregation.h:95
@ NeverExpandThreads
Never expand any thread, this is fast.
Definition aggregation.h:91
@ TopmostMessage
The thread grouping is computed from the topmost message (very similar to least recent,...
Definition aggregation.h:79
@ MostRecentMessage
The thread grouping is computed from the most recent message.
Definition aggregation.h:81
@ FavorSpeed
Do larger chunks of work, zero intervals between chunks.
@ FavorInteractivity
Do small chunks of work, small intervals between chunks to allow for UI event processing.
void reloadAllWidgets()
Explicitly reloads the contents of all the widgets.
Definition manager.cpp:822
void themesConfigurationCompleted()
This is called by the theme configuration dialog once the sets have been changed.
Definition manager.cpp:810
void reloadGlobalConfiguration()
Reloads the global configuration from the config files (so we assume it has changed) The settings pri...
Definition manager.cpp:832
void aggregationsConfigurationCompleted()
This is called by the aggregation configuration dialog once the sets have been changed.
Definition manager.cpp:326
const QString & id() const
Returns the unique id of this OptionSet.
Definition optionset.h:51
void setName(const QString &name)
Sets the name of this OptionSet.
Definition optionset.h:69
void setDescription(const QString &description)
Sets the description for this option set.
Definition optionset.h:87
bool loadFromString(const QString &data)
Attempts to unpack this configuration object from a string (that is likely to come out from a config ...
Definition optionset.cpp:69
A class which holds information about sorting, e.g.
Definition sortorder.h:23
void readConfig(KConfigGroup &conf, const QString &storageId, bool *storageUsesPrivateSortOrder)
Reads the sort order from a config group.
void writeConfig(KConfigGroup &conf, const QString &storageId, bool storageUsesPrivateSortOrder) const
Writes the sort order to a config group.
MessageSorting
The available message sorting options.
Definition sortorder.h:60
@ SortMessagesBySize
Sort the messages by size.
Definition sortorder.h:68
@ SortMessagesByAttachmentStatus
Sort the messages By "Important" flags of status.
Definition sortorder.h:72
@ SortMessagesByDateTime
Sort the messages by date and time.
Definition sortorder.h:62
@ SortMessagesByActionItemStatus
Sort the messages by the "Action Item" flag of status.
Definition sortorder.h:69
@ NoMessageSorting
Don't sort the messages at all.
Definition sortorder.h:61
@ SortMessagesBySenderOrReceiver
Sort the messages by sender or receiver.
Definition sortorder.h:64
@ SortMessagesBySender
Sort the messages by sender.
Definition sortorder.h:65
@ SortMessagesByReceiver
Sort the messages by receiver.
Definition sortorder.h:66
@ SortMessagesByDateTimeOfMostRecent
Sort the messages by date and time of the most recent message in subtree.
Definition sortorder.h:63
@ SortMessagesByUnreadStatus
Sort the messages by the "Unread" flags of status.
Definition sortorder.h:70
@ SortMessagesBySubject
Sort the messages by subject.
Definition sortorder.h:67
The QAbstractItemModel based interface that you need to provide for your storage to work with Message...
virtual QString id() const =0
Returns an unique id for this Storage collection.
The Column class defines a view column available inside this theme.
Definition theme.h:501
void setMessageSorting(SortOrder::MessageSorting ms)
Sets the sort order for messages that we should switch to when clicking on this column's header (if v...
Definition theme.cpp:675
void setVisibleByDefault(bool vbd)
Sets the "visible by default" tag for this column.
Definition theme.cpp:654
void setIsSenderOrReceiver(bool sor)
Marks this column as containing the "sender/receiver" field.
Definition theme.cpp:644
void addGroupHeaderRow(Row *row)
Appends a group header row to this theme.
Definition theme.cpp:724
void setLabel(const QString &label)
Sets the label for this column.
Definition theme.cpp:624
void addMessageRow(Row *row)
Appends a message row to this theme column.
Definition theme.cpp:712
The ContentItem class defines a content item inside a Row.
Definition theme.h:56
void setSoftenByBlending(bool softenByBlending)
Sets the flag that causes this item to be painted "softly".
Definition theme.cpp:256
void setBold(bool isBold)
Makes this item use a bold font.
Definition theme.cpp:200
void setSoftenByBlendingWhenDisabled(bool softenByBlendingWhenDisabled)
Sets the flag that causes this item to be painted "softly" when disabled.
Definition theme.cpp:242
void setItalic(bool isItalic)
Makes this item use italic font.
Definition theme.cpp:214
Type
The available ContentItem types.
Definition theme.h:106
@ InvitationIcon
Whether the message is an invitation.
Definition theme.h:198
@ CombinedReadRepliedStateIcon
The combined icon that displays the unread/read/replied/forwarded state (never disabled)
Definition theme.h:190
@ SignatureStateIcon
The Signature state icon for messages.
Definition theme.h:174
@ Date
Formatted date time of the message/group.
Definition theme.h:114
@ MostRecentDate
The date of the most recent message in subtree.
Definition theme.h:186
@ ReadStateIcon
The icon that displays the unread/read state (never disabled)
Definition theme.h:134
@ RepliedStateIcon
The icon that displays the replied/forwarded state (may be disabled)
Definition theme.h:142
@ WatchedIgnoredStateIcon
The Watched/Ignored state icon.
Definition theme.h:162
@ ImportantStateIcon
The Important tag icon.
Definition theme.h:154
@ AttachmentStateIcon
The icon that displays the attachment state (may be disabled)
Definition theme.h:138
@ ExpandedStateIcon
The Expanded state icon for group headers.
Definition theme.h:166
@ SenderOrReceiver
From: or To: strip, depending on the folder settings.
Definition theme.h:118
@ GroupHeaderLabel
The group header label.
Definition theme.h:146
@ Subject
Display the subject of the message item.
Definition theme.h:110
@ EncryptionStateIcon
The Encryption state icon for messages.
Definition theme.h:170
@ TagList
The list of MessageItem::Tag entries.
Definition theme.h:194
@ Size
Formatted size of the message.
Definition theme.h:130
@ Receiver
To: strip, always.
Definition theme.h:126
@ ActionItemStateIcon
The ActionItem state icon.
Definition theme.h:150
@ Sender
From: strip, always.
Definition theme.h:122
@ SpamHamStateIcon
The Spam/Ham state icon.
Definition theme.h:158
void setHideWhenDisabled(bool hideWhenDisabled)
Sets the flag that causes this item to be hidden when disabled.
Definition theme.cpp:228
The Row class defines a row of items inside a Column.
Definition theme.h:408
void addRightItem(ContentItem *item)
Adds a right aligned item to this row.
Definition theme.cpp:379
void addLeftItem(ContentItem *item)
Adds a left aligned item to this row.
Definition theme.cpp:367
The Theme class defines the visual appearance of the MessageList.
Definition theme.h:48
void setViewHeaderPolicy(ViewHeaderPolicy vhp)
Sets the ViewHeaderPolicy for this theme.
Definition theme.cpp:1049
void addColumn(Column *column)
Appends a column to this theme.
Definition theme.cpp:967
void resetColumnState()
Resets the column state (visibility and width) to their default values (the "visible by default" ones...
Definition theme.cpp:935
Provides a widget which has the messagelist and the most important helper widgets,...
Definition widgetbase.h:42
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & end()
The implementation independent part of the MessageList library.
Definition aggregation.h:22
typedef ConstIterator
typedef ConstIterator
const_iterator constBegin() const const
Q_EMITQ_EMIT
bool disconnect(const QMetaObject::Connection &connection)
bool isEmpty() const const
QString number(double n, char format, int precision)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:46:36 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.