Messagelib

theme.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/theme.h"
10
11#include <QApplication>
12#include <QDataStream>
13#include <QIcon>
14#include <QPixmap>
15#include <QStandardPaths>
16
17#include "messagelist_debug.h"
18#include <KLocalizedString>
19
20using namespace MessageList::Core;
21
22//
23// Theme versioning
24//
25// The themes simply have a DWORD version number attached.
26// The earliest version we're able to load is 0x1013.
27//
28// Theme revision history:
29//
30// Version Date introduced Description
31// --------------------------------------------------------------------------------------------------------------
32// 0x1013 08.11.2008 Initial theme version, introduced when this piece of code has been moved into trunk.
33// 0x1014 12.11.2008 Added runtime column data: width and column visibility
34// 0x1015 03.03.2009 Added icon size
35// 0x1016 08.03.2009 Added support for sorting by New/Unread status
36// 0x1017 16.08.2009 Added support for column icon
37// 0x1018 17.01.2010 Added support for annotation icon
38// 0x1019 13.07.2010 Added support for invitation icon
39//
40static const int gThemeCurrentVersion = 0x1019; // increase if you add new fields or change the meaning of some
41// you don't need to change the values below, but you might want to add new ones
42static const int gThemeMinimumSupportedVersion = 0x1013;
43static const int gThemeMinimumVersionWithColumnRuntimeData = 0x1014;
44static const int gThemeMinimumVersionWithIconSizeField = 0x1015;
45static const int gThemeMinimumVersionWithSortingByUnreadStatusAllowed = 0x1016;
46static const int gThemeMinimumVersionWithColumnIcon = 0x1017;
47static const int gThemeMinimumVersionWithAnnotationIcon = 0x1018;
48static const int gThemeMinimumVersionWithInvitationIcon = 0x1019;
49
50// the default icon size
51static const int gThemeDefaultIconSize = 16;
52
54 : mType(type)
55 , mFlags(0)
56{
57}
58
60
61 = default;
62
64{
65 return mType;
66}
67
69{
70 return (static_cast<int>(mType) & CanBeDisabled) != 0;
71}
72
74{
75 return (static_cast<int>(mType) & CanUseCustomColor) != 0;
76}
77
79{
80 return (static_cast<int>(mType) & DisplaysText) != 0;
81}
82
84{
85 return (static_cast<int>(mType) & LongText) != 0;
86}
87
89{
90 return (static_cast<int>(mType) & IsIcon) != 0;
91}
92
94{
95 return (static_cast<int>(mType) & IsClickable) != 0;
96}
97
99{
100 return (static_cast<int>(mType) & IsSpacer) != 0;
101}
102
104{
105 switch (type) {
106 case Subject:
107 return i18nc("Description of Type Subject", "Subject");
108 break;
109 case Date:
110 return i18nc("Description of Type Date", "Date");
111 break;
112 case SenderOrReceiver:
113 return i18n("Sender/Receiver");
114 break;
115 case Sender:
116 return i18nc("Description of Type Sender", "Sender");
117 break;
118 case Receiver:
119 return i18nc("Description of Type Receiver", "Receiver");
120 break;
121 case Size:
122 return i18nc("Description of Type Size", "Size");
123 break;
124 case ReadStateIcon:
125 return i18n("Unread/Read Icon");
126 break;
127 case AttachmentStateIcon:
128 return i18n("Attachment Icon");
129 break;
130 case RepliedStateIcon:
131 return i18n("Replied/Forwarded Icon");
132 break;
133 case CombinedReadRepliedStateIcon:
134 return i18n("Combined New/Unread/Read/Replied/Forwarded Icon");
135 break;
136 case ActionItemStateIcon:
137 return i18n("Action Item Icon");
138 break;
139 case ImportantStateIcon:
140 return i18n("Important Icon");
141 break;
142 case GroupHeaderLabel:
143 return i18n("Group Header Label");
144 break;
145 case SpamHamStateIcon:
146 return i18n("Spam/Ham Icon");
147 break;
148 case WatchedIgnoredStateIcon:
149 return i18n("Watched/Ignored Icon");
150 break;
151 case ExpandedStateIcon:
152 return i18n("Group Header Expand/Collapse Icon");
153 break;
154 case EncryptionStateIcon:
155 return i18n("Encryption State Icon");
156 break;
157 case SignatureStateIcon:
158 return i18n("Signature State Icon");
159 break;
160 case VerticalLine:
161 return i18n("Vertical Separation Line");
162 break;
163 case HorizontalSpacer:
164 return i18n("Horizontal Spacer");
165 break;
166 case MostRecentDate:
167 return i18n("Max Date");
168 break;
169 case TagList:
170 return i18n("Message Tags");
171 break;
172 case AnnotationIcon:
173 return i18n("Note Icon");
174 case InvitationIcon:
175 return i18n("Invitation Icon");
176 case Folder:
177 return i18nc("Description of Type Folder", "Folder");
178 default:
179 return i18nc("Description for an Unknown Type", "Unknown");
180 break;
181 }
182}
183
185{
186 return mFlags & UseCustomColor;
187}
188
190{
191 if (useCustomColor) {
192 mFlags |= UseCustomColor;
193 } else {
194 mFlags &= ~UseCustomColor;
195 }
196}
197
199{
200 return mFlags & IsBold;
201}
202
204{
205 if (isBold) {
206 mFlags |= IsBold;
207 } else {
208 mFlags &= ~IsBold;
209 }
210}
211
213{
214 return mFlags & IsItalic;
215}
216
218{
219 if (isItalic) {
220 mFlags |= IsItalic;
221 } else {
222 mFlags &= ~IsItalic;
223 }
224}
225
227{
228 return mFlags & HideWhenDisabled;
229}
230
232{
233 if (hideWhenDisabled) {
234 mFlags |= HideWhenDisabled;
235 } else {
236 mFlags &= ~HideWhenDisabled;
237 }
238}
239
241{
242 return mFlags & SoftenByBlendingWhenDisabled;
243}
244
245void Theme::ContentItem::setSoftenByBlendingWhenDisabled(bool softenByBlendingWhenDisabled)
246{
247 if (softenByBlendingWhenDisabled) {
248 mFlags |= SoftenByBlendingWhenDisabled;
249 } else {
250 mFlags &= ~SoftenByBlendingWhenDisabled;
251 }
252}
253
255{
256 return mFlags & SoftenByBlending;
257}
258
260{
261 if (softenByBlending) {
262 mFlags |= SoftenByBlending;
263 } else {
264 mFlags &= ~SoftenByBlending;
265 }
266}
267
269{
270 return mCustomColor;
271}
272
274{
275 mCustomColor = clr;
276}
277
279{
280 return static_cast<int>(type) & ApplicableToMessageItems;
281}
282
284{
285 return static_cast<int>(type) & ApplicableToGroupHeaderItems;
286}
287
289{
290 stream << (int)mType;
291 stream << mFlags;
292 stream << mCustomColor;
293}
294
295bool Theme::ContentItem::load(QDataStream &stream, int /*themeVersion*/)
296{
297 int val;
298
299 stream >> val;
300 mType = static_cast<Type>(val);
301 switch (mType) {
302 case Subject:
303 case Date:
304 case SenderOrReceiver:
305 case Sender:
306 case Receiver:
307 case Size:
308 case ReadStateIcon:
309 case AttachmentStateIcon:
310 case RepliedStateIcon:
311 case GroupHeaderLabel:
312 case ActionItemStateIcon:
313 case ImportantStateIcon:
314 case SpamHamStateIcon:
315 case WatchedIgnoredStateIcon:
316 case ExpandedStateIcon:
317 case EncryptionStateIcon:
318 case SignatureStateIcon:
319 case VerticalLine:
320 case HorizontalSpacer:
321 case MostRecentDate:
322 case CombinedReadRepliedStateIcon:
323 case TagList:
324 case AnnotationIcon:
325 case InvitationIcon:
326 case Folder:
327 // ok
328 break;
329 default:
330 qCDebug(MESSAGELIST_LOG) << "Invalid content item type";
331 return false; // b0rken
332 break;
333 }
334
335 stream >> mFlags;
336 stream >> mCustomColor;
337 if (mFlags & UseCustomColor) {
338 if (!mCustomColor.isValid()) {
339 mFlags &= ~UseCustomColor;
340 }
341 }
342 return true;
343}
344
345Theme::Row::Row() = default;
346
347Theme::Row::Row(const Row &src)
348{
349 for (const auto ci : std::as_const(src.mLeftItems)) {
350 addLeftItem(new ContentItem(*ci));
351 }
352
353 for (const auto ci : std::as_const(src.mRightItems)) {
354 addRightItem(new ContentItem(*ci));
355 }
356}
357
358Theme::Row::~Row()
359{
360 removeAllLeftItems();
361 removeAllRightItems();
362}
363
365{
366 while (!mLeftItems.isEmpty()) {
367 delete mLeftItems.takeFirst();
368 }
369}
370
372{
373 mLeftItems.append(item);
374}
375
377{
378 while (!mRightItems.isEmpty()) {
379 delete mRightItems.takeFirst();
380 }
381}
382
384{
385 mRightItems.append(item);
386}
387
389{
390 if (idx >= mLeftItems.count()) {
391 mLeftItems.append(item);
392 return;
393 }
394 mLeftItems.insert(idx, item);
395}
396
398{
399 mLeftItems.removeAll(item);
400}
401
403{
404 return mRightItems;
405}
406
408{
409 if (idx >= mRightItems.count()) {
410 mRightItems.append(item);
411 return;
412 }
413 mRightItems.insert(idx, item);
414}
415
417{
418 mRightItems.removeAll(item);
419}
420
422{
423 for (const auto ci : std::as_const(mLeftItems)) {
424 if (ci->displaysText()) {
425 return true;
426 }
427 }
428 for (const auto ci : std::as_const(mRightItems)) {
429 if (ci->displaysText()) {
430 return true;
431 }
432 }
433 return false;
434}
435
436void Theme::Row::save(QDataStream &stream) const
437{
438 stream << (int)mLeftItems.count();
439
440 int cnt = mLeftItems.count();
441
442 for (int i = 0; i < cnt; ++i) {
443 ContentItem *ci = mLeftItems.at(i);
444 ci->save(stream);
445 }
446
447 stream << (int)mRightItems.count();
448
449 cnt = mRightItems.count();
450
451 for (int i = 0; i < cnt; ++i) {
452 ContentItem *ci = mRightItems.at(i);
453 ci->save(stream);
454 }
455}
456
457bool Theme::Row::LoadContentItem(int val, QDataStream &stream, int themeVersion, bool leftItem)
458{
459 if ((val < 0) || (val > 50)) {
460 return false; // senseless
461 }
462
463 // FIXME: Remove code duplication here
464
465 for (int i = 0; i < val; ++i) {
466 auto ci = new ContentItem(ContentItem::Subject); // dummy type
467 if (!ci->load(stream, themeVersion)) {
468 qCDebug(MESSAGELIST_LOG) << "Left content item loading failed";
469 delete ci;
470 return false;
471 }
472 if (leftItem) {
473 addLeftItem(ci);
474 } else {
475 addRightItem(ci);
476 }
477
478 // Add the annotation item next to the attachment icon, so that users upgrading from old
479 // versions don't manually need to set this.
480 // Don't do this for the stand-alone attachment column.
481 if (ci->type() == ContentItem::AttachmentStateIcon && themeVersion < gThemeMinimumVersionWithAnnotationIcon && val > 1) {
482 qCDebug(MESSAGELIST_LOG) << "Old theme version detected, adding annotation item next to attachment icon.";
483 auto annotationItem = new ContentItem(ContentItem::AnnotationIcon);
484 annotationItem->setHideWhenDisabled(true);
485 if (leftItem) {
486 addLeftItem(annotationItem);
487 } else {
488 addRightItem(annotationItem);
489 }
490 }
491
492 // Same as above, for the invitation icon
493 if (ci->type() == ContentItem::AttachmentStateIcon && themeVersion < gThemeMinimumVersionWithInvitationIcon && val > 1) {
494 qCDebug(MESSAGELIST_LOG) << "Old theme version detected, adding invitation item next to attachment icon.";
495 auto invitationItem = new ContentItem(ContentItem::InvitationIcon);
496 invitationItem->setHideWhenDisabled(true);
497 if (leftItem) {
498 addLeftItem(invitationItem);
499 } else {
500 addRightItem(invitationItem);
501 }
502 }
503 }
504 return true;
505}
506
508{
509 return mLeftItems;
510}
511
512bool Theme::Row::load(QDataStream &stream, int themeVersion)
513{
514 removeAllLeftItems();
515 removeAllRightItems();
516
517 int val;
518
519 // left item count
520
521 stream >> val;
522 if (!LoadContentItem(val, stream, themeVersion, true)) {
523 return false;
524 }
525
526 // right item count
527
528 stream >> val;
529
530 if (!LoadContentItem(val, stream, themeVersion, false)) {
531 return false;
532 }
533
534 return true;
535}
536
537Theme::Column::SharedRuntimeData::SharedRuntimeData(bool currentlyVisible, double currentWidth)
538 : mReferences(0)
539 , mCurrentlyVisible(currentlyVisible)
540 , mCurrentWidth(currentWidth)
541{
542}
543
545
547{
548 mReferences++;
549}
550
552{
553 mReferences--;
554 Q_ASSERT(mReferences >= 0);
555 return mReferences > 0;
556}
557
559{
560 return mReferences;
561}
562
564{
565 return mCurrentlyVisible;
566}
567
569{
570 mCurrentlyVisible = visible;
571}
572
574{
575 return mCurrentWidth;
576}
577
582
584{
585 stream << mCurrentlyVisible;
586 stream << mCurrentWidth;
587}
588
589bool Theme::Column::SharedRuntimeData::load(QDataStream &stream, int /* themeVersion */)
590{
591 stream >> mCurrentlyVisible;
592 stream >> mCurrentWidth;
593 if (mCurrentWidth > 10000) {
594 qCDebug(MESSAGELIST_LOG) << "Theme has insane column width " << mCurrentWidth << " chopping to 100";
595 mCurrentWidth = 100; // avoid really insane values
596 }
597 return mCurrentWidth >= -1;
598}
599
601 : mVisibleByDefault(true)
602 , mIsSenderOrReceiver(false)
603 , mMessageSorting(SortOrder::NoMessageSorting)
604{
605 mSharedRuntimeData = new SharedRuntimeData(true, -1);
606 mSharedRuntimeData->addReference();
607}
608
610{
611 mLabel = src.mLabel;
612 mPixmapName = src.mPixmapName;
613 mVisibleByDefault = src.mVisibleByDefault;
614 mIsSenderOrReceiver = src.mIsSenderOrReceiver;
615 mMessageSorting = src.mMessageSorting;
616
617 mSharedRuntimeData = src.mSharedRuntimeData;
618 mSharedRuntimeData->addReference();
619 for (const auto row : std::as_const(src.mMessageRows)) {
620 addMessageRow(new Row(*row));
621 }
622
623 for (const auto row : std::as_const(src.mGroupHeaderRows)) {
624 addGroupHeaderRow(new Row(*row));
625 }
626}
627
629{
630 removeAllMessageRows();
631 removeAllGroupHeaderRows();
632 if (!(mSharedRuntimeData->deleteReference())) {
633 delete mSharedRuntimeData;
634 }
635}
636
638{
639 return mLabel;
640}
641
643{
644 mLabel = label;
645}
646
648{
649 return mPixmapName;
650}
651
653{
654 mPixmapName = pixmapName;
655}
656
658{
659 return mIsSenderOrReceiver;
660}
661
663{
664 mIsSenderOrReceiver = sor;
665}
666
668{
669 return mVisibleByDefault;
670}
671
673{
674 mVisibleByDefault = vbd;
675}
676
678{
679 if (mSharedRuntimeData->referenceCount() < 2) {
680 return; // nothing to detach
681 }
682 mSharedRuntimeData->deleteReference();
683
684 mSharedRuntimeData = new SharedRuntimeData(mVisibleByDefault, -1);
685 mSharedRuntimeData->addReference();
686}
687
689{
690 return mMessageSorting;
691}
692
694{
695 mMessageSorting = ms;
696}
697
699{
700 return mSharedRuntimeData->currentlyVisible();
701}
702
703void Theme::Column::setCurrentlyVisible(bool currentlyVisible)
704{
705 mSharedRuntimeData->setCurrentlyVisible(currentlyVisible);
706}
707
709{
710 return mSharedRuntimeData->currentWidth();
711}
712
713void Theme::Column::setCurrentWidth(double currentWidth)
714{
715 mSharedRuntimeData->setCurrentWidth(currentWidth);
716}
717
719{
720 return mMessageRows;
721}
722
724{
725 while (!mMessageRows.isEmpty()) {
726 delete mMessageRows.takeFirst();
727 }
728}
729
731{
732 mMessageRows.append(row);
733}
734
736{
737 while (!mGroupHeaderRows.isEmpty()) {
738 delete mGroupHeaderRows.takeFirst();
739 }
740}
741
743{
744 mGroupHeaderRows.append(row);
745}
746
748{
749 if (idx >= mMessageRows.count()) {
750 mMessageRows.append(row);
751 return;
752 }
753 mMessageRows.insert(idx, row);
754}
755
757{
758 mMessageRows.removeAll(row);
759}
760
762{
763 return mGroupHeaderRows;
764}
765
767{
768 if (idx >= mGroupHeaderRows.count()) {
769 mGroupHeaderRows.append(row);
770 return;
771 }
772 mGroupHeaderRows.insert(idx, row);
773}
774
776{
777 mGroupHeaderRows.removeAll(row);
778}
779
781{
782 for (const auto row : std::as_const(mMessageRows)) {
783 if (row->containsTextItems()) {
784 return true;
785 }
786 }
787 for (const auto row : std::as_const(mGroupHeaderRows)) {
788 if (row->containsTextItems()) {
789 return true;
790 }
791 }
792 return false;
793}
794
796{
797 stream << mLabel;
798 stream << mPixmapName;
799 stream << mVisibleByDefault;
800 stream << mIsSenderOrReceiver;
801 stream << static_cast<int>(mMessageSorting);
802
803 stream << static_cast<int>(mGroupHeaderRows.count());
804
805 int cnt = mGroupHeaderRows.count();
806
807 for (int i = 0; i < cnt; ++i) {
808 Row *row = mGroupHeaderRows.at(i);
809 row->save(stream);
810 }
811
812 cnt = mMessageRows.count();
813 stream << static_cast<int>(cnt);
814
815 for (int i = 0; i < cnt; ++i) {
816 Row *row = mMessageRows.at(i);
817 row->save(stream);
818 }
819
820 // added in version 0x1014
821 mSharedRuntimeData->save(stream);
822}
823
824bool Theme::Column::load(QDataStream &stream, int themeVersion)
825{
826 removeAllGroupHeaderRows();
827 removeAllMessageRows();
828
829 stream >> mLabel;
830
831 if (themeVersion >= gThemeMinimumVersionWithColumnIcon) {
832 stream >> mPixmapName;
833 }
834
835 stream >> mVisibleByDefault;
836 stream >> mIsSenderOrReceiver;
837
838 int val;
839
840 stream >> val;
841 mMessageSorting = static_cast<SortOrder::MessageSorting>(val);
842 if (!SortOrder::isValidMessageSorting(mMessageSorting)) {
843 qCDebug(MESSAGELIST_LOG) << "Invalid message sorting";
844 return false;
845 }
846
847 if (themeVersion < gThemeMinimumVersionWithSortingByUnreadStatusAllowed) {
848 // The default "Classic" theme "Unread" column had sorting disabled here.
849 // We want to be nice to the existing users and automatically set
850 // the new sorting method for this column (so they don't have to make the
851 // complex steps to set it by themselves).
852 // This piece of code isn't strictly required: it's just a niceness :)
853 if ((mMessageSorting == SortOrder::NoMessageSorting) && (mLabel == i18n("Unread"))) {
855 }
856 }
857
858 // group header row count
859 stream >> val;
860
861 if ((val < 0) || (val > 50)) {
862 qCDebug(MESSAGELIST_LOG) << "Invalid group header row count";
863 return false; // senseless
864 }
865
866 for (int i = 0; i < val; ++i) {
867 Row *row = new Row();
868 if (!row->load(stream, themeVersion)) {
869 qCDebug(MESSAGELIST_LOG) << "Group header row loading failed";
870 delete row;
871 return false;
872 }
873 addGroupHeaderRow(row);
874 }
875
876 // message row count
877 stream >> val;
878
879 if ((val < 0) || (val > 50)) {
880 qCDebug(MESSAGELIST_LOG) << "Invalid message row count";
881 return false; // senseless
882 }
883
884 for (int i = 0; i < val; ++i) {
885 Row *row = new Row();
886 if (!row->load(stream, themeVersion)) {
887 qCDebug(MESSAGELIST_LOG) << "Message row loading failed";
888 delete row;
889 return false;
890 }
891 addMessageRow(row);
892 }
893
894 if (themeVersion >= gThemeMinimumVersionWithColumnRuntimeData) {
895 // starting with version 0x1014 we have runtime data too
896 if (!mSharedRuntimeData->load(stream, themeVersion)) {
897 qCDebug(MESSAGELIST_LOG) << "Shared runtime data loading failed";
898 return false;
899 }
900 } else {
901 // assume default shared data
902 mSharedRuntimeData->setCurrentlyVisible(mVisibleByDefault);
903 mSharedRuntimeData->setCurrentWidth(-1);
904 }
905
906 return true;
907}
908
910 : OptionSet()
911{
912 mGroupHeaderBackgroundMode = AutoColor;
913 mViewHeaderPolicy = ShowHeaderAlways;
914 mIconSize = gThemeDefaultIconSize;
915 mGroupHeaderBackgroundStyle = StyledJoinedRect;
916}
917
918Theme::Theme(const QString &name, const QString &description, bool readOnly)
919 : OptionSet(name, description, readOnly)
920{
921 mGroupHeaderBackgroundMode = AutoColor;
922 mGroupHeaderBackgroundStyle = StyledJoinedRect;
923 mViewHeaderPolicy = ShowHeaderAlways;
924 mIconSize = gThemeDefaultIconSize;
925}
926
928 : OptionSet(src)
929{
930 mGroupHeaderBackgroundMode = src.mGroupHeaderBackgroundMode;
931 mGroupHeaderBackgroundColor = src.mGroupHeaderBackgroundColor;
932 mGroupHeaderBackgroundStyle = src.mGroupHeaderBackgroundStyle;
933 mViewHeaderPolicy = src.mViewHeaderPolicy;
934 mIconSize = src.mIconSize;
935 for (const auto col : std::as_const(src.mColumns)) {
936 addColumn(new Column(*col));
937 }
938}
939
941{
942 clearPixmapCache();
944}
945
947{
948 for (const auto col : std::as_const(mColumns)) {
949 col->detach();
950 }
951}
952
954{
955 for (const auto col : std::as_const(mColumns)) {
956 col->setCurrentlyVisible(col->visibleByDefault());
957 col->setCurrentWidth(-1);
958 }
959}
960
962{
963 for (const auto col : std::as_const(mColumns)) {
964 col->setCurrentWidth(-1);
965 }
966}
967
969{
970 return mColumns;
971}
972
974{
975 return mColumns.count() > idx ? mColumns.at(idx) : nullptr;
976}
977
979{
980 while (!mColumns.isEmpty()) {
981 delete mColumns.takeFirst();
982 }
983}
984
986{
987 mColumns.append(column);
988}
989
990void Theme::insertColumn(int idx, Column *column)
991{
992 if (idx >= mColumns.count()) {
993 mColumns.append(column);
994 return;
995 }
996 mColumns.insert(idx, column);
997}
998
1000{
1001 mColumns.removeAll(col);
1002}
1003
1005{
1006 return mGroupHeaderBackgroundMode;
1007}
1008
1009void Theme::moveColumn(int idx, int newPosition)
1010{
1011 if ((newPosition >= mColumns.count()) || newPosition < 0) {
1012 return;
1013 }
1014 mColumns.move(idx, newPosition);
1015}
1016
1018{
1019 mGroupHeaderBackgroundMode = bm;
1020 if ((bm == CustomColor) && !mGroupHeaderBackgroundColor.isValid()) {
1021 mGroupHeaderBackgroundColor = QColor(127, 127, 127); // something neutral
1022 }
1023}
1024
1026{
1027 return mGroupHeaderBackgroundColor;
1028}
1029
1031{
1032 mGroupHeaderBackgroundColor = clr;
1033}
1034
1036{
1037 return mGroupHeaderBackgroundStyle;
1038}
1039
1041{
1042 mGroupHeaderBackgroundStyle = groupHeaderBackgroundStyle;
1043}
1044
1046{
1047 return {{i18n("Never Show"), NeverShowHeader}, {i18n("Always Show"), ShowHeaderAlways}};
1048}
1049
1051{
1052 return {{i18n("Plain Rectangles"), PlainRect},
1053 {i18n("Plain Joined Rectangle"), PlainJoinedRect},
1054 {i18n("Rounded Rectangles"), RoundedRect},
1055 {i18n("Rounded Joined Rectangle"), RoundedJoinedRect},
1056 {i18n("Gradient Rectangles"), GradientRect},
1057 {i18n("Gradient Joined Rectangle"), GradientJoinedRect},
1058 {i18n("Styled Rectangles"), StyledRect},
1059 {i18n("Styled Joined Rectangles"), StyledJoinedRect}};
1060}
1061
1063{
1064 return mViewHeaderPolicy;
1065}
1066
1068{
1069 mViewHeaderPolicy = vhp;
1070}
1071
1073{
1074 return mIconSize;
1075}
1076
1077void Theme::setIconSize(int iconSize)
1078{
1079 if (mIconSize != iconSize) {
1080 clearPixmapCache();
1081
1082 mIconSize = iconSize;
1083 if ((mIconSize < 8) || (mIconSize > 64)) {
1084 mIconSize = gThemeDefaultIconSize;
1085 }
1086 }
1087}
1088
1090{
1092
1093 int themeVersion;
1094
1095 stream >> themeVersion;
1096
1097 // We support themes starting at version gThemeMinimumSupportedVersion (0x1013 actually)
1098
1099 if ((themeVersion > gThemeCurrentVersion) || (themeVersion < gThemeMinimumSupportedVersion)) {
1100 qCDebug(MESSAGELIST_LOG) << "Invalid theme version";
1101 return false; // b0rken (invalid version)
1102 }
1103
1104 int val;
1105
1106 stream >> val;
1107 mGroupHeaderBackgroundMode = static_cast<GroupHeaderBackgroundMode>(val);
1108 switch (mGroupHeaderBackgroundMode) {
1109 case Transparent:
1110 case AutoColor:
1111 case CustomColor:
1112 // ok
1113 break;
1114 default:
1115 qCDebug(MESSAGELIST_LOG) << "Invalid theme group header background mode";
1116 return false; // b0rken
1117 }
1118
1119 stream >> mGroupHeaderBackgroundColor;
1120
1121 stream >> val;
1122 mGroupHeaderBackgroundStyle = static_cast<GroupHeaderBackgroundStyle>(val);
1123 switch (mGroupHeaderBackgroundStyle) {
1124 case PlainRect:
1125 case PlainJoinedRect:
1126 case RoundedRect:
1127 case RoundedJoinedRect:
1128 case GradientRect:
1129 case GradientJoinedRect:
1130 case StyledRect:
1131 case StyledJoinedRect:
1132 // ok
1133 break;
1134 default:
1135 qCDebug(MESSAGELIST_LOG) << "Invalid theme group header background style";
1136 return false; // b0rken
1137 }
1138
1139 stream >> val;
1140 mViewHeaderPolicy = (ViewHeaderPolicy)val;
1141 switch (mViewHeaderPolicy) {
1142 case ShowHeaderAlways:
1143 case NeverShowHeader:
1144 // ok
1145 break;
1146 default:
1147 qCDebug(MESSAGELIST_LOG) << "Invalid theme view header policy";
1148 return false; // b0rken
1149 }
1150
1151 if (themeVersion >= gThemeMinimumVersionWithIconSizeField) {
1152 // icon size parameter
1153 stream >> mIconSize;
1154 if ((mIconSize < 8) || (mIconSize > 64)) {
1155 mIconSize = gThemeDefaultIconSize; // limit insane values
1156 }
1157 } else {
1158 mIconSize = gThemeDefaultIconSize;
1159 }
1160
1161 // column count
1162 stream >> val;
1163 if (val < 1 || val > 50) {
1164 return false; // plain b0rken ( negative, zero or more than 50 columns )
1165 }
1166
1167 for (int i = 0; i < val; ++i) {
1168 auto col = new Column();
1169 if (!col->load(stream, themeVersion)) {
1170 qCDebug(MESSAGELIST_LOG) << "Column loading failed";
1171 delete col;
1172 return false;
1173 }
1174 addColumn(col);
1175 }
1176
1177 return true;
1178}
1179
1180void Theme::save(QDataStream &stream) const
1181{
1182 stream << (int)gThemeCurrentVersion;
1183
1184 stream << (int)mGroupHeaderBackgroundMode;
1185 stream << mGroupHeaderBackgroundColor;
1186 stream << (int)mGroupHeaderBackgroundStyle;
1187 stream << (int)mViewHeaderPolicy;
1188 stream << mIconSize;
1189
1190 const int cnt = mColumns.count();
1191 stream << (int)cnt;
1192
1193 for (int i = 0; i < cnt; ++i) {
1194 Column *col = mColumns.at(i);
1195 col->save(stream);
1196 }
1197}
1198
1199void Theme::clearPixmapCache() const
1200{
1201 qDeleteAll(mPixmaps);
1202 mPixmaps.clear();
1203}
1204
1205void Theme::populatePixmapCache() const
1206{
1207 clearPixmapCache();
1208
1209 mPixmaps.reserve(_IconCount);
1210 // WARNING: The order of those icons must be in sync with order of the
1211 // corresponding enum values in ThemeIcon!
1212 mPixmaps << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-unread-new")).pixmap(mIconSize, mIconSize))
1213 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-unread")).pixmap(mIconSize, mIconSize))
1214 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-read")).pixmap(mIconSize, mIconSize))
1215 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-deleted")).pixmap(mIconSize, mIconSize))
1216 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-replied")).pixmap(mIconSize, mIconSize))
1217 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-forwarded-replied")).pixmap(mIconSize, mIconSize))
1218 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-queued")).pixmap(mIconSize, mIconSize)) // mail-queue ?
1219 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-task")).pixmap(mIconSize, mIconSize))
1220 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-sent")).pixmap(mIconSize, mIconSize))
1221 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-forwarded")).pixmap(mIconSize, mIconSize))
1222 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-important")).pixmap(mIconSize, mIconSize)) // "flag"
1223 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-thread-watch")).pixmap(mIconSize, mIconSize))
1224 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-thread-ignored")).pixmap(mIconSize, mIconSize))
1225 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-junk")).pixmap(mIconSize, mIconSize))
1226 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-mark-notjunk")).pixmap(mIconSize, mIconSize))
1227 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-signed-verified")).pixmap(mIconSize, mIconSize))
1228 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-signed-part")).pixmap(mIconSize, mIconSize))
1229 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-signed")).pixmap(mIconSize, mIconSize))
1230 << new QPixmap(QIcon::fromTheme(QStringLiteral("text-plain")).pixmap(mIconSize, mIconSize))
1231 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-encrypted-full")).pixmap(mIconSize, mIconSize))
1232 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-encrypted-part")).pixmap(mIconSize, mIconSize))
1233 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-encrypted")).pixmap(mIconSize, mIconSize))
1234 << new QPixmap(QIcon::fromTheme(QStringLiteral("text-plain")).pixmap(mIconSize, mIconSize))
1235 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-attachment")).pixmap(mIconSize, mIconSize))
1236 << new QPixmap(QIcon::fromTheme(QStringLiteral("view-pim-notes")).pixmap(mIconSize, mIconSize))
1237 << new QPixmap(QIcon::fromTheme(QStringLiteral("mail-invitation")).pixmap(mIconSize, mIconSize))
1238 << ((QApplication::isRightToLeft()) ? new QPixmap(QIcon::fromTheme(QStringLiteral("arrow-left")).pixmap(mIconSize, mIconSize))
1239 : new QPixmap(QIcon::fromTheme(QStringLiteral("arrow-right")).pixmap(mIconSize, mIconSize)))
1240 << new QPixmap(QIcon::fromTheme(QStringLiteral("arrow-down")).pixmap(mIconSize, mIconSize))
1241 << new QPixmap(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("messagelist/pics/mail-vertical-separator-line.png")))
1242 << new QPixmap(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("messagelist/pics/mail-horizontal-space.png")));
1243}
A set of options that can be applied to the MessageList in one shot.
Definition optionset.h:33
A class which holds information about sorting, e.g.
Definition sortorder.h:23
static bool isValidMessageSorting(SortOrder::MessageSorting ms)
Returns true if the ms parameter specifies a valid MessageSorting option.
MessageSorting
The available message sorting options.
Definition sortorder.h:60
@ NoMessageSorting
Don't sort the messages at all.
Definition sortorder.h:61
@ SortMessagesByUnreadStatus
Sort the messages by the "Unread" flags of status.
Definition sortorder.h:70
double currentWidth() const
Returns the current width or -1 if the width is unspecified/invalid.
Definition theme.cpp:573
void setCurrentlyVisible(bool visible)
Sets the current visibility state.
Definition theme.cpp:568
void addReference()
Increments the reference count for this shared runtime data object.
Definition theme.cpp:546
int referenceCount() const
Returns the current number of reference counts, that is, the number of Theme::Column objects that use...
Definition theme.cpp:558
bool deleteReference()
Decrements the reference count for this shared runtime data object.
Definition theme.cpp:551
~SharedRuntimeData()
Destroy a shared runtime data object.
bool currentlyVisible() const
Returns the current visibility state.
Definition theme.cpp:563
void setCurrentWidth(double currentWidth)
Sets the current width of the column.
Definition theme.cpp:578
SharedRuntimeData(bool currentlyVisible, double currentWidth)
Create a shared runtime data object.
Definition theme.cpp:537
void save(QDataStream &stream) const
Saves this runtime data to the specified stream.
Definition theme.cpp:583
bool load(QDataStream &stream, int themeVersion)
Loads the shared runtime data from the specified stream assuming that it uses the specified theme ver...
Definition theme.cpp:589
The Column class defines a view column available inside this theme.
Definition theme.h:506
bool isSenderOrReceiver() const
Returns true if this column is marked as "sender/receiver" and we should update its label on-the-fly.
Definition theme.cpp:657
void removeGroupHeaderRow(Row *row)
Removes the specified group header row.
Definition theme.cpp:775
void removeMessageRow(Row *row)
Removes the specified message row.
Definition theme.cpp:756
void setPixmapName(const QString &pixmapName)
Sets the icon's name (used in SmallIcon) for this column.
Definition theme.cpp:652
double currentWidth() const
Returns the current shared width setting for this column or -1 if the width is not specified and shou...
Definition theme.cpp:708
const QList< Row * > & groupHeaderRows() const
Returns the list of rows visible in this column for a GroupHeaderItem.
Definition theme.cpp:761
void save(QDataStream &stream) const
Handles column saving (used by Theme::save())
Definition theme.cpp:795
void detach()
Detaches the shared runtime data object and makes this object totally independent.
Definition theme.cpp:677
void insertMessageRow(int idx, Row *row)
Inserts a message row to this theme column in the specified position.
Definition theme.cpp:747
SortOrder::MessageSorting messageSorting() const
Returns the sort order for messages that we should switch to when clicking on this column's header (i...
Definition theme.cpp:688
bool visibleByDefault() const
Returns true if this column has to be shown by default.
Definition theme.cpp:667
void setMessageSorting(SortOrder::MessageSorting ms)
Sets the sort order for messages that we should switch to when clicking on this column's header (if v...
Definition theme.cpp:693
void setVisibleByDefault(bool vbd)
Sets the "visible by default" tag for this column.
Definition theme.cpp:672
void setIsSenderOrReceiver(bool sor)
Marks this column as containing the "sender/receiver" field.
Definition theme.cpp:662
void addGroupHeaderRow(Row *row)
Appends a group header row to this theme.
Definition theme.cpp:742
void setCurrentWidth(double currentWidth)
Sets the current shared width setting for this column.
Definition theme.cpp:713
Column()
Create an empty column with default settings.
Definition theme.cpp:600
const QList< Row * > & messageRows() const
Returns the list of rows visible in this column for a MessageItem.
Definition theme.cpp:718
bool currentlyVisible() const
Returns the current shared visibility state for this column.
Definition theme.cpp:698
const QString & pixmapName() const
Returns the icon's name (used in SmallIcon) set for this column.
Definition theme.cpp:647
void setLabel(const QString &label)
Sets the label for this column.
Definition theme.cpp:642
void setCurrentlyVisible(bool currentlyVisible)
Sets the current shared visibility state for this column.
Definition theme.cpp:703
void insertGroupHeaderRow(int idx, Row *row)
Inserts a group header row to this theme column in the specified position.
Definition theme.cpp:766
const QString & label() const
Returns the label set for this column.
Definition theme.cpp:637
void removeAllGroupHeaderRows()
Removes all the group header rows from this column.
Definition theme.cpp:735
bool containsTextItems() const
Returns true if this column contains text items.
Definition theme.cpp:780
void removeAllMessageRows()
Removes all the message rows from this column.
Definition theme.cpp:723
~Column()
Kill a column object.
Definition theme.cpp:628
bool load(QDataStream &stream, int themeVersion)
Handles column loading (used by Theme::load())
Definition theme.cpp:824
void addMessageRow(Row *row)
Appends a message row to this theme column.
Definition theme.cpp:730
The ContentItem class defines a content item inside a Row.
Definition theme.h:56
bool displaysText() const
Returns true if this item displays some kind of text.
Definition theme.cpp:78
bool canBeDisabled() const
Returns true if this ContentItem can be in a "disabled" state.
Definition theme.cpp:68
void setSoftenByBlending(bool softenByBlending)
Sets the flag that causes this item to be painted "softly".
Definition theme.cpp:259
bool hideWhenDisabled() const
Returns true if this item should be hidden when in disabled state.
Definition theme.cpp:226
void setBold(bool isBold)
Makes this item use a bold font.
Definition theme.cpp:203
void setUseCustomColor(bool useCustomColor)
Makes this item use the custom color that can be set by setCustomColor().
Definition theme.cpp:189
static QString description(Type type)
Returns a descriptive name for the specified content item type.
Definition theme.cpp:103
static bool applicableToMessageItems(Type type)
Static test that returns true if an instance of ContentItem with the specified type makes sense in a ...
Definition theme.cpp:278
bool isSpacer() const
Returns true if this item is a small spacer.
Definition theme.cpp:98
void setSoftenByBlendingWhenDisabled(bool softenByBlendingWhenDisabled)
Sets the flag that causes this item to be painted "softly" when disabled.
Definition theme.cpp:245
bool isBold() const
Returns true if this item uses a bold text.
Definition theme.cpp:198
void setItalic(bool isItalic)
Makes this item use italic font.
Definition theme.cpp:217
void setCustomColor(const QColor &clr)
Sets the custom color for this item.
Definition theme.cpp:273
Type type() const
Returns the type of this content item.
Definition theme.cpp:63
bool canUseCustomColor() const
Returns true if this ContentItem can make use of a custom color.
Definition theme.cpp:73
bool useCustomColor() const
Returns true if this item uses a custom color.
Definition theme.cpp:184
void save(QDataStream &stream) const
Handles content item saving (used by Theme::Row::save())
Definition theme.cpp:288
bool isIcon() const
Returns true if this item displays an icon.
Definition theme.cpp:88
bool softenByBlending() const
Returns true if this item should be always painted in a "soft" fashion.
Definition theme.cpp:254
bool softenByBlendingWhenDisabled() const
Returns true if this item should be painted in a "soft" fashion when in disabled state.
Definition theme.cpp:240
bool load(QDataStream &stream, int themeVersion)
Handles content item loading (used by Theme::Row::load())
Definition theme.cpp:295
bool isClickable() const
Returns true if clicking on this kind of item can perform an action.
Definition theme.cpp:93
Type
The available ContentItem types.
Definition theme.h:106
@ InvitationIcon
Whether the message is an invitation.
Definition theme.h:203
@ AttachmentStateIcon
The icon that displays the attachment state (may be disabled)
Definition theme.h:138
@ Subject
Display the subject of the message item.
Definition theme.h:110
@ AnnotationIcon
Whether the message has a annotation/note.
Definition theme.h:198
void setHideWhenDisabled(bool hideWhenDisabled)
Sets the flag that causes this item to be hidden when disabled.
Definition theme.cpp:231
const QColor & customColor() const
Returns the custom color set for this item.
Definition theme.cpp:268
bool displaysLongText() const
Returns true if this item displays a long text.
Definition theme.cpp:83
ContentItem(Type type)
Creates a ContentItem with the specified type.
Definition theme.cpp:53
static bool applicableToGroupHeaderItems(Type type)
Static test that returns true if an instance of ContentItem with the specified type makes sense in a ...
Definition theme.cpp:283
bool isItalic() const
Returns true if this item uses an italic text.
Definition theme.cpp:212
The Row class defines a row of items inside a Column.
Definition theme.h:413
void removeLeftItem(ContentItem *item)
Removes the specified left aligned content item from this row.
Definition theme.cpp:397
void addRightItem(ContentItem *item)
Adds a right aligned item to this row.
Definition theme.cpp:383
void insertRightItem(int idx, ContentItem *item)
Adds a right aligned item at the specified position in this row.
Definition theme.cpp:407
bool containsTextItems() const
Returns true if this row contains text items.
Definition theme.cpp:421
void removeAllLeftItems()
Removes all the left items from this row: the items are deleted.
Definition theme.cpp:364
bool load(QDataStream &stream, int themeVersion)
Handles row loading (used by Theme::Column::load())
Definition theme.cpp:512
void insertLeftItem(int idx, ContentItem *item)
Adds a left aligned item at the specified position in this row.
Definition theme.cpp:388
void removeAllRightItems()
Removes all the right items from this row.
Definition theme.cpp:376
void removeRightItem(ContentItem *item)
Removes the specified right aligned content item from this row.
Definition theme.cpp:416
void save(QDataStream &stream) const
Handles row saving (used by Theme::Column::save())
Definition theme.cpp:436
void addLeftItem(ContentItem *item)
Adds a left aligned item to this row.
Definition theme.cpp:371
const QList< ContentItem * > & rightItems() const
Returns the list of right aligned items for this row.
Definition theme.cpp:402
const QList< ContentItem * > & leftItems() const
Returns the list of left aligned items for this row.
Definition theme.cpp:507
The Theme class defines the visual appearance of the MessageList.
Definition theme.h:48
GroupHeaderBackgroundMode
Which color do we use to paint group header background ?
Definition theme.h:798
@ Transparent
No background at all: use style default.
Definition theme.h:799
@ CustomColor
Use a custom color.
Definition theme.h:801
@ AutoColor
Automatically determine the color (somewhere in the middle between background and text)
Definition theme.h:800
void setGroupHeaderBackgroundStyle(GroupHeaderBackgroundStyle groupHeaderBackgroundStyle)
Sets the group header background style for this theme.
Definition theme.cpp:1040
void setGroupHeaderBackgroundColor(const QColor &clr)
Sets the group header background color for this theme.
Definition theme.cpp:1030
void setViewHeaderPolicy(ViewHeaderPolicy vhp)
Sets the ViewHeaderPolicy for this theme.
Definition theme.cpp:1067
ViewHeaderPolicy
How do we manage the QHeaderView attached to our View ?
Definition theme.h:821
void removeColumn(Column *col)
Removes the specified message row.
Definition theme.cpp:999
void setGroupHeaderBackgroundMode(GroupHeaderBackgroundMode bm)
Sets the group header background mode for this theme.
Definition theme.cpp:1017
void setIconSize(int iconSize)
Sets the icon size for this theme.
Definition theme.cpp:1077
void detach()
Detaches this object from the shared runtime data for columns.
Definition theme.cpp:946
const QColor & groupHeaderBackgroundColor() const
Returns the group header background color for this theme.
Definition theme.cpp:1025
static QList< QPair< QString, int > > enumerateGroupHeaderBackgroundStyles()
Enumerates the available group header background styles.
Definition theme.cpp:1050
~Theme() override
Destroys this theme object.
Definition theme.cpp:940
Theme()
Creates a totally uninitialized theme object.
Definition theme.cpp:909
GroupHeaderBackgroundMode groupHeaderBackgroundMode() const
Returns the group header background mode for this theme.
Definition theme.cpp:1004
void addColumn(Column *column)
Appends a column to this theme.
Definition theme.cpp:985
void insertColumn(int idx, Column *column)
Inserts a column to this theme at the specified position.
Definition theme.cpp:990
ViewHeaderPolicy viewHeaderPolicy() const
Returns the currently set ViewHeaderPolicy.
Definition theme.cpp:1062
void resetColumnSizes()
Resets the column sizes to "default" (subset of resetColumnState() above).
Definition theme.cpp:961
GroupHeaderBackgroundStyle groupHeaderBackgroundStyle() const
Returns the group header background style for this theme.
Definition theme.cpp:1035
static QList< QPair< QString, int > > enumerateViewHeaderPolicyOptions()
Enumerates the available view header policy options.
Definition theme.cpp:1045
bool load(QDataStream &stream) override
Pure virtual reimplemented from OptionSet.
Definition theme.cpp:1089
int iconSize() const
Returns the currently set icon size.
Definition theme.cpp:1072
const QList< Column * > & columns() const
Returns the list of columns available in this theme.
Definition theme.cpp:968
void save(QDataStream &stream) const override
Pure virtual reimplemented from OptionSet.
Definition theme.cpp:1180
void removeAllColumns()
Removes all columns from this theme.
Definition theme.cpp:978
Column * column(int idx) const
Returns a pointer to the column at the specified index or 0 if there is no such column.
Definition theme.cpp:973
void resetColumnState()
Resets the column state (visibility and width) to their default values (the "visible by default" ones...
Definition theme.cpp:953
GroupHeaderBackgroundStyle
How do we paint group header background ?
Definition theme.h:807
@ RoundedJoinedRect
One big rounded rect for all the columns.
Definition theme.h:811
@ PlainRect
One plain rect per column.
Definition theme.h:808
@ GradientRect
One rounded gradient filled rect per column.
Definition theme.h:812
@ StyledRect
One styled rect per column.
Definition theme.h:814
@ RoundedRect
One rounded rect per column.
Definition theme.h:810
@ StyledJoinedRect
One big styled rect per column.
Definition theme.h:815
@ PlainJoinedRect
One big plain rect for all the columns.
Definition theme.h:809
@ GradientJoinedRect
One big rounded gradient rect for all the columns.
Definition theme.h:813
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
The implementation independent part of the MessageList library.
Definition aggregation.h:22
bool isValid() const const
bool isRightToLeft()
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
void clear()
void reserve(qsizetype size)
value_type takeFirst()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.