RenamedTodo/src/TaskModel.cpp
2018-01-20 15:44:35 -08:00

665 lines
16 KiB
C++

/*
* Copyright 2012-2018 Morgan McMillian <gilag@monkeystew.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "TaskModel.h"
TaskModel::TaskModel(QObject* parent) {
setParent(parent);
QSettings settings;
mOffline = false;
mDelete = -1;
ts_settings = new QSettings("Morgan McMillian", "RenamedTodo");
toast = new SystemToast(this);
mSort = settings.value("sort").toInt();
if (!mSort) mSort = PRIORITY;
QString fpath = settings.value("path").toString();
if (!fpath.isNull()) {
localfile.setPath(fpath);
}
regxComp.setPattern("^x ");
regxPri.setPattern("\\(([A-Z])\\) ");
regxDate.setPattern("([0-9]{4}-[0-9]{2}-[0-9]{2}) ?");
regxProj.setPattern("(\\+[^ ]+)");
regxCtx.setPattern("(@[^ ]+)");
qDebug() << "I am life! " << this;
}
TaskModel::~TaskModel() {
delete toast;
qDebug() << "I am death! " << this;
}
int TaskModel::sort() const {
return mSort;
}
void TaskModel::setSort(const int &sort) {
QSettings settings;
mSort = sort;
settings.setValue("sort", sort);
setView();
emit listUpdated();
}
QVariant TaskModel::setting(const QString &setting, const QVariant &defVal) const {
QSettings settings;
if (settings.value(setting).isNull()) {
return defVal;
}
return settings.value(setting);
}
QString TaskModel::activeFilters() {
QString filterlist;
foreach(QString item, mFilters) {
filterlist = filterlist + item + ",";
}
if (filterlist.size() > 0) {
return " Filter: " + filterlist;
} else {
return "";
}
}
QString TaskModel::formatDate(const QDateTime &date) const {
return date.toString("yyyy-MM-dd");
}
void TaskModel::modifySetting(const QString &setting, const QVariant &value) {
QSettings settings;
settings.setValue(setting, value);
if (setting == "sort") mSort = value.toInt();
if (setting == "path") {
localfile.setPath(value.toString());
load();
}
}
void TaskModel::onFileDataChanged(QStringList data) {
localfile.save(data);
load();
setView();
}
void TaskModel::onArchiveData(QStringList data) {
localfile.saveArchive(data);
mArchiveData = data;
}
QVariantList TaskModel::filters() const {
QVariantList filters;
foreach (QString project, mProjects) {
QVariantMap map;
map["type"] = "Projects";
map["title"] = project;
if (mFilters.indexOf(project) >= 0) {
map["checked"] = true;
} else {
map["checked"] = false;
}
filters.append(map);
}
foreach (QString context, mContexts) {
QVariantMap map;
map["type"] = "Contexts";
map["title"] = context;
if (mFilters.indexOf(context) >= 0) {
map["checked"] = true;
} else {
map["checked"] = false;
}
filters.append(map);
}
foreach (QString duedate, mDueDates) {
QVariantMap map;
qDebug() << "::" + duedate;
map["type"] = "Due Dates";
map["title"] = duedate;
if (mFilters.indexOf(duedate) >= 0) {
map["checked"] = true;
} else {
map["checked"] = false;
}
filters.append(map);
}
return filters;
}
QVariantList TaskModel::tododata() {
QVariantList rData;
foreach (QString item, mTodoList) {
QVariantMap map = parseTask(item);
rData.append(map);
}
return rData;
}
void TaskModel::refresh() {
QSettings settings;
load();
emit listUpdated();
}
void TaskModel::commit() {
QSettings settings;
QString lnend = settings.value("windowsbreak").toBool() ? "\r\n" : "\n";
save();
}
void TaskModel::load() {
mTodoData.clear();
QStringList dummy;
dummy = localfile.load();
foreach (QString item, dummy) {
QVariantMap map = parseTask(item);
mTodoData.append(map);
}
mArchiveData = localfile.loadArchive();
}
void TaskModel::save() {
QStringList data;
foreach (QVariant map, mTodoData) {
QString listitem = buildTask(map.toMap());
data.append(listitem);
}
localfile.save(data);
load();
setView();
emit listUpdated();
}
void TaskModel::archive() {
QSettings settings;
QString lnend = settings.value("windowsbreak").toBool() ? "\r\n" : "\n";
foreach (QVariant map, mTodoData) {
QVariantMap task = map.toMap();
if (task["complete"].toBool()) {
QString architem = buildTask(task);
mArchiveData.append(architem);
mTodoData.removeAt(mTodoData.indexOf(task));
}
}
if (mArchiveData.size() > 0) {
localfile.saveArchive(mArchiveData);
commit();
} else {
load();
setView();
}
}
void TaskModel::search(const QString &text) {
mSearch = text;
setView();
}
void TaskModel::setValue(int idx, const QString &key, const QVariant &value) {
//qDebug() << "index: " << idx;
//qDebug() << "key: " << key;
//qDebug() << "value: " << value;
//qDebug() << "check: " << QVariantListDataModel::value(idx).value<QVariantMap>();
QSettings settings;
if (idx >=0 && idx < size()) {
QVariantMap current = QVariantListDataModel::value(idx).value<QVariantMap>();
QVariantMap updated = current;
updated[key] = value;
if (key == "complete" && value == true) {
updated["priority"] = "";
updated["dateCompleted"] = QDate::currentDate().toString("yyyy-MM-dd");
} else if (key == "complete" && value == false) {
updated["dateCompleted"] = "";
}
QString rtask = buildTask(updated);
updated["text"] = QVariant(rtask);
//qDebug() << "current: " << current;
//qDebug() << "idx: " << idx;
//qDebug() << "orig idx: " << mTodoData.indexOf(current);
//qDebug() << "test0:" << mTodoData.value(0);
//qDebug() << "testX:" << mTodoData.value(mTodoData.size()-1);
int origidx = mTodoData.indexOf(current);
mTodoData.replace(origidx, updated);
//setView();
commit();
if (key == "complete" && value == true && settings.value("autoarchive").toBool()) {
archive();
}
}
//qDebug() << "new: " << QVariantListDataModel::value(idx).value<QVariantMap>();
}
void TaskModel::exportFiles(const QStringList &files) {
QString epath = files[0];
QRegExp epathrgx("todo.txt$");
epath.replace(epathrgx, "");
LocalFile exportfrom;
LocalFile exportto;
exportto.setPath(epath);
QStringList todo = exportfrom.load();
QStringList done = exportfrom.loadArchive();
exportto.save(todo);
exportto.saveArchive(done);
}
void TaskModel::promptPurgeSandbox() {
dialog = new SystemDialog("PURGE", "CANCEL");
dialog->setTitle("Purge local sandbox");
dialog->setBody("Erase local sandbox files? WARNING This cannot be undone.");
bool success = QObject::connect(dialog, SIGNAL(finished(bb::system::SystemUiResult::Type)),
this, SLOT(onConfirmPurge(bb::system::SystemUiResult::Type)));
if (success) {
dialog->show();
} else {
dialog->deleteLater();
}
}
void TaskModel::onConfirmPurge(bb::system::SystemUiResult::Type value) {
QStringList empty;
LocalFile sandbox;
switch(value) {
case bb::system::SystemUiResult::ConfirmButtonSelection:
qDebug() << "PURGE";
sandbox.save(empty);
sandbox.saveArchive(empty);
load();
setView();
break;
case bb::system::SystemUiResult::CancelButtonSelection:
qDebug() << "CANCEL";
break;
default:
break;
}
dialog->deleteLater();
}
void TaskModel::promptDelete(int idx) {
dialog = new SystemDialog("DELETE", "CANCEL");
dialog->setTitle("Delete Task");
dialog->setBody("Permanently delete this Entry?");
bool success = QObject::connect(dialog, SIGNAL(finished(bb::system::SystemUiResult::Type)),
this, SLOT(onDeleteTask(bb::system::SystemUiResult::Type)));
if (success) {
mDelete = idx;
dialog->show();
} else {
mDelete = -1;
dialog->deleteLater();
}
}
void TaskModel::onDeleteTask(bb::system::SystemUiResult::Type value) {
switch(value) {
case bb::system::SystemUiResult::ConfirmButtonSelection:
//qDebug() << "DELETE";
delTask(mDelete);
break;
case bb::system::SystemUiResult::CancelButtonSelection:
//qDebug() << "CANCEL";
mDelete = -1;
break;
default:
break;
}
dialog->deleteLater();
}
void TaskModel::delTask(int idx) {
if (idx >=0 && idx < size()) {
QVariantMap current = QVariantListDataModel::value(idx).value<QVariantMap>();
int origidx = mTodoData.indexOf(current);
//qDebug() << "idx: " << idx;
//qDebug() << "orig idx: " << mTodoData.indexOf(current);
mTodoData.removeAt(origidx);
//setView();
commit();
mDelete = -1;
}
}
void TaskModel::changeFilter(const QString &filter, const bool &enabled) {
int idx = mFilters.indexOf(filter);
if (enabled) {
if (idx == -1) mFilters.append(filter);
} else {
if (idx >= 0) mFilters.removeAt(idx);
}
//qDebug() << "Filter List: " << mFilters;
}
void TaskModel::resetFilter() {
mFilters.clear();
setView();
toast->setBody("Filters removed.");
toast->show();
}
void TaskModel::addFullTask(const QString &task) {
QVariantMap map = parseTask(task);
QSettings settings;
if (settings.value("datenew").toBool()) {
map["dateCreated"] = QDate::currentDate().toString("yyyy-MM-dd");
}
mTodoData.append(map);
//setView();
commit();
}
void TaskModel::updateTask(int idx, const QString &task) {
if (idx >=0 && idx < size()) {
QVariantMap current = QVariantListDataModel::value(idx).value<QVariantMap>();
int origidx = mTodoData.indexOf(current);
QVariantMap map = parseTask(task);
if (origidx >= 0) {
mTodoData.replace(origidx, map);
//setView();
commit();
} else {
// TODO produce some error message
qDebug() << "ERROR: origidx = " << origidx;
}
}
}
QVariantMap TaskModel::parseTask(QString rawTask) {
QVariantMap task;
int pos = 0;
QString item = rawTask;
bool complete = false;
QString priority = "";
QString dateCompleted = "";
QString dateCreated = "";
QString dateDue = "";
QString detail = "";
//qDebug() << "..parsing projects..";
QStringList prj;
pos = 0;
while ((pos = regxProj.indexIn(item,pos)) != -1) {
prj << regxProj.cap(1);
pos += regxProj.matchedLength();
}
if (prj.size() > 0) {
foreach (QString p, prj) {
if (mProjects.indexOf(p) < 0) {
mProjects << p;
}
}
}
//qDebug() << "my projects: " << mProjects;
//qDebug() << "..parsing contexts..";
QStringList ctx;
pos = 0;
while ((pos = regxCtx.indexIn(item,pos)) != -1) {
ctx << regxCtx.cap(1);
pos += regxCtx.matchedLength();
}
if (ctx.size() > 0) {
foreach (QString c, ctx) {
if (mContexts.indexOf(c) < 0) {
mContexts << c;
}
}
}
//qDebug() << "my contexts: " << mContexts;
QRegExp regxDue("due:([0-9]{4}-[0-9]{2}-[0-9]{2}) ?");
if (regxDue.indexIn(item,0) != -1) {
dateDue = regxDue.cap(1);
QString duestr = "due:" + dateDue;
if (dateDue.length() > 0) {
if (mDueDates.indexOf(duestr) < 0) {
mDueDates << duestr;
}
}
}
//qDebug() << "---*****---";
if (regxComp.indexIn(item) == 0) {
//qDebug() << "complete: true";
complete = true;
item.replace(regxComp, "");
pos = 0;
QStringList dates;
while ((pos = regxDate.indexIn(item,pos)) != -1) {
dates << regxDate.cap(1);
pos += regxDate.matchedLength();
}
if (dates.size() > 0) {
//qDebug() << "completed: " << dates.at(0);
dateCompleted = dates.at(0);
if (dates.size() >= 2) {
//qDebug() << "created: " << dates.at(1);
dateCreated = dates.at(1);
pos = regxDate.indexIn(item,0);
item.replace(QRegExp("^([0-9]{4}-[0-9]{2}-[0-9]{2} )"), "");
}
}
pos = regxDate.indexIn(item,0);
item.replace(QRegExp("^([0-9]{4}-[0-9]{2}-[0-9]{2} )"), "");
} else if (regxDate.indexIn(item) >= 0 && regxDate.indexIn(item) < 5) {
dateCreated = regxDate.cap(1);
// item.replace(regxDate.cap(1) + " ", "");
// item.replace(QRegExp("^([0-9]{4}-[0-9]{2}-[0-9]{2} )"), "");
item.remove(regxDate.indexIn(item), 11);
}
if (regxPri.indexIn(item) >= 0) {
//qDebug() << "priority: " << regxPri.cap(1);
priority = regxPri.cap(1);
item.replace(regxPri, "");
}
detail = item;
task["complete"] = QVariant(complete);
task["priority"] = QVariant(priority);
task["dateCompleted"] = QVariant(dateCompleted);
task["dateCreated"] = QVariant(dateCreated);
task["dateDue"] = QVariant(dateDue);
task["detail"] = QVariant(detail);
task["text"] = QVariant(rawTask);
return task;
}
QString TaskModel::buildTask(const QVariantMap &task) {
QString text = "";
bool complete = task["complete"].toBool();
QString priority = task["priority"].toString();
QString dateCompleted = task["dateCompleted"].toString();
QString dateCreated = task["dateCreated"].toString();
QString detail = task["detail"].toString();
if (complete) text = text + "x ";
if (priority.length() > 0) text = text + "(" + priority + ") ";
if (dateCompleted.length() > 0) text = text + dateCompleted + " ";
if (dateCreated.length() > 0) text = text + dateCreated + " ";
if (detail.length() > 0) text = text + detail;
return text;
}
bool TaskModel::sortByPriority(const QString &a, const QString &b) {
QString matchA;
QString matchB;
QRegExp pri("\\(([A-Z])\\) ");
QRegExp done("^x ");
QRegExp date("([0-9]{4}-[0-9]{2}-[0-9]{2}) ?");
QRegExp sca("^@");
QRegExp scp("^\\+");
if (pri.indexIn(a) >= 0) {
matchA = pri.cap(1);
} else if (done.indexIn(a) == 0) {
matchA = "}";
} else {
matchA = a.toLower();
matchA.replace(date,"");
matchA.replace(sca,"");
matchA.replace(scp,"");
}
//qDebug() << "a: " << a;
//qDebug() << "matchA: " << matchA;
if (pri.indexIn(b) >= 0) {
matchB = pri.cap(1);
} else if (done.indexIn(b) == 0) {
matchB = "}";
} else {
matchB = b.toLower();
matchB.replace(date,"");
matchB.replace(sca,"");
matchB.replace(scp,"");
}
//qDebug() << "b: " << b;
//qDebug() << "matchB: " << matchB;
return matchA < matchB;
}
bool TaskModel::sortByText(const QString &a, const QString &b) {
QString matchA = a;
QString matchB = b;
QRegExp pri("\\([A-Z]\\) ");
QRegExp done("^x ");
QRegExp date("([0-9]{4}-[0-9]{2}-[0-9]{2}) ?");
QRegExp sca("^@");
QRegExp scp("^\\+");
matchA.replace(done, "");
matchA.replace(pri, "");
matchA.replace(date, "");
matchA.replace(sca,"");
matchA.replace(scp,"");
//qDebug() << "A: " << matchA;
matchB.replace(done, "");
matchB.replace(pri, "");
matchB.replace(date, "");
matchB.replace(sca,"");
matchB.replace(scp,"");
//qDebug() << "B: " << matchB;
return matchA.toLower() < matchB.toLower();
}
bool TaskModel::sortByDueDate(const QString &a, const QString &b) {
QString matchA = a;
QString matchB = b;
QRegExp regxDue("(due:)([0-9]{4}-[0-9]{2}-[0-9]{2}) ?");
QRegExp pri("\\([A-Z]\\) ");
QRegExp done("^x ");
QRegExp date("([0-9]{4}-[0-9]{2}-[0-9]{2}) ?");
QRegExp sca("^@");
QRegExp scp("^\\+");
if (regxDue.indexIn(a,0) != -1) {
matchA = regxDue.cap(2);
} else if (done.indexIn(a) == 0) {
matchA = "}";
} else {
matchA.replace(done, "");
matchA.replace(pri, "");
matchA.replace(date, "");
matchA.replace(sca,"");
matchA.replace(scp,"");
}
if (regxDue.indexIn(b,0) != -1) {
matchB = regxDue.cap(2);
} else if (done.indexIn(b) == 0) {
matchB = "}";
} else {
matchB.replace(done, "");
matchB.replace(pri, "");
matchB.replace(date, "");
matchB.replace(sca,"");
matchB.replace(scp,"");
}
return matchA.toLower() < matchB.toLower();
}
void TaskModel::setView() {
mTodoList.clear();
mProjects.clear();
mContexts.clear();
mDueDates.clear();
//qDebug() << "::" << mFilters;
foreach (QVariant map, mTodoData) {
QString listitem = buildTask(map.toMap());
if (mSearch.length() > 0) {
if (listitem.contains(mSearch, Qt::CaseInsensitive)) {
mTodoList.append(listitem);
}
} else if (mFilters.length() > 0) {
// TODO this results in an "or" condition, probably should be "and" instead
foreach (QString filter, mFilters) {
if (listitem.indexOf(filter) >=0) {
mTodoList.append(listitem);
break;
}
}
} else {
mTodoList.append(listitem);
}
}
clear();
switch (mSort) {
case PRIORITY:
qSort(mTodoList.begin(), mTodoList.end(), sortByPriority);
break;
case ID:
// this is the default
break;
case TEXT:
qSort(mTodoList.begin(), mTodoList.end(), sortByText);
break;
case DUE:
qSort(mTodoList.begin(), mTodoList.end(), sortByDueDate);
break;
}
foreach (QString item, mTodoList) {
QVariantMap map = parseTask(item);
append(map);
}
emit listUpdated();
}