File indexing completed on 2024-05-26 05:28:14
0001 /* Copyright (C) 2012 Thomas Gahr <thomas.gahr@physik.uni-muenchen.de> 0002 Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org> 0003 0004 This file is part of the Trojita Qt IMAP e-mail client, 0005 http://trojita.flaska.net/ 0006 0007 This program is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU General Public License as 0009 published by the Free Software Foundation; either version 2 of 0010 the License or (at your option) version 3 or any later version 0011 accepted by the membership of KDE e.V. (or its successor approved 0012 by the membership of KDE e.V.), which shall act as a proxy 0013 defined in Section 14 of version 3 of the license. 0014 0015 This program is distributed in the hope that it will be useful, 0016 but WITHOUT ANY WARRANTY; without even the implied warranty of 0017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0018 GNU General Public License for more details. 0019 0020 You should have received a copy of the GNU General Public License 0021 along with this program. If not, see <http://www.gnu.org/licenses/>. 0022 */ 0023 0024 #include <QDragMoveEvent> 0025 #include <QDropEvent> 0026 #include <QMenu> 0027 #include <QMimeData> 0028 #include "Common/SettingsNames.h" 0029 #include "Gui/MailBoxTreeView.h" 0030 #include "Gui/Util.h" 0031 #include "Imap/Model/ItemRoles.h" 0032 #include "Imap/Model/MailboxFinder.h" 0033 #include "Imap/Model/DragAndDrop.h" 0034 #include "UiUtils/IconLoader.h" 0035 0036 namespace Gui { 0037 0038 MailBoxTreeView::MailBoxTreeView(QWidget *parent, QSettings *settings) 0039 : QTreeView(parent) 0040 , m_mailboxFinder(nullptr), m_settings(settings) 0041 { 0042 setUniformRowHeights(true); 0043 setContextMenuPolicy(Qt::CustomContextMenu); 0044 setSelectionMode(QAbstractItemView::SingleSelection); 0045 setAllColumnsShowFocus(true); 0046 setAcceptDrops(true); 0047 setDropIndicatorShown(true); 0048 setHeaderHidden(true); 0049 // I wonder what's the best value to use here. Unfortunately, the default is to disable auto expanding. 0050 setAutoExpandDelay(800); 0051 0052 // Track expansion/collapsing so that we remember this state despite the asynchronous nature of mailbox loading 0053 connect(this, &QTreeView::expanded, this, [this](const QModelIndex &what) { 0054 auto name = what.data(Imap::Mailbox::RoleMailboxName).toString(); 0055 if (!m_desiredExpansionState.contains(name)) { 0056 m_desiredExpansionState.insert(name); 0057 emit mailboxExpansionChanged(m_desiredExpansionState.values()); 0058 } 0059 }); 0060 connect(this, &QTreeView::collapsed, this, [this](const QModelIndex &what) { 0061 auto name = what.data(Imap::Mailbox::RoleMailboxName).toString(); 0062 if (m_desiredExpansionState.remove(name)) { 0063 emit mailboxExpansionChanged(m_desiredExpansionState.values()); 0064 } 0065 }); 0066 } 0067 0068 /** \reimp 0069 0070 The MailboxFinder has to be kept up-to-speed about these changes. 0071 */ 0072 void MailBoxTreeView::setModel(QAbstractItemModel *model) 0073 { 0074 delete m_mailboxFinder; 0075 m_mailboxFinder = nullptr; 0076 0077 if (model) { 0078 m_mailboxFinder = new Imap::Mailbox::MailboxFinder(this, model); 0079 connect(m_mailboxFinder, &Imap::Mailbox::MailboxFinder::mailboxFound, 0080 this, [this](const QString &, const QModelIndex &index) { 0081 expand(index); 0082 }); 0083 connect(model, &QAbstractItemModel::layoutChanged, this, &MailBoxTreeView::resetWatchedMailboxes); 0084 connect(model, &QAbstractItemModel::rowsRemoved, this, &MailBoxTreeView::resetWatchedMailboxes); 0085 connect(model, &QAbstractItemModel::modelReset, this, &MailBoxTreeView::resetWatchedMailboxes); 0086 } 0087 QTreeView::setModel(model); 0088 resetWatchedMailboxes(); 0089 } 0090 /** @short Returns the user's default action from Qt::DropAction or Qt::IgnoreAction if not set */ 0091 Qt::DropAction MailBoxTreeView::defaultDropAction() 0092 { 0093 auto mboxDropAction = m_settings->value(Common::SettingsNames::mboxDropAction, QVariant(QStringLiteral("ask"))).toString(); 0094 if (mboxDropAction == QStringLiteral("move")) { 0095 return Qt::MoveAction; 0096 } else if (mboxDropAction == QStringLiteral("copy")) { 0097 return Qt::CopyAction; 0098 } else { 0099 return Qt::IgnoreAction; 0100 } 0101 } 0102 0103 /** @short Reimplemented for more consistent handling of modifiers 0104 0105 Qt's default behaviour is odd here: 0106 If you selected the move-action by pressing shift and you release the shift 0107 key while moving the mouse, the action does not change back to copy. But if you 0108 then move the mouse over a widget border - i.e. cause dragLeaveEvent, the action 0109 WILL change back to copy. 0110 This implementation immitates KDE's behaviour: react to a change in modifiers 0111 immediately. 0112 */ 0113 void MailBoxTreeView::dragMoveEvent(QDragMoveEvent *event) 0114 { 0115 QTreeView::dragMoveEvent(event); 0116 if (!event->isAccepted()) 0117 return; 0118 if (event->keyboardModifiers() == Qt::ShiftModifier) 0119 event->setDropAction(Qt::MoveAction); 0120 else 0121 event->setDropAction(Qt::CopyAction); 0122 } 0123 0124 /** @short Reimplemented to present the user with a menu to choose between copy or move. 0125 0126 Does not show the menu if either ctrl (copy messages) or shift (move messages) 0127 is pressed 0128 */ 0129 void MailBoxTreeView::dropEvent(QDropEvent *event) 0130 { 0131 if (Gui::Util::isFromDistinctImapAccount(event)) { 0132 event->ignore(); 0133 return; 0134 } 0135 if (event->keyboardModifiers() == Qt::ControlModifier) { 0136 event->setDropAction(Qt::CopyAction); 0137 } else if (event->keyboardModifiers() == Qt::ShiftModifier) { 0138 event->setDropAction(Qt::MoveAction); 0139 } else if (defaultDropAction() != Qt::IgnoreAction) { 0140 event->setDropAction(defaultDropAction()); 0141 } else { 0142 QMenu menu(this); 0143 QAction *moveAction = menu.addAction(UiUtils::loadIcon(QStringLiteral("go-jump")), tr("Move here\tShift")); 0144 menu.addAction(UiUtils::loadIcon(QStringLiteral("edit-copy")), tr("Copy here\tCtrl")); 0145 QAction *cancelAction = menu.addAction(UiUtils::loadIcon(QStringLiteral("process-stop")), tr("Cancel")); 0146 0147 QAction *selectedAction = menu.exec(mapToGlobal(event->pos())); 0148 0149 // if user closes the menu or selects cancel, ignore the event 0150 if (!selectedAction || selectedAction == cancelAction) { 0151 event->ignore(); 0152 return; 0153 } 0154 0155 event->setDropAction(selectedAction == moveAction ? Qt::MoveAction : Qt::CopyAction); 0156 } 0157 0158 QTreeView::dropEvent(event); 0159 } 0160 0161 void MailBoxTreeView::dragEnterEvent(QDragEnterEvent *event) 0162 { 0163 if (Gui::Util::isFromDistinctImapAccount(event)) { 0164 event->ignore(); 0165 return; 0166 } 0167 QTreeView::dragEnterEvent(event); 0168 } 0169 0170 /** @short Specify which mailboxes should be expanded 0171 0172 The mailboxes might appear and disappear at any time, so let's make sure that 0173 they are properly expanded/collapsed once they pop in. 0174 */ 0175 void MailBoxTreeView::setDesiredExpansion(const QStringList &mailboxNames) 0176 { 0177 m_desiredExpansionState = QSet<QString>(mailboxNames.begin(), mailboxNames.end()); 0178 resetWatchedMailboxes(); 0179 } 0180 0181 /** @short Ensure that we watch stuff that we need to watch */ 0182 void MailBoxTreeView::resetWatchedMailboxes() 0183 { 0184 if (m_mailboxFinder) { 0185 for (const auto &mailbox: m_desiredExpansionState) { 0186 m_mailboxFinder->addMailbox(mailbox); 0187 } 0188 } 0189 } 0190 0191 }