]> git.sven.stormbind.net Git - sven/vym.git/blobdiff - src/taskmodel.cpp
New upstream version 2.9.22
[sven/vym.git] / src / taskmodel.cpp
diff --git a/src/taskmodel.cpp b/src/taskmodel.cpp
new file mode 100644 (file)
index 0000000..b76baf3
--- /dev/null
@@ -0,0 +1,529 @@
+#include "taskmodel.h"
+
+#include <QDebug>
+
+#include "branchitem.h"
+#include "branchobj.h"
+#include "task.h"
+#include "vymmodel.h"
+
+TaskModel::TaskModel(QObject *parent) : QAbstractTableModel(parent)
+{
+    showParentsLevel = 0;
+
+    QSize size = QSize(22, 22);
+    QSize size2 = QSize(44, 22);
+
+    arrow_up_icon =
+        QIcon(QPixmap(":/flag-arrow-up.svg").scaled(size, Qt::KeepAspectRatio));
+    arrow_2up_icon = QIcon(
+        QPixmap(":/flag-arrow-2up.svg").scaled(size, Qt::KeepAspectRatio));
+
+    task_new_icon =
+        QIcon(QPixmap(":/flag-task-new.svg").scaled(size, Qt::KeepAspectRatio));
+    task_new_morning_icon = QIcon(QPixmap(":/flag-task-new-morning.svg")
+                                      .scaled(size, Qt::KeepAspectRatio));
+    task_new_sleeping_icon = QIcon(QPixmap(":/flag-task-new-sleeping.svg")
+                                       .scaled(size, Qt::KeepAspectRatio));
+
+    task_wip_icon =
+        QIcon(QPixmap(":/flag-task-wip.svg").scaled(size, Qt::KeepAspectRatio));
+    task_wip_morning_icon = QIcon(QPixmap(":/flag-task-wip-morning.svg")
+                                      .scaled(size, Qt::KeepAspectRatio));
+    task_wip_sleeping_icon = QIcon(QPixmap(":/flag-task-wip-sleeping.svg")
+                                       .scaled(size, Qt::KeepAspectRatio));
+
+    task_finished_icon = QIcon(
+        QPixmap(":/flag-task-finished.svg").scaled(size, Qt::KeepAspectRatio));
+
+    taskfilter_stopsign_icon =
+        QIcon(QPixmap(":/flag-stopsign.svg").scaled(size, Qt::KeepAspectRatio));
+    taskfilter_stopsign_arrow_up_icon =
+        QIcon(QPixmap(":/flag-stopsign-arrow-up.png")
+                  .scaled(size2, Qt::KeepAspectRatio));
+    taskfilter_stopsign_arrow_2up_icon =
+        QIcon(QPixmap(":/flag-stopsign-arrow-2up.png")
+                  .scaled(size2, Qt::KeepAspectRatio));
+}
+
+QModelIndex TaskModel::index(Task *t) const
+{
+    int n = tasks.indexOf(t);
+    if (n < 0)
+        return QModelIndex();
+    else
+        return createIndex(n, 0, t);
+}
+
+QModelIndex TaskModel::indexRowEnd(Task *t)
+{
+    int n = tasks.indexOf(t);
+    if (n < 0)
+        return QModelIndex();
+    else
+        return createIndex(n, 8, t);
+}
+
+Task *TaskModel::getTask(const QModelIndex &ix) const
+{
+    if (ix.isValid())
+        return tasks.at(ix.row());
+    else
+        return NULL;
+}
+
+Task *TaskModel::getTask(const int i) const
+{
+    if (i >= 0 && i < count())
+        return getTask(createIndex(i, 0));
+    else
+        return NULL;
+}
+
+int TaskModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    return tasks.size();
+}
+
+int TaskModel::columnCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    return 9;
+}
+
+QVariant TaskModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid())
+        return QVariant();
+
+    if (index.row() >= tasks.size() || index.row() < 0)
+        return QVariant();
+
+    BranchItem *bi = tasks.at(index.row())->getBranch();
+    Task *t = tasks.at(index.row());
+
+    if (role == Qt::DisplayRole) {
+        if (index.column() == 0)
+            return t->getPriority();
+        else if (index.column() == 1)
+            return t->getPriorityDelta();
+        else if (index.column() == 2)
+            return QString(); // return t->getStatusString() + " - "
+                              // +t->getAwakeString();
+        else if (index.column() == 3)
+            return t->getAgeCreation();
+        else if (index.column() == 4)
+            return t->getAgeModification();
+        else if (index.column() == 5) {
+            if (t->getDaysSleep() > 0)
+                return t->getDaysSleep();
+            else
+                return "-";
+        }
+        else if (index.column() == 6) {
+            QString s = bi->getModel()->getMapName();
+            if (s.isEmpty())
+                return "-";
+            else
+                return bi->getModel()->getMapName();
+        }
+        else if (index.column() == 8) {
+            BranchItem *bi = tasks.at(index.row())->getBranch();
+            return bi->getHeadingPlainWithParents(showParentsLevel);
+        }
+    }
+    else if (role == Qt::DecorationRole && index.column() == 2) {
+        QString s = t->getIconString();
+        if (s == "task-new")
+            return task_new_icon;
+        else if (s == "task-new-morning")
+            return task_new_morning_icon;
+        else if (s == "task-new-sleeping")
+            return task_new_sleeping_icon;
+        else if (s == "task-wip")
+            return task_wip_icon;
+        else if (s == "task-wip-sleeping")
+            return task_wip_sleeping_icon;
+        else if (s == "task-wip-morning")
+            return task_wip_morning_icon;
+        else if (s == "task-finished")
+            return task_finished_icon;
+        else {
+            qWarning() << "Unknown task type in TaskModel::data: " << s;
+            return QVariant();
+        }
+    }
+    else if (role == Qt::DecorationRole && index.column() == 7) {
+        BranchItem *bi = t->getBranch();
+        if (bi->hasActiveFlag("stopsign")) {
+            if (bi->hasActiveFlag("2arrow-up"))
+                return taskfilter_stopsign_arrow_2up_icon;
+            else if (bi->hasActiveFlag("arrow-up"))
+                return taskfilter_stopsign_arrow_up_icon;
+            else
+                return taskfilter_stopsign_icon;
+        }
+        else {
+            if (bi->hasActiveFlag("2arrow-up"))
+                return arrow_2up_icon;
+            else if (bi->hasActiveFlag("arrow-up"))
+                return arrow_up_icon;
+        }
+        return QIcon();
+    }
+    else // role != Qt::DisplayRole
+    {
+        if (role == Qt::EditRole && index.column() == 1) // DeltaPrio
+            return t->getPriorityDelta();
+        if (role == Qt::ForegroundRole && bi)
+            return bi->getHeadingColor();
+        if (role == Qt::BackgroundRole && bi) {
+            BranchItem *frameBI = bi->getFramedParentBranch(bi);
+            if (frameBI && index.column() != 5) {
+                BranchObj *bo = frameBI->getBranchObj();
+                if (bo)
+                    // Return frame background
+                    return bo->getFrameBrushColor();
+            }
+            else {
+                // Return map background
+                return bi->getModel()->getMapBackgroundColor();
+            }
+        }
+    }
+
+    return QVariant();
+}
+
+QVariant TaskModel::headerData(int section, Qt::Orientation orientation,
+                               int role) const
+{
+    if (role != Qt::DisplayRole)
+        return QVariant();
+
+    if (orientation == Qt::Horizontal) {
+        switch (section) {
+        case 0:
+            return tr("Prio", "TaskEditor");
+        case 1:
+            return tr("Delta", "TaskEditor");
+        case 2:
+            return tr("Status", "TaskEditor");
+        case 3:
+            return tr("Age total", "TaskEditor");
+        case 4:
+            return tr("Age mod.", "TaskEditor");
+        case 5:
+            return tr("Sleep", "TaskEditor");
+        case 6:
+            return tr("Map", "TaskEditor");
+        case 7:
+            return tr("Flags", "TaskEditor");
+        case 8:
+            return tr("Task", "TaskEditor");
+        default:
+            return QVariant();
+        }
+    }
+    return QVariant();
+}
+
+bool TaskModel::insertRows(int position, int rows, const QModelIndex &index,
+                           Task *t)
+{
+    Q_UNUSED(index);
+    beginInsertRows(QModelIndex(), position, position + rows - 1);
+
+    for (int row = 0; row < rows; row++)
+        tasks.insert(position, t);
+
+    endInsertRows();
+    return true;
+}
+
+bool TaskModel::removeRows(int position, int rows, const QModelIndex &index)
+{
+    Q_UNUSED(index);
+    beginRemoveRows(QModelIndex(), position, position + rows - 1);
+
+    for (int row = 0; row < rows; ++row)
+        delete (tasks.takeAt(position));
+
+    endRemoveRows();
+    return true;
+}
+
+bool TaskModel::setData(const QModelIndex &index, const QVariant &value,
+                        int role)
+{
+    if (index.isValid() && role == Qt::EditRole) {
+        Task *t = tasks.at(index.row());
+        if (!t) {
+            qWarning() << "TaskModel::setData  no task found";
+            return false;
+        }
+
+        if (index.column() == 1) // set Delta Priority
+        {
+            BranchItem *bi = t->getBranch();
+            VymModel *m = bi->getModel();
+            m->setTaskPriorityDelta(value.toInt(), bi);
+            recalcPriorities();
+            emit(dataChanged(index, index));
+            return true;
+        }
+        if (index.column() == 8) // set Heading
+        {
+            BranchItem *bi = t->getBranch();
+            VymModel *m = bi->getModel();
+            m->setHeadingPlainText(value.toString(), bi);
+            emit(dataChanged(index, index));
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void TaskModel::emitDataChanged(Task *t)
+{
+    QModelIndex ix = index(t);
+    if (ix.isValid()) {
+        int row = ix.row();
+        int col = 0;
+        while (col < columnCount(QModelIndex())) {
+            ix = createIndex(row, col, t);
+            if (ix.isValid())
+                emit(dataChanged(ix, ix));
+            col++;
+        }
+    }
+}
+
+Qt::ItemFlags TaskModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return Qt::ItemIsEnabled;
+
+    return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled |
+           Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
+}
+
+int TaskModel::count(VymModel *model) const
+{
+    if (!model)
+        return tasks.size();
+    int n = 0;
+    foreach (Task *t, tasks)
+        if (t->getBranch()->getModel() == model)
+            n++;
+    return n;
+}
+
+Task *TaskModel::createTask(BranchItem *bi)
+{
+    if (bi) {
+        foreach (Task *t, tasks) {
+            if (t->getBranch() == bi) {
+                qWarning() << "TaskModel::createTask Branch exists already!";
+                return NULL;
+            }
+        }
+        Task *task = new Task(this);
+        task->setBranch(bi);
+        task->setAwake(Task::Morning);
+        insertRows(tasks.count(), 1, QModelIndex(), task);
+
+        bi->setTask(task);
+
+        return task;
+    }
+    qWarning() << "TaskEditor::addItem - item exists";
+    return NULL;
+}
+
+void TaskModel::deleteTask(Task *t)
+{
+    int pos = tasks.indexOf(t);
+    if (pos >= 0)
+        removeRows(pos, 1, QModelIndex());
+}
+
+bool TaskModel::updateAwake(bool force)
+{
+    bool awake_changed = false;
+    foreach (Task *t, tasks) {
+        if (t->updateAwake() || force) {
+            t->getBranch()->updateTaskFlag();
+            awake_changed = true;
+        }
+    }
+    return awake_changed;
+}
+
+void TaskModel::recalcPriorities()
+{
+    emit(layoutAboutToBeChanged());
+    int minPrio = 1000000;
+    foreach (Task *t, tasks) {
+        int p = 0;
+        BranchItem *bi = t->getBranch();
+
+        // Status
+        switch (t->getStatus()) {
+        case Task::NotStarted:
+            break;
+        case Task::WIP:
+            p += 10;
+            break;
+        case Task::Finished:
+            p += 2000;
+            break;
+        }
+
+        // Awake and sleeping
+        switch (t->getAwake()) {
+        case Task::Morning:
+            p -= 1000;
+            break;
+        case Task::WideAwake:
+            break;
+        case Task::Sleeping:
+            //p += 1000 + t->getDaysSleep();
+            break;
+        }
+
+        // Color (importance)
+        QColor c = bi->getHeadingColor();
+
+        // light blueish green
+        if (c == QColor("#00aa7f"))
+            p -= 20;
+
+        // green (e.g. from vym < 2.6.3 with #005500)
+        if (c.red() == 0 && c.blue() == 0 && c.green() < 160)
+            p -= 40;
+
+        // orange
+        if (c == QColor("#d95100"))
+            p -= 60;
+
+        // red
+        if (c == QColor("#ff0000"))
+            p -= 80;
+
+        // Flags
+        if (bi->hasActiveFlag("stopsign"))
+            p -= 450;
+        if (bi->hasActiveFlag("2arrow-up"))
+            p -= 1000;
+        if (bi->hasActiveFlag("arrow-up"))
+            p -= 500;
+
+        // Age
+        p -= t->getAgeModification();
+        p -= t->getAgeCreation() * 1.0 / 365 *
+             80; // After a year, this is as important as "red"
+
+        // Position in subtree
+        p += bi->num();
+
+        // Priority delta (set menually)
+        p -= t->getPriorityDelta();
+
+        // Set priority finally
+        t->setPriority(p);
+        if (p < minPrio)
+            minPrio = p;
+    }
+    // Normalize, so that most important task has prio 1
+    foreach (Task *t, tasks) {
+        t->setPriority(1 - minPrio + t->getPriority());
+    }
+
+    emit(layoutChanged());
+}
+
+void TaskModel::setShowParentsLevel(uint i)
+{
+    showParentsLevel = i;
+    recalcPriorities(); // Triggers update of view
+}
+
+uint TaskModel::getShowParentsLevel() { return showParentsLevel; }
+
+Qt::DropActions TaskModel::supportedDropActions() const
+{
+    return Qt::MoveAction;
+}
+
+QStringList TaskModel::mimeTypes() const
+{
+    QStringList types;
+    types << "application/vnd.text.list";
+    return types;
+}
+
+QMimeData *TaskModel::mimeData(const QModelIndexList &indexes) const
+{
+    QMimeData *mimeData = new QMimeData();
+    QByteArray encodedData;
+
+    QDataStream stream(&encodedData, QIODevice::WriteOnly);
+
+    if (indexes.count() > 0 && indexes.first().isValid()) {
+        Task *task = getTask(indexes.first());
+
+        // Field 0: Heading
+        QString text = task->getBranch()->getHeadingPlain();
+        stream << text;
+
+        // Field 1: task row
+        stream << QString::number(index(task).row());
+    }
+
+    mimeData->setData("application/vnd.text.list", encodedData);
+    return mimeData;
+}
+
+bool TaskModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+                             int row, int column, const QModelIndex &parent)
+{
+    Q_UNUSED(row);
+
+    if (action == Qt::IgnoreAction)
+        return true;
+
+    if (!data->hasFormat("application/vnd.text.list"))
+        return false;
+
+    if (column > 0)
+        return false;
+
+    QByteArray encodedData = data->data("application/vnd.text.list");
+    QDataStream stream(&encodedData, QIODevice::ReadOnly);
+    QStringList newItems;
+    int rows = 0;
+
+    while (!stream.atEnd()) {
+        QString text;
+        stream >> text;
+        newItems << text;
+        ++rows;
+    }
+
+    Task *dst = getTask(parent);
+    Task *src = getTask(newItems[1].toInt());
+
+    // qDebug() << "Dropping: " <<  src->getBranch()->getHeadingPlain() << " on
+    // " << dst->getBranch()->getHeadingPlain();
+
+    int delta_p = dst->getPriority() - src->getPriority();
+
+    src->setPriorityDelta(src->getPriorityDelta() - delta_p + 1);
+    BranchItem *bi = src->getBranch();
+    bi->getModel()->emitDataChanged(bi);
+    bi->getModel()->setChanged();
+    return true;
+}