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 }