File indexing completed on 2024-11-24 04:53:14
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include "TaskPresentationModel.h" 0024 #include "Imap/Tasks/GetAnyConnectionTask.h" 0025 #include "Imap/Tasks/KeepMailboxOpenTask.h" 0026 #include "Imap/Tasks/NoopTask.h" 0027 #include "Imap/Tasks/OpenConnectionTask.h" 0028 #include "Imap/Tasks/SortTask.h" 0029 #include "Imap/Tasks/UnSelectTask.h" 0030 #include "ItemRoles.h" 0031 #include "Model.h" 0032 0033 #ifdef TROJITA_DEBUG_TASK_TREE 0034 #undef CHECK_TASK_TREE 0035 #define CHECK_TASK_TREE {m_model->checkTaskTreeConsistency();} 0036 #else 0037 #define CHECK_TASK_TREE {} 0038 #endif 0039 0040 namespace Imap 0041 { 0042 namespace Mailbox 0043 { 0044 0045 TaskPresentationModel::TaskPresentationModel(Model *model) : 0046 QAbstractItemModel(model), m_model(model) 0047 { 0048 } 0049 0050 QModelIndex TaskPresentationModel::index(int row, int column, const QModelIndex &parent) const 0051 { 0052 if (column != 0) 0053 return QModelIndex(); 0054 if (row < 0) 0055 return QModelIndex(); 0056 0057 if (parent.isValid()) { 0058 // Parent is a valid index, so the child is definitely an ImapTask. The parent could still be a ParserState, though. 0059 if (parent.data(RoleTaskIsParserState).toBool()) { 0060 // The parent is a ParserState 0061 Imap::Parser *parser = static_cast<Imap::Parser *>(parent.internalPointer()); 0062 ParserState &parserState = m_model->accessParser(parser); 0063 if (row >= parserState.activeTasks.size()) { 0064 return QModelIndex(); 0065 } else { 0066 return createIndex(row, 0, parserState.activeTasks.at(row)); 0067 } 0068 } else { 0069 // The parent is a regular ImapTask 0070 ImapTask *task = static_cast<ImapTask *>(parent.internalPointer()); 0071 Q_ASSERT(task); 0072 if (row >= task->dependentTasks.size()) { 0073 return QModelIndex(); 0074 } else { 0075 return createIndex(row, 0, task->dependentTasks.at(row)); 0076 } 0077 } 0078 } else { 0079 // So this is about a ParserState -- fair enough 0080 if (row >= m_model->m_parsers.size()) { 0081 return QModelIndex(); 0082 } else { 0083 return createIndex(row, 0, m_model->m_parsers.keys().at(row)); 0084 } 0085 } 0086 } 0087 0088 QModelIndex TaskPresentationModel::parent(const QModelIndex &child) const 0089 { 0090 if (!child.isValid()) 0091 return QModelIndex(); 0092 0093 if (child.data(RoleTaskIsParserState).toBool()) { 0094 // A parent of the parser state is always the root item 0095 return QModelIndex(); 0096 } 0097 // The child is definitely an ImapTask; let's find what the parent is 0098 ImapTask *task = static_cast<ImapTask *>(child.internalPointer()); 0099 if (task->parentTask) { 0100 return indexForTask(task->parentTask); 0101 } else { 0102 Q_ASSERT(task->parser); 0103 int index = m_model->m_parsers.keys().indexOf(task->parser); 0104 Q_ASSERT(index != -1); 0105 return createIndex(index, 0, task->parser); 0106 } 0107 } 0108 0109 /** @short Return a QModelIndex for the specified ImapTask* */ 0110 QModelIndex TaskPresentationModel::indexForTask(ImapTask *const task) const 0111 { 0112 Q_ASSERT(task); 0113 if (task->parentTask) { 0114 // The target task is a child of another task 0115 int index = task->parentTask->dependentTasks.indexOf(task); 0116 Q_ASSERT(index != -1); 0117 return createIndex(index, 0, task); 0118 } else { 0119 // The child has no parent task, so the child is apparently the top-level task for a given parser, 0120 Q_ASSERT(task->parser); 0121 int index = m_model->accessParser(task->parser).activeTasks.indexOf(task); 0122 if (index == -1) { 0123 #ifdef TROJITA_DEBUG_TASK_TREE 0124 m_model->checkTaskTreeConsistency(); 0125 Q_ASSERT(index != -1); 0126 #endif 0127 return QModelIndex(); 0128 } 0129 return createIndex(index, 0, task); 0130 } 0131 } 0132 0133 int TaskPresentationModel::rowCount(const QModelIndex &parent) const 0134 { 0135 if (parent.isValid()) { 0136 // This is where it starts to get complicated -- we're somewhere inside the tree 0137 if (parent.data(RoleTaskIsParserState).toBool()) { 0138 // A child of the top level item, ie. a ParserState object 0139 Imap::Parser *parser = static_cast<Imap::Parser *>(parent.internalPointer()); 0140 ParserState &parserState = m_model->accessParser(parser); 0141 return parserState.activeTasks.size(); 0142 } else { 0143 // It's a regular ImapTask 0144 ImapTask *task = static_cast<ImapTask *>(parent.internalPointer()); 0145 Q_ASSERT(task); 0146 return task->dependentTasks.size(); 0147 } 0148 } else { 0149 // The top-level stuff children represent the list of active connections 0150 return m_model->m_parsers.size(); 0151 } 0152 } 0153 0154 int TaskPresentationModel::columnCount(const QModelIndex &parent) const 0155 { 0156 return (rowCount(parent) > 0) ? 1 : 0; 0157 } 0158 0159 QVariant TaskPresentationModel::data(const QModelIndex &index, int role) const 0160 { 0161 if (!index.isValid()) { 0162 return QVariant(); 0163 } 0164 0165 bool isParserState = m_model->m_parsers.find(static_cast<Imap::Parser *>(index.internalPointer())) != m_model->m_parsers.end(); 0166 0167 switch (role) { 0168 case RoleTaskIsParserState: 0169 return isParserState; 0170 case RoleTaskIsVisible: 0171 { 0172 if (isParserState) { 0173 // That's not a task at all 0174 return false; 0175 } 0176 0177 ImapTask *task = static_cast<ImapTask *>(index.internalPointer()); 0178 if (dynamic_cast<GetAnyConnectionTask *>(task) || dynamic_cast<UnSelectTask *>(task)) { 0179 // Internal, auxiliary tasks 0180 return false; 0181 } else if (auto keep = dynamic_cast<KeepMailboxOpenTask *>(task)) { 0182 // KeepMailboxOpenTask might be actually busy 0183 return keep->hasItsOwnActivity(); 0184 } else if (dynamic_cast<SortTask *>(task) && dynamic_cast<SortTask *>(task)->isJustUpdatingNow()) { 0185 // This is a persistent task responsible for further maintenance of the sort order 0186 return false; 0187 } else { 0188 return true; 0189 } 0190 } 0191 case Qt::DisplayRole: 0192 if (isParserState) { 0193 Imap::Parser *parser = static_cast<Imap::Parser *>(index.internalPointer()); 0194 return tr("Parser %1").arg(QString::number(parser->parserId())); 0195 } else { 0196 ImapTask *task = static_cast<ImapTask *>(index.internalPointer()); 0197 QString className = QLatin1String(task->metaObject()->className()); 0198 className.remove(QStringLiteral("Imap::Mailbox::")); 0199 return tr("%1: %2").arg(className, task->debugIdentification()); 0200 } 0201 case RoleTaskCompactName: { 0202 if (isParserState) { 0203 return QVariant(); 0204 } else { 0205 ImapTask *task = static_cast<ImapTask *>(index.internalPointer()); 0206 return task->taskData(RoleTaskCompactName); 0207 } 0208 } 0209 default: 0210 return QVariant(); 0211 } 0212 } 0213 0214 /** @short Called when a particular task ceases to exist 0215 0216 The ImapTask might be in various stages of destruction at this point, so it is not advisable to access its contents from 0217 this function. 0218 */ 0219 void TaskPresentationModel::slotSomeTaskDestroyed() 0220 { 0221 CHECK_TASK_TREE 0222 beginResetModel(); 0223 endResetModel(); 0224 CHECK_TASK_TREE 0225 } 0226 0227 /** @short A new parser just got created 0228 0229 We don't bother with proper fine-grained signals here. 0230 */ 0231 void TaskPresentationModel::slotParserCreated(Parser *parser) 0232 { 0233 Q_UNUSED(parser); 0234 CHECK_TASK_TREE 0235 beginResetModel(); 0236 endResetModel(); 0237 CHECK_TASK_TREE 0238 } 0239 0240 /** @short A parser has just been deleted 0241 0242 We don't bother with proper fine-grained signals here. 0243 */ 0244 void TaskPresentationModel::slotParserDeleted(Parser *parser) 0245 { 0246 Q_UNUSED(parser); 0247 CHECK_TASK_TREE 0248 beginResetModel(); 0249 endResetModel(); 0250 CHECK_TASK_TREE 0251 } 0252 0253 /** @short A parent of the given Imaptask has just changed 0254 0255 The task might or might not have been present in the model before. We don't know. 0256 */ 0257 void TaskPresentationModel::slotTaskGotReparented(const ImapTask *const task) 0258 { 0259 Q_UNUSED(task); 0260 CHECK_TASK_TREE 0261 beginResetModel(); 0262 endResetModel(); 0263 CHECK_TASK_TREE 0264 } 0265 0266 /** @short The textual description, the state or something else related to this task might have changed */ 0267 void TaskPresentationModel::slotTaskMighHaveChanged(ImapTask *task) 0268 { 0269 CHECK_TASK_TREE 0270 if (task->isFinished()) { 0271 // finished tasks are not located in our tree, so indexForTask would assert on them 0272 return; 0273 } 0274 if (KeepMailboxOpenTask *keepTask = dynamic_cast<KeepMailboxOpenTask*>(task)) { 0275 if (keepTask->isReadyToTerminate()) { 0276 // this one could be missing from the mapping, too 0277 return; 0278 } 0279 } 0280 QModelIndex index = indexForTask(task); 0281 emit dataChanged(index, index); 0282 } 0283 0284 void dumpModelContents(QAbstractItemModel *model, QModelIndex index, int offset) 0285 { 0286 qDebug() << QByteArray().fill(' ', offset) << index.data(Qt::DisplayRole).toString(); 0287 for (int i=0; i < model->rowCount(index); ++i) { 0288 QModelIndex child = model->index(i, 0, index); 0289 if (!child.isValid()) { 0290 qDebug() << "FAIL: " << index << child << i << model->rowCount(index); 0291 } 0292 Q_ASSERT(child.isValid()); 0293 dumpModelContents(model, child, offset + 1); 0294 } 0295 } 0296 0297 0298 } 0299 }