Messagelib

widget.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Kevin Ottens <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "widget.h"
8 
9 #include <Akonadi/Collection>
10 #include <Akonadi/ItemCopyJob>
11 #include <Akonadi/ItemMoveJob>
12 
13 #include "core/messageitem.h"
14 #include "core/view.h"
15 #include "storagemodel.h"
16 #include <messagelistsettings.h>
17 
18 #include <QAction>
19 #include <QApplication>
20 #include <QDrag>
21 #include <QDragMoveEvent>
22 #include <QDropEvent>
23 #include <QMimeData>
24 #include <QUrlQuery>
25 
26 #include "messagelist_debug.h"
27 #include <KJob>
28 #include <KLocalizedString>
29 #include <KXMLGUIClient>
30 #include <KXMLGUIFactory>
31 #include <QIcon>
32 #include <QMenu>
33 #include <QUrl>
34 
35 #include "core/groupheaderitem.h"
36 
37 #include <Akonadi/Monitor>
38 #include <Akonadi/Tag>
39 #include <Akonadi/TagAttribute>
40 #include <Akonadi/TagFetchJob>
41 #include <Akonadi/TagFetchScope>
42 
43 namespace MessageList
44 {
45 class MessageList::Widget::WidgetPrivate
46 {
47 public:
48  WidgetPrivate(Widget *owner)
49  : q(owner)
50  {
51  }
52 
53  Q_REQUIRED_RESULT Akonadi::Item::List selectionAsItems() const;
54  Q_REQUIRED_RESULT Akonadi::Item itemForRow(int row) const;
55  Q_REQUIRED_RESULT KMime::Message::Ptr messageForRow(int row) const;
56 
57  Widget *const q;
58 
59  int mLastSelectedMessage = -1;
60  KXMLGUIClient *mXmlGuiClient = nullptr;
61  QModelIndex mGroupHeaderItemIndex;
62  Akonadi::Monitor *mMonitor = nullptr;
63 };
64 } // namespace MessageList
65 
66 using namespace MessageList;
67 using namespace Akonadi;
68 
70  : Core::Widget(parent)
71  , d(new WidgetPrivate(this))
72 {
73  populateStatusFilterCombo();
74 
75  d->mMonitor = new Akonadi::Monitor(this);
76  d->mMonitor->setObjectName(QStringLiteral("MessageListTagMonitor"));
77  d->mMonitor->setTypeMonitored(Akonadi::Monitor::Tags);
78  connect(d->mMonitor, &Akonadi::Monitor::tagAdded, this, &Widget::populateStatusFilterCombo);
79  connect(d->mMonitor, &Akonadi::Monitor::tagRemoved, this, &Widget::populateStatusFilterCombo);
80  connect(d->mMonitor, &Akonadi::Monitor::tagChanged, this, &Widget::populateStatusFilterCombo);
81 }
82 
83 MessageList::Widget::~Widget() = default;
84 
86 {
87  d->mXmlGuiClient = xmlGuiClient;
88 }
89 
91 {
92  if (e->source() == view()->viewport()) {
93  return false;
94  }
95 
96  Collection::List collections = static_cast<const MessageList::StorageModel *>(storageModel())->displayedCollections();
97  if (collections.size() != 1) {
98  return false; // no folder here or too many (in case we can't decide where the drop will end)
99  }
100 
101  const Collection target = collections.first();
102 
103  if ((target.rights() & Collection::CanCreateItem) == 0) {
104  return false; // no way to drag into
105  }
106 
107  const QList<QUrl> urls = e->mimeData()->urls();
108  for (const QUrl &url : urls) {
109  const Collection collection = Collection::fromUrl(url);
110  if (collection.isValid()) { // You're not supposed to drop collections here
111  return false;
112  } else { // Yay, this is an item!
113  QUrlQuery query(url);
114  const QString type = query.queryItemValue(QStringLiteral("type"));
115  if (!target.contentMimeTypes().contains(type)) {
116  return false;
117  }
118  }
119  }
120 
121  return true;
122 }
123 
125  MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour,
126  bool centerItem,
127  bool loop)
128 {
129  return view()->selectNextMessageItem(messageTypeFilter, existingSelectionBehaviour, centerItem, loop);
130 }
131 
133  MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour,
134  bool centerItem,
135  bool loop)
136 {
137  return view()->selectPreviousMessageItem(messageTypeFilter, existingSelectionBehaviour, centerItem, loop);
138 }
139 
140 bool MessageList::Widget::focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop)
141 {
142  return view()->focusNextMessageItem(messageTypeFilter, centerItem, loop);
143 }
144 
145 bool MessageList::Widget::focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop)
146 {
147  return view()->focusPreviousMessageItem(messageTypeFilter, centerItem, loop);
148 }
149 
151 {
152  view()->selectFocusedMessageItem(centerItem);
153 }
154 
156 {
157  return view()->selectFirstMessageItem(messageTypeFilter, centerItem);
158 }
159 
160 bool MessageList::Widget::selectLastMessageItem(Core::MessageTypeFilter messageTypeFilter, bool centerItem)
161 {
162  return view()->selectLastMessageItem(messageTypeFilter, centerItem);
163 }
164 
166 {
167  view()->setAllGroupsExpanded(true);
168  view()->selectAll();
169 }
170 
172 {
173  view()->setCurrentThreadExpanded(expand);
174 }
175 
177 {
178  view()->setAllThreadsExpanded(expand);
179 }
180 
182 {
183  view()->setAllGroupsExpanded(expand);
184 }
185 
186 void MessageList::Widget::focusQuickSearch(const QString &selectedText)
187 {
188  view()->focusQuickSearch(selectedText);
189 }
190 
191 void MessageList::Widget::setQuickSearchClickMessage(const QString &msg)
192 {
193  view()->setQuickSearchClickMessage(msg);
194 }
195 
197 {
198  auto fetchJob = new Akonadi::TagFetchJob(this);
199  fetchJob->fetchScope().fetchAttribute<Akonadi::TagAttribute>();
200  connect(fetchJob, &Akonadi::TagFetchJob::result, this, &Widget::slotTagsFetched);
201 }
202 
203 void MessageList::Widget::slotTagsFetched(KJob *job)
204 {
205  if (job->error()) {
206  qCWarning(MESSAGELIST_LOG) << "Failed to load tags " << job->errorString();
207  return;
208  }
209  auto fetchJob = static_cast<Akonadi::TagFetchJob *>(job);
210 
211  KConfigGroup conf(MessageList::MessageListSettings::self()->config(), "MessageListView");
212  const QString tagSelected = conf.readEntry(QStringLiteral("TagSelected"));
213  if (tagSelected.isEmpty()) {
214  setCurrentStatusFilterItem();
215  return;
216  }
217  const QStringList tagSelectedLst = tagSelected.split(QLatin1Char(','));
218 
219  addMessageTagItem(QIcon::fromTheme(QStringLiteral("mail-flag")).pixmap(16, 16),
220  i18nc("Item in list of Akonadi tags, to show all e-mails", "All"),
221  QString());
222 
223  QStringList tagFound;
224  const auto tags{fetchJob->tags()};
225  for (const Akonadi::Tag &akonadiTag : tags) {
226  if (tagSelectedLst.contains(akonadiTag.url().url())) {
227  tagFound.append(akonadiTag.url().url());
228  QString iconName = QStringLiteral("mail-tagged");
229  const QString label = akonadiTag.name();
230  const QString id = akonadiTag.url().url();
231  const auto attr = akonadiTag.attribute<Akonadi::TagAttribute>();
232  if (attr) {
233  iconName = attr->iconName();
234  }
235  addMessageTagItem(QIcon::fromTheme(iconName).pixmap(16, 16), label, QVariant(id));
236  }
237  }
238  conf.writeEntry(QStringLiteral("TagSelected"), tagFound);
239  conf.sync();
240 
241  setCurrentStatusFilterItem();
242 }
243 
245 {
246  int row = -1;
247  if (msg) {
248  row = msg->currentModelIndexRow();
249  }
250 
251  if (!msg || !msg->isValid() || !storageModel()) {
252  d->mLastSelectedMessage = -1;
253  Q_EMIT messageSelected(Akonadi::Item());
254  return;
255  }
256 
257  Q_ASSERT(row >= 0);
258 
259  d->mLastSelectedMessage = row;
260 
261  Q_EMIT messageSelected(d->itemForRow(row)); // this MAY be null
262 }
263 
265 {
266  Q_ASSERT(msg); // must not be null
267  Q_ASSERT(storageModel());
268 
269  if (!msg->isValid()) {
270  return;
271  }
272 
273  int row = msg->currentModelIndexRow();
274  Q_ASSERT(row >= 0);
275 
276  // The assert below may fail when quickly opening and closing a non-selected thread.
277  // This will actually activate the item without selecting it...
278  // Q_ASSERT( d->mLastSelectedMessage == row );
279 
280  if (d->mLastSelectedMessage != row) {
281  // Very ugly. We are activating a non selected message.
282  // This is very likely a double click on the plus sign near a thread leader.
283  // Dealing with mLastSelectedMessage here would be expensive: it would involve releasing the last selected,
284  // emitting signals, handling recursion... ugly.
285  // We choose a very simple solution: double clicking on the plus sign near a thread leader does
286  // NOT activate the message (i.e open it in a toplevel window) if it isn't previously selected.
287  return;
288  }
289 
290  Q_EMIT messageActivated(d->itemForRow(row)); // this MAY be null
291 }
292 
294 {
295  Q_EMIT selectionChanged();
296  if (!currentMessageItem()) {
297  Q_EMIT messageSelected(Akonadi::Item());
298  }
299 }
300 
302 {
303  Q_UNUSED(selectedItems)
304 
305  if (!d->mXmlGuiClient) {
306  return;
307  }
308 
309  QMenu *popup = static_cast<QMenu *>(d->mXmlGuiClient->factory()->container(QStringLiteral("akonadi_messagelist_contextmenu"), d->mXmlGuiClient));
310  if (popup) {
311  popup->exec(globalPos);
312  }
313 }
314 
316 {
317  Q_ASSERT(msg); // must not be null
318  Q_ASSERT(storageModel());
319 
320  if (!msg->isValid()) {
321  return;
322  }
323 
324  int row = msg->currentModelIndexRow();
325  Q_ASSERT(row >= 0);
326 
327  Akonadi::Item item = d->itemForRow(row);
328  Q_ASSERT(item.isValid());
329 
330  Q_EMIT messageStatusChangeRequest(item, set, clear);
331 }
332 
333 void MessageList::Widget::viewGroupHeaderContextPopupRequest(MessageList::Core::GroupHeaderItem *ghi, const QPoint &globalPos)
334 {
335  Q_UNUSED(ghi)
336 
337  QMenu menu(this);
338 
339  QAction *act = nullptr;
340 
341  QModelIndex index = view()->model()->index(ghi, 0);
342  d->mGroupHeaderItemIndex = index;
343 
344  if (view()->isExpanded(index)) {
345  act = menu.addAction(i18n("Collapse Group"));
346  connect(act, &QAction::triggered, this, &Widget::slotCollapseItem);
347  } else {
348  act = menu.addAction(i18n("Expand Group"));
349  connect(act, &QAction::triggered, this, &Widget::slotExpandItem);
350  }
351 
352  menu.addSeparator();
353 
354  act = menu.addAction(i18n("Expand All Groups"));
355  connect(act, &QAction::triggered, view(), &Core::View::slotExpandAllGroups);
356 
357  act = menu.addAction(i18n("Collapse All Groups"));
358  connect(act, &QAction::triggered, view(), &Core::View::slotCollapseAllGroups);
359 
360  menu.addSeparator();
361  act = menu.addAction(i18n("Expand All Threads"));
362  connect(act, &QAction::triggered, view(), &Core::View::slotExpandAllThreads);
363  act = menu.addAction(i18n("Collapse All Threads"));
364  connect(act, &QAction::triggered, view(), &Core::View::slotCollapseAllThreads);
365 
366  menu.exec(globalPos);
367 }
368 
370 {
371  if (!canAcceptDrag(e)) {
372  e->ignore();
373  return;
374  }
375 
376  e->accept();
377 }
378 
380 {
381  if (!canAcceptDrag(e)) {
382  e->ignore();
383  return;
384  }
385 
386  e->accept();
387 }
388 
389 enum DragMode { DragCopy, DragMove, DragCancel };
390 
392 {
393  if (!canAcceptDrag(e)) {
394  e->ignore();
395  return;
396  }
397 
398  const QList<QUrl> urls = e->mimeData()->urls();
399  if (urls.isEmpty()) {
400  qCWarning(MESSAGELIST_LOG) << "Could not decode drag data!";
401  e->ignore();
402  return;
403  }
404 
405  e->accept();
406 
407  int action;
408  if ((e->possibleActions() & Qt::MoveAction) == 0) { // We can't move anyway
409  action = DragCopy;
410  } else {
411  const auto keybstate = QApplication::keyboardModifiers();
412  if (keybstate & Qt::CTRL) {
413  action = DragCopy;
414  } else if (keybstate & Qt::SHIFT) {
415  action = DragMove;
416  } else {
417  QMenu menu;
418  QAction *moveAction =
419  menu.addAction(QIcon::fromTheme(QStringLiteral("edit-move"), QIcon::fromTheme(QStringLiteral("go-jump"))), i18n("&Move Here"));
420  QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy Here"));
421  menu.addSeparator();
422  menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-cancel")), i18n("C&ancel"));
423 
424  QAction *menuChoice = menu.exec(QCursor::pos());
425  if (menuChoice == moveAction) {
426  action = DragMove;
427  } else if (menuChoice == copyAction) {
428  action = DragCopy;
429  } else {
430  action = DragCancel;
431  }
432  }
433  }
434  if (action == DragCancel) {
435  return;
436  }
437 
438  Collection::List collections = static_cast<const MessageList::StorageModel *>(storageModel())->displayedCollections();
439  Collection target = collections.at(0);
440  Akonadi::Item::List items;
441  items.reserve(urls.count());
442  for (const QUrl &url : std::as_const(urls)) {
443  items << Akonadi::Item::fromUrl(url);
444  }
445 
446  if (action == DragCopy) {
447  new ItemCopyJob(items, target, this);
448  } else if (action == DragMove) {
449  new ItemMoveJob(items, target, this);
450  }
451 }
452 
454 {
455  Collection::List collections = static_cast<const MessageList::StorageModel *>(storageModel())->displayedCollections();
456 
457  if (collections.isEmpty()) {
458  return; // no folder here
459  }
460 
461  const QVector<Core::MessageItem *> selection = view()->selectionAsMessageItemList();
462  if (selection.isEmpty()) {
463  return;
464  }
465 
466  bool readOnly = false;
467 
468  for (const Collection &c : std::as_const(collections)) {
469  // We won't be able to remove items from this collection
470  if ((c.rights() & Collection::CanDeleteItem) == 0) {
471  // So the drag will be read-only
472  readOnly = true;
473  break;
474  }
475  }
476 
477  QList<QUrl> urls;
478  urls.reserve(selection.count());
479  for (Core::MessageItem *mi : selection) {
480  const Akonadi::Item i = d->itemForRow(mi->currentModelIndexRow());
482  QUrlQuery query(url);
483  query.addQueryItem(QStringLiteral("parent"), QString::number(mi->parentCollectionId()));
484  url.setQuery(query);
485  urls << url;
486  }
487 
488  auto mimeData = new QMimeData;
489  mimeData->setUrls(urls);
490 
491  auto drag = new QDrag(view()->viewport());
492  drag->setMimeData(mimeData);
493 
494  // Set pixmap
495  QPixmap pixmap;
496  if (selection.size() == 1) {
497  pixmap = QIcon::fromTheme(QStringLiteral("mail-message")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize));
498  } else {
499  pixmap = QIcon::fromTheme(QStringLiteral("document-multiple")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize));
500  }
501 
502  // Calculate hotspot (as in Konqueror)
503  if (!pixmap.isNull()) {
504  drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
505  drag->setPixmap(pixmap);
506  }
507 
508  if (readOnly) {
509  drag->exec(Qt::CopyAction);
510  } else {
511  drag->exec(Qt::CopyAction | Qt::MoveAction);
512  }
513 }
514 
515 Akonadi::Item::List MessageList::Widget::WidgetPrivate::selectionAsItems() const
516 {
518  const QVector<Core::MessageItem *> selection = q->view()->selectionAsMessageItemList();
519  res.reserve(selection.count());
520 
521  for (Core::MessageItem *mi : std::as_const(selection)) {
522  Akonadi::Item i = itemForRow(mi->currentModelIndexRow());
523  Q_ASSERT(i.isValid());
524  res << i;
525  }
526 
527  return res;
528 }
529 
530 Akonadi::Item MessageList::Widget::WidgetPrivate::itemForRow(int row) const
531 {
532  return static_cast<const MessageList::StorageModel *>(q->storageModel())->itemForRow(row);
533 }
534 
535 KMime::Message::Ptr MessageList::Widget::WidgetPrivate::messageForRow(int row) const
536 {
537  return static_cast<const MessageList::StorageModel *>(q->storageModel())->messageForRow(row);
538 }
539 
541 {
542  Core::MessageItem *mi = view()->currentMessageItem();
543 
544  if (mi == nullptr) {
545  return {};
546  }
547 
548  return d->itemForRow(mi->currentModelIndexRow());
549 }
550 
552 {
553  Core::MessageItem *mi = view()->currentMessageItem();
554 
555  if (mi == nullptr) {
556  return {};
557  }
558 
559  return d->messageForRow(mi->currentModelIndexRow());
560 }
561 
563 {
565  const QVector<Core::MessageItem *> lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren);
566  if (lstMi.isEmpty()) {
567  return lstMiPtr;
568  }
569  lstMiPtr.reserve(lstMi.count());
570  for (Core::MessageItem *it : std::as_const(lstMi)) {
571  lstMiPtr.append(d->messageForRow(it->currentModelIndexRow()));
572  }
573  return lstMiPtr;
574 }
575 
576 Akonadi::Item::List MessageList::Widget::selectionAsMessageItemList(bool includeCollapsedChildren) const
577 {
578  Akonadi::Item::List lstMiPtr;
579  const QVector<Core::MessageItem *> lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren);
580  if (lstMi.isEmpty()) {
581  return lstMiPtr;
582  }
583  lstMiPtr.reserve(lstMi.count());
584  for (Core::MessageItem *it : std::as_const(lstMi)) {
585  lstMiPtr.append(d->itemForRow(it->currentModelIndexRow()));
586  }
587  return lstMiPtr;
588 }
589 
591 {
592  QVector<qlonglong> lstMiPtr;
593  const QVector<Core::MessageItem *> lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren);
594  if (lstMi.isEmpty()) {
595  return lstMiPtr;
596  }
597  lstMiPtr.reserve(lstMi.count());
598  for (Core::MessageItem *it : std::as_const(lstMi)) {
599  lstMiPtr.append(d->itemForRow(it->currentModelIndexRow()).id());
600  }
601  return lstMiPtr;
602 }
603 
604 QVector<Akonadi::Item::Id> MessageList::Widget::selectionAsListMessageId(bool includeCollapsedChildren) const
605 {
606  QVector<qlonglong> lstMiPtr;
607  const QVector<Core::MessageItem *> lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren);
608  if (lstMi.isEmpty()) {
609  return lstMiPtr;
610  }
611  lstMiPtr.reserve(lstMi.count());
612  for (Core::MessageItem *it : std::as_const(lstMi)) {
613  lstMiPtr.append(d->itemForRow(it->currentModelIndexRow()).id());
614  }
615  return lstMiPtr;
616 }
617 
619 {
620  Akonadi::Item::List lstMiPtr;
621  const QVector<Core::MessageItem *> lstMi = view()->currentThreadAsMessageItemList();
622  if (lstMi.isEmpty()) {
623  return lstMiPtr;
624  }
625  lstMiPtr.reserve(lstMi.count());
626  for (Core::MessageItem *it : std::as_const(lstMi)) {
627  lstMiPtr.append(d->itemForRow(it->currentModelIndexRow()));
628  }
629  return lstMiPtr;
630 }
631 
632 MessageList::Core::QuickSearchLine::SearchOptions MessageList::Widget::currentOptions() const
633 {
634  return view()->currentOptions();
635 }
636 
638 {
639  return view()->currentFilterStatus();
640 }
641 
643 {
644  return view()->currentFilterSearchString();
645 }
646 
648 {
649  return view()->isThreaded();
650 }
651 
653 {
654  return view()->selectionEmpty();
655 }
656 
658  Akonadi::Item::List &selectedVisibleItems,
659  bool *allSelectedBelongToSameThread,
660  bool includeCollapsedChildren) const
661 {
662  if (!storageModel()) {
663  return false;
664  }
665 
666  selectedItems.clear();
667  selectedVisibleItems.clear();
668 
669  const QVector<Core::MessageItem *> selected = view()->selectionAsMessageItemList(includeCollapsedChildren);
670 
671  Core::MessageItem *topmost = nullptr;
672 
673  *allSelectedBelongToSameThread = true;
674 
675  for (Core::MessageItem *it : std::as_const(selected)) {
676  const Akonadi::Item item = d->itemForRow(it->currentModelIndexRow());
677  selectedItems.append(item);
678  if (view()->isDisplayedWithParentsExpanded(it)) {
679  selectedVisibleItems.append(item);
680  }
681  if (topmost == nullptr) {
682  topmost = (*it).topmostMessage();
683  } else {
684  if (topmost != (*it).topmostMessage()) {
685  *allSelectedBelongToSameThread = false;
686  }
687  }
688  }
689  return true;
690 }
691 
692 void MessageList::Widget::deletePersistentSet(MessageList::Core::MessageItemSetReference ref)
693 {
694  view()->deletePersistentSet(ref);
695 }
696 
697 void MessageList::Widget::markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark)
698 {
699  QList<Core::MessageItem *> lstPersistent = view()->persistentSetCurrentMessageItemList(ref);
700  if (!lstPersistent.isEmpty()) {
701  view()->markMessageItemsAsAboutToBeRemoved(lstPersistent, bMark);
702  }
703 }
704 
705 Akonadi::Item::List MessageList::Widget::itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref)
706 {
707  Akonadi::Item::List lstItem;
708  const QList<Core::MessageItem *> refList = view()->persistentSetCurrentMessageItemList(ref);
709  if (!refList.isEmpty()) {
710  lstItem.reserve(refList.count());
711  for (Core::MessageItem *it : std::as_const(refList)) {
712  lstItem.append(d->itemForRow(it->currentModelIndexRow()));
713  }
714  }
715  return lstItem;
716 }
717 
718 MessageList::Core::MessageItemSetReference MessageList::Widget::selectionAsPersistentSet(bool includeCollapsedChildren) const
719 {
720  QVector<Core::MessageItem *> lstMi = view()->selectionAsMessageItemList(includeCollapsedChildren);
721  if (lstMi.isEmpty()) {
722  return -1;
723  }
724  return view()->createPersistentSet(lstMi);
725 }
726 
727 MessageList::Core::MessageItemSetReference MessageList::Widget::currentThreadAsPersistentSet() const
728 {
729  QVector<Core::MessageItem *> lstMi = view()->currentThreadAsMessageItemList();
730  if (lstMi.isEmpty()) {
731  return -1;
732  }
733  return view()->createPersistentSet(lstMi);
734 }
735 
736 Akonadi::Collection MessageList::Widget::currentCollection() const
737 {
738  Collection::List collections = static_cast<const MessageList::StorageModel *>(storageModel())->displayedCollections();
739  if (collections.size() != 1) {
740  return {}; // no folder here or too many (in case we can't decide where the drop will end)
741  }
742  return collections.first();
743 }
744 
745 void MessageList::Widget::slotCollapseItem()
746 {
747  view()->setCollapseItem(d->mGroupHeaderItemIndex);
748 }
749 
750 void MessageList::Widget::slotExpandItem()
751 {
752  view()->setExpandItem(d->mGroupHeaderItemIndex);
753 }
bool isValid() const
void append(const T &value)
bool isThreaded() const
Returns true if the current Aggregation is threaded, false otherwise (or if there is no current Aggre...
Qt::KeyboardModifiers keyboardModifiers()
void setCurrentThreadExpanded(bool expand)
If expand is true then it expands the current thread, otherwise collapses it.
bool canAcceptDrag(const QDropEvent *e)
Returns true if this drag can be accepted by the underlying view.
bool isEmpty() const const
The MessageItem class.
Definition: messageitem.h:34
void markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark)
If bMark is true this function marks the messages as "about to be removed" so they appear dimmer and ...
void viewSelectionChanged() override
Reimplemented from MessageList::Core::Widget.
QString number(int n, int base)
void setUrls(const QList< QUrl > &urls)
bool selectionEmpty() const
Fast function that determines if the selection is empty.
Qt::DropActions possibleActions() const const
void result(KJob *job)
void viewDragMoveEvent(QDragMoveEvent *e) override
Reimplemented from MessageList::Core::Widget.
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
void tagRemoved(const Akonadi::Tag &tag)
int count(const T &value) const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
void append(const T &value)
QIcon fromTheme(const QString &name)
void tagAdded(const Akonadi::Tag &tag)
QAction * addSeparator()
bool focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop)
Focuses the next message item in the view without actually selecting it.
QString currentFilterSearchString() const
Returns the search term in the current quicksearch field.
const QMimeData * mimeData() const const
bool getSelectionStats(Akonadi::Item::List &selectedSernums, Akonadi::Item::List &selectedVisibleSernums, bool *allSelectedBelongToSameThread, bool includeCollapsedChildren=true) const
Fills the lists of the selected message serial numbers and of the selected+visible ones.
void viewDragEnterEvent(QDragEnterEvent *e) override
Reimplemented from MessageList::Core::Widget.
T & first()
MessageList::Core::MessageItemSetReference currentThreadAsPersistentSet() const
Return a persistent set from current thread.
QAction * addAction(const QString &text)
void reserve(int alloc)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
The Akonadi specific implementation of the Core::Widget.
Definition: widget.h:32
bool focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop)
Focuses the previous message item in the view without actually selecting it.
QVector< Akonadi::MessageStatus > currentFilterStatus() const
Returns the Akonadi::MessageStatus in the current quicksearch field.
QUrl url(UrlType type=UrlShort) const
void viewGroupHeaderContextPopupRequest(MessageList::Core::GroupHeaderItem *group, const QPoint &globalPos) override
Reimplemented from MessageList::Core::Widget.
void clear()
QString i18n(const char *text, const TYPE &arg...)
MessageList::Core::MessageItemSetReference selectionAsPersistentSet(bool includeCollapsedChildren=true) const
Return a persistent set from current selection.
void selectFocusedMessageItem(bool centerItem)
Selects the currently focused message item.
void deletePersistentSet(MessageList::Core::MessageItemSetReference ref)
Deletes the persistent set pointed by the specified reference.
QVector< qlonglong > selectionAsMessageItemListId(bool includeCollapsedChildren) const
Returns the currently selected Items id (bound to current StorageModel).
static Item fromUrl(const QUrl &url)
const T & at(int i) const const
void setAllThreadsExpanded(bool expand)
If expand is true then it expands all the threads, otherwise collapses them.
bool isEmpty() const const
The Akonadi specific implementation of the Core::StorageModel.
Definition: storagemodel.h:35
bool selectNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop)
Selects the next message item in the view.
QVector< KMime::Message::Ptr > selectionAsMessageList(bool includeCollapsedChildren=true) const
Returns the currently selected KMime::Message::Ptr (bound to current StorageModel).
bool isValid() const
Returns true if this ModelInvariantIndex is valid, that is, it has been attached to a ModelInvariantR...
Akonadi::Item::List currentThreadAsMessageList() const
Returns the Akonadi::Item bound to the current StorageModel that are part of the current thread.
bool isEmpty() const const
Rights rights() const
QStringList contentMimeTypes() const
int height() const const
void setXmlGuiClient(KXMLGUIClient *xmlGuiClient)
Sets the XML GUI client which the view is used in.
KSharedConfigPtr config()
QPoint pos()
void reserve(int size)
PM_SmallIconSize
void viewMessageListContextPopupRequest(const QVector< Core::MessageItem * > &selectedItems, const QPoint &globalPos) override
Reimplemented from MessageList::Core::Widget.
QObject * source() const const
void setQuery(const QString &query, QUrl::ParsingMode mode)
bool isNull() const const
QString label(StandardShortcut id)
int currentModelIndexRow()
Returns the current model index row for this invariant index.
QList< QUrl > urls() const const
MoveAction
void triggered(bool checked)
void viewMessageSelected(MessageList::Core::MessageItem *msg) override
Reimplemented from MessageList::Core::Widget.
KMime::Message::Ptr currentMessage() const
Returns the current message for the list as KMime::Message::Ptr.
Widget(QWidget *parent)
Create a new message list widget.
Akonadi::Item::List selectionAsMessageItemList(bool includeCollapsedChildren=true) const
Returns the currently selected Items (bound to current StorageModel).
Akonadi::Item::List itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref)
Return Akonadi::Item from messageItemReference.
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
void viewDropEvent(QDropEvent *e) override
Reimplemented from MessageList::Core::Widget.
void ignore()
void viewStartDragRequest() override
Reimplemented from MessageList::Core::Widget.
void selectAll()
Selects all the items in the current folder.
ExistingSelectionBehaviour
This enum is used in the view message selection functions (for instance View::selectNextMessage())
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void viewMessageActivated(MessageList::Core::MessageItem *msg) override
Reimplemented from MessageList::Core::Widget.
void setAllGroupsExpanded(bool expand)
If expand is true then it expands all the groups (only the toplevel group item: inner threads are NOT...
int count(const T &value) const const
int size() const const
bool isValid() const
void viewMessageStatusChangeRequest(MessageList::Core::MessageItem *msg, Akonadi::MessageStatus set, Akonadi::MessageStatus clear) override
Reimplemented from MessageList::Core::Widget.
MessageTypeFilter
This enum is used in the view message selection functions (for instance View::nextMessageItem()).
void fillMessageTagCombo() override
Reimplemented from MessageList::Core::Widget.
bool selectFirstMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem)
Selects the first message item in the view that matches the specified Core::MessageTypeFilter.
void focusQuickSearch(const QString &selectedText)
Sets the focus on the quick search line of the currently active tab.
virtual QString errorString() const
int error() const
Akonadi::Item currentItem() const
Returns the current message for the list as Akonadi::Item.
bool selectPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop)
Selects the previous message item in the view.
bool selectLastMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem)
Selects the last message item in the view that matches the specified Core::MessageTypeFilter.
const QAbstractItemModel * model() const const
QAction * exec()
void tagChanged(const Akonadi::Tag &tag)
int width() const const
void accept()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:08:12 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.