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/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 {
36  mInstance = this;
37 
38  mCachedLocalizedUnknownText = i18nc("Unknown date", "Unknown");
39 
40  loadConfiguration();
41  connect(MessageListSettings::self(), &MessageListSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
42  connect(MessageCore::MessageCoreSettings::self(), &MessageCore::MessageCoreSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
43 }
44 
45 Manager::~Manager()
46 {
47  disconnect(MessageListSettings::self(), &MessageListSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
48  disconnect(MessageCore::MessageCoreSettings::self(), &MessageCore::MessageCoreSettings::configChanged, this, &Manager::reloadGlobalConfiguration);
49 
50  saveConfiguration();
51  removeAllAggregations();
52  removeAllThemes();
53 
54  delete mDateFormatter;
55 
56  mInstance = nullptr;
57 }
58 
59 void Manager::registerWidget(Widget *pWidget)
60 {
61  if (!mInstance) {
62  mInstance = new Manager();
63  }
64 
65  mInstance->mWidgetList.append(pWidget);
66 }
67 
68 void Manager::unregisterWidget(Widget *pWidget)
69 {
70  if (!mInstance) {
71  qCWarning(MESSAGELIST_LOG) << ("ERROR: MessageList::Manager::unregisterWidget() called when Manager::mInstance is null");
72  return;
73  }
74 
75  mInstance->mWidgetList.removeAll(pWidget);
76 
77  if (mInstance->mWidgetList.isEmpty()) {
78  delete mInstance;
79  mInstance = nullptr;
80  }
81 }
82 
83 const Aggregation *Manager::aggregation(const QString &id)
84 {
85  Aggregation *opt = mAggregations.value(id);
86  if (opt) {
87  return opt;
88  }
89 
90  return defaultAggregation();
91 }
92 
93 const Aggregation *Manager::defaultAggregation()
94 {
95  KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup());
96 
97  const QString aggregationId = conf.readEntry(QStringLiteral("DefaultSet"), "");
98 
99  Aggregation *opt = nullptr;
100 
101  if (!aggregationId.isEmpty()) {
102  opt = mAggregations.value(aggregationId);
103  }
104 
105  if (opt) {
106  return opt;
107  }
108 
109  // try just the first one
111  if (it != mAggregations.constEnd()) {
112  return *it;
113  }
114 
115  // aargh
116  createDefaultAggregations();
117 
118  return *(mAggregations.constBegin());
119 }
120 
121 void Manager::saveAggregationForStorageModel(const Akonadi::Collection &col, const QString &id, bool storageUsesPrivateAggregation)
122 {
123  if (!col.isValid()) {
124  return;
125  }
126  saveAggregationForStorageModel(QString::number(col.id()), id, storageUsesPrivateAggregation);
127 }
128 
129 void Manager::saveAggregationForStorageModel(const StorageModel *storageModel, const QString &id, bool storageUsesPrivateAggregation)
130 {
131  saveAggregationForStorageModel(storageModel->id(), id, storageUsesPrivateAggregation);
132 }
133 
134 void Manager::saveAggregationForStorageModel(const QString &modelId, const QString &id, bool storageUsesPrivateAggregation)
135 {
136  KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelAggregationsGroup());
137 
138  if (storageUsesPrivateAggregation) {
139  conf.writeEntry(MessageList::Util::setForStorageModelConfigName().arg(modelId), id);
140  } else {
141  conf.deleteEntry(MessageList::Util::setForStorageModelConfigName().arg(modelId));
142  }
143 
144  if (!storageUsesPrivateAggregation) {
145  conf.writeEntry(QStringLiteral("DefaultSet"), id);
146  }
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 }
430 
431 const Theme *Manager::themeForStorageModel(const Akonadi::Collection &col, bool *storageUsesPrivateTheme)
432 {
433  Q_ASSERT(storageUsesPrivateTheme);
434 
435  *storageUsesPrivateTheme = false; // this is by default
436 
437  if (!col.isValid()) {
438  return defaultTheme();
439  }
440  return Manager::themeForStorageModel(QString::number(col.id()), storageUsesPrivateTheme);
441 }
442 
443 const Theme *Manager::themeForStorageModel(const StorageModel *storageModel, bool *storageUsesPrivateTheme)
444 {
445  Q_ASSERT(storageUsesPrivateTheme);
446 
447  *storageUsesPrivateTheme = false; // this is by default
448 
449  if (!storageModel) {
450  return defaultTheme();
451  }
452  return Manager::themeForStorageModel(storageModel->id(), storageUsesPrivateTheme);
453 }
454 
455 const Theme *Manager::themeForStorageModel(const QString &id, bool *storageUsesPrivateTheme)
456 {
457  KConfigGroup conf(MessageListSettings::self()->config(), MessageList::Util::storageModelThemesGroup());
458  const QString themeId = conf.readEntry(MessageList::Util::setForStorageModelConfigName().arg(id), "");
459 
460  Theme *opt = nullptr;
461 
462  if (!themeId.isEmpty()) {
463  // a private theme was stored
464  opt = mThemes.value(themeId);
465  *storageUsesPrivateTheme = (opt != nullptr);
466  }
467 
468  if (opt) {
469  return opt;
470  }
471 
472  // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset...
473  // We could even try to guess if the storageModel is a mailing list
474 
475  // FIXME: Prefer right-to-left themes when application layout is RTL.
476 
477  return defaultTheme();
478 }
479 
480 void Manager::addTheme(Theme *set)
481 {
482  Theme *old = mThemes.value(set->id());
483  delete old;
484  mThemes.insert(set->id(), set);
485 }
486 
487 static Theme::Column *add_theme_simple_text_column(Theme *s,
488  const QString &name,
490  bool visibleByDefault,
491  SortOrder::MessageSorting messageSorting,
492  bool alignRight,
493  bool addGroupHeaderItem)
494 {
495  auto c = new Theme::Column();
496  c->setLabel(name);
497  c->setVisibleByDefault(visibleByDefault);
498  c->setMessageSorting(messageSorting);
499 
500  auto r = new Theme::Row();
501 
502  auto i = new Theme::ContentItem(type);
503 
504  if (alignRight) {
505  r->addRightItem(i);
506  } else {
507  r->addLeftItem(i);
508  }
509 
510  c->addMessageRow(r);
511 
512  if (addGroupHeaderItem) {
513  auto row = new Theme::Row();
514 
515  auto iRow = new Theme::ContentItem(type);
516 
517  if (alignRight) {
518  row->addRightItem(iRow);
519  } else {
520  row->addLeftItem(iRow);
521  }
522 
523  c->addGroupHeaderRow(row);
524  }
525 
526  s->addColumn(c);
527 
528  return c;
529 }
530 
531 static Theme::Column *add_theme_simple_icon_column(Theme *s,
532  const QString &name,
533  const QString &pixmapName,
535  bool visibleByDefault,
536  SortOrder::MessageSorting messageSorting)
537 {
538  auto c = new Theme::Column();
539  c->setLabel(name);
540  c->setPixmapName(pixmapName);
541  c->setVisibleByDefault(visibleByDefault);
542  c->setMessageSorting(messageSorting);
543 
544  auto r = new Theme::Row();
545 
546  auto i = new Theme::ContentItem(type);
547  i->setSoftenByBlendingWhenDisabled(true);
548 
549  r->addLeftItem(i);
550 
551  c->addMessageRow(r);
552 
553  s->addColumn(c);
554 
555  return c;
556 }
557 
558 void Manager::createDefaultThemes()
559 {
560  Theme *s;
561  Theme::Column *c;
562  Theme::Row *r;
563  Theme::ContentItem *i;
564 
565  // The "Classic" backward compatible theme
566 
567  s = new Theme(i18nc("Default theme name", "Classic"), i18n("A simple, backward compatible, single row theme"), true /*readOnly*/
568  );
569 
570  c = new Theme::Column();
571  c->setLabel(i18nc("@title:column Subject of messages", "Subject"));
572  c->setMessageSorting(SortOrder::SortMessagesBySubject);
573 
574  r = new Theme::Row();
575  i = new Theme::ContentItem(Theme::ContentItem::ExpandedStateIcon);
576  r->addLeftItem(i);
577  i = new Theme::ContentItem(Theme::ContentItem::GroupHeaderLabel);
578  i->setBold(true);
579  r->addLeftItem(i);
580  c->addGroupHeaderRow(r);
581 
582  r = new Theme::Row();
583  i = new Theme::ContentItem(Theme::ContentItem::CombinedReadRepliedStateIcon);
584  r->addLeftItem(i);
585  i = new Theme::ContentItem(Theme::ContentItem::AttachmentStateIcon);
586  i->setHideWhenDisabled(true);
587  r->addLeftItem(i);
588  i = new Theme::ContentItem(Theme::ContentItem::AnnotationIcon);
589  i->setHideWhenDisabled(true);
590  r->addLeftItem(i);
591  i = new Theme::ContentItem(Theme::ContentItem::InvitationIcon);
592  i->setHideWhenDisabled(true);
593  r->addLeftItem(i);
594  i = new Theme::ContentItem(Theme::ContentItem::SignatureStateIcon);
595  i->setHideWhenDisabled(true);
596  r->addLeftItem(i);
597  i = new Theme::ContentItem(Theme::ContentItem::EncryptionStateIcon);
598  i->setHideWhenDisabled(true);
599  r->addLeftItem(i);
600  i = new Theme::ContentItem(Theme::ContentItem::Subject);
601  r->addLeftItem(i);
602  c->addMessageRow(r);
603 
604  s->addColumn(c);
605 
606  c = add_theme_simple_text_column(s,
607  i18n("Sender/Receiver"),
608  Theme::ContentItem::SenderOrReceiver,
609  true,
610  SortOrder::SortMessagesBySenderOrReceiver,
611  false,
612  false);
613  c->setIsSenderOrReceiver(true);
614  add_theme_simple_text_column(s, i18nc("Sender of a message", "Sender"), Theme::ContentItem::Sender, false, SortOrder::SortMessagesBySender, false, false);
615  add_theme_simple_text_column(s,
616  i18nc("Receiver of a message", "Receiver"),
617  Theme::ContentItem::Receiver,
618  false,
619  SortOrder::SortMessagesByReceiver,
620  false,
621  false);
622  add_theme_simple_text_column(s, i18nc("Date of a message", "Date"), Theme::ContentItem::Date, true, SortOrder::SortMessagesByDateTime, false, false);
623  add_theme_simple_text_column(s,
624  i18n("Most Recent Date"),
625  Theme::ContentItem::MostRecentDate,
626  false,
627  SortOrder::SortMessagesByDateTimeOfMostRecent,
628  false,
629  true);
630  add_theme_simple_text_column(s, i18nc("Size of a message", "Size"), Theme::ContentItem::Size, false, SortOrder::SortMessagesBySize, false, false);
631  add_theme_simple_icon_column(s,
632  i18nc("Attachment indication", "Attachment"),
633  QStringLiteral("mail-attachment"),
634  Theme::ContentItem::AttachmentStateIcon,
635  false,
636  SortOrder::SortMessagesByAttachmentStatus);
637  add_theme_simple_icon_column(s,
638  i18n("Read/Unread"),
639  QStringLiteral("mail-mark-unread-new"),
640  Theme::ContentItem::ReadStateIcon,
641  false,
642  SortOrder::SortMessagesByUnreadStatus);
643  add_theme_simple_icon_column(s, i18n("Replied"), QStringLiteral("mail-replied"), Theme::ContentItem::RepliedStateIcon, false, SortOrder::NoMessageSorting);
644  add_theme_simple_icon_column(s,
645  i18nc("Message importance indication", "Important"),
646  QStringLiteral("mail-mark-important"),
647  Theme::ContentItem::ImportantStateIcon,
648  false,
649  SortOrder::SortMessagesByImportantStatus);
650  add_theme_simple_icon_column(s,
651  i18n("Action Item"),
652  QStringLiteral("mail-task"),
653  Theme::ContentItem::ActionItemStateIcon,
654  false,
655  SortOrder::SortMessagesByActionItemStatus);
656  add_theme_simple_icon_column(s,
657  i18n("Spam/Ham"),
658  QStringLiteral("mail-mark-junk"),
659  Theme::ContentItem::SpamHamStateIcon,
660  false,
661  SortOrder::NoMessageSorting);
662  add_theme_simple_icon_column(s,
663  i18n("Watched/Ignored"),
664  QStringLiteral("mail-thread-watch"),
665  Theme::ContentItem::WatchedIgnoredStateIcon,
666  false,
667  SortOrder::NoMessageSorting);
668  add_theme_simple_icon_column(s,
669  i18n("Encryption"),
670  QStringLiteral("mail-encrypted-full"),
671  Theme::ContentItem::EncryptionStateIcon,
672  false,
673  SortOrder::NoMessageSorting);
674  add_theme_simple_icon_column(s,
675  i18n("Signature"),
676  QStringLiteral("mail-signed-verified"),
677  Theme::ContentItem::SignatureStateIcon,
678  false,
679  SortOrder::NoMessageSorting);
680  add_theme_simple_icon_column(s, i18n("Tag List"), QStringLiteral("feed-subscribe"), Theme::ContentItem::TagList, false, SortOrder::NoMessageSorting);
681 
682  s->resetColumnState(); // so it's initially set from defaults
683 
684  addTheme(s);
685 
686  // The Fancy theme
687 
688  s = new Theme(i18n("Smart"), i18n("A smart multiline and multi item theme"), true /*readOnly*/
689  );
690 
691  c = new Theme::Column();
692  c->setLabel(i18n("Message"));
693 
694  r = new Theme::Row();
695  i = new Theme::ContentItem(Theme::ContentItem::ExpandedStateIcon);
696  r->addLeftItem(i);
697  i = new Theme::ContentItem(Theme::ContentItem::GroupHeaderLabel);
698  i->setBold(true);
699  r->addLeftItem(i);
700  c->addGroupHeaderRow(r);
701 
702  r = new Theme::Row();
703  i = new Theme::ContentItem(Theme::ContentItem::Subject);
704  r->addLeftItem(i);
705  i = new Theme::ContentItem(Theme::ContentItem::ReadStateIcon);
706  r->addRightItem(i);
707  i = new Theme::ContentItem(Theme::ContentItem::RepliedStateIcon);
708  i->setHideWhenDisabled(true);
709  r->addRightItem(i);
710  i = new Theme::ContentItem(Theme::ContentItem::AttachmentStateIcon);
711  i->setHideWhenDisabled(true);
712  r->addRightItem(i);
713  i = new Theme::ContentItem(Theme::ContentItem::AnnotationIcon);
714  i->setHideWhenDisabled(true);
715  r->addRightItem(i);
716  i = new Theme::ContentItem(Theme::ContentItem::InvitationIcon);
717  i->setHideWhenDisabled(true);
718  r->addRightItem(i);
719  i = new Theme::ContentItem(Theme::ContentItem::EncryptionStateIcon);
720  i->setHideWhenDisabled(true);
721  r->addRightItem(i);
722  i = new Theme::ContentItem(Theme::ContentItem::SignatureStateIcon);
723  i->setHideWhenDisabled(true);
724  r->addRightItem(i);
725  i = new Theme::ContentItem(Theme::ContentItem::TagList);
726  i->setHideWhenDisabled(true);
727  r->addRightItem(i);
728  c->addMessageRow(r);
729 
730  Theme::Row *firstFancyRow = r; // save it so we can continue adding stuff below (after cloning the theme)
731 
732  r = new Theme::Row();
733  i = new Theme::ContentItem(Theme::ContentItem::SenderOrReceiver);
734  i->setSoftenByBlending(true);
735  i->setItalic(true);
736  r->addLeftItem(i);
737  i = new Theme::ContentItem(Theme::ContentItem::Date);
738  i->setSoftenByBlending(true);
739  i->setItalic(true);
740  r->addRightItem(i);
741  c->addMessageRow(r);
742 
743  s->addColumn(c);
744 
745  // clone the "Fancy theme" here so we'll use it as starting point for the "Fancy with clickable status"
746  auto fancyWithClickableStatus = new Theme(*s);
747  fancyWithClickableStatus->detach();
748  fancyWithClickableStatus->generateUniqueId();
749 
750  // and continue the "Fancy" specific settings
751  r = firstFancyRow;
752 
753  i = new Theme::ContentItem(Theme::ContentItem::ActionItemStateIcon);
754  i->setHideWhenDisabled(true);
755  r->addRightItem(i);
756  i = new Theme::ContentItem(Theme::ContentItem::ImportantStateIcon);
757  i->setHideWhenDisabled(true);
758  r->addRightItem(i);
759  i = new Theme::ContentItem(Theme::ContentItem::SpamHamStateIcon);
760  i->setHideWhenDisabled(true);
761  r->addRightItem(i);
762  i = new Theme::ContentItem(Theme::ContentItem::WatchedIgnoredStateIcon);
763  i->setHideWhenDisabled(true);
764  r->addRightItem(i);
765 
766  s->setViewHeaderPolicy(Theme::NeverShowHeader);
767 
768  s->resetColumnState(); // so it's initially set from defaults
769 
770  addTheme(s);
771 
772  // The "Fancy with Clickable Status" theme
773 
774  s = fancyWithClickableStatus;
775 
776  s->setName(i18n("Smart with Clickable Status"));
777  s->setDescription(i18n("A smart multiline and multi item theme with a clickable status column"));
778  s->setReadOnly(true);
779 
780  c = new Theme::Column();
781  c->setLabel(i18n("Status"));
782  c->setVisibleByDefault(true);
783 
784  r = new Theme::Row();
785  i = new Theme::ContentItem(Theme::ContentItem::ActionItemStateIcon);
786  i->setSoftenByBlendingWhenDisabled(true);
787  r->addLeftItem(i);
788  i = new Theme::ContentItem(Theme::ContentItem::ImportantStateIcon);
789  i->setSoftenByBlendingWhenDisabled(true);
790  r->addLeftItem(i);
791  c->addMessageRow(r);
792 
793  r = new Theme::Row();
794  i = new Theme::ContentItem(Theme::ContentItem::SpamHamStateIcon);
795  i->setSoftenByBlendingWhenDisabled(true);
796  r->addLeftItem(i);
797  i = new Theme::ContentItem(Theme::ContentItem::WatchedIgnoredStateIcon);
798  i->setSoftenByBlendingWhenDisabled(true);
799  r->addLeftItem(i);
800  c->addMessageRow(r);
801 
802  s->addColumn(c);
803 
804  s->resetColumnState(); // so it's initially set from defaults
805 
806  addTheme(s);
807 }
808 
809 void Manager::removeAllThemes()
810 {
811  QMap<QString, Theme *>::ConstIterator end(mThemes.constEnd());
812  for (QMap<QString, Theme *>::ConstIterator it = mThemes.constBegin(); it != end; ++it) {
813  delete (*it);
814  }
815 
816  mThemes.clear();
817 }
818 
819 void Manager::themesConfigurationCompleted()
820 {
821  if (mThemes.isEmpty()) {
822  createDefaultThemes(); // panic
823  }
824 
825  saveConfiguration(); // just to be sure :)
826 
827  // notify all the widgets that they should reload the option set combos
828  Q_EMIT themesChanged();
829 }
830 
831 void Manager::reloadAllWidgets()
832 {
833  QList<Widget *>::ConstIterator end(mWidgetList.constEnd());
834  for (QList<Widget *>::ConstIterator it = mWidgetList.constBegin(); it != end; ++it) {
835  if ((*it)->view()) {
836  (*it)->view()->reload();
837  }
838  }
839 }
840 
841 void Manager::reloadGlobalConfiguration()
842 {
843  // This is called when configuration changes (probably edited by the options dialog)
844  const int oldDateFormat = (int)mDateFormatter->format();
845  const QString oldDateCustomFormat = mDateFormatter->customFormat();
846 
847  loadGlobalConfiguration();
848 
849  if ((oldDateFormat != (int)mDateFormatter->format()) || (oldDateCustomFormat != mDateFormatter->customFormat())) {
850  reloadAllWidgets();
851  }
852 }
853 
854 void Manager::loadGlobalConfiguration()
855 {
856  // Load the date format
857  const auto type = static_cast<KMime::DateFormatter::FormatType>(MessageCore::MessageCoreSettings::self()->dateFormat());
858  mDateFormatter->setCustomFormat(MessageCore::MessageCoreSettings::self()->customDateFormat());
859  mDateFormatter->setFormat(type);
860 }
861 
862 void Manager::loadConfiguration()
863 {
864  loadGlobalConfiguration();
865 
866  {
867  // load Aggregations
868 
869  KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Aggregations");
870 
871  mAggregations.clear();
872 
873  const int cnt = conf.readEntry("Count", 0);
874 
875  int idx = 0;
876  while (idx < cnt) {
877  const QString data = conf.readEntry(QStringLiteral("Set%1").arg(idx), QString());
878  if (!data.isEmpty()) {
879  auto set = new Aggregation();
880  if (set->loadFromString(data)) {
881  if (Aggregation *old = mAggregations.value(set->id())) {
882  delete old;
883  }
884  mAggregations.insert(set->id(), set);
885  } else {
886  delete set; // b0rken
887  }
888  }
889  idx++;
890  }
891 
892  if (mAggregations.isEmpty()) {
893  // don't allow zero configuration, create some presets
894  createDefaultAggregations();
895  }
896  }
897 
898  {
899  // load Themes
900 
901  KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Themes");
902 
903  mThemes.clear();
904 
905  const int cnt = conf.readEntry("Count", 0);
906 
907  int idx = 0;
908  while (idx < cnt) {
909  const QString data = conf.readEntry(QStringLiteral("Set%1").arg(idx), QString());
910  if (!data.isEmpty()) {
911  auto set = new Theme();
912  if (set->loadFromString(data)) {
913  if (Theme *old = mThemes.value(set->id())) {
914  delete old;
915  }
916  mThemes.insert(set->id(), set);
917  } else {
918  qCWarning(MESSAGELIST_LOG) << "Saved theme loading failed";
919  delete set; // b0rken
920  }
921  }
922  ++idx;
923  }
924 
925  if (mThemes.isEmpty()) {
926  // don't allow zero configuration, create some presets
927  createDefaultThemes();
928  }
929  }
930 }
931 
932 void Manager::saveGlobalConfiguration()
933 {
934  MessageListSettings::self()->save();
935 }
936 
937 void Manager::saveConfiguration()
938 {
939  saveGlobalConfiguration();
940 
941  {
942  // store aggregations
943 
944  KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Aggregations");
945  // conf.clear();
946 
947  conf.writeEntry("Count", mAggregations.count());
948 
949  int idx = 0;
951  for (QMap<QString, Aggregation *>::ConstIterator it = mAggregations.constBegin(); it != end; ++it) {
952  conf.writeEntry(QStringLiteral("Set%1").arg(idx), (*it)->saveToString());
953  ++idx;
954  }
955  }
956 
957  {
958  // store themes
959 
960  KConfigGroup conf(MessageListSettings::self()->config(), "MessageListView::Themes");
961  // conf.clear();
962 
963  conf.writeEntry("Count", mThemes.count());
964 
965  int idx = 0;
966  QMap<QString, Theme *>::ConstIterator end(mThemes.constEnd());
967  for (QMap<QString, Theme *>::ConstIterator it = mThemes.constBegin(); it != end; ++it) {
968  conf.writeEntry(QStringLiteral("Set%1").arg(idx), (*it)->saveToString());
969  ++idx;
970  }
971  }
972 
973  MessageListSettings::self()->config()->sync();
974 }
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
KSharedConfigPtr config()
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:48
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:107
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Mar 24 2023 04:08:31 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.