2015-01-15 17:07:43 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2009 Andrew Stromme <astromme@chatonka.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Library General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this program; if not, write to the
|
|
|
|
* Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "taskmodel.h"
|
|
|
|
#include "taskitem.h"
|
|
|
|
|
|
|
|
#include <QMimeData>
|
|
|
|
#include <KDebug>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <Plasma/Service>
|
|
|
|
|
|
|
|
|
|
|
|
TaskModel::TaskModel(Plasma::DataEngine* e, QObject* parent)
|
|
|
|
: QStandardItemModel(parent),
|
|
|
|
engine(e),
|
|
|
|
dropType(SortPriority)
|
|
|
|
{
|
|
|
|
currentListIndex = 0;
|
|
|
|
|
|
|
|
rootitem = invisibleRootItem();
|
|
|
|
|
|
|
|
refreshToplevel();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TaskModel::~TaskModel() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskModel::setDropType(SortBy dropType)
|
|
|
|
{
|
|
|
|
this->dropType = dropType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskModel::dayChanged()
|
|
|
|
{
|
|
|
|
QDateTime nextMidnight = QDateTime(QDate::currentDate().addDays(1));
|
|
|
|
int secsUntilMidnight = QDateTime::currentDateTime().secsTo(nextMidnight);
|
|
|
|
midnightAlarm.setInterval(1000*secsUntilMidnight+5000); // 5 secs over to ensure we're not under
|
|
|
|
midnightAlarm.start();
|
|
|
|
|
|
|
|
m_dateItems.at(0)->setData(0, Qt::RTMTimeTRole); // Overdue
|
|
|
|
m_dateItems.at(0)->setData(0, Qt::RTMSortRole);
|
|
|
|
|
|
|
|
m_dateItems.at(1)->setData(QDateTime(QDate::currentDate()).toTime_t(), Qt::RTMTimeTRole); // Today
|
|
|
|
m_dateItems.at(1)->setData(QDateTime(QDate::currentDate()).toTime_t(), Qt::RTMSortRole);
|
|
|
|
|
|
|
|
m_dateItems.at(2)->setData(QDateTime(QDate::currentDate()).addDays(1).toTime_t(), Qt::RTMTimeTRole); // Tomorrow
|
|
|
|
m_dateItems.at(2)->setData(QDateTime(QDate::currentDate()).addDays(1).toTime_t(), Qt::RTMSortRole);
|
|
|
|
|
|
|
|
m_dateItems.at(3)->setData(QDateTime(QDate::currentDate()).addDays(2).toTime_t(), Qt::RTMTimeTRole); // Past Tomorrow/Never
|
|
|
|
m_dateItems.at(3)->setData(QDateTime(QDate::currentDate()).addDays(2).toTime_t(), Qt::RTMSortRole);
|
|
|
|
|
|
|
|
emit modelUpdated();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskModel::refreshToplevel()
|
|
|
|
{
|
|
|
|
m_priorityItems.clear();
|
|
|
|
m_dateItems.clear();
|
|
|
|
rootitem->removeRows(0, rootitem->rowCount()); // FIXME: Crash candidate?
|
|
|
|
|
|
|
|
QStringList priorityStrings;
|
|
|
|
priorityStrings.append(i18n("Top Priority:"));
|
|
|
|
priorityStrings.append(i18n("Medium Priority:"));
|
|
|
|
priorityStrings.append(i18n("Low Priority:"));
|
|
|
|
priorityStrings.append(i18n("No Priority:"));
|
|
|
|
|
|
|
|
QStringList dateStrings;
|
|
|
|
dateStrings.append(i18n("Overdue"));
|
|
|
|
dateStrings.append(i18n("Today"));
|
|
|
|
dateStrings.append(i18n("Tomorrow"));
|
|
|
|
dateStrings.append(i18n("Anytime")); //FIXME: STRINGFREEZE: Consider a change to 'Later'
|
|
|
|
|
|
|
|
for(int i=0;i<4;i++) {
|
|
|
|
HeaderItem *priority = new HeaderItem(RTMPriorityHeader);
|
|
|
|
priority->setData(i+1, Qt::RTMPriorityRole);
|
|
|
|
priority->setData(i+1, Qt::RTMSortRole); // Put it in both places so both the coloring and the sorting work
|
|
|
|
priority->setData(priorityStrings.at(i), Qt::DisplayRole);
|
|
|
|
priority->setEditable(false);
|
|
|
|
m_priorityItems.append(priority);
|
|
|
|
rootitem->insertRow(rootitem->rowCount(), priority);
|
|
|
|
|
|
|
|
HeaderItem *date = new HeaderItem(RTMDateHeader);
|
|
|
|
date->setData(dateStrings.at(i), Qt::DisplayRole);
|
|
|
|
date->setEditable(false);
|
|
|
|
m_dateItems.append(date);
|
|
|
|
rootitem->insertRow(rootitem->rowCount(), date);
|
|
|
|
}
|
|
|
|
dayChanged();
|
|
|
|
connect(&midnightAlarm, SIGNAL(timeout()), SLOT(dayChanged()));
|
|
|
|
}
|
|
|
|
QFlags< Qt::DropAction > TaskModel::supportedDropActions() const {
|
|
|
|
//kDebug() << "TaskModel::supportedDropActions()";
|
|
|
|
|
|
|
|
return Qt::MoveAction;
|
|
|
|
}
|
|
|
|
|
|
|
|
QFlags< Qt::ItemFlag > TaskModel::flags(const QModelIndex& index) const {
|
|
|
|
Qt::ItemFlags defaultFlags = QStandardItemModel::flags(index);
|
|
|
|
|
|
|
|
if (defaultFlags.testFlag(Qt::ItemIsDragEnabled))
|
|
|
|
defaultFlags ^= Qt::ItemIsDragEnabled; // bitwise xor, i.e. remove this flag if it is there.
|
|
|
|
|
|
|
|
if (index.data(Qt::RTMItemType).toInt() != RTMTaskItem) //header item
|
|
|
|
return Qt::ItemIsDropEnabled | defaultFlags;
|
|
|
|
else
|
|
|
|
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList TaskModel::mimeTypes() const {
|
|
|
|
QStringList types;
|
|
|
|
types << "application/vnd.text.list";
|
|
|
|
return types;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMimeData* TaskModel::mimeData(const QList< QModelIndex >& indexes) const {
|
|
|
|
kDebug() << "TaskModel::mimeData";
|
|
|
|
QMimeData *mimeData = new QMimeData();
|
|
|
|
QByteArray encodedData;
|
|
|
|
|
|
|
|
QDataStream stream(&encodedData, QIODevice::WriteOnly);
|
|
|
|
|
|
|
|
foreach(const QModelIndex &index, indexes)
|
|
|
|
if (index.isValid())
|
|
|
|
stream << index.data(Qt::RTMTaskIdRole).toString();
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
RTMItemType dropHeaderType;
|
|
|
|
|
|
|
|
if (dropType == SortDue)
|
|
|
|
dropHeaderType = RTMDateHeader;
|
|
|
|
else if (dropType == SortPriority)
|
|
|
|
dropHeaderType = RTMPriorityHeader;
|
|
|
|
else
|
|
|
|
dropHeaderType = RTMDateHeader; // default it
|
|
|
|
|
|
|
|
QModelIndex parentItem = parent;
|
|
|
|
|
|
|
|
while (parentItem.data(Qt::RTMItemType).toInt() != dropHeaderType && parentItem.row() >= 0)
|
|
|
|
parentItem = index(parentItem.row()-1, 0, rootitem->index()); // This is buggy. going up rows doesn't always give the right headers...
|
|
|
|
|
|
|
|
kDebug() << parentItem.data(Qt::RTMItemType).toInt();
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//int beginRow = rowCount(priorityParent);
|
|
|
|
foreach(const QString &id, newItems) {
|
|
|
|
if (m_taskItems.contains(id.toULongLong())) {
|
|
|
|
TaskItem *item = taskFromId(id.toULongLong());
|
|
|
|
if (item) {
|
|
|
|
Plasma::Service *service = engine->serviceForSource("Task:" + id);
|
|
|
|
connect(service, SIGNAL(finished(Plasma::ServiceJob*)), SIGNAL(jobFinished(Plasma::ServiceJob*)));
|
|
|
|
if (service) {
|
|
|
|
if (dropType == SortDue) {
|
|
|
|
QDate headerDate = QDateTime::fromTime_t(parentItem.data(Qt::RTMTimeTRole).toUInt()).date();
|
|
|
|
kDebug() << headerDate;
|
|
|
|
if (headerDate < QDate::currentDate())
|
|
|
|
headerDate = QDate::currentDate().addDays(-1); // set to due yesterday
|
|
|
|
else if (headerDate == QDate::currentDate().addDays(2)) // set to due never
|
|
|
|
headerDate = QDate();
|
|
|
|
kDebug() << "Setting Item to be due: " << headerDate.toString(Qt::SystemLocaleShortDate);
|
|
|
|
KConfigGroup cg = service->operationDescription("setDueText");
|
|
|
|
cg.writeEntry("dueText", headerDate.toString(Qt::SystemLocaleShortDate));
|
|
|
|
emit jobStarted(service->startOperationCall(cg));
|
|
|
|
}
|
|
|
|
else if (dropType == SortPriority) {
|
|
|
|
kDebug() << "Setting Item to priority: " << parentItem.data(Qt::RTMPriorityRole).toInt();
|
|
|
|
KConfigGroup cg = service->operationDescription("setPriority");
|
|
|
|
cg.writeEntry("priority", parentItem.data(Qt::RTMPriorityRole).toInt());
|
|
|
|
emit jobStarted(service->startOperationCall(cg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false; // We don't actually process the drop right now. We just send off the request and let the library->dataengine->plasmoid take care of it.
|
|
|
|
}
|
|
|
|
|
|
|
|
TaskItem* TaskModel::taskFromId(qulonglong id) {
|
|
|
|
if (m_taskItems.contains(id))
|
|
|
|
return m_taskItems.value(id);
|
|
|
|
|
|
|
|
TaskItem *item = new TaskItem();
|
|
|
|
item->setEditable(false); // We override the normal Qt editing structure because we provide TaskEditor overlay
|
|
|
|
m_taskItems.insert(id, item);
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
ListItem* TaskModel::listFromId(qulonglong id) {
|
|
|
|
if (m_listItems.contains(id))
|
|
|
|
return m_listItems.value(id);
|
|
|
|
|
|
|
|
ListItem *item = new ListItem();
|
|
|
|
m_listItems.insert(id, item);
|
|
|
|
listUpdate(id);
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskModel::listUpdate(qulonglong listId)
|
|
|
|
{
|
|
|
|
if (!m_listItems.contains(listId)) {
|
|
|
|
engine->connectSource("List:" + QString::number(listId), this);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
foreach(const qulonglong &taskid, m_listItems.value(listId)->tasks) {
|
|
|
|
engine->connectSource("Task:" + QString::number(taskid), this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskModel::switchToList(qulonglong listId) {
|
|
|
|
m_currentList = listId;
|
|
|
|
emit listSwitched(listId);
|
|
|
|
emit modelUpdated();
|
|
|
|
}
|
|
|
|
|
|
|
|
const ListItem* TaskModel::currentList() {
|
|
|
|
return listFromId(m_currentList);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskModel::dataUpdated(const QString& name, const Plasma::DataEngine::Data& data) {
|
|
|
|
//kDebug() << name;
|
|
|
|
if (name.startsWith(QLatin1String("List:"))) {
|
|
|
|
//kDebug() << data.value("id");
|
|
|
|
qulonglong id = data.value("id").toULongLong();
|
|
|
|
if (id == 0)
|
|
|
|
return;
|
|
|
|
ListItem *item = listFromId(id);
|
|
|
|
|
|
|
|
item->id = id;
|
|
|
|
item->name = data.value("name").toString();
|
|
|
|
item->smart = data.value("smart").toBool();
|
|
|
|
|
|
|
|
item->tasks.clear();
|
|
|
|
|
|
|
|
foreach(const QString &key, data.keys()) {
|
|
|
|
if (key != "id" && key != "name" && key != "smart")
|
|
|
|
item->tasks.append(key.toULongLong());
|
|
|
|
}
|
|
|
|
if (!item->tasks.count()) {
|
|
|
|
kDebug() << "No tasks for: " << item->name << item->id << data.keys() << item->tasks;
|
|
|
|
}
|
|
|
|
if (id == m_currentList)
|
|
|
|
switchToList(m_currentList);
|
|
|
|
}
|
|
|
|
else if (name.startsWith(QLatin1String("Task:"))) {
|
|
|
|
qulonglong id = data.value("id").toULongLong();
|
|
|
|
if (id == 0) // Seems to happen when multiple applets are running
|
|
|
|
return;
|
|
|
|
TaskItem *item = taskFromId(id);
|
|
|
|
|
|
|
|
item->setData(data.value("id"), Qt::RTMTaskIdRole);
|
|
|
|
item->setData(data.value("priority"), Qt::RTMPriorityRole);
|
|
|
|
item->setData(data.value("name"), Qt::RTMNameRole);
|
|
|
|
item->setData(data.value("tags"), Qt::RTMTagsRole);
|
|
|
|
item->setData(data.value("due"), Qt::RTMDueRole);
|
|
|
|
item->setData(data.value("isCompleted"), Qt::RTMCompletedRole);
|
|
|
|
|
|
|
|
QDateTime due = data.value("due").toDateTime();
|
|
|
|
if (due.isValid())
|
|
|
|
item->setData(due.toTime_t(), Qt::RTMTimeTRole);
|
|
|
|
else
|
|
|
|
item->setData(UINT_MAX, Qt::RTMTimeTRole);
|
|
|
|
|
|
|
|
//item->setData(data.value("priority"), Qt::RTMSortRole);
|
|
|
|
|
|
|
|
if (item->parent()) {
|
|
|
|
item->parent()->takeRow(item->row());
|
|
|
|
}
|
|
|
|
insertTask(id);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kDebug() << "Error, unknown source: " << name;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit modelUpdated();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TaskModel::insertTask(qulonglong taskid)
|
|
|
|
{
|
|
|
|
TaskItem *task = taskFromId(taskid);
|
|
|
|
if (!task->model())
|
|
|
|
rootitem->insertRow(rootitem->rowCount(), task);
|
|
|
|
}
|
|
|
|
|
2015-02-27 11:02:43 +00:00
|
|
|
#include "moc_taskmodel.cpp"
|