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

KDE's Doxygen guidelines are available online.