Messagelib

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

KDE's Doxygen guidelines are available online.