]> git.sven.stormbind.net Git - sven/vym.git/blob - src/taskmodel.cpp
Replace Pierre as the maintainer
[sven/vym.git] / src / taskmodel.cpp
1 #include "taskmodel.h"
2
3 #include <QDebug>
4
5 #include "branchitem.h"
6 #include "branchobj.h"
7 #include "task.h"
8 #include "vymmodel.h"
9
10 TaskModel::TaskModel(QObject *parent) : QAbstractTableModel(parent)
11 {
12     showParentsLevel = 0;
13
14     QSize size = QSize(22, 22);
15     QSize size2 = QSize(44, 22);
16
17     arrow_up_icon =
18         QIcon(QPixmap(":/flag-arrow-up.svg").scaled(size, Qt::KeepAspectRatio));
19     arrow_2up_icon = QIcon(
20         QPixmap(":/flag-arrow-2up.svg").scaled(size, Qt::KeepAspectRatio));
21
22     task_new_icon =
23         QIcon(QPixmap(":/flag-task-new.svg").scaled(size, Qt::KeepAspectRatio));
24     task_new_morning_icon = QIcon(QPixmap(":/flag-task-new-morning.svg")
25                                       .scaled(size, Qt::KeepAspectRatio));
26     task_new_sleeping_icon = QIcon(QPixmap(":/flag-task-new-sleeping.svg")
27                                        .scaled(size, Qt::KeepAspectRatio));
28
29     task_wip_icon =
30         QIcon(QPixmap(":/flag-task-wip.svg").scaled(size, Qt::KeepAspectRatio));
31     task_wip_morning_icon = QIcon(QPixmap(":/flag-task-wip-morning.svg")
32                                       .scaled(size, Qt::KeepAspectRatio));
33     task_wip_sleeping_icon = QIcon(QPixmap(":/flag-task-wip-sleeping.svg")
34                                        .scaled(size, Qt::KeepAspectRatio));
35
36     task_finished_icon = QIcon(
37         QPixmap(":/flag-task-finished.svg").scaled(size, Qt::KeepAspectRatio));
38
39     taskfilter_stopsign_icon =
40         QIcon(QPixmap(":/flag-stopsign.svg").scaled(size, Qt::KeepAspectRatio));
41     taskfilter_stopsign_arrow_up_icon =
42         QIcon(QPixmap(":/flag-stopsign-arrow-up.png")
43                   .scaled(size2, Qt::KeepAspectRatio));
44     taskfilter_stopsign_arrow_2up_icon =
45         QIcon(QPixmap(":/flag-stopsign-arrow-2up.png")
46                   .scaled(size2, Qt::KeepAspectRatio));
47 }
48
49 QModelIndex TaskModel::index(Task *t) const
50 {
51     int n = tasks.indexOf(t);
52     if (n < 0)
53         return QModelIndex();
54     else
55         return createIndex(n, 0, t);
56 }
57
58 QModelIndex TaskModel::indexRowEnd(Task *t)
59 {
60     int n = tasks.indexOf(t);
61     if (n < 0)
62         return QModelIndex();
63     else
64         return createIndex(n, 8, t);
65 }
66
67 Task *TaskModel::getTask(const QModelIndex &ix) const
68 {
69     if (ix.isValid())
70         return tasks.at(ix.row());
71     else
72         return NULL;
73 }
74
75 Task *TaskModel::getTask(const int i) const
76 {
77     if (i >= 0 && i < count())
78         return getTask(createIndex(i, 0));
79     else
80         return NULL;
81 }
82
83 int TaskModel::rowCount(const QModelIndex &parent) const
84 {
85     Q_UNUSED(parent);
86     return tasks.size();
87 }
88
89 int TaskModel::columnCount(const QModelIndex &parent) const
90 {
91     Q_UNUSED(parent);
92     return 9;
93 }
94
95 QVariant TaskModel::data(const QModelIndex &index, int role) const
96 {
97     if (!index.isValid())
98         return QVariant();
99
100     if (index.row() >= tasks.size() || index.row() < 0)
101         return QVariant();
102
103     BranchItem *bi = tasks.at(index.row())->getBranch();
104     Task *t = tasks.at(index.row());
105
106     if (role == Qt::DisplayRole) {
107         if (index.column() == 0)
108             return t->getPriority();
109         else if (index.column() == 1)
110             return t->getPriorityDelta();
111         else if (index.column() == 2)
112             return QString(); // return t->getStatusString() + " - "
113                               // +t->getAwakeString();
114         else if (index.column() == 3)
115             return t->getAgeCreation();
116         else if (index.column() == 4)
117             return t->getAgeModification();
118         else if (index.column() == 5) {
119             if (t->getDaysSleep() > 0)
120                 return t->getDaysSleep();
121             else
122                 return "-";
123         }
124         else if (index.column() == 6) {
125             QString s = bi->getModel()->getMapName();
126             if (s.isEmpty())
127                 return "-";
128             else
129                 return bi->getModel()->getMapName();
130         }
131         else if (index.column() == 8) {
132             BranchItem *bi = tasks.at(index.row())->getBranch();
133             return bi->getHeadingPlainWithParents(showParentsLevel);
134         }
135     }
136     else if (role == Qt::DecorationRole && index.column() == 2) {
137         QString s = t->getIconString();
138         if (s == "task-new")
139             return task_new_icon;
140         else if (s == "task-new-morning")
141             return task_new_morning_icon;
142         else if (s == "task-new-sleeping")
143             return task_new_sleeping_icon;
144         else if (s == "task-wip")
145             return task_wip_icon;
146         else if (s == "task-wip-sleeping")
147             return task_wip_sleeping_icon;
148         else if (s == "task-wip-morning")
149             return task_wip_morning_icon;
150         else if (s == "task-finished")
151             return task_finished_icon;
152         else {
153             qWarning() << "Unknown task type in TaskModel::data: " << s;
154             return QVariant();
155         }
156     }
157     else if (role == Qt::DecorationRole && index.column() == 7) {
158         BranchItem *bi = t->getBranch();
159         if (bi->hasActiveFlag("stopsign")) {
160             if (bi->hasActiveFlag("2arrow-up"))
161                 return taskfilter_stopsign_arrow_2up_icon;
162             else if (bi->hasActiveFlag("arrow-up"))
163                 return taskfilter_stopsign_arrow_up_icon;
164             else
165                 return taskfilter_stopsign_icon;
166         }
167         else {
168             if (bi->hasActiveFlag("2arrow-up"))
169                 return arrow_2up_icon;
170             else if (bi->hasActiveFlag("arrow-up"))
171                 return arrow_up_icon;
172         }
173         return QIcon();
174     }
175     else // role != Qt::DisplayRole
176     {
177         if (role == Qt::EditRole && index.column() == 1) // DeltaPrio
178             return t->getPriorityDelta();
179         if (role == Qt::ForegroundRole && bi)
180             return bi->getHeadingColor();
181         if (role == Qt::BackgroundRole && bi) {
182             BranchItem *frameBI = bi->getFramedParentBranch(bi);
183             if (frameBI && index.column() != 5) {
184                 BranchObj *bo = frameBI->getBranchObj();
185                 if (bo)
186                     // Return frame background
187                     return bo->getFrameBrushColor();
188             }
189             else {
190                 // Return map background
191                 return bi->getModel()->getMapBackgroundColor();
192             }
193         }
194     }
195
196     return QVariant();
197 }
198
199 QVariant TaskModel::headerData(int section, Qt::Orientation orientation,
200                                int role) const
201 {
202     if (role != Qt::DisplayRole)
203         return QVariant();
204
205     if (orientation == Qt::Horizontal) {
206         switch (section) {
207         case 0:
208             return tr("Prio", "TaskEditor");
209         case 1:
210             return tr("Delta", "TaskEditor");
211         case 2:
212             return tr("Status", "TaskEditor");
213         case 3:
214             return tr("Age total", "TaskEditor");
215         case 4:
216             return tr("Age mod.", "TaskEditor");
217         case 5:
218             return tr("Sleep", "TaskEditor");
219         case 6:
220             return tr("Map", "TaskEditor");
221         case 7:
222             return tr("Flags", "TaskEditor");
223         case 8:
224             return tr("Task", "TaskEditor");
225         default:
226             return QVariant();
227         }
228     }
229     return QVariant();
230 }
231
232 bool TaskModel::insertRows(int position, int rows, const QModelIndex &index,
233                            Task *t)
234 {
235     Q_UNUSED(index);
236     beginInsertRows(QModelIndex(), position, position + rows - 1);
237
238     for (int row = 0; row < rows; row++)
239         tasks.insert(position, t);
240
241     endInsertRows();
242     return true;
243 }
244
245 bool TaskModel::removeRows(int position, int rows, const QModelIndex &index)
246 {
247     Q_UNUSED(index);
248     beginRemoveRows(QModelIndex(), position, position + rows - 1);
249
250     for (int row = 0; row < rows; ++row)
251         delete (tasks.takeAt(position));
252
253     endRemoveRows();
254     return true;
255 }
256
257 bool TaskModel::setData(const QModelIndex &index, const QVariant &value,
258                         int role)
259 {
260     if (index.isValid() && role == Qt::EditRole) {
261         Task *t = tasks.at(index.row());
262         if (!t) {
263             qWarning() << "TaskModel::setData  no task found";
264             return false;
265         }
266
267         if (index.column() == 1) // set Delta Priority
268         {
269             BranchItem *bi = t->getBranch();
270             VymModel *m = bi->getModel();
271             m->setTaskPriorityDelta(value.toInt(), bi);
272             recalcPriorities();
273             emit(dataChanged(index, index));
274             return true;
275         }
276         if (index.column() == 8) // set Heading
277         {
278             BranchItem *bi = t->getBranch();
279             VymModel *m = bi->getModel();
280             m->setHeadingPlainText(value.toString(), bi);
281             emit(dataChanged(index, index));
282             return true;
283         }
284     }
285
286     return false;
287 }
288
289 void TaskModel::emitDataChanged(Task *t)
290 {
291     QModelIndex ix = index(t);
292     if (ix.isValid()) {
293         int row = ix.row();
294         int col = 0;
295         while (col < columnCount(QModelIndex())) {
296             ix = createIndex(row, col, t);
297             if (ix.isValid())
298                 emit(dataChanged(ix, ix));
299             col++;
300         }
301     }
302 }
303
304 Qt::ItemFlags TaskModel::flags(const QModelIndex &index) const
305 {
306     if (!index.isValid())
307         return Qt::ItemIsEnabled;
308
309     return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled |
310            Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
311 }
312
313 int TaskModel::count(VymModel *model) const
314 {
315     if (!model)
316         return tasks.size();
317     int n = 0;
318     foreach (Task *t, tasks)
319         if (t->getBranch()->getModel() == model)
320             n++;
321     return n;
322 }
323
324 Task *TaskModel::createTask(BranchItem *bi)
325 {
326     if (bi) {
327         foreach (Task *t, tasks) {
328             if (t->getBranch() == bi) {
329                 qWarning() << "TaskModel::createTask Branch exists already!";
330                 return NULL;
331             }
332         }
333         Task *task = new Task(this);
334         task->setBranch(bi);
335         task->setAwake(Task::Morning);
336         insertRows(tasks.count(), 1, QModelIndex(), task);
337
338         bi->setTask(task);
339
340         return task;
341     }
342     qWarning() << "TaskEditor::addItem - item exists";
343     return NULL;
344 }
345
346 void TaskModel::deleteTask(Task *t)
347 {
348     int pos = tasks.indexOf(t);
349     if (pos >= 0)
350         removeRows(pos, 1, QModelIndex());
351 }
352
353 bool TaskModel::updateAwake(bool force)
354 {
355     bool awake_changed = false;
356     foreach (Task *t, tasks) {
357         if (t->updateAwake() || force) {
358             t->getBranch()->updateTaskFlag();
359             awake_changed = true;
360         }
361     }
362     return awake_changed;
363 }
364
365 void TaskModel::recalcPriorities()
366 {
367     emit(layoutAboutToBeChanged());
368     int minPrio = 1000000;
369     foreach (Task *t, tasks) {
370         int p = 0;
371         BranchItem *bi = t->getBranch();
372
373         // Status
374         switch (t->getStatus()) {
375         case Task::NotStarted:
376             break;
377         case Task::WIP:
378             p += 10;
379             break;
380         case Task::Finished:
381             p += 2000;
382             break;
383         }
384
385         // Awake and sleeping
386         switch (t->getAwake()) {
387         case Task::Morning:
388             p -= 1000;
389             break;
390         case Task::WideAwake:
391             break;
392         case Task::Sleeping:
393             //p += 1000 + t->getDaysSleep();
394             break;
395         }
396
397         // Color (importance)
398         QColor c = bi->getHeadingColor();
399
400         // light blueish green
401         if (c == QColor("#00aa7f"))
402             p -= 20;
403
404         // green (e.g. from vym < 2.6.3 with #005500)
405         if (c.red() == 0 && c.blue() == 0 && c.green() < 160)
406             p -= 40;
407
408         // orange
409         if (c == QColor("#d95100"))
410             p -= 60;
411
412         // red
413         if (c == QColor("#ff0000"))
414             p -= 80;
415
416         // Flags
417         if (bi->hasActiveFlag("stopsign"))
418             p -= 450;
419         if (bi->hasActiveFlag("2arrow-up"))
420             p -= 1000;
421         if (bi->hasActiveFlag("arrow-up"))
422             p -= 500;
423
424         // Age
425         p -= t->getAgeModification();
426         p -= t->getAgeCreation() * 1.0 / 365 *
427              80; // After a year, this is as important as "red"
428
429         // Position in subtree
430         p += bi->num();
431
432         // Priority delta (set menually)
433         p -= t->getPriorityDelta();
434
435         // Set priority finally
436         t->setPriority(p);
437         if (p < minPrio)
438             minPrio = p;
439     }
440     // Normalize, so that most important task has prio 1
441     foreach (Task *t, tasks) {
442         t->setPriority(1 - minPrio + t->getPriority());
443     }
444
445     emit(layoutChanged());
446 }
447
448 void TaskModel::setShowParentsLevel(uint i)
449 {
450     showParentsLevel = i;
451     recalcPriorities(); // Triggers update of view
452 }
453
454 uint TaskModel::getShowParentsLevel() { return showParentsLevel; }
455
456 Qt::DropActions TaskModel::supportedDropActions() const
457 {
458     return Qt::MoveAction;
459 }
460
461 QStringList TaskModel::mimeTypes() const
462 {
463     QStringList types;
464     types << "application/vnd.text.list";
465     return types;
466 }
467
468 QMimeData *TaskModel::mimeData(const QModelIndexList &indexes) const
469 {
470     QMimeData *mimeData = new QMimeData();
471     QByteArray encodedData;
472
473     QDataStream stream(&encodedData, QIODevice::WriteOnly);
474
475     if (indexes.count() > 0 && indexes.first().isValid()) {
476         Task *task = getTask(indexes.first());
477
478         // Field 0: Heading
479         QString text = task->getBranch()->getHeadingPlain();
480         stream << text;
481
482         // Field 1: task row
483         stream << QString::number(index(task).row());
484     }
485
486     mimeData->setData("application/vnd.text.list", encodedData);
487     return mimeData;
488 }
489
490 bool TaskModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
491                              int row, int column, const QModelIndex &parent)
492 {
493     Q_UNUSED(row);
494
495     if (action == Qt::IgnoreAction)
496         return true;
497
498     if (!data->hasFormat("application/vnd.text.list"))
499         return false;
500
501     if (column > 0)
502         return false;
503
504     QByteArray encodedData = data->data("application/vnd.text.list");
505     QDataStream stream(&encodedData, QIODevice::ReadOnly);
506     QStringList newItems;
507     int rows = 0;
508
509     while (!stream.atEnd()) {
510         QString text;
511         stream >> text;
512         newItems << text;
513         ++rows;
514     }
515
516     Task *dst = getTask(parent);
517     Task *src = getTask(newItems[1].toInt());
518
519     // qDebug() << "Dropping: " <<  src->getBranch()->getHeadingPlain() << " on
520     // " << dst->getBranch()->getHeadingPlain();
521
522     int delta_p = dst->getPriority() - src->getPriority();
523
524     src->setPriorityDelta(src->getPriorityDelta() - delta_p + 1);
525     BranchItem *bi = src->getBranch();
526     bi->getModel()->emitDataChanged(bi);
527     bi->getModel()->setChanged();
528     return true;
529 }