Warning, file /pim/mailcommon/src/folder/foldertreeview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 0003 SPDX-FileCopyrightText: 2009-2024 Laurent Montel <montel@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "foldertreeview.h" 0009 #include "kernel/mailkernel.h" 0010 #include "util/mailutil_p.h" 0011 0012 #include <Akonadi/CollectionStatistics> 0013 #include <Akonadi/CollectionStatisticsDelegate> 0014 #include <Akonadi/EntityTreeModel> 0015 0016 #include <KConfigGroup> 0017 #include <KGuiItem> 0018 #include <KLocalizedString> 0019 #include <KMessageBox> 0020 #include <QMenu> 0021 0022 #include <QActionGroup> 0023 #include <QHeaderView> 0024 #include <QMouseEvent> 0025 0026 using namespace MailCommon; 0027 0028 FolderTreeView::FolderTreeView(QWidget *parent, bool showUnreadCount) 0029 : Akonadi::EntityTreeView(parent) 0030 { 0031 setProperty("_breeze_force_frame", false); 0032 init(showUnreadCount); 0033 } 0034 0035 FolderTreeView::FolderTreeView(KXMLGUIClient *xmlGuiClient, QWidget *parent, bool showUnreadCount) 0036 : Akonadi::EntityTreeView(xmlGuiClient, parent) 0037 { 0038 setProperty("_breeze_force_frame", false); 0039 init(showUnreadCount); 0040 } 0041 0042 FolderTreeView::~FolderTreeView() = default; 0043 0044 void FolderTreeView::disableSaveConfig() 0045 { 0046 mbDisableSaveConfig = true; 0047 } 0048 0049 void FolderTreeView::setTooltipsPolicy(FolderTreeWidget::ToolTipDisplayPolicy policy) 0050 { 0051 if (mToolTipDisplayPolicy == policy) { 0052 return; 0053 } 0054 0055 mToolTipDisplayPolicy = policy; 0056 Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy); 0057 writeConfig(); 0058 } 0059 0060 void FolderTreeView::disableContextMenuAndExtraColumn() 0061 { 0062 mbDisableContextMenuAndExtraColumn = true; 0063 const int nbColumn = header()->count(); 0064 for (int i = 1; i < nbColumn; ++i) { 0065 setColumnHidden(i, true); 0066 } 0067 } 0068 0069 void FolderTreeView::init(bool showUnreadCount) 0070 { 0071 setIconSize(QSize(22, 22)); 0072 setUniformRowHeights(true); 0073 mSortingPolicy = FolderTreeWidget::SortByCurrentColumn; 0074 mToolTipDisplayPolicy = FolderTreeWidget::DisplayAlways; 0075 0076 header()->setContextMenuPolicy(Qt::CustomContextMenu); 0077 connect(header(), &QWidget::customContextMenuRequested, this, &FolderTreeView::slotHeaderContextMenuRequested); 0078 0079 mCollectionStatisticsDelegate = new Akonadi::CollectionStatisticsDelegate(this); 0080 mCollectionStatisticsDelegate->setProgressAnimationEnabled(true); 0081 setItemDelegate(mCollectionStatisticsDelegate); 0082 mCollectionStatisticsDelegate->setUnreadCountShown(showUnreadCount && !header()->isSectionHidden(1)); 0083 } 0084 0085 void FolderTreeView::showStatisticAnimation(bool anim) 0086 { 0087 mCollectionStatisticsDelegate->setProgressAnimationEnabled(anim); 0088 } 0089 0090 void FolderTreeView::writeConfig() 0091 { 0092 if (mbDisableSaveConfig) { 0093 return; 0094 } 0095 0096 KConfigGroup myGroup(KernelIf->config(), QStringLiteral("MainFolderView")); 0097 myGroup.writeEntry("IconSize", iconSize().width()); 0098 myGroup.writeEntry("ToolTipDisplayPolicy", (int)mToolTipDisplayPolicy); 0099 myGroup.writeEntry("SortingPolicy", (int)mSortingPolicy); 0100 } 0101 0102 void FolderTreeView::readConfig() 0103 { 0104 KConfigGroup myGroup(KernelIf->config(), QStringLiteral("MainFolderView")); 0105 int iIconSize = myGroup.readEntry("IconSize", iconSize().width()); 0106 if ((iIconSize < 16) || (iIconSize > 32)) { 0107 iIconSize = 22; 0108 } 0109 setIconSize(QSize(iIconSize, iIconSize)); 0110 mToolTipDisplayPolicy = 0111 static_cast<FolderTreeWidget::ToolTipDisplayPolicy>(myGroup.readEntry("ToolTipDisplayPolicy", static_cast<int>(FolderTreeWidget::DisplayAlways))); 0112 0113 Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy); 0114 0115 setSortingPolicy((FolderTreeWidget::SortingPolicy)myGroup.readEntry("SortingPolicy", (int)FolderTreeWidget::SortByCurrentColumn), false); 0116 } 0117 0118 void FolderTreeView::slotHeaderContextMenuRequested(const QPoint &pnt) 0119 { 0120 if (mbDisableContextMenuAndExtraColumn) { 0121 readConfig(); 0122 return; 0123 } 0124 0125 // the menu for the columns 0126 QMenu menu; 0127 QAction *act = nullptr; 0128 const int nbColumn = header()->count(); 0129 if (nbColumn > 1) { 0130 menu.addSection(i18n("View Columns")); 0131 for (int i = 1; i < nbColumn; ++i) { 0132 act = menu.addAction(model()->headerData(i, Qt::Horizontal).toString()); 0133 act->setCheckable(true); 0134 act->setChecked(!header()->isSectionHidden(i)); 0135 act->setData(QVariant(i)); 0136 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeHeader); 0137 } 0138 } 0139 0140 menu.addSection(i18n("Icon Size")); 0141 0142 static const int icon_sizes[] = {16, 22, 32}; 0143 0144 auto grp = new QActionGroup(&menu); 0145 for (int i : icon_sizes) { 0146 act = menu.addAction(QStringLiteral("%1x%2").arg(i).arg(i)); 0147 act->setCheckable(true); 0148 grp->addAction(act); 0149 if (iconSize().width() == i) { 0150 act->setChecked(true); 0151 } 0152 act->setData(QVariant(i)); 0153 0154 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeIconSize); 0155 } 0156 menu.addSection(i18n("Display Tooltips")); 0157 0158 grp = new QActionGroup(&menu); 0159 0160 act = menu.addAction(i18nc("@action:inmenu Always display tooltips", "Always")); 0161 act->setCheckable(true); 0162 grp->addAction(act); 0163 act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayAlways); 0164 act->setData(QVariant((int)FolderTreeWidget::DisplayAlways)); 0165 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy); 0166 0167 act = menu.addAction(i18nc("@action:inmenu Never display tooltips.", "Never")); 0168 act->setCheckable(true); 0169 grp->addAction(act); 0170 act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayNever); 0171 act->setData(QVariant((int)FolderTreeWidget::DisplayNever)); 0172 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy); 0173 0174 menu.addSection(i18nc("@action:inmenu", "Sort Items")); 0175 0176 grp = new QActionGroup(&menu); 0177 0178 act = menu.addAction(i18nc("@action:inmenu", "Automatically, by Current Column")); 0179 act->setCheckable(true); 0180 grp->addAction(act); 0181 act->setChecked(mSortingPolicy == FolderTreeWidget::SortByCurrentColumn); 0182 act->setData(QVariant((int)FolderTreeWidget::SortByCurrentColumn)); 0183 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy); 0184 0185 act = menu.addAction(i18nc("@action:inmenu", "Manually, by Drag And Drop")); 0186 act->setCheckable(true); 0187 grp->addAction(act); 0188 act->setChecked(mSortingPolicy == FolderTreeWidget::SortByDragAndDropKey); 0189 act->setData(QVariant((int)FolderTreeWidget::SortByDragAndDropKey)); 0190 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy); 0191 0192 menu.exec(header()->mapToGlobal(pnt)); 0193 } 0194 0195 void FolderTreeView::slotHeaderContextMenuChangeSortingPolicy(bool) 0196 { 0197 auto act = qobject_cast<QAction *>(sender()); 0198 if (!act) { 0199 return; 0200 } 0201 0202 QVariant data = act->data(); 0203 0204 bool ok; 0205 int policy = data.toInt(&ok); 0206 if (!ok) { 0207 return; 0208 } 0209 0210 setSortingPolicy((FolderTreeWidget::SortingPolicy)policy, true); 0211 } 0212 0213 void FolderTreeView::setSortingPolicy(FolderTreeWidget::SortingPolicy policy, bool writeInConfig) 0214 { 0215 if (mSortingPolicy == policy) { 0216 return; 0217 } 0218 0219 mSortingPolicy = policy; 0220 switch (mSortingPolicy) { 0221 case FolderTreeWidget::SortByCurrentColumn: 0222 header()->setSectionsClickable(true); 0223 header()->setSortIndicatorShown(true); 0224 setSortingEnabled(true); 0225 Q_EMIT manualSortingChanged(false); 0226 break; 0227 0228 case FolderTreeWidget::SortByDragAndDropKey: 0229 header()->setSectionsClickable(false); 0230 header()->setSortIndicatorShown(false); 0231 0232 setSortingEnabled(false); // hack for qutie bug: this call shouldn't be here at all 0233 Q_EMIT manualSortingChanged(true); 0234 0235 break; 0236 default: 0237 // should never happen 0238 break; 0239 } 0240 if (writeInConfig) { 0241 writeConfig(); 0242 } 0243 } 0244 0245 void FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy(bool) 0246 { 0247 auto act = qobject_cast<QAction *>(sender()); 0248 if (!act) { 0249 return; 0250 } 0251 0252 QVariant data = act->data(); 0253 0254 bool ok; 0255 const int id = data.toInt(&ok); 0256 if (!ok) { 0257 return; 0258 } 0259 Q_EMIT changeTooltipsPolicy((FolderTreeWidget::ToolTipDisplayPolicy)id); 0260 } 0261 0262 void FolderTreeView::slotHeaderContextMenuChangeHeader(bool) 0263 { 0264 auto act = qobject_cast<QAction *>(sender()); 0265 if (!act) { 0266 return; 0267 } 0268 0269 QVariant data = act->data(); 0270 0271 bool ok; 0272 const int id = data.toInt(&ok); 0273 if (!ok) { 0274 return; 0275 } 0276 0277 if (id >= header()->count()) { 0278 return; 0279 } 0280 0281 if (id == 1) { 0282 mCollectionStatisticsDelegate->setUnreadCountShown(!act->isChecked()); 0283 } 0284 0285 setColumnHidden(id, !act->isChecked()); 0286 } 0287 0288 void FolderTreeView::slotHeaderContextMenuChangeIconSize(bool) 0289 { 0290 auto act = qobject_cast<QAction *>(sender()); 0291 if (!act) { 0292 return; 0293 } 0294 0295 QVariant data = act->data(); 0296 0297 bool ok; 0298 const int size = data.toInt(&ok); 0299 if (!ok) { 0300 return; 0301 } 0302 0303 const QSize newIconSize(QSize(size, size)); 0304 if (newIconSize == iconSize()) { 0305 return; 0306 } 0307 setIconSize(newIconSize); 0308 0309 writeConfig(); 0310 } 0311 0312 void FolderTreeView::setCurrentModelIndex(const QModelIndex &index) 0313 { 0314 if (index.isValid()) { 0315 clearSelection(); 0316 scrollTo(index); 0317 selectionModel()->setCurrentIndex(index, QItemSelectionModel::Rows); 0318 } 0319 } 0320 0321 void FolderTreeView::selectModelIndex(const QModelIndex &index) 0322 { 0323 if (index.isValid()) { 0324 scrollTo(index); 0325 selectionModel()->select(index, QItemSelectionModel::Rows | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Clear); 0326 } 0327 } 0328 0329 void FolderTreeView::slotSelectFocusFolder() 0330 { 0331 const QModelIndex index = currentIndex(); 0332 if (index.isValid()) { 0333 setCurrentIndex(index); 0334 } 0335 } 0336 0337 void FolderTreeView::slotFocusNextFolder() 0338 { 0339 const QModelIndex nextFolder = selectNextFolder(currentIndex()); 0340 0341 if (nextFolder.isValid()) { 0342 expand(nextFolder); 0343 setCurrentModelIndex(nextFolder); 0344 } 0345 } 0346 0347 QModelIndex FolderTreeView::selectNextFolder(const QModelIndex ¤t) 0348 { 0349 QModelIndex below; 0350 if (current.isValid()) { 0351 model()->fetchMore(current); 0352 if (model()->hasChildren(current)) { 0353 expand(current); 0354 below = indexBelow(current); 0355 } else if (current.row() < model()->rowCount(model()->parent(current)) - 1) { 0356 below = model()->index(current.row() + 1, current.column(), model()->parent(current)); 0357 } else { 0358 below = indexBelow(current); 0359 } 0360 } 0361 return below; 0362 } 0363 0364 void FolderTreeView::slotFocusPrevFolder() 0365 { 0366 const QModelIndex current = currentIndex(); 0367 if (current.isValid()) { 0368 QModelIndex above = indexAbove(current); 0369 setCurrentModelIndex(above); 0370 } 0371 } 0372 0373 void FolderTreeView::slotFocusFirstFolder() 0374 { 0375 const QModelIndex first = moveCursor(QAbstractItemView::MoveHome, Qt::NoModifier); 0376 if (first.isValid()) { 0377 setCurrentModelIndex(first); 0378 } 0379 } 0380 0381 void FolderTreeView::slotFocusLastFolder() 0382 { 0383 const QModelIndex last = moveCursor(QAbstractItemView::MoveEnd, Qt::NoModifier); 0384 if (last.isValid()) { 0385 setCurrentModelIndex(last); 0386 } 0387 } 0388 0389 void FolderTreeView::selectNextUnreadFolder(bool confirm) 0390 { 0391 // find next unread collection starting from current position 0392 if (!trySelectNextUnreadFolder(currentIndex(), ForwardSearch, confirm)) { 0393 // if there is none, jump to the last collection and try again 0394 trySelectNextUnreadFolder(model()->index(0, 0), ForwardSearch, confirm); 0395 } 0396 } 0397 0398 // helper method to find last item in the model tree 0399 static QModelIndex lastChildOf(QAbstractItemModel *model, const QModelIndex ¤t) 0400 { 0401 if (model->rowCount(current) == 0) { 0402 return current; 0403 } 0404 0405 return lastChildOf(model, model->index(model->rowCount(current) - 1, 0, current)); 0406 } 0407 0408 void FolderTreeView::selectPrevUnreadFolder(bool confirm) 0409 { 0410 // find next unread collection starting from current position 0411 if (!trySelectNextUnreadFolder(currentIndex(), BackwardSearch, confirm)) { 0412 // if there is none, jump to top and try again 0413 const QModelIndex index = lastChildOf(model(), QModelIndex()); 0414 trySelectNextUnreadFolder(index, BackwardSearch, confirm); 0415 } 0416 } 0417 0418 bool FolderTreeView::trySelectNextUnreadFolder(const QModelIndex ¤t, SearchDirection direction, bool confirm) 0419 { 0420 QModelIndex index = current; 0421 while (true) { 0422 index = nextUnreadCollection(index, direction); 0423 0424 if (!index.isValid()) { 0425 return false; 0426 } 0427 0428 const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0429 if (collection == Kernel::self()->trashCollectionFolder() || collection == Kernel::self()->outboxCollectionFolder()) { 0430 continue; 0431 } 0432 0433 if (ignoreUnreadFolder(collection, confirm)) { 0434 continue; 0435 } 0436 0437 if (allowedToEnterFolder(collection, confirm)) { 0438 expand(index); 0439 setCurrentIndex(index); 0440 selectModelIndex(index); 0441 return true; 0442 } else { 0443 return false; 0444 } 0445 } 0446 0447 return false; 0448 } 0449 0450 bool FolderTreeView::ignoreUnreadFolder(const Akonadi::Collection &collection, bool confirm) const 0451 { 0452 if (!confirm) { 0453 return false; 0454 } 0455 0456 // Skip drafts, sent mail and templates as well, when reading mail with the 0457 // space bar - but not when changing into the next folder with unread mail 0458 // via ctrl+ or ctrl- so we do this only if (confirm == true), which means 0459 // we are doing readOn. 0460 0461 return collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder() 0462 || collection == Kernel::self()->sentCollectionFolder(); 0463 } 0464 0465 bool FolderTreeView::allowedToEnterFolder(const Akonadi::Collection &collection, bool confirm) const 0466 { 0467 if (!confirm) { 0468 return true; 0469 } 0470 0471 // warn user that going to next folder - but keep track of 0472 // whether he wishes to be notified again in "AskNextFolder" 0473 // parameter (kept in the config file for kmail) 0474 const int result = KMessageBox::questionTwoActions(const_cast<FolderTreeView *>(this), 0475 i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()), 0476 i18n("Go to Next Unread Message"), 0477 KGuiItem(i18n("Go To")), 0478 KGuiItem(i18n("Do Not Go To")), // defaults 0479 QStringLiteral(":kmail_AskNextFolder"), 0480 KMessageBox::Option()); 0481 0482 return result == KMessageBox::ButtonCode::PrimaryAction; 0483 } 0484 0485 bool FolderTreeView::isUnreadFolder(const QModelIndex ¤t, QModelIndex &index, FolderTreeView::Move move, bool confirm) 0486 { 0487 if (current.isValid()) { 0488 if (move == FolderTreeView::Next) { 0489 index = selectNextFolder(current); 0490 } else if (move == FolderTreeView::Previous) { 0491 index = indexAbove(current); 0492 } 0493 0494 if (index.isValid()) { 0495 const auto collection = index.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0496 0497 if (collection.isValid()) { 0498 if (collection.statistics().unreadCount() > 0) { 0499 if (!confirm) { 0500 selectModelIndex(current); 0501 return true; 0502 } else { 0503 // Skip drafts, sent mail and templates as well, when reading mail with the 0504 // space bar - but not when changing into the next folder with unread mail 0505 // via ctrl+ or ctrl- so we do this only if (confirm == true), which means 0506 // we are doing readOn. 0507 0508 if (collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder() 0509 || collection == Kernel::self()->sentCollectionFolder()) { 0510 return false; 0511 } 0512 0513 // warn user that going to next folder - but keep track of 0514 // whether he wishes to be notified again in "AskNextFolder" 0515 // parameter (kept in the config file for kmail) 0516 if (KMessageBox::questionTwoActions(this, 0517 i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()), 0518 i18n("Go to Next Unread Message"), 0519 KGuiItem(i18n("Go To")), 0520 KGuiItem(i18n("Do Not Go To")), // defaults 0521 QStringLiteral(":kmail_AskNextFolder"), 0522 KMessageBox::Option()) 0523 == KMessageBox::ButtonCode::SecondaryAction) { 0524 return true; // assume selected (do not continue looping) 0525 } 0526 0527 selectModelIndex(current); 0528 return true; 0529 } 0530 } 0531 } 0532 } 0533 } 0534 return false; 0535 } 0536 0537 Akonadi::Collection FolderTreeView::currentFolder() const 0538 { 0539 const QModelIndex current = currentIndex(); 0540 if (current.isValid()) { 0541 const auto collection = current.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0542 return collection; 0543 } 0544 return {}; 0545 } 0546 0547 void FolderTreeView::mousePressEvent(QMouseEvent *e) 0548 { 0549 const bool buttonPressedIsMiddle = (e->button() == Qt::MiddleButton); 0550 Q_EMIT newTabRequested(buttonPressedIsMiddle); 0551 EntityTreeView::mousePressEvent(e); 0552 } 0553 0554 void FolderTreeView::restoreHeaderState(const QByteArray &data) 0555 { 0556 if (data.isEmpty()) { 0557 const int nbColumn = header()->count(); 0558 for (int i = 1; i < nbColumn; ++i) { 0559 setColumnHidden(i, true); 0560 } 0561 } else { 0562 header()->restoreState(data); 0563 } 0564 mCollectionStatisticsDelegate->setUnreadCountShown(header()->isSectionHidden(1)); 0565 } 0566 0567 void FolderTreeView::updatePalette() 0568 { 0569 mCollectionStatisticsDelegate->updatePalette(); 0570 } 0571 0572 void FolderTreeView::keyboardSearch(const QString &) 0573 { 0574 // Disable keyboardSearch: it interfers with filtering in the 0575 // FolderSelectionDialog. We don't want it in KMail main window 0576 // either because KMail has one-letter keyboard shortcuts. 0577 } 0578 0579 void FolderTreeView::setEnableDragDrop(bool enabled) 0580 { 0581 #ifndef QT_NO_DRAGANDDROP 0582 setDragDropMode(enabled ? QAbstractItemView::DragDrop : QAbstractItemView::NoDragDrop); 0583 #endif 0584 } 0585 0586 QModelIndex FolderTreeView::indexBelow(const QModelIndex ¤t) const 0587 { 0588 // if we have children, return first child 0589 if (model()->rowCount(current) > 0) { 0590 return model()->index(0, 0, current); 0591 } 0592 0593 // if we have siblings, return next sibling 0594 const QModelIndex parent = model()->parent(current); 0595 const QModelIndex sibling = model()->index(current.row() + 1, 0, parent); 0596 0597 if (sibling.isValid()) { // found valid sibling 0598 return sibling; 0599 } 0600 0601 if (!parent.isValid()) { // our parent is the tree root and we have no siblings 0602 return {}; // we reached the bottom of the tree 0603 } 0604 0605 // We are the last child, the next index to check is our uncle, parent's first sibling 0606 const QModelIndex parentsSibling = parent.sibling(parent.row() + 1, 0); 0607 if (parentsSibling.isValid()) { 0608 return parentsSibling; 0609 } 0610 0611 // iterate over our parents back to root until we find a parent with a valid sibling 0612 QModelIndex currentParent = parent; 0613 QModelIndex grandParent = model()->parent(currentParent); 0614 while (currentParent.isValid()) { 0615 // check if the parent has children except from us 0616 if (model()->rowCount(grandParent) > currentParent.row() + 1) { 0617 const auto index = indexBelow(model()->index(currentParent.row() + 1, 0, grandParent)); 0618 if (index.isValid()) { 0619 return index; 0620 } 0621 } 0622 0623 currentParent = grandParent; 0624 grandParent = model()->parent(currentParent); 0625 } 0626 0627 return {}; // nothing found -> end of tree 0628 } 0629 0630 QModelIndex FolderTreeView::lastChild(const QModelIndex ¤t) const 0631 { 0632 if (model()->rowCount(current) == 0) { 0633 return current; 0634 } 0635 0636 return lastChild(model()->index(model()->rowCount(current) - 1, 0, current)); 0637 } 0638 0639 QModelIndex FolderTreeView::indexAbove(const QModelIndex ¤t) const 0640 { 0641 const QModelIndex parent = model()->parent(current); 0642 0643 if (current.row() == 0) { 0644 // we have no previous siblings -> our parent is the next item above us 0645 return parent; 0646 } 0647 0648 // find previous sibling 0649 const QModelIndex previousSibling = model()->index(current.row() - 1, 0, parent); 0650 0651 // the item above us is the last child (or grandchild, or grandgrandchild... etc) 0652 // of our previous sibling 0653 return lastChild(previousSibling); 0654 } 0655 0656 QModelIndex FolderTreeView::nextUnreadCollection(const QModelIndex ¤t, SearchDirection direction) const 0657 { 0658 QModelIndex index = current; 0659 while (true) { 0660 if (direction == ForwardSearch) { 0661 index = indexBelow(index); 0662 } else if (direction == BackwardSearch) { 0663 index = indexAbove(index); 0664 } 0665 0666 if (!index.isValid()) { // reach end or top of the model 0667 return {}; 0668 } 0669 0670 // check if the index is a collection 0671 const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0672 0673 if (collection.isValid()) { 0674 // check if it is unread 0675 if (collection.statistics().unreadCount() > 0) { 0676 if (!MailCommon::Util::ignoreNewMailInFolder(collection)) { 0677 return index; // we found the next unread collection 0678 } 0679 } 0680 } 0681 } 0682 0683 return {}; // no unread collection found 0684 } 0685 0686 #include "moc_foldertreeview.cpp"