File indexing completed on 2024-11-17 04:49:33
0001 /* 0002 * Copyright (C) 1997 by Stephan Kulow <coolo@kde.org> 0003 * Copyright (C) 2019 Alexander Potashev <aspotashev@gmail.com> 0004 * 0005 * This program is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation; either version 2 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License along 0016 * with this program; if not, write to the 0017 * Free Software Foundation, Inc. 0018 * 51 Franklin Street, Fifth Floor 0019 * Boston, MA 02110-1301 USA. 0020 * 0021 */ 0022 0023 #include "task.h" 0024 0025 #include <KCalendarCore/CalFormat> 0026 0027 #include "ktimetracker.h" 0028 #include "ktimetrackerutility.h" 0029 #include "ktt_debug.h" 0030 #include "model/eventsmodel.h" 0031 #include "model/projectmodel.h" 0032 #include "model/tasksmodel.h" 0033 #include "timetrackerstorage.h" 0034 0035 static const QByteArray eventAppName = QByteArray("ktimetracker"); 0036 0037 Task::Task(const QString &taskName, 0038 const QString &taskDescription, 0039 int64_t minutes, 0040 int64_t sessionTime, 0041 const DesktopList &desktops, 0042 ProjectModel *projectModel, 0043 Task *parentTask) 0044 : TasksModelItem(projectModel->tasksModel(), parentTask) 0045 , m_projectModel(projectModel) 0046 { 0047 if (parentTask) { 0048 parentTask->addChild(this); 0049 } else { 0050 m_projectModel->tasksModel()->addChild(this); 0051 } 0052 0053 init(taskName, taskDescription, minutes, sessionTime, QStringLiteral(), desktops, 0, 0); 0054 0055 m_uid = KCalendarCore::CalFormat::createUniqueId(); 0056 } 0057 0058 Task::Task(const KCalendarCore::Todo::Ptr &todo, ProjectModel *projectModel) 0059 : TasksModelItem(projectModel->tasksModel(), nullptr) 0060 , m_projectModel(projectModel) 0061 { 0062 projectModel->tasksModel()->addChild(this); 0063 0064 int64_t minutes = 0; 0065 QString name; 0066 QString description; 0067 int64_t sessionTime = 0; 0068 QString sessionStartTiMe; 0069 int percent_complete = 0; 0070 int priority = 0; 0071 DesktopList desktops; 0072 0073 parseIncidence(todo, 0074 minutes, 0075 sessionTime, 0076 sessionStartTiMe, 0077 name, 0078 description, 0079 desktops, 0080 percent_complete, 0081 priority); 0082 init(name, description, minutes, sessionTime, sessionStartTiMe, desktops, percent_complete, priority); 0083 } 0084 0085 Task::~Task() 0086 { 0087 disconnectFromParent(); 0088 } 0089 0090 int Task::depth() 0091 { 0092 int res = 0; 0093 for (Task *t = parentTask(); t; t = t->parentTask()) { 0094 res++; 0095 } 0096 0097 qCDebug(KTT_LOG) << "Leaving function. depth is:" << res; 0098 return res; 0099 } 0100 0101 void Task::init(const QString &taskName, 0102 const QString &taskDescription, 0103 int64_t minutes, 0104 int64_t sessionTime, 0105 const QString &sessionStartTiMe, 0106 const DesktopList &desktops, 0107 int percent_complete, 0108 int priority) 0109 { 0110 m_isRunning = false; 0111 m_name = taskName.trimmed(); 0112 m_description = taskDescription.trimmed(); 0113 m_lastStart = QDateTime::currentDateTime(); 0114 m_totalTime = m_time = minutes; 0115 m_totalSessionTime = m_sessionTime = sessionTime; 0116 m_desktops = desktops; 0117 0118 m_percentComplete = percent_complete; 0119 m_priority = priority; 0120 m_sessionStartTime = QDateTime::fromString(sessionStartTiMe); 0121 0122 update(); 0123 changeParentTotalTimes(m_sessionTime, m_time); 0124 } 0125 0126 void Task::delete_recursive() 0127 { 0128 while (this->child(0)) { 0129 Task *t = (Task *)this->child(0); 0130 t->delete_recursive(); 0131 } 0132 delete this; 0133 } 0134 0135 // This is the back-end, the front-end is StartTimerFor() 0136 void Task::setRunning(bool on, const QDateTime &when) 0137 { 0138 if (on != m_isRunning) { 0139 m_isRunning = on; 0140 invalidateRunningState(); 0141 0142 if (on) { 0143 m_lastStart = when; 0144 qCDebug(KTT_LOG) << "task has been started for " << when; 0145 0146 // m_projectModel->eventsModel()->startTask(this, when); 0147 m_projectModel->eventsModel()->startTask(this); 0148 } else { 0149 m_projectModel->eventsModel()->stopTask(this, when); 0150 } 0151 } 0152 } 0153 0154 // setRunning is the back-end, the front-end is StartTimerFor(). 0155 // resumeRunning does the same as setRunning, but not add a new 0156 // start date to the storage. 0157 void Task::resumeRunning() 0158 { 0159 qCDebug(KTT_LOG) << "Entering function"; 0160 if (!isRunning()) { 0161 m_isRunning = true; 0162 invalidateRunningState(); 0163 } 0164 } 0165 0166 bool Task::isRunning() const 0167 { 0168 return m_isRunning; 0169 } 0170 0171 void Task::setName(const QString &name) 0172 { 0173 qCDebug(KTT_LOG) << "Entering function, name=" << name; 0174 0175 QString oldname = m_name; 0176 if (oldname != name) { 0177 m_name = name; 0178 update(); 0179 } 0180 } 0181 0182 void Task::setDescription(const QString &description) 0183 { 0184 qCDebug(KTT_LOG) << "Entering function, description=" << description; 0185 0186 QString olddescription = m_description; 0187 if (olddescription != description) { 0188 m_description = description; 0189 update(); 0190 } 0191 } 0192 0193 void Task::setPercentComplete(int percent) 0194 { 0195 qCDebug(KTT_LOG) << "Entering function(" << percent << "):" << m_uid; 0196 0197 if (!percent) { 0198 m_percentComplete = 0; 0199 } else if (percent > 100) { 0200 m_percentComplete = 100; 0201 } else if (percent < 0) { 0202 m_percentComplete = 0; 0203 } else { 0204 m_percentComplete = percent; 0205 } 0206 0207 if (isRunning() && m_percentComplete == 100) { 0208 Q_EMIT m_projectModel->tasksModel()->taskCompleted(this); 0209 } 0210 0211 invalidateCompletedState(); 0212 0213 // When parent marked as complete, mark all children as complete as well. 0214 // This behavior is consistent with KOrganizer (as of 2003-09-24). 0215 if (m_percentComplete == 100) { 0216 for (int i = 0; i < childCount(); ++i) { 0217 Task *task = dynamic_cast<Task *>(child(i)); 0218 task->setPercentComplete(m_percentComplete); 0219 } 0220 } 0221 // maybe there is a column "percent completed", so do a ... 0222 update(); 0223 } 0224 0225 void Task::setPriority(int priority) 0226 { 0227 if (priority < 0) { 0228 priority = 0; 0229 } else if (priority > 9) { 0230 priority = 9; 0231 } 0232 0233 m_priority = priority; 0234 update(); 0235 } 0236 0237 bool Task::isComplete() 0238 { 0239 return m_percentComplete == 100; 0240 } 0241 0242 void Task::setDesktopList(const DesktopList &desktopList) 0243 { 0244 m_desktops = desktopList; 0245 } 0246 0247 void Task::addTime(int64_t minutes) 0248 { 0249 m_time += minutes; 0250 this->addTotalTime(minutes); 0251 } 0252 0253 void Task::addTotalTime(int64_t minutes) 0254 { 0255 m_totalTime += minutes; 0256 if (parentTask()) { 0257 parentTask()->addTotalTime(minutes); 0258 } 0259 } 0260 0261 void Task::addSessionTime(int64_t minutes) 0262 { 0263 m_sessionTime += minutes; 0264 this->addTotalSessionTime(minutes); 0265 } 0266 0267 void Task::addTotalSessionTime(int64_t minutes) 0268 { 0269 m_totalSessionTime += minutes; 0270 if (parentTask()) { 0271 parentTask()->addTotalSessionTime(minutes); 0272 } 0273 } 0274 0275 QString Task::setTime(int64_t minutes) 0276 { 0277 m_time = minutes; 0278 m_totalTime += minutes; 0279 return QString(); 0280 } 0281 0282 void Task::recalculateTotalTimesSubtree() 0283 { 0284 int64_t totalMinutes = time(); 0285 int64_t totalSessionMinutes = sessionTime(); 0286 for (int i = 0; i < this->childCount(); ++i) { 0287 Task *subTask = dynamic_cast<Task *>(child(i)); 0288 subTask->recalculateTotalTimesSubtree(); 0289 0290 totalMinutes += subTask->totalTime(); 0291 totalSessionMinutes += subTask->totalSessionTime(); 0292 } 0293 0294 setTotalTime(totalMinutes); 0295 setTotalSessionTime(totalSessionMinutes); 0296 } 0297 0298 QString Task::setSessionTime(int64_t minutes) 0299 { 0300 m_sessionTime = minutes; 0301 m_totalSessionTime += minutes; 0302 return QString(); 0303 } 0304 0305 void Task::changeTimes(int64_t minutesSession, int64_t minutes, EventsModel *eventsModel) 0306 { 0307 qDebug() << "Task's sessionStartTiMe is " << m_sessionStartTime; 0308 if (minutesSession != 0 || minutes != 0) { 0309 m_sessionTime += minutesSession; 0310 m_time += minutes; 0311 if (eventsModel) { 0312 eventsModel->changeTime(this, minutes * secsPerMinute); 0313 } 0314 changeTotalTimes(minutesSession, minutes); 0315 } 0316 } 0317 0318 void Task::changeTime(int64_t minutes, EventsModel *eventsModel) 0319 { 0320 changeTimes(minutes, minutes, eventsModel); 0321 } 0322 0323 void Task::changeTotalTimes(int64_t minutesSession, int64_t minutes) 0324 { 0325 qCDebug(KTT_LOG) << "Task::changeTotalTimes(" << minutesSession << "," << minutes << ") for" << name(); 0326 m_totalSessionTime += minutesSession; 0327 m_totalTime += minutes; 0328 update(); 0329 changeParentTotalTimes(minutesSession, minutes); 0330 } 0331 0332 void Task::resetTimes() 0333 { 0334 m_totalSessionTime -= m_sessionTime; 0335 m_totalTime -= m_time; 0336 changeParentTotalTimes(-m_sessionTime, -m_time); 0337 m_sessionTime = 0; 0338 m_time = 0; 0339 update(); 0340 } 0341 0342 void Task::changeParentTotalTimes(int64_t minutesSession, int64_t minutes) 0343 { 0344 if (parentTask()) { 0345 parentTask()->changeTotalTimes(minutesSession, minutes); 0346 } 0347 } 0348 0349 bool Task::remove() 0350 { 0351 qCDebug(KTT_LOG) << "entering function" << m_name; 0352 bool ok = true; 0353 0354 for (int i = 0; i < childCount(); ++i) { 0355 Task *task = dynamic_cast<Task *>(child(i)); 0356 if (!task) { 0357 qFatal("Task::remove: task is nullptr"); 0358 } 0359 0360 task->remove(); 0361 } 0362 0363 setRunning(false); 0364 0365 m_projectModel->eventsModel()->removeAllForTask(this); 0366 0367 changeParentTotalTimes(-m_sessionTime, -m_time); 0368 0369 return ok; 0370 } 0371 0372 QString Task::fullName() const 0373 { 0374 if (isRoot()) { 0375 return name(); 0376 } else { 0377 return parentTask()->fullName() + QString::fromLatin1("/") + name(); 0378 } 0379 } 0380 0381 KCalendarCore::Todo::Ptr Task::asTodo(const KCalendarCore::Todo::Ptr &todo) const 0382 { 0383 Q_ASSERT(todo != nullptr); 0384 0385 qCDebug(KTT_LOG) << "Task::asTodo: name() = '" << name() << "'"; 0386 todo->setUid(uid()); 0387 todo->setSummary(name()); 0388 todo->setDescription(description()); 0389 0390 // Note: if the date start is empty, the KOrganizer GUI will have the 0391 // checkbox blank, but will prefill the todo's starting datetime to the 0392 // time the file is opened. 0393 // todo->setDtStart( current ); 0394 0395 todo->setCustomProperty(eventAppName, QByteArray("totalTaskTime"), QString::number(m_time)); 0396 todo->setCustomProperty(eventAppName, QByteArray("totalSessionTime"), QString::number(m_sessionTime)); 0397 todo->setCustomProperty(eventAppName, QByteArray("sessionStartTiMe"), m_sessionStartTime.toString()); 0398 qDebug() << "m_sessionStartTime=" << m_sessionStartTime.toString(); 0399 0400 if (getDesktopStr().isEmpty()) { 0401 todo->removeCustomProperty(eventAppName, QByteArray("desktopList")); 0402 } else { 0403 todo->setCustomProperty(eventAppName, QByteArray("desktopList"), getDesktopStr()); 0404 } 0405 0406 todo->setPercentComplete(m_percentComplete); 0407 todo->setPriority(m_priority); 0408 0409 if (parentTask()) { 0410 todo->setRelatedTo(parentTask()->uid()); 0411 } 0412 0413 return todo; 0414 } 0415 0416 bool Task::parseIncidence(const KCalendarCore::Incidence::Ptr &incident, 0417 int64_t &minutes, 0418 int64_t &sessionMinutes, 0419 QString &sessionStartTiMe, 0420 QString &name, 0421 QString &description, 0422 DesktopList &desktops, 0423 int &percent_complete, 0424 int &priority) 0425 { 0426 qCDebug(KTT_LOG) << "Entering function"; 0427 bool ok; 0428 name = incident->summary(); 0429 description = incident->description(); 0430 m_uid = incident->uid(); 0431 0432 ok = false; 0433 minutes = getCustomProperty(incident, QStringLiteral("totalTaskTime")).toInt(&ok); 0434 if (!ok) { 0435 minutes = 0; 0436 } 0437 0438 ok = false; 0439 sessionMinutes = getCustomProperty(incident, QStringLiteral("totalSessionTime")).toInt(&ok); 0440 if (!ok) { 0441 sessionMinutes = 0; 0442 } 0443 0444 sessionStartTiMe = getCustomProperty(incident, QStringLiteral("sessionStartTiMe")); 0445 0446 QString desktopList = getCustomProperty(incident, QStringLiteral("desktopList")); 0447 QStringList desktopStrList = desktopList.split(QStringLiteral(","), Qt::SkipEmptyParts); 0448 desktops.clear(); 0449 0450 for (const QString &desktopStr : desktopStrList) { 0451 int desktopInt = desktopStr.toInt(&ok); 0452 if (ok) { 0453 desktops.push_back(desktopInt); 0454 } 0455 } 0456 percent_complete = incident.staticCast<KCalendarCore::Todo>()->percentComplete(); 0457 priority = incident->priority(); 0458 return true; 0459 } 0460 0461 QString Task::getDesktopStr() const 0462 { 0463 if (m_desktops.empty()) { 0464 return QString(); 0465 } 0466 0467 QString desktopsStr; 0468 for (const int desktop : m_desktops) { 0469 desktopsStr += QString::number(desktop) + QString::fromLatin1(","); 0470 } 0471 desktopsStr.remove(desktopsStr.length() - 1, 1); 0472 return desktopsStr; 0473 } 0474 0475 // This is needed e.g. to move a task under its parent when loading. 0476 void Task::cut() 0477 { 0478 changeParentTotalTimes(-m_totalSessionTime, -m_totalTime); 0479 if (!parentTask()) { 0480 m_projectModel->tasksModel()->takeTopLevelItem(m_projectModel->tasksModel()->indexOfTopLevelItem(this)); 0481 } else { 0482 parentTask()->takeChild(parentTask()->indexOfChild(this)); 0483 } 0484 } 0485 0486 // This is needed e.g. to move a task under its parent when loading. 0487 void Task::paste(TasksModelItem *destination) 0488 { 0489 destination->insertChild(0, this); 0490 changeParentTotalTimes(m_totalSessionTime, m_totalTime); 0491 } 0492 0493 // This is used e.g. to move each task under its parent after loading. 0494 void Task::move(TasksModelItem *destination) 0495 { 0496 cut(); 0497 paste(destination); 0498 } 0499 0500 QVariant Task::data(int column, int role) const 0501 { 0502 switch (role) { 0503 case Qt::DisplayRole: { 0504 bool b = KTimeTrackerSettings::decimalFormat(); 0505 switch (column) { 0506 case 0: 0507 return m_name; 0508 case 1: 0509 return formatTime(m_sessionTime, b); 0510 case 2: 0511 return formatTime(m_time, b); 0512 case 3: 0513 return formatTime(m_totalSessionTime, b); 0514 case 4: 0515 return formatTime(m_totalTime, b); 0516 case 5: 0517 return m_priority > 0 ? QString::number(m_priority) : QStringLiteral("--"); 0518 case 6: 0519 return QString::number(m_percentComplete); 0520 default: 0521 return {}; 0522 } 0523 } 0524 case SortRole: { 0525 // QSortFilterProxyModel::lessThan() supports comparison of a few data 0526 // types, here we use some of those: QString, qlonglong, int. 0527 switch (column) { 0528 case 0: 0529 return m_name; 0530 case 1: 0531 return QVariant::fromValue<qlonglong>(m_sessionTime); 0532 case 2: 0533 return QVariant::fromValue<qlonglong>(m_time); 0534 case 3: 0535 return QVariant::fromValue<qlonglong>(m_totalSessionTime); 0536 case 4: 0537 return QVariant::fromValue<qlonglong>(m_totalTime); 0538 case 5: 0539 return QVariant::fromValue<int>(m_priority); 0540 case 6: 0541 return QVariant::fromValue<int>(m_percentComplete); 0542 default: 0543 return {}; 0544 } 0545 } 0546 default: 0547 return {}; 0548 } 0549 } 0550 0551 // Update a row, containing one task 0552 void Task::update() 0553 { 0554 QModelIndex first = m_projectModel->tasksModel()->index(this, 0); 0555 QModelIndex last = m_projectModel->tasksModel()->index(this, 6); 0556 Q_EMIT m_projectModel->tasksModel()->dataChanged(first, last, QVector<int>{Qt::DisplayRole}); 0557 } 0558 0559 void Task::startNewSession() 0560 { 0561 changeTimes(-m_sessionTime, 0, nullptr); 0562 m_sessionStartTime = QDateTime::currentDateTime(); 0563 } 0564 0565 //BEGIN Properties 0566 QString Task::uid() const 0567 { 0568 return m_uid; 0569 } 0570 0571 int Task::percentComplete() const 0572 { 0573 return m_percentComplete; 0574 } 0575 0576 int Task::priority() const 0577 { 0578 return m_priority; 0579 } 0580 0581 QString Task::name() const 0582 { 0583 return m_name; 0584 } 0585 0586 QString Task::description() const 0587 { 0588 return m_description; 0589 } 0590 0591 QDateTime Task::startTime() const 0592 { 0593 return m_lastStart; 0594 } 0595 0596 QDateTime Task::sessionStartTiMe() const 0597 { 0598 return m_sessionStartTime; 0599 } 0600 0601 DesktopList Task::desktops() const 0602 { 0603 return m_desktops; 0604 } 0605 //END