File indexing completed on 2025-01-19 04:56:39
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org> 0003 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 0007 #include "editormodel.h" 0008 0009 #include <QAbstractListModel> 0010 #include <QDebug> 0011 #include <QDesktopServices> 0012 #include <QDir> 0013 #include <QIcon> 0014 #include <QMimeDatabase> 0015 #include <QTemporaryFile> 0016 #include <QTimer> 0017 0018 #include <KLocalizedString> 0019 0020 #include "errorhandler.h" 0021 0022 namespace Presentation { 0023 class AttachmentModel : public QAbstractListModel 0024 { 0025 Q_OBJECT 0026 public: 0027 explicit AttachmentModel(QObject *parent = nullptr) 0028 : QAbstractListModel(parent) 0029 { 0030 } 0031 0032 void setTask(const Domain::Task::Ptr &task) 0033 { 0034 if (m_task == task) 0035 return; 0036 0037 beginResetModel(); 0038 if (m_task) { 0039 disconnect(m_task.data(), &Domain::Task::attachmentsChanged, 0040 this, &AttachmentModel::triggerReset); 0041 } 0042 m_task = task; 0043 if (m_task) { 0044 connect(m_task.data(), &Domain::Task::attachmentsChanged, 0045 this, &AttachmentModel::triggerReset); 0046 } 0047 endResetModel(); 0048 } 0049 0050 int rowCount(const QModelIndex &parent) const override 0051 { 0052 if (parent.isValid() || !m_task) 0053 return 0; 0054 0055 return m_task->attachments().size(); 0056 } 0057 0058 QVariant data(const QModelIndex &index, int role) const override 0059 { 0060 if (!index.isValid()) 0061 return QVariant(); 0062 0063 auto attachment = m_task->attachments().at(index.row()); 0064 0065 switch (role) { 0066 case Qt::DisplayRole: 0067 return attachment.label(); 0068 case Qt::DecorationRole: 0069 return QVariant::fromValue(QIcon::fromTheme(attachment.iconName())); 0070 default: 0071 return QVariant(); 0072 } 0073 } 0074 0075 private slots: 0076 void triggerReset() 0077 { 0078 beginResetModel(); 0079 endResetModel(); 0080 } 0081 0082 private: 0083 Domain::Task::Ptr m_task; 0084 }; 0085 } 0086 0087 using namespace Presentation; 0088 0089 static int s_autoSaveDelay = 500; 0090 0091 EditorModel::EditorModel(QObject *parent) 0092 : QObject(parent), 0093 m_done(false), 0094 m_recurrence(Domain::Task::NoRecurrence), 0095 m_attachmentModel(new AttachmentModel(this)), 0096 m_saveTimer(new QTimer(this)), 0097 m_saveNeeded(false), 0098 m_editingInProgress(false) 0099 { 0100 m_saveTimer->setSingleShot(true); 0101 connect(m_saveTimer, &QTimer::timeout, this, &EditorModel::save); 0102 } 0103 0104 EditorModel::~EditorModel() 0105 { 0106 save(); 0107 } 0108 0109 Domain::Task::Ptr EditorModel::task() const 0110 { 0111 return m_task; 0112 } 0113 0114 void EditorModel::setTask(const Domain::Task::Ptr &task) 0115 { 0116 if (m_task == task) 0117 return; 0118 0119 save(); 0120 0121 m_text = QString(); 0122 m_title = QString(); 0123 m_done = false; 0124 m_start = QDate(); 0125 m_due = QDate(); 0126 m_recurrence = Domain::Task::NoRecurrence; 0127 m_attachmentModel->setTask(Domain::Task::Ptr()); 0128 0129 if (m_task) 0130 disconnect(m_task.data(), nullptr, this, nullptr); 0131 0132 m_task = task; 0133 0134 if (m_task) { 0135 m_text = m_task->text(); 0136 m_title = m_task->title(); 0137 m_done = m_task->isDone(); 0138 m_start = m_task->startDate(); 0139 m_due = m_task->dueDate(); 0140 m_recurrence = m_task->recurrence(); 0141 m_attachmentModel->setTask(m_task); 0142 0143 connect(m_task.data(), &Domain::Task::textChanged, this, &EditorModel::onTextChanged); 0144 connect(m_task.data(), &Domain::Task::titleChanged, this, &EditorModel::onTitleChanged); 0145 connect(m_task.data(), &Domain::Task::doneChanged, this, &EditorModel::onDoneChanged); 0146 connect(m_task.data(), &Domain::Task::startDateChanged, this, &EditorModel::onStartDateChanged); 0147 connect(m_task.data(), &Domain::Task::dueDateChanged, this, &EditorModel::onDueDateChanged); 0148 connect(m_task.data(), &Domain::Task::recurrenceChanged, this, &EditorModel::onRecurrenceChanged); 0149 } 0150 0151 0152 emit textChanged(m_text); 0153 emit titleChanged(m_title); 0154 emit doneChanged(m_done); 0155 emit startDateChanged(m_start); 0156 emit dueDateChanged(m_due); 0157 emit recurrenceChanged(m_recurrence); 0158 emit taskChanged(m_task); 0159 } 0160 0161 bool EditorModel::hasSaveFunction() const 0162 { 0163 return bool(m_saveFunction); 0164 } 0165 0166 void EditorModel::setSaveFunction(const SaveFunction &function) 0167 { 0168 m_saveFunction = function; 0169 } 0170 0171 QString EditorModel::text() const 0172 { 0173 return m_text; 0174 } 0175 0176 QString EditorModel::title() const 0177 { 0178 return m_title; 0179 } 0180 0181 bool EditorModel::isDone() const 0182 { 0183 return m_done; 0184 } 0185 0186 QDate EditorModel::startDate() const 0187 { 0188 return m_start; 0189 } 0190 0191 QDate EditorModel::dueDate() const 0192 { 0193 return m_due; 0194 } 0195 0196 Domain::Task::Recurrence EditorModel::recurrence() const 0197 { 0198 return m_recurrence; 0199 } 0200 0201 QAbstractItemModel *EditorModel::attachmentModel() const 0202 { 0203 return m_attachmentModel; 0204 } 0205 0206 int EditorModel::autoSaveDelay() 0207 { 0208 return s_autoSaveDelay; 0209 } 0210 0211 void EditorModel::setAutoSaveDelay(int delay) 0212 { 0213 s_autoSaveDelay = delay; 0214 } 0215 0216 bool EditorModel::editingInProgress() const 0217 { 0218 return m_editingInProgress; 0219 } 0220 0221 void EditorModel::setText(const QString &text) 0222 { 0223 if (m_text == text) 0224 return; 0225 applyNewText(text); 0226 setSaveNeeded(true); 0227 } 0228 0229 void EditorModel::setTitle(const QString &title) 0230 { 0231 if (m_title == title) 0232 return; 0233 applyNewTitle(title); 0234 setSaveNeeded(true); 0235 } 0236 0237 void EditorModel::setDone(bool done) 0238 { 0239 if (m_done == done) 0240 return; 0241 applyNewDone(done); 0242 setSaveNeeded(true); 0243 } 0244 0245 void EditorModel::setStartDate(const QDate &start) 0246 { 0247 if (m_start == start) 0248 return; 0249 applyNewStartDate(start); 0250 setSaveNeeded(true); 0251 } 0252 0253 void EditorModel::setDueDate(const QDate &due) 0254 { 0255 if (m_due == due) 0256 return; 0257 applyNewDueDate(due); 0258 setSaveNeeded(true); 0259 } 0260 0261 void EditorModel::setRecurrence(Domain::Task::Recurrence recurrence) 0262 { 0263 if (m_recurrence == recurrence) 0264 return; 0265 applyNewRecurrence(recurrence); 0266 setSaveNeeded(true); 0267 } 0268 0269 void EditorModel::addAttachment(const QString &fileName) 0270 { 0271 if (!m_task) 0272 return; 0273 0274 QMimeDatabase mimeDb; 0275 auto mimeType = mimeDb.mimeTypeForFile(fileName); 0276 0277 auto attachment = Domain::Task::Attachment(); 0278 attachment.setLabel(QFileInfo(fileName).fileName()); 0279 attachment.setMimeType(mimeType.name()); 0280 attachment.setIconName(mimeType.iconName()); 0281 0282 QFile file(fileName); 0283 if (!file.open(QFile::ReadOnly)) { 0284 // TODO: Might be worth extending error handling 0285 // to deal with job-less errors later on 0286 qWarning() << "Couldn't open" << fileName; 0287 return; 0288 } 0289 0290 attachment.setData(file.readAll()); 0291 0292 file.close(); 0293 0294 auto attachments = m_task->attachments(); 0295 attachments.append(attachment); 0296 m_task->setAttachments(attachments); 0297 0298 setSaveNeeded(true); 0299 } 0300 0301 void EditorModel::removeAttachment(const QModelIndex &index) 0302 { 0303 if (!m_task) 0304 return; 0305 0306 auto attachments = m_task->attachments(); 0307 attachments.removeAt(index.row()); 0308 m_task->setAttachments(attachments); 0309 0310 setSaveNeeded(true); 0311 } 0312 0313 void EditorModel::openAttachment(const QModelIndex &index) 0314 { 0315 Q_ASSERT(m_task); 0316 auto attachment = m_task->attachments().at(index.row()); 0317 0318 auto uri = attachment.uri(); 0319 if (!attachment.isUri()) { 0320 auto tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/zanshin_attachment_XXXXXX"), this); 0321 tempFile->open(); 0322 tempFile->setPermissions(QFile::ReadUser); 0323 tempFile->write(attachment.data()); 0324 tempFile->close(); 0325 uri = QUrl::fromLocalFile(tempFile->fileName()); 0326 } 0327 0328 QDesktopServices::openUrl(uri); 0329 } 0330 0331 void EditorModel::setEditingInProgress(bool editing) 0332 { 0333 m_editingInProgress = editing; 0334 } 0335 0336 void EditorModel::onTextChanged(const QString &text) 0337 { 0338 if (!m_editingInProgress) 0339 applyNewText(text); 0340 } 0341 0342 void EditorModel::onTitleChanged(const QString &title) 0343 { 0344 if (!m_editingInProgress) 0345 applyNewTitle(title); 0346 } 0347 0348 void EditorModel::onDoneChanged(bool done) 0349 { 0350 if (!m_editingInProgress) 0351 applyNewDone(done); 0352 } 0353 0354 void EditorModel::onStartDateChanged(const QDate &start) 0355 { 0356 if (!m_editingInProgress) 0357 applyNewStartDate(start); 0358 } 0359 0360 void EditorModel::onDueDateChanged(const QDate &due) 0361 { 0362 if (!m_editingInProgress) 0363 applyNewDueDate(due); 0364 } 0365 0366 void EditorModel::onRecurrenceChanged(Domain::Task::Recurrence recurrence) 0367 { 0368 if (!m_editingInProgress) 0369 applyNewRecurrence(recurrence); 0370 } 0371 0372 void EditorModel::save() 0373 { 0374 if (!isSaveNeeded()) 0375 return; 0376 0377 Q_ASSERT(m_task); 0378 0379 const auto currentTitle = m_task->title(); 0380 m_task->setTitle(m_title); 0381 m_task->setText(m_text); 0382 0383 m_task->setDone(m_done); 0384 m_task->setStartDate(m_start); 0385 m_task->setDueDate(m_due); 0386 m_task->setRecurrence(m_recurrence); 0387 0388 const auto job = m_saveFunction(m_task); 0389 installHandler(job, i18n("Cannot modify task %1", currentTitle)); 0390 setSaveNeeded(false); 0391 } 0392 0393 void EditorModel::setSaveNeeded(bool needed) 0394 { 0395 if (needed) 0396 m_saveTimer->start(autoSaveDelay()); 0397 else 0398 m_saveTimer->stop(); 0399 0400 m_saveNeeded = needed; 0401 } 0402 0403 bool EditorModel::isSaveNeeded() const 0404 { 0405 return m_saveNeeded; 0406 } 0407 0408 void EditorModel::applyNewText(const QString &text) 0409 { 0410 m_text = text; 0411 emit textChanged(m_text); 0412 } 0413 0414 void EditorModel::applyNewTitle(const QString &title) 0415 { 0416 m_title = title; 0417 emit titleChanged(m_title); 0418 } 0419 0420 void EditorModel::applyNewDone(bool done) 0421 { 0422 m_done = done; 0423 emit doneChanged(m_done); 0424 } 0425 0426 void EditorModel::applyNewStartDate(const QDate &start) 0427 { 0428 m_start = start; 0429 emit startDateChanged(m_start); 0430 } 0431 0432 void EditorModel::applyNewDueDate(const QDate &due) 0433 { 0434 m_due = due; 0435 emit dueDateChanged(m_due); 0436 } 0437 0438 void EditorModel::applyNewRecurrence(Domain::Task::Recurrence recurrence) 0439 { 0440 m_recurrence = recurrence; 0441 emit recurrenceChanged(m_recurrence); 0442 } 0443 0444 #include "editormodel.moc" 0445 0446 #include "moc_editormodel.cpp"