Messagelib

themeeditor.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 "utils/themeeditor.h"
10#include "core/groupheaderitem.h"
11#include "core/messageitem.h"
12#include "core/modelinvariantrowmapper.h"
13#include "utils/comboboxutils.h"
14
15#include <Akonadi/MessageStatus>
16
17#include <KTextEdit>
18
19#include <QActionGroup>
20#include <QCheckBox>
21#include <QCursor>
22#include <QDrag>
23#include <QGridLayout>
24#include <QGroupBox>
25#include <QHeaderView>
26#include <QMimeData>
27#include <QMouseEvent>
28#include <QPaintEvent>
29#include <QPainter>
30#include <QPixmap>
31#include <QPushButton>
32#include <QStringList>
33
34#include <KIconLoader>
35#include <KLocalizedString>
36#include <KPluralHandlingSpinBox>
37#include <QColorDialog>
38#include <QComboBox>
39#include <QLineEdit>
40#include <QMenu>
41
42#include <QDialogButtonBox>
43#include <QVBoxLayout>
44#include <ctime> // for time_t
45
46using namespace MessageList::Utils;
47using namespace MessageList::Core;
48
49static const char gThemeContentItemTypeDndMimeDataFormat[] = "application/x-kmail-messagelistview-theme-contentitem-type";
50
51ThemeColumnPropertiesDialog::ThemeColumnPropertiesDialog(QWidget *parent, Theme::Column *column, const QString &title)
52 : QDialog(parent)
53 , mColumn(column)
54{
55 auto mainLayout = new QVBoxLayout(this);
57 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
58 okButton->setDefault(true);
60 connect(buttonBox, &QDialogButtonBox::rejected, this, &ThemeColumnPropertiesDialog::reject);
61 setWindowTitle(title);
62
63 auto base = new QWidget(this);
64 mainLayout->addWidget(base);
65 mainLayout->addWidget(buttonBox);
66
67 auto g = new QGridLayout(base);
68
69 auto l = new QLabel(i18nc("@label:textbox Property name", "Name:"), base);
70 g->addWidget(l, 0, 0);
71
72 mNameEdit = new QLineEdit(base);
73 mNameEdit->setToolTip(i18n("The label that will be displayed in the column header."));
74 g->addWidget(mNameEdit, 0, 1);
75
76 l = new QLabel(i18n("Header click sorts messages:"), base);
77 g->addWidget(l, 1, 0);
78
79 mMessageSortingCombo = new QComboBox(base);
80 mMessageSortingCombo->setToolTip(i18n("The sorting order that clicking on this column header will switch to."));
81 g->addWidget(mMessageSortingCombo, 1, 1);
82
83 mVisibleByDefaultCheck = new QCheckBox(i18n("Visible by default"), base);
84 mVisibleByDefaultCheck->setToolTip(i18n("Check this if this column should be visible when the theme is selected."));
85 g->addWidget(mVisibleByDefaultCheck, 2, 1);
86
87 mIsSenderOrReceiverCheck = new QCheckBox(i18n("Contains \"Sender or Receiver\" field"), base);
88 mIsSenderOrReceiverCheck->setToolTip(i18n("Check this if this column label should be updated depending on the folder \"inbound\"/\"outbound\" type."));
89 g->addWidget(mIsSenderOrReceiverCheck, 3, 1);
90
91 g->setColumnStretch(1, 1);
92 g->setRowStretch(10, 1);
93
94 connect(okButton, &QPushButton::clicked, this, &ThemeColumnPropertiesDialog::slotOkButtonClicked);
95
96 // Display the current settings
97 mNameEdit->setText(mColumn->label());
98 mVisibleByDefaultCheck->setChecked(mColumn->visibleByDefault());
99 mIsSenderOrReceiverCheck->setChecked(mColumn->isSenderOrReceiver());
101 ComboBoxUtils::setIntegerOptionComboValue(mMessageSortingCombo, mColumn->messageSorting());
102}
103
104void ThemeColumnPropertiesDialog::slotOkButtonClicked()
105{
106 QString text = mNameEdit->text();
107 if (text.isEmpty()) {
108 text = i18n("Unnamed Column");
109 }
110 mColumn->setLabel(text);
111 mColumn->setVisibleByDefault(mVisibleByDefaultCheck->isChecked());
112 mColumn->setIsSenderOrReceiver(mIsSenderOrReceiverCheck->isChecked());
113 mColumn->setMessageSorting(
115
116 accept();
117}
118
119ThemeContentItemSourceLabel::ThemeContentItemSourceLabel(QWidget *parent, Theme::ContentItem::Type type)
120 : QLabel(parent)
121 , mType(type)
122{
123 setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
124}
125
126ThemeContentItemSourceLabel::~ThemeContentItemSourceLabel() = default;
127
128MessageList::Core::Theme::ContentItem::Type ThemeContentItemSourceLabel::type() const
129{
130 return mType;
131}
132
133void ThemeContentItemSourceLabel::mousePressEvent(QMouseEvent *e)
134{
135 if (e->button() == Qt::LeftButton) {
136 mMousePressPoint = e->pos();
137 }
138}
139
140void ThemeContentItemSourceLabel::mouseMoveEvent(QMouseEvent *e)
141{
142 if (e->buttons() & Qt::LeftButton) {
143 const QPoint diff = mMousePressPoint - e->pos();
144 if (diff.manhattanLength() > 4) {
145 startDrag();
146 }
147 }
148}
149
150void ThemeContentItemSourceLabel::startDrag()
151{
152 // QPixmap pix = QPixmap::grabWidget( this );
153 // QPixmap alpha( pix.width(), pix.height() );
154 // alpha.fill(0x0f0f0f0f);
155 // pix.setAlphaChannel( alpha ); // <-- this crashes... no alpha for dragged pixmap :(
156 auto data = new QMimeData();
158 arry.resize(sizeof(Theme::ContentItem::Type));
159 *((Theme::ContentItem::Type *)arry.data()) = mType;
160 data->setData(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat), arry);
161 auto drag = new QDrag(this);
162 drag->setMimeData(data);
163 // drag->setPixmap( pix );
164 // drag->setHotSpot( mapFromGlobal( QCursor::pos() ) );
166}
167
168ThemePreviewDelegate::ThemePreviewDelegate(QAbstractItemView *parent)
169 : ThemeDelegate(parent)
170{
171 mRowMapper = new ModelInvariantRowMapper();
172
173 mSampleGroupHeaderItem = new GroupHeaderItem(i18n("Message Group"));
174
175 mSampleGroupHeaderItem->setDate(time(nullptr));
176 mSampleGroupHeaderItem->setMaxDate(time(nullptr) + 31337);
177 mSampleGroupHeaderItem->setSubject(i18n("Very long subject very long subject very long subject very long subject very long subject very long"));
178
179 mSampleMessageItem = new FakeItem();
180
181 mSampleMessageItem->setDate(time(nullptr));
182 mSampleMessageItem->setSize(0x31337);
183 mSampleMessageItem->setMaxDate(time(nullptr) + 31337);
184 mSampleMessageItem->setSender(i18n("Sender"));
185 mSampleMessageItem->setReceiver(i18n("Receiver"));
186 mSampleMessageItem->setSubject(i18n("Very long subject very long subject very long subject very long subject very long subject very long"));
187 mSampleMessageItem->setFolder(i18n("Folder"));
188 mSampleMessageItem->setSignatureState(MessageItem::FullySigned);
189 mSampleMessageItem->setEncryptionState(MessageItem::FullyEncrypted);
190
192 list.append(new MessageItem::Tag(QIcon::fromTheme(QStringLiteral("feed-subscribe")).pixmap(KIconLoader::SizeSmall), i18n("Sample Tag 1"), QString()));
193 list.append(new MessageItem::Tag(QIcon::fromTheme(QStringLiteral("feed-subscribe")).pixmap(KIconLoader::SizeSmall), i18n("Sample Tag 2"), QString()));
194 list.append(new MessageItem::Tag(QIcon::fromTheme(QStringLiteral("feed-subscribe")).pixmap(KIconLoader::SizeSmall), i18n("Sample Tag 3"), QString()));
195 mSampleMessageItem->setFakeTags(list);
196
197 mRowMapper->createModelInvariantIndex(0, mSampleMessageItem);
198
199 mSampleGroupHeaderItem->rawAppendChildItem(mSampleMessageItem);
200 mSampleMessageItem->setParent(mSampleGroupHeaderItem);
201
203
204 stat.fromQInt32(0x7fffffff);
205 stat.setQueued(false);
206 stat.setSent(false);
207 stat.setSpam(true);
208 stat.setWatched(true);
209 stat.setHasInvitation();
210 // stat.setHasAttachment( false );
211
212 mSampleMessageItem->setStatus(stat);
213}
214
215ThemePreviewDelegate::~ThemePreviewDelegate()
216{
217 delete mSampleGroupHeaderItem;
218 // delete mSampleMessageItem; (deleted by the parent)
219 delete mRowMapper;
220}
221
222Item *ThemePreviewDelegate::itemFromIndex(const QModelIndex &index) const
223{
224 if (index.parent().isValid()) {
225 return mSampleMessageItem;
226 }
227
228 return mSampleGroupHeaderItem;
229}
230
231ThemePreviewWidget::ThemePreviewWidget(QWidget *parent)
232 : QTreeWidget(parent)
233 , mTheme(nullptr)
234{
235 mSelectedThemeContentItem = nullptr;
236 mSelectedThemeColumn = nullptr;
237 mFirstShow = true;
238 mReadOnly = false;
239
240 mDelegate = new ThemePreviewDelegate(this);
241 setItemDelegate(mDelegate);
242 setRootIsDecorated(false);
243 viewport()->setAcceptDrops(true);
244
245 header()->setContextMenuPolicy(Qt::CustomContextMenu);
246 connect(header(), &QWidget::customContextMenuRequested, this, &ThemePreviewWidget::slotHeaderContextMenuRequested);
247
248 mGroupHeaderSampleItem = new QTreeWidgetItem(this);
249 mGroupHeaderSampleItem->setText(0, QString());
250 mGroupHeaderSampleItem->setFlags(Qt::ItemIsEnabled);
251
252 auto m = new QTreeWidgetItem(mGroupHeaderSampleItem);
253 m->setText(0, QString());
254
255 mGroupHeaderSampleItem->setExpanded(true);
256 header()->setSectionsMovable(false);
257}
258
259void ThemePreviewWidget::changeEvent(QEvent *event)
260{
261 if (event->type() == QEvent::FontChange) {
262 mDelegate->generalFontChanged();
263 }
265}
266
267ThemePreviewWidget::~ThemePreviewWidget() = default;
268
269QSize ThemePreviewWidget::sizeHint() const
270{
271 return {350, 180};
272}
273
274void ThemePreviewWidget::setReadOnly(bool readOnly)
275{
276 mReadOnly = readOnly;
277}
278
279void ThemePreviewWidget::applyThemeColumnWidths()
280{
281 if (!mTheme) {
282 return;
283 }
284
285 const QList<Theme::Column *> &columns = mTheme->columns();
286
287 if (columns.isEmpty()) {
288 viewport()->update(); // trigger a repaint
289 return;
290 }
291
292 // Now we want to distribute the available width on all the columns.
293 // The algorithm used here is very similar to the one used in View::applyThemeColumns().
294 // It just takes care of ALL the columns instead of the visible ones.
295
297
298 // Gather size hints for all sections.
299 int idx = 0;
300 int totalVisibleWidthHint = 0;
302
303 for (it = columns.constBegin(); it != end; ++it) {
304 totalVisibleWidthHint += mDelegate->sizeHintForItemTypeAndColumn(Item::Message, idx).width();
305 idx++;
306 }
307
308 if (totalVisibleWidthHint < 16) {
309 totalVisibleWidthHint = 16; // be reasonable
310 }
311
312 // Now we can compute proportional widths.
313
314 idx = 0;
315
317 realWidths.reserve(columns.count());
318 int totalVisibleWidth = 0;
319
320 end = columns.constEnd();
321 for (it = columns.constBegin(); it != end; ++it) {
322 int hintWidth = mDelegate->sizeHintForItemTypeAndColumn(Item::Message, idx).width();
323 int realWidth;
324 if ((*it)->containsTextItems()) {
325 // the column contains text items, it should get more space
326 realWidth = ((hintWidth * viewport()->width()) / totalVisibleWidthHint) - 2; // -2 is heuristic
327 if (realWidth < (hintWidth + 2)) {
328 realWidth = hintWidth + 2; // can't be less
329 }
330 } else {
331 // the column contains no text items, it should get just a little bit more than its sizeHint().
332 realWidth = hintWidth + 2;
333 }
334
335 realWidths.append(realWidth);
337
338 idx++;
339 }
340
341 idx = 0;
342
343 totalVisibleWidth += 4; // account for some view's border
344
345 if (totalVisibleWidth < viewport()->width()) {
346 // give the additional space to the text columns
347 // also give more space to the first ones and less space to the last ones
348 int available = viewport()->width() - totalVisibleWidth;
349
350 for (it = columns.begin(); it != columns.end(); ++it) {
351 if (((*it)->visibleByDefault() || (idx == 0)) && (*it)->containsTextItems()) {
352 // give more space to this column
353 available >>= 1; // eat half of the available space
354 realWidths[idx] += available; // and give it to this column
355 }
356
357 idx++;
358 }
359
360 // if any space is still available, give it to the first column
361 if (available) {
362 realWidths[0] += available;
363 }
364 }
365
366 idx = 0;
367
368 // We're ready.
369 // Assign widths. Hide the sections that are not visible by default, show the other ones.
370 for (it = columns.begin(); it != columns.end(); ++it) {
372 idx++;
373 }
374
375#if 0
376 if (mTheme->viewHeaderPolicy() == Theme::NeverShowHeader) {
377 header()->hide();
378 } else {
379 header()->show();
380 }
381#endif
382}
383
384void ThemePreviewWidget::setTheme(Theme *theme)
385{
386 bool themeChanged = theme != mTheme;
387
388 mSelectedThemeContentItem = nullptr;
389 mThemeSelectedContentItemRect = QRect();
390 mDropIndicatorPoint1 = QPoint();
391 mDropIndicatorPoint2 = QPoint();
392 mTheme = theme;
393 mDelegate->setTheme(theme);
394 if (!mTheme) {
395 return;
396 }
397 mGroupHeaderSampleItem->setExpanded(true);
398
399 const QList<Theme::Column *> &columns = mTheme->columns();
400
401 setColumnCount(columns.count());
402
404 headerLabels.reserve(columns.count());
407 QString label = (*it)->label();
408 if ((*it)->visibleByDefault()) {
409 label += QStringLiteral(" (%1)").arg(i18nc("Indicates whether or not a header label is visible", "Visible"));
410 }
411
412 headerLabels.append(label);
413 }
414
416
417 if (themeChanged) {
418 applyThemeColumnWidths();
419 }
420
421 viewport()->update(); // trigger a repaint
422}
423
424void ThemePreviewWidget::internalHandleDragEnterEvent(QDragEnterEvent *e)
425{
426 e->ignore();
427
428 if (!e->mimeData()) {
429 return;
430 }
431 if (!e->mimeData()->hasFormat(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat))) {
432 return;
433 }
434
435 e->accept();
436}
437
438void ThemePreviewWidget::showEvent(QShowEvent *e)
439{
441
442 if (mFirstShow) {
443 // Make sure we re-apply column widths the first time we're shown.
444 // The first "apply" call was made while the widget was still hidden and
445 // almost surely had wrong sizes.
446 applyThemeColumnWidths();
447 mFirstShow = false;
448 }
449}
450
451void ThemePreviewWidget::dragEnterEvent(QDragEnterEvent *e)
452{
453 internalHandleDragEnterEvent(e);
454
455 mThemeSelectedContentItemRect = QRect();
456
457 viewport()->update(); // trigger a repaint
458}
459
460void ThemePreviewWidget::internalHandleDragMoveEvent(QDragMoveEvent *e)
461{
462 e->ignore();
463
464 if (mReadOnly) {
465 return;
466 }
467
468 if (!e->mimeData()) {
469 return;
470 }
471 if (!e->mimeData()->hasFormat(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat))) {
472 return;
473 }
474
475 QByteArray arry = e->mimeData()->data(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat));
476
477 if (arry.size() != sizeof(Theme::ContentItem::Type)) {
478 return; // ugh
479 }
480
482 if (!computeContentItemInsertPosition(e->position().toPoint(), type)) {
483 return;
484 }
485
486 e->accept();
487}
488
489void ThemePreviewWidget::dragMoveEvent(QDragMoveEvent *e)
490{
491 if (mReadOnly) {
492 return;
493 }
494
495 internalHandleDragMoveEvent(e);
496
497 mThemeSelectedContentItemRect = QRect();
498
499 viewport()->update(); // trigger a repaint
500}
501
502void ThemePreviewWidget::dropEvent(QDropEvent *e)
503{
504 mDropIndicatorPoint1 = mDropIndicatorPoint2;
505
506 e->ignore();
507
508 if (mReadOnly) {
509 return;
510 }
511
512 if (!e->mimeData()) {
513 return;
514 }
515
516 if (!e->mimeData()->hasFormat(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat))) {
517 return;
518 }
519
520 QByteArray arry = e->mimeData()->data(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat));
521
522 if (arry.size() != sizeof(Theme::ContentItem::Type)) {
523 return; // ugh
524 }
525
527 if (!computeContentItemInsertPosition(e->position().toPoint(), type)) {
528 viewport()->update();
529 return;
530 }
531
532 Theme::Row *row = nullptr;
533
534 switch (mRowInsertPosition) {
535 case AboveRow:
536 row = new Theme::Row();
537 if (mDelegate->hitItem()->type() == Item::Message) {
538 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertMessageRow(mDelegate->hitRowIndex(), row);
539 } else {
540 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertGroupHeaderRow(mDelegate->hitRowIndex(), row);
541 }
542 break;
543 case InsideRow:
544 row = const_cast<Theme::Row *>(mDelegate->hitRow());
545 break;
546 case BelowRow:
547 row = new Theme::Row();
548 if (mDelegate->hitItem()->type() == Item::Message) {
549 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertMessageRow(mDelegate->hitRowIndex() + 1, row);
550 } else {
551 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertGroupHeaderRow(mDelegate->hitRowIndex() + 1, row);
552 }
553 break;
554 }
555
556 if (!row) {
557 return;
558 }
559
560 auto ci = new Theme::ContentItem(type);
561 if (ci->canBeDisabled()) {
562 if (ci->isClickable()) {
563 ci->setSoftenByBlendingWhenDisabled(true); // default to softened
564 } else {
565 ci->setHideWhenDisabled(true); // default to hidden
566 }
567 }
568
569 int idx;
570
571 switch (mItemInsertPosition) {
572 case OnLeftOfItem:
573 if (!mDelegate->hitContentItem()) {
574 // bleah
575 delete ci;
576 return;
577 }
578 idx = mDelegate->hitContentItemRight() ? row->rightItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()))
579 : row->leftItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()));
580 if (idx < 0) {
581 // bleah
582 delete ci;
583 return;
584 }
585 if (mDelegate->hitContentItemRight()) {
586 row->insertRightItem(idx + 1, ci);
587 } else {
588 row->insertLeftItem(idx, ci);
589 }
590 break;
591 case OnRightOfItem:
592 if (!mDelegate->hitContentItem()) {
593 // bleah
594 delete ci;
595 return;
596 }
597 idx = mDelegate->hitContentItemRight() ? row->rightItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()))
598 : row->leftItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()));
599 if (idx < 0) {
600 // bleah
601 delete ci;
602 return;
603 }
604 if (mDelegate->hitContentItemRight()) {
605 row->insertRightItem(idx, ci);
606 } else {
607 row->insertLeftItem(idx + 1, ci);
608 }
609 break;
610 case AsLastLeftItem:
611 row->addLeftItem(ci);
612 break;
613 case AsLastRightItem:
614 row->addRightItem(ci);
615 break;
616 case AsFirstLeftItem:
617 row->insertLeftItem(0, ci);
618 break;
619 case AsFirstRightItem:
620 row->insertRightItem(0, ci);
621 break;
622 default: // should never happen
623 row->addRightItem(ci);
624 break;
625 }
626
628
629 mThemeSelectedContentItemRect = QRect();
630 mDropIndicatorPoint1 = mDropIndicatorPoint2;
631 mSelectedThemeContentItem = nullptr;
632
633 setTheme(mTheme); // this will reset theme cache and trigger a global update
634}
635
636bool ThemePreviewWidget::computeContentItemInsertPosition(const QPoint &pos, Theme::ContentItem::Type type)
637{
638 mDropIndicatorPoint1 = mDropIndicatorPoint2; // this marks the position as invalid
639
640 if (!mDelegate->hitTest(pos, false)) {
641 return false;
642 }
643
644 if (!mDelegate->hitRow()) {
645 return false;
646 }
647
648 if (mDelegate->hitRowIsMessageRow()) {
650 return false;
651 }
652 } else {
654 return false;
655 }
656 }
657
658 QRect rowRect = mDelegate->hitRowRect();
659
660 if (pos.y() < rowRect.top() + 3) {
661 // above a row
662 mRowInsertPosition = AboveRow;
663 if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) {
664 mDropIndicatorPoint1 = rowRect.topLeft();
665 mItemInsertPosition = AsLastLeftItem;
666 } else {
667 mDropIndicatorPoint1 = rowRect.topRight();
668 mItemInsertPosition = AsLastRightItem;
669 }
670 mDropIndicatorPoint2 = QPoint(rowRect.left() + (rowRect.width() / 2), rowRect.top());
671 return true;
672 }
673
674 if (pos.y() > rowRect.bottom() - 3) {
675 // below a row
676 mRowInsertPosition = BelowRow;
677 if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) {
678 mDropIndicatorPoint1 = rowRect.bottomLeft();
679 mItemInsertPosition = AsLastLeftItem;
680 } else {
681 mDropIndicatorPoint1 = rowRect.bottomRight();
682 mItemInsertPosition = AsLastRightItem;
683 }
684 mDropIndicatorPoint2 = QPoint(rowRect.left() + (rowRect.width() / 2), rowRect.bottom());
685 return true;
686 }
687
688 mRowInsertPosition = InsideRow;
689
690 if (!mDelegate->hitContentItem()) {
691 // didn't hit anything... probably no items in the row
692 if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) {
693 mItemInsertPosition = AsLastLeftItem;
694 mDropIndicatorPoint1 = QPoint(rowRect.left(), rowRect.top());
695 mDropIndicatorPoint2 = QPoint(rowRect.left(), rowRect.bottom());
696 } else {
697 mItemInsertPosition = AsLastRightItem;
698 mDropIndicatorPoint1 = QPoint(rowRect.right(), rowRect.top());
699 mDropIndicatorPoint2 = QPoint(rowRect.right(), rowRect.bottom());
700 }
701 return true;
702 }
703
704 // hit something, maybe inexactly
705 QRect itemRect = mDelegate->hitContentItemRect();
706
707 if (!itemRect.contains(pos)) {
708 // inexact hit: outside an item
709 if (pos.x() > itemRect.right()) {
710 // right side of an item
711 if (mDelegate->hitRow()->rightItems().count() < 1) {
712 // between the last left item and the right side
713 if (pos.x() > (itemRect.right() + ((rowRect.right() - itemRect.right()) / 2))) {
714 // first/last right item
715 mItemInsertPosition = AsFirstRightItem;
716 mDropIndicatorPoint1 = rowRect.topRight();
717 mDropIndicatorPoint2 = rowRect.bottomRight();
718 }
719 return true;
720 }
721 // either there were some right items (so the theme delegate knows that the reported item is the closest)
722 // or there were no right items but the position is closest to the left item than the right row end
723 mItemInsertPosition = OnRightOfItem;
724 mDropIndicatorPoint1 = itemRect.topRight();
725 mDropIndicatorPoint2 = itemRect.bottomRight();
726 return true;
727 }
728
729 // left side of an item
730 if (mDelegate->hitRow()->leftItems().count() < 1) {
731 // between the left side and the leftmost right item
732 if (pos.x() < (itemRect.left() - ((itemRect.left() - rowRect.left()) / 2))) {
733 mItemInsertPosition = AsFirstLeftItem;
734 mDropIndicatorPoint1 = rowRect.topLeft();
735 mDropIndicatorPoint2 = rowRect.bottomLeft();
736 return true;
737 }
738 }
739 mItemInsertPosition = OnLeftOfItem;
740 mDropIndicatorPoint1 = itemRect.topLeft();
741 mDropIndicatorPoint2 = itemRect.bottomLeft();
742 return true;
743 }
744
745 // exact hit
746 if (pos.x() < (itemRect.left() + (itemRect.width() / 2))) {
747 // left side
748 mItemInsertPosition = OnLeftOfItem;
749 mDropIndicatorPoint1 = itemRect.topLeft();
750 mDropIndicatorPoint2 = itemRect.bottomLeft();
751 return true;
752 }
753
754 // right side
755 mItemInsertPosition = OnRightOfItem;
756 mDropIndicatorPoint1 = itemRect.topRight();
757 mDropIndicatorPoint2 = itemRect.bottomRight();
758 return true;
759}
760
761void ThemePreviewWidget::mouseMoveEvent(QMouseEvent *e)
762{
763 if (!(mSelectedThemeContentItem && (e->buttons() & Qt::LeftButton)) || mReadOnly) {
765 return;
766 }
767
768 if (mSelectedThemeContentItem != mDelegate->hitContentItem()) {
770 return; // ugh.. something weird happened
771 }
772
773 // starting a drag ?
774 const QPoint diff = e->pos() - mMouseDownPoint;
775 if (diff.manhattanLength() <= 4) {
777 return; // ugh.. something weird happened
778 }
779
780 // starting a drag
781 auto data = new QMimeData();
783 arry.resize(sizeof(Theme::ContentItem::Type));
784 *((Theme::ContentItem::Type *)arry.data()) = mSelectedThemeContentItem->type();
785 data->setData(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat), arry);
786 auto drag = new QDrag(this);
787 drag->setMimeData(data);
788
789 // remove the Theme::ContentItem from the Theme
790 if (mDelegate->hitContentItemRight()) {
791 const_cast<Theme::Row *>(mDelegate->hitRow())->removeRightItem(mSelectedThemeContentItem);
792 } else {
793 const_cast<Theme::Row *>(mDelegate->hitRow())->removeLeftItem(mSelectedThemeContentItem);
794 }
795
796 delete mSelectedThemeContentItem;
797
798 if (mDelegate->hitRow()->rightItems().isEmpty() && mDelegate->hitRow()->leftItems().isEmpty()) {
799 if (mDelegate->hitItem()->type() == Item::Message) {
800 if (mDelegate->hitColumn()->messageRows().count() > 1) {
801 const_cast<Theme::Column *>(mDelegate->hitColumn())->removeMessageRow(const_cast<Theme::Row *>(mDelegate->hitRow()));
802 delete mDelegate->hitRow();
803 }
804 } else {
805 if (mDelegate->hitColumn()->groupHeaderRows().count() > 1) {
806 const_cast<Theme::Column *>(mDelegate->hitColumn())->removeGroupHeaderRow(const_cast<Theme::Row *>(mDelegate->hitRow()));
807 delete mDelegate->hitRow();
808 }
809 }
810 }
811
812 mSelectedThemeContentItem = nullptr;
813 mThemeSelectedContentItemRect = QRect();
814 mDropIndicatorPoint1 = mDropIndicatorPoint2;
815
816 setTheme(mTheme); // this will reset theme cache and trigger a global update
817
818 // and do drag
820}
821
822void ThemePreviewWidget::mousePressEvent(QMouseEvent *e)
823{
824 if (mReadOnly) {
826 return;
827 }
828
829 mMouseDownPoint = e->pos();
830
831 if (mDelegate->hitTest(mMouseDownPoint)) {
832 mSelectedThemeContentItem = const_cast<Theme::ContentItem *>(mDelegate->hitContentItem());
833 mThemeSelectedContentItemRect = mSelectedThemeContentItem ? mDelegate->hitContentItemRect() : QRect();
834 } else {
835 mSelectedThemeContentItem = nullptr;
836 mThemeSelectedContentItemRect = QRect();
837 }
838
840 viewport()->update();
841
842 if (e->button() == Qt::RightButton) {
843 QMenu menu;
844
845 if (mSelectedThemeContentItem) {
846 menu.addSection(Theme::ContentItem::description(mSelectedThemeContentItem->type()));
847
848 if (mSelectedThemeContentItem->displaysText()) {
849 QAction *act = menu.addAction(i18nc("@action:inmenu soften the text color", "Soften"));
850 act->setCheckable(true);
851 act->setChecked(mSelectedThemeContentItem->softenByBlending());
852 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotSoftenActionTriggered);
853
854 auto childmenu = new QMenu(&menu);
855
856 act = childmenu->addAction(i18nc("@action:inmenu Font setting", "Bold"));
857 act->setData(QVariant(static_cast<int>(Theme::ContentItem::IsBold)));
858 act->setCheckable(true);
859 act->setChecked(mSelectedThemeContentItem->isBold());
860 act = childmenu->addAction(i18nc("@action:inmenu Font setting", "Italic"));
861 act->setData(QVariant(static_cast<int>(Theme::ContentItem::IsItalic)));
862 act->setCheckable(true);
863 act->setChecked(mSelectedThemeContentItem->isItalic());
864
865 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotFontMenuTriggered);
866
867 menu.addMenu(childmenu)->setText(i18n("Font"));
868 }
869
870 if (mSelectedThemeContentItem->canUseCustomColor()) {
871 auto childmenu = new QMenu(&menu);
872
873 auto grp = new QActionGroup(childmenu);
874
875 QAction *act = childmenu->addAction(i18nc("@action:inmenu Foreground color setting", "Default"));
876 act->setData(QVariant(static_cast<int>(0)));
877 act->setCheckable(true);
878 act->setChecked(!mSelectedThemeContentItem->useCustomColor());
879 grp->addAction(act);
880 act = childmenu->addAction(i18nc("@action:inmenu Foreground color setting", "Custom..."));
881 act->setData(QVariant(static_cast<int>(Theme::ContentItem::UseCustomColor)));
882 act->setCheckable(true);
883 act->setChecked(mSelectedThemeContentItem->useCustomColor());
884 grp->addAction(act);
885
886 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotForegroundColorMenuTriggered);
887
888 menu.addMenu(childmenu)->setText(i18n("Foreground Color"));
889 }
890
891 if (mSelectedThemeContentItem->canBeDisabled()) {
892 auto childmenu = new QMenu(&menu);
893
894 auto grp = new QActionGroup(childmenu);
895
896 QAction *act =
897 childmenu->addAction(i18nc("Hide a mark if the mail does not have the attribute, e.g. Important mark on a non important mail", "Hide"));
898 act->setData(QVariant(static_cast<int>(Theme::ContentItem::HideWhenDisabled)));
899 act->setCheckable(true);
900 act->setChecked(mSelectedThemeContentItem->hideWhenDisabled());
901 grp->addAction(act);
902 act = childmenu->addAction(
903 i18nc("Keep a empty space in the list if the mail does not have the attribute, e.g. Important mark on a non important mail",
904 "Keep Empty Space"));
905 act->setData(QVariant(static_cast<int>(0)));
906 act->setCheckable(true);
907 act->setChecked(!(mSelectedThemeContentItem->softenByBlendingWhenDisabled() || mSelectedThemeContentItem->hideWhenDisabled()));
908 grp->addAction(act);
909 act = childmenu->addAction(
910 i18nc("Show the icon softened in the list if the mail does not have the attribute, e.g. Important mark on a non important mail",
911 "Keep Softened Icon"));
913 act->setCheckable(true);
914 act->setChecked(mSelectedThemeContentItem->softenByBlendingWhenDisabled());
915 grp->addAction(act);
916
917 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotDisabledFlagsMenuTriggered);
918
919 menu.addMenu(childmenu)->setText(i18n("When Disabled"));
920 }
921 }
922
923 if (mDelegate->hitItem()) {
924 if (mDelegate->hitItem()->type() == Item::GroupHeader) {
925 menu.addSection(i18n("Group Header"));
926
927 // Background color (mode) submenu
928 auto childmenu = new QMenu(&menu);
929
930 auto grp = new QActionGroup(childmenu);
931
932 QAction *act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "None"));
933 act->setData(QVariant(static_cast<int>(Theme::Transparent)));
934 act->setCheckable(true);
935 act->setChecked(mTheme->groupHeaderBackgroundMode() == Theme::Transparent);
936 grp->addAction(act);
937 act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "Automatic"));
938 act->setData(QVariant(static_cast<int>(Theme::AutoColor)));
939 act->setCheckable(true);
940 act->setChecked(mTheme->groupHeaderBackgroundMode() == Theme::AutoColor);
941 grp->addAction(act);
942 act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "Custom..."));
943 act->setData(QVariant(static_cast<int>(Theme::CustomColor)));
944 act->setCheckable(true);
945 act->setChecked(mTheme->groupHeaderBackgroundMode() == Theme::CustomColor);
946 grp->addAction(act);
947
948 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered);
949
950 menu.addMenu(childmenu)->setText(i18n("Background Color"));
951
952 // Background style submenu
953 childmenu = new QMenu(&menu);
954
957 QList<QPair<QString, int>>::ConstIterator end(styles.constEnd());
958
959 for (QList<QPair<QString, int>>::ConstIterator it = styles.constBegin(); it != end; ++it) {
960 act = childmenu->addAction((*it).first);
961 act->setData(QVariant((*it).second));
962 act->setCheckable(true);
963 act->setChecked(mTheme->groupHeaderBackgroundStyle() == static_cast<Theme::GroupHeaderBackgroundStyle>((*it).second));
964 grp->addAction(act);
965 }
966
967 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered);
968
969 act = menu.addMenu(childmenu);
970 act->setText(i18n("Background Style"));
972 act->setEnabled(false);
973 }
974 }
975 }
976
977 if (menu.isEmpty()) {
978 return;
979 }
980
981 menu.exec(viewport()->mapToGlobal(e->pos()));
982 }
983}
984
985void ThemePreviewWidget::slotDisabledFlagsMenuTriggered(QAction *act)
986{
987 if (!mSelectedThemeContentItem) {
988 return;
989 }
990
991 bool ok;
992 const int flags = act->data().toInt(&ok);
993 if (!ok) {
994 return;
995 }
996
997 mSelectedThemeContentItem->setHideWhenDisabled(flags == Theme::ContentItem::HideWhenDisabled);
999
1000 setTheme(mTheme); // this will reset theme cache and trigger a global update
1001}
1002
1003void ThemePreviewWidget::slotForegroundColorMenuTriggered(QAction *act)
1004{
1005 if (!mSelectedThemeContentItem) {
1006 return;
1007 }
1008
1009 bool ok;
1010 const int flag = act->data().toInt(&ok);
1011 if (!ok) {
1012 return;
1013 }
1014
1015 if (flag == 0) {
1016 mSelectedThemeContentItem->setUseCustomColor(false);
1017 setTheme(mTheme); // this will reset theme cache and trigger a global update
1018 return;
1019 }
1020
1021 QColor clr;
1022 clr = QColorDialog::getColor(mSelectedThemeContentItem->customColor(), this);
1023 if (!clr.isValid()) {
1024 return;
1025 }
1026
1027 mSelectedThemeContentItem->setCustomColor(clr);
1028 mSelectedThemeContentItem->setUseCustomColor(true);
1029
1030 setTheme(mTheme); // this will reset theme cache and trigger a global update
1031}
1032
1033void ThemePreviewWidget::slotSoftenActionTriggered(bool)
1034{
1035 if (!mSelectedThemeContentItem) {
1036 return;
1037 }
1038
1039 mSelectedThemeContentItem->setSoftenByBlending(!mSelectedThemeContentItem->softenByBlending());
1040 setTheme(mTheme); // this will reset theme cache and trigger a global update
1041}
1042
1043void ThemePreviewWidget::slotFontMenuTriggered(QAction *act)
1044{
1045 if (!mSelectedThemeContentItem) {
1046 return;
1047 }
1048
1049 bool ok;
1050 const int flag = act->data().toInt(&ok);
1051 if (!ok) {
1052 return;
1053 }
1054
1055 if (flag == Theme::ContentItem::IsBold && mSelectedThemeContentItem->isBold() != act->isChecked()) {
1056 mSelectedThemeContentItem->setBold(act->isChecked());
1057 setTheme(mTheme);
1058 } else if (flag == Theme::ContentItem::IsItalic && mSelectedThemeContentItem->isItalic() != act->isChecked()) {
1059 mSelectedThemeContentItem->setItalic(act->isChecked());
1060 setTheme(mTheme);
1061 }
1062}
1063
1064void ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered(QAction *act)
1065{
1066 bool ok;
1067 Theme::GroupHeaderBackgroundMode mode = static_cast<Theme::GroupHeaderBackgroundMode>(act->data().toInt(&ok));
1068 if (!ok) {
1069 return;
1070 }
1071
1072 switch (mode) {
1073 case Theme::Transparent:
1075 break;
1076 case Theme::AutoColor:
1078 break;
1079 case Theme::CustomColor: {
1080 QColor clr;
1082 if (!clr.isValid()) {
1083 return;
1084 }
1085
1088 break;
1089 }
1090 }
1091
1092 setTheme(mTheme); // this will reset theme cache and trigger a global update
1093}
1094
1095void ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered(QAction *act)
1096{
1097 bool ok;
1098 Theme::GroupHeaderBackgroundStyle mode = static_cast<Theme::GroupHeaderBackgroundStyle>(act->data().toInt(&ok));
1099 if (!ok) {
1100 return;
1101 }
1102
1103 mTheme->setGroupHeaderBackgroundStyle(mode);
1104
1105 setTheme(mTheme); // this will reset theme cache and trigger a global update
1106}
1107
1108void ThemePreviewWidget::paintEvent(QPaintEvent *e)
1109{
1111
1112 if (mThemeSelectedContentItemRect.isValid() || (mDropIndicatorPoint1 != mDropIndicatorPoint2)) {
1113 QPainter painter(viewport());
1114
1115 if (mThemeSelectedContentItemRect.isValid()) {
1116 painter.setPen(QPen(Qt::black));
1117 painter.drawRect(mThemeSelectedContentItemRect);
1118 }
1119 if (mDropIndicatorPoint1 != mDropIndicatorPoint2) {
1120 painter.setPen(QPen(Qt::black, 3));
1121 painter.drawLine(mDropIndicatorPoint1, mDropIndicatorPoint2);
1122 }
1123 }
1124}
1125
1126void ThemePreviewWidget::slotHeaderContextMenuRequested(const QPoint &pos)
1127{
1128 if (mReadOnly) {
1129 return;
1130 }
1131
1133 if (!hitem) {
1134 return; // ooops
1135 }
1136
1137 int col = header()->logicalIndexAt(pos);
1138
1139 if (col < 0) {
1140 return;
1141 }
1142
1143 if (col >= mTheme->columns().count()) {
1144 return;
1145 }
1146
1147 mSelectedThemeColumn = mTheme->column(col);
1148 if (!mSelectedThemeColumn) {
1149 return;
1150 }
1151
1152 QMenu menu;
1153
1154 menu.setTitle(mSelectedThemeColumn->label());
1155
1156 QAction *act = menu.addAction(i18n("Column Properties..."));
1157 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotColumnProperties);
1158
1159 act = menu.addAction(i18n("Add Column..."));
1160 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotAddColumn);
1161
1162 act = menu.addAction(i18n("Delete Column"));
1163 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotDeleteColumn);
1164 act->setEnabled(col > 0);
1165
1166 menu.addSeparator();
1167
1168 act = menu.addAction(i18n("Move Column to Left"));
1169 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotMoveColumnToLeft);
1170 act->setEnabled(col > 0);
1171
1172 act = menu.addAction(i18n("Move Column to Right"));
1173 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotMoveColumnToRight);
1174 act->setEnabled(col < mTheme->columns().count() - 1);
1175
1176 menu.exec(header()->mapToGlobal(pos));
1177}
1178
1179void ThemePreviewWidget::slotMoveColumnToLeft()
1180{
1181 if (!mSelectedThemeColumn) {
1182 return;
1183 }
1184
1185 const int columnIndex = mTheme->columns().indexOf(mSelectedThemeColumn);
1186 mTheme->moveColumn(columnIndex, columnIndex - 1);
1187 setTheme(mTheme); // this will reset theme cache and trigger a global update
1188}
1189
1190void ThemePreviewWidget::slotMoveColumnToRight()
1191{
1192 if (!mSelectedThemeColumn) {
1193 return;
1194 }
1195
1196 const int columnIndex = mTheme->columns().indexOf(mSelectedThemeColumn);
1197 mTheme->moveColumn(columnIndex, columnIndex + 1);
1198 setTheme(mTheme); // this will reset theme cache and trigger a global update
1199}
1200
1201void ThemePreviewWidget::slotAddColumn()
1202{
1203 int newColumnIndex = mTheme->columns().count();
1204
1205 if (mSelectedThemeColumn) {
1206 newColumnIndex = mTheme->columns().indexOf(mSelectedThemeColumn);
1207 if (newColumnIndex < 0) {
1208 newColumnIndex = mTheme->columns().count();
1209 } else {
1211 }
1212 }
1213
1214 mSelectedThemeColumn = new Theme::Column();
1215 mSelectedThemeColumn->setLabel(i18n("New Column"));
1216 mSelectedThemeColumn->setVisibleByDefault(true);
1217
1218 mSelectedThemeColumn->addMessageRow(new Theme::Row());
1219 mSelectedThemeColumn->addGroupHeaderRow(new Theme::Row());
1220
1221 auto dlg = new ThemeColumnPropertiesDialog(this, mSelectedThemeColumn, i18n("Add New Column"));
1222
1223 if (dlg->exec() == QDialog::Accepted) {
1224 mTheme->insertColumn(newColumnIndex, mSelectedThemeColumn);
1225
1226 mSelectedThemeContentItem = nullptr;
1227 mThemeSelectedContentItemRect = QRect();
1228 mDropIndicatorPoint1 = mDropIndicatorPoint2;
1229
1230 setTheme(mTheme); // this will reset theme cache and trigger a global update
1231 } else {
1232 delete mSelectedThemeColumn;
1233 mSelectedThemeColumn = nullptr;
1234 }
1235
1236 delete dlg;
1237}
1238
1239void ThemePreviewWidget::slotColumnProperties()
1240{
1241 if (!mSelectedThemeColumn) {
1242 return;
1243 }
1244
1245 auto dlg = new ThemeColumnPropertiesDialog(this, mSelectedThemeColumn, i18n("Column Properties"));
1246
1247 if (dlg->exec() == QDialog::Accepted) {
1248 mSelectedThemeContentItem = nullptr;
1249 mThemeSelectedContentItemRect = QRect();
1250 mDropIndicatorPoint1 = mDropIndicatorPoint2;
1251
1252 setTheme(mTheme); // this will reset theme cache and trigger a global update
1253 }
1254
1255 delete dlg;
1256}
1257
1258void ThemePreviewWidget::slotDeleteColumn()
1259{
1260 if (!mSelectedThemeColumn) {
1261 return;
1262 }
1263
1264 const int idx = mTheme->columns().indexOf(mSelectedThemeColumn);
1265 if (idx < 1) { // first column can't be deleted
1266 return;
1267 }
1268
1269 mTheme->removeColumn(mSelectedThemeColumn);
1270 delete mSelectedThemeColumn;
1271 mSelectedThemeColumn = nullptr;
1272
1273 mSelectedThemeContentItem = nullptr;
1274 mThemeSelectedContentItemRect = QRect();
1275 mDropIndicatorPoint1 = mDropIndicatorPoint2;
1276
1277 setTheme(mTheme); // this will reset theme cache and trigger a global update
1278}
1279
1280ThemeEditor::ThemeEditor(QWidget *parent)
1281 : OptionSetEditor(parent)
1282{
1283 mCurrentTheme = nullptr;
1284
1285 // Appearance tab
1286 auto tab = new QWidget(this);
1287 addTab(tab, i18n("Appearance"));
1288
1289 auto tabg = new QGridLayout(tab);
1290
1291 auto gb = new QGroupBox(i18n("Content Items"), tab);
1292 tabg->addWidget(gb, 0, 0);
1293
1294 auto gblayout = new QGridLayout(gb);
1295
1296 Theme dummyTheme;
1297
1298 auto cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Subject);
1299 cil->setText(Theme::ContentItem::description(cil->type()));
1300 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1301 gblayout->addWidget(cil, 0, 0);
1302
1303 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Date);
1304 cil->setText(Theme::ContentItem::description(cil->type()));
1305 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1306 gblayout->addWidget(cil, 1, 0);
1307
1308 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Size);
1309 cil->setText(Theme::ContentItem::description(cil->type()));
1310 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1311 gblayout->addWidget(cil, 2, 0);
1312
1313 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Sender);
1314 cil->setText(Theme::ContentItem::description(cil->type()));
1315 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1316 gblayout->addWidget(cil, 0, 1);
1317
1318 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Receiver);
1319 cil->setText(Theme::ContentItem::description(cil->type()));
1320 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1321 gblayout->addWidget(cil, 1, 1);
1322
1323 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SenderOrReceiver);
1324 cil->setText(Theme::ContentItem::description(cil->type()));
1325 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1326 gblayout->addWidget(cil, 2, 1);
1327
1328 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::MostRecentDate);
1329 cil->setText(Theme::ContentItem::description(cil->type()));
1330 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1331 gblayout->addWidget(cil, 0, 2);
1332
1333 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::TagList);
1334 cil->setText(Theme::ContentItem::description(cil->type()));
1335 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1336 gblayout->addWidget(cil, 1, 2);
1337
1338 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Folder);
1339 cil->setText(Theme::ContentItem::description(cil->type()));
1340 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1341 gblayout->addWidget(cil, 2, 2);
1342
1343 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::CombinedReadRepliedStateIcon);
1344 cil->setPixmap(*dummyTheme.pixmap(Theme::IconRepliedAndForwarded));
1345 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1346 gblayout->addWidget(cil, 0, 3);
1347
1348 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ReadStateIcon);
1349 cil->setPixmap(*dummyTheme.pixmap(Theme::IconNew));
1350 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1351 gblayout->addWidget(cil, 1, 3);
1352
1353 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::RepliedStateIcon);
1354 cil->setPixmap(*dummyTheme.pixmap(Theme::IconReplied));
1355 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1356 gblayout->addWidget(cil, 2, 3);
1357
1358 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::AttachmentStateIcon);
1359 cil->setPixmap(*dummyTheme.pixmap(Theme::IconAttachment));
1360 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1361 gblayout->addWidget(cil, 0, 4);
1362
1363 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::EncryptionStateIcon);
1364 cil->setPixmap(*dummyTheme.pixmap(Theme::IconFullyEncrypted));
1365 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1366 gblayout->addWidget(cil, 1, 4);
1367
1368 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SignatureStateIcon);
1369 cil->setPixmap(*dummyTheme.pixmap(Theme::IconFullySigned));
1370 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1371 gblayout->addWidget(cil, 2, 4);
1372
1373 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ActionItemStateIcon);
1374 cil->setPixmap(*dummyTheme.pixmap(Theme::IconActionItem));
1375 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1376 gblayout->addWidget(cil, 0, 5);
1377
1378 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::AnnotationIcon);
1379 cil->setPixmap(*dummyTheme.pixmap(Theme::IconAnnotation));
1380 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1381 gblayout->addWidget(cil, 1, 5);
1382
1383 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::InvitationIcon);
1384 cil->setPixmap(*dummyTheme.pixmap(Theme::IconInvitation));
1385 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1386 gblayout->addWidget(cil, 2, 5);
1387
1388 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ImportantStateIcon);
1389 cil->setPixmap(*dummyTheme.pixmap(Theme::IconImportant));
1390 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1391 gblayout->addWidget(cil, 0, 6);
1392
1393 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SpamHamStateIcon);
1394 cil->setPixmap(*dummyTheme.pixmap(Theme::IconSpam));
1395 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1396 gblayout->addWidget(cil, 1, 6);
1397
1398 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::WatchedIgnoredStateIcon);
1399 cil->setPixmap(*dummyTheme.pixmap(Theme::IconWatched));
1400 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1401 gblayout->addWidget(cil, 2, 6);
1402
1403 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ExpandedStateIcon);
1404 cil->setPixmap(*dummyTheme.pixmap(Theme::IconShowMore));
1405 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1406 gblayout->addWidget(cil, 0, 7);
1407
1408 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::VerticalLine);
1409 cil->setPixmap(*dummyTheme.pixmap(Theme::IconVerticalLine));
1410 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1411 gblayout->addWidget(cil, 1, 7);
1412
1413 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::HorizontalSpacer);
1414 cil->setPixmap(*dummyTheme.pixmap(Theme::IconHorizontalSpacer));
1415 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1416 gblayout->addWidget(cil, 2, 7);
1417
1418 mPreviewWidget = new ThemePreviewWidget(tab);
1419 tabg->addWidget(mPreviewWidget, 1, 0);
1420
1421 auto l = new QLabel(tab);
1422 l->setText(
1423 i18n("Right click on the header to add or modify columns. Drag the content items and drop them on the columns in order to compose your theme. Right "
1424 "click on the items inside the view for more options."));
1425 l->setWordWrap(true);
1426 l->setAlignment(Qt::AlignCenter);
1427 tabg->addWidget(l, 2, 0);
1428
1429 tabg->setRowStretch(1, 1);
1430
1431 // Advanced tab
1432 tab = new QWidget(this);
1433 addTab(tab, i18nc("@title:tab Advanced theme settings", "Advanced"));
1434
1435 tabg = new QGridLayout(tab);
1436
1437 l = new QLabel(i18n("Header:"), tab);
1438 tabg->addWidget(l, 0, 0);
1439
1440 mViewHeaderPolicyCombo = new QComboBox(tab);
1441 tabg->addWidget(mViewHeaderPolicyCombo, 0, 1);
1442
1443 l = new QLabel(i18n("Icon size:"), tab);
1444 tabg->addWidget(l, 1, 0);
1445
1446 mIconSizeSpinBox = new KPluralHandlingSpinBox(tab);
1447 mIconSizeSpinBox->setMinimum(8);
1448 mIconSizeSpinBox->setMaximum(64);
1449 mIconSizeSpinBox->setSuffix(ki18ncp("suffix in a spinbox", " pixel", " pixels"));
1450
1451 QObject::connect(mIconSizeSpinBox, &KPluralHandlingSpinBox::valueChanged, this, &ThemeEditor::slotIconSizeSpinBoxValueChanged);
1452
1453 tabg->addWidget(mIconSizeSpinBox, 1, 1);
1454
1455 tabg->setColumnStretch(1, 1);
1456 tabg->setRowStretch(2, 1);
1457 fillViewHeaderPolicyCombo();
1458}
1459
1460ThemeEditor::~ThemeEditor() = default;
1461
1462void ThemeEditor::editTheme(Theme *set)
1463{
1464 mCurrentTheme = set;
1465 mPreviewWidget->setTheme(mCurrentTheme);
1466
1467 if (!mCurrentTheme) {
1468 setEnabled(false);
1469 return;
1470 }
1471 setEnabled(true);
1472
1473 nameEdit()->setText(set->name());
1475
1476 ComboBoxUtils::setIntegerOptionComboValue(mViewHeaderPolicyCombo, (int)mCurrentTheme->viewHeaderPolicy());
1477
1478 mIconSizeSpinBox->setValue(set->iconSize());
1479 setReadOnly(mCurrentTheme->readOnly());
1480}
1481
1482void ThemeEditor::setReadOnly(bool readOnly)
1483{
1484 mPreviewWidget->setReadOnly(readOnly);
1485 mViewHeaderPolicyCombo->setEnabled(!readOnly);
1486 mIconSizeSpinBox->setEnabled(!readOnly);
1487 OptionSetEditor::setReadOnly(readOnly);
1488}
1489
1490void ThemeEditor::commit()
1491{
1492 if (!mCurrentTheme || mCurrentTheme->readOnly()) {
1493 return;
1494 }
1495
1496 mCurrentTheme->setName(nameEdit()->text());
1497 mCurrentTheme->setDescription(descriptionEdit()->toPlainText());
1498
1500 mCurrentTheme->setIconSize(mIconSizeSpinBox->value());
1501 // other settings are already committed to this theme
1502}
1503
1504void ThemeEditor::fillViewHeaderPolicyCombo()
1505{
1507}
1508
1509void ThemeEditor::slotNameEditTextEdited(const QString &newName)
1510{
1511 if (!mCurrentTheme) {
1512 return;
1513 }
1514 mCurrentTheme->setName(newName);
1515 Q_EMIT themeNameChanged();
1516}
1517
1518void ThemeEditor::slotIconSizeSpinBoxValueChanged(int val)
1519{
1520 if (!mCurrentTheme) {
1521 return;
1522 }
1523 mCurrentTheme->setIconSize(val);
1524
1525 mPreviewWidget->setTheme(mCurrentTheme); // will trigger a cache reset and a view update
1526}
1527
1528MessageList::Core::Theme *ThemeEditor::editedTheme() const
1529{
1530 return mCurrentTheme;
1531}
1532
1533#include "moc_themeeditor.cpp"
constexpr bool isEmpty() const
@ PerfectReferencesAndSubject
Thread by all of the above and try to match subjects too.
Definition aggregation.h:69
A message item that can have a fake tag list and a fake annotation.
Type type() const
Returns the type of this item.
Definition item.cpp:342
This class is an optimizing helper for dealing with large flat QAbstractItemModel objects.
const QString & description() const
Returns a description of this option set.
Definition optionset.h:79
void setName(const QString &name)
Sets the name of this OptionSet.
Definition optionset.h:69
const QString & name() const
Returns the name of this OptionSet.
Definition optionset.h:59
void setDescription(const QString &description)
Sets the description for this option set.
Definition optionset.h:87
MessageSorting
The available message sorting options.
Definition sortorder.h:60
@ NoMessageSorting
Don't sort the messages at all.
Definition sortorder.h:61
static QList< QPair< QString, int > > enumerateMessageSortingOptions(Aggregation::Threading t)
Enumerates the message sorting options compatible with the specified Threading setting.
Definition sortorder.cpp:60
The ThemeDelegate paints the message list view message and group items by using the supplied Theme.
QRect hitRowRect() const
Returns the rectangle of the row that was reported as hit by the previous call to hitTest().
const Theme::Column * hitColumn() const
Returns the theme column that was reported as hit by the previous call to hitTest().
bool hitTest(const QPoint &viewportPoint, bool exact=true)
Performs a hit test on the specified viewport point.
bool hitContentItemRight() const
Returns true if the hit theme content item was a right item and false otherwise.
Item * hitItem() const
Returns the Item that was reported as hit by the previous call to hitTest().
QRect hitContentItemRect() const
Returns the bounding rect of the content item that was reported as hit by the previous call to hitTes...
const Theme::ContentItem * hitContentItem() const
Returns the theme content item that was reported as hit by the previous call to hitTest().
QSize sizeHintForItemTypeAndColumn(Item::Type type, int column, const Item *item=nullptr) const
Returns a heuristic sizeHint() for the specified item type and column.
void generalFontChanged()
Called when the global fonts change (from systemsettings)
bool hitRowIsMessageRow() const
Returns true if the hitRow() is a message row, false otherwise.
const Theme::Row * hitRow() const
Returns the theme row that was reported as hit by the previous call to hitTest().
int hitRowIndex() const
Returns the index of the theme row that was reported as hit by the previous call to hitTest().
The Column class defines a view column available inside this theme.
Definition theme.h:506
const QList< Row * > & groupHeaderRows() const
Returns the list of rows visible in this column for a GroupHeaderItem.
Definition theme.cpp:761
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
const QList< Row * > & messageRows() const
Returns the list of rows visible in this column for a MessageItem.
Definition theme.cpp:718
void setLabel(const QString &label)
Sets the label for this column.
Definition theme.cpp:642
const QString & label() const
Returns the label set for this column.
Definition theme.cpp:637
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
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
@ HideWhenDisabled
In disabled state the icon should take no space (overrides SoftenByBlendingWhenDisabled)
Definition theme.h:216
@ SoftenByBlendingWhenDisabled
In disabled state the icon should be still shown, but made very soft by alpha blending.
Definition theme.h:217
@ UseCustomColor
For text and vertical line. If set then always use a custom color, otherwise use default text color.
Definition theme.h:218
@ IsItalic
Fot text items. If set then always show as italic, otherwise use the default font style.
Definition theme.h:220
@ IsBold
For text items. If set then always show as bold, otherwise use the default font weight.
Definition theme.h:219
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
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
Type
The available ContentItem types.
Definition theme.h:106
@ InvitationIcon
Whether the message is an invitation.
Definition theme.h:203
@ CombinedReadRepliedStateIcon
The combined icon that displays the unread/read/replied/forwarded state (never disabled)
Definition theme.h:190
@ SignatureStateIcon
The Signature state icon for messages.
Definition theme.h:174
@ Date
Formatted date time of the message/group.
Definition theme.h:114
@ MostRecentDate
The date of the most recent message in subtree.
Definition theme.h:186
@ ReadStateIcon
The icon that displays the unread/read state (never disabled)
Definition theme.h:134
@ RepliedStateIcon
The icon that displays the replied/forwarded state (may be disabled)
Definition theme.h:142
@ WatchedIgnoredStateIcon
The Watched/Ignored state icon.
Definition theme.h:162
@ ImportantStateIcon
The Important tag icon.
Definition theme.h:154
@ Folder
Folder of the message.
Definition theme.h:207
@ AttachmentStateIcon
The icon that displays the attachment state (may be disabled)
Definition theme.h:138
@ ExpandedStateIcon
The Expanded state icon for group headers.
Definition theme.h:166
@ SenderOrReceiver
From: or To: strip, depending on the folder settings.
Definition theme.h:118
@ Subject
Display the subject of the message item.
Definition theme.h:110
@ VerticalLine
A vertical separation line.
Definition theme.h:178
@ HorizontalSpacer
A small empty spacer usable as separator.
Definition theme.h:182
@ EncryptionStateIcon
The Encryption state icon for messages.
Definition theme.h:170
@ TagList
The list of MessageItem::Tag entries.
Definition theme.h:194
@ Size
Formatted size of the message.
Definition theme.h:130
@ AnnotationIcon
Whether the message has a annotation/note.
Definition theme.h:198
@ Receiver
To: strip, always.
Definition theme.h:126
@ ActionItemStateIcon
The ActionItem state icon.
Definition theme.h:150
@ Sender
From: strip, always.
Definition theme.h:122
@ SpamHamStateIcon
The Spam/Ham state icon.
Definition theme.h:158
void setHideWhenDisabled(bool hideWhenDisabled)
Sets the flag that causes this item to be hidden when disabled.
Definition theme.cpp:231
const QColor & customColor() const
Returns the custom color set for this item.
Definition theme.cpp:268
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 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
void insertLeftItem(int idx, ContentItem *item)
Adds a left aligned item at the specified position in this row.
Definition theme.cpp:388
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
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
GroupHeaderBackgroundMode groupHeaderBackgroundMode() const
Returns the group header background mode for this theme.
Definition theme.cpp:1004
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
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
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
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
GroupHeaderBackgroundStyle
How do we paint group header background ?
Definition theme.h:807
The base class for the OptionSet editors.
QLineEdit * nameEdit() const
Returns the editor for the name of the OptionSet.
KTextEdit * descriptionEdit() const
Returns the editor for the description of the OptionSet.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
KLocalizedString KI18N_EXPORT ki18ncp(const char *context, const char *singular, const char *plural)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
const QList< QKeySequence > & end()
The implementation independent part of the MessageList library.
Definition aggregation.h:22
void fillIntegerOptionCombo(QComboBox *combo, const QList< QPair< QString, int > > &optionDescriptors)
Fills the specified QComboBox with the options available in optionDescriptors.
void setIntegerOptionComboValue(QComboBox *combo, int value)
Sets the currently selected option in the specified combo.
int getIntegerOptionComboValue(QComboBox *combo, int defaultValue)
Returns the identifier of the currently selected option in the specified combo.
bool isChecked() const const
void clicked(bool checked)
void setShortcut(const QKeySequence &key)
virtual void mouseMoveEvent(QMouseEvent *event) override
virtual void mousePressEvent(QMouseEvent *event) override
virtual void paintEvent(QPaintEvent *event) override
QWidget * viewport() const const
void setText(const QString &text)
void triggered(bool checked)
QColor getColor(const QColor &initial, QWidget *parent, const QString &title, QColorDialog::ColorDialogOptions options)
virtual void accept()
void acceptProposedAction()
const QMimeData * mimeData() const const
void ignore()
virtual void changeEvent(QEvent *ev) override
int logicalIndexAt(int position) const const
void resizeSection(int logicalIndex, int size)
QIcon fromTheme(const QString &name)
void append(const T &value)
QList::iterator begin()
QList::const_iterator constBegin() const const
QList::const_iterator constEnd() const const
int count(const T &value) const const
QList::iterator end()
bool isEmpty() const const
QAction * addAction(const QString &text)
QAction * addMenu(QMenu *menu)
QAction * addSection(const QString &text)
QAction * addSeparator()
QAction * exec()
bool isEmpty() const const
void setTitle(const QString &title)
void triggered(QAction *action)
QByteArray data(const QString &mimeType) const const
virtual bool hasFormat(const QString &mimeType) const const
bool isValid() const const
QModelIndex parent() const const
Qt::MouseButton button() const const
Qt::MouseButtons buttons() const const
QPoint pos() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
void setDefault(bool)
QPoint bottomLeft() const const
QPoint bottomRight() const const
bool contains(const QRect &rectangle, bool proper) const const
bool isValid() const const
int left() const const
int right() const const
QPoint topLeft() const const
QPoint topRight() const const
int width() const const
int width() const const
void setValue(int val)
void valueChanged(int i)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
AlignCenter
CustomContextMenu
CopyAction
ItemIsEnabled
Key_Return
LeftButton
void setPlainText(const QString &text)
QHeaderView * header() const const
void setColumnCount(int columns)
virtual bool event(QEvent *e) override
QTreeWidgetItem * headerItem() const const
void setHeaderLabels(const QStringList &labels)
void setExpanded(bool expand)
void customContextMenuRequested(const QPoint &pos)
void setEnabled(bool)
void hide()
QPoint mapToGlobal(const QPoint &pos) const const
void show()
virtual void showEvent(QShowEvent *event)
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:37:31 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.