diff --git a/client/helper.cpp b/client/helper.cpp index 36fe984..9ef1419 100644 --- a/client/helper.cpp +++ b/client/helper.cpp @@ -24,7 +24,10 @@ char* __strlcat_chk (char* dest, const char* src, int len, int destcapacity) #endif #include "settings.h" -void ThemeHelper::applyCurrentTheme(Settings& settings) + +using namespace helper; + +void theme::applyCurrent(Settings& settings) { // Dark theme if (settings.data()[KEY_DARK_THEME].toBool()) @@ -42,18 +45,36 @@ void ThemeHelper::applyCurrentTheme(Settings& settings) } -QString TimeHelper::secondsToDisplay(int seconds, bool showSeconds) +std::string chrono::secondsToDisplay(int seconds, bool showSeconds) { int hours = seconds / 3600; int minutes = (seconds % 3600) / 60; int secs = seconds % 60; + char r[32]; if (showSeconds) - return QString("%1:%2:%3").arg(hours, 2, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(secs, 2, 10, QLatin1Char('0')); + sprintf(r, "%2d:%2d:%2d", hours, minutes, secs); else - return QString("%1:%2").arg(hours, 2, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')); + sprintf(r, "%2d:%2d", hours, minutes); + + return r; } -QString PathHelper::pathToSettings() +std::string chrono::timeToStr(time_t timestamp) +{ + char buf[128]; + strftime(buf, sizeof buf, "%FT%TZ", gmtime(×tamp)); + return buf; +} + +time_t chrono::strToTime(const std::string& s) +{ + struct tm t; + memset(&t, 0, sizeof t); + strptime(s.c_str(), "%FT%TZ", &t); + return timegm(&t); +} + +QString path::pathToSettings() { #if QT_VERSION >= 0x050000 QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation); @@ -64,7 +85,7 @@ QString PathHelper::pathToSettings() return path; } -QString PathHelper::pathToDatabase() +QString path::pathToDatabase() { #if QT_VERSION >= 0x050000 QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation); @@ -75,7 +96,7 @@ QString PathHelper::pathToDatabase() return path; } -QString PathHelper::pathToDesktop() +QString path::pathToDesktop() { #if QT_VERSION >= 0x050000 QString folder = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); @@ -85,7 +106,7 @@ QString PathHelper::pathToDesktop() return folder; } -QString PathHelper::pathToDatabaseTemplate() +QString path::pathToDatabaseTemplate() { #ifdef TARGET_WIN return QCoreApplication::applicationDirPath() + "/" + DATABASENAME; @@ -96,7 +117,7 @@ QString PathHelper::pathToDatabaseTemplate() #endif } -QString PathHelper::pathToLog() +QString path::pathToLog() { #if QT_VERSION >= 0x050000 QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation); @@ -106,7 +127,7 @@ QString PathHelper::pathToLog() return folder + "/" + LOGNAME; } -bool ActivityTrackerHelper::ensureSmartTrackingIsPossible() +bool activityTracker::ensureSmartTrackingIsPossible() { bool result = false; HIDActivityTracker tracker; diff --git a/client/helper.h b/client/helper.h index 18473b1..10514a0 100644 --- a/client/helper.h +++ b/client/helper.h @@ -5,46 +5,59 @@ #include class Settings; -class ThemeHelper +namespace helper { -public: - static void applyCurrentTheme(Settings& settings); -}; + class theme + { + public: + static void applyCurrent(Settings& settings); + }; -class TimeHelper -{ -public: - static QString secondsToDisplay(int seconds, bool showSeconds); -}; + struct date + { + int mYear = -1, mMonth = -1, mDay = -1; + + time_t toTimestamp(); + static date fromTimestamp(); + }; + + class chrono + { + public: + static std::string secondsToDisplay(int seconds, bool showSeconds); + static std::string timeToStr(time_t timestamp); + static time_t strToTime(const std::string& s); + }; -class PathHelper -{ -public: - static QString pathToDatabase(); - static QString pathToDesktop(); - static QString pathToSettings(); - static QString pathToDatabaseTemplate(); - static QString pathToLog(); -}; + class path + { + public: + static QString pathToDatabase(); + static QString pathToDesktop(); + static QString pathToSettings(); + static QString pathToDatabaseTemplate(); + static QString pathToLog(); + }; -class ActivityTrackerHelper -{ -public: - static bool ensureSmartTrackingIsPossible(); -}; + class activityTracker + { + public: + static bool ensureSmartTrackingIsPossible(); + }; -class EscapeKeyEventFilter: public QObject -{ - Q_OBJECT + class EscapeKeyEventFilter: public QObject + { + Q_OBJECT -public: - explicit EscapeKeyEventFilter(QObject* parent = 0); - bool eventFilter(QObject *obj, QEvent * event); + public: + explicit EscapeKeyEventFilter(QObject* parent = 0); + bool eventFilter(QObject *obj, QEvent * event); -signals: - void escapePressed(QObject* obj); + signals: + void escapePressed(QObject* obj); -}; + }; +} #endif diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index bd1d67a..cf76ddd 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -74,7 +74,7 @@ MainWindow::~MainWindow() void MainWindow::attachDatabase() { // Open database - QString path = PathHelper::pathToDatabase(); + QString path = helper::path::pathToDatabase(); if (mSettings->data()[KEY_DB_FILENAME_SPECIFIED].toBool()) path = mSettings->data()[KEY_DB_FILENAME].toString(); diff --git a/client/storage.cpp b/client/storage.cpp index 7e057d2..9d6bbd8 100644 --- a/client/storage.cpp +++ b/client/storage.cpp @@ -8,63 +8,63 @@ #endif enum class DbProperty { - Version = 1, - InternalCipher = 2 + Version = 1, + InternalCipher = 2 }; #define CURRENT_DBVERSION "0" // Function to make encryption. Input is raw content blob, output is blob with IV, original length and encrypted data. -static void EncryptBlob(const QByteArray& input, QByteArray& output) +/*static void EncryptBlob(const QByteArray& input, QByteArray& output) { - TwofishCipher cipher; + TwofishCipher cipher; - // Generate 16 bytes IV - QByteArray iv(16, 0); - IV::Generate(iv); - cipher.setIV(iv); + // Generate 16 bytes IV + QByteArray iv(16, 0); + IV::Generate(iv); + cipher.setIV(iv); - // Make padding - int padded = (input.length() % cipher.blocksize()) ? (cipher.blocksize() - input.length() % cipher.blocksize()) : 0; + // Make padding + int padded = (input.length() % cipher.blocksize()) ? (cipher.blocksize() - input.length() % cipher.blocksize()) : 0; - // Prepare output buffer - output.resize(iv.length() + 4 + input.length() + padded); + // Prepare output buffer + output.resize(iv.length() + 4 + input.length() + padded); - // Copy IV - memcpy(output.data(), iv.data(), iv.length()); + // Copy IV + memcpy(output.data(), iv.data(), iv.length()); - // Copy original length in network byte order - uint32_t lengthNbo = htonl(input.length()); - memcpy(output.data() + iv.length(), &lengthNbo, 4); + // Copy original length in network byte order + uint32_t lengthNbo = htonl(input.length()); + memcpy(output.data() + iv.length(), &lengthNbo, 4); - // Encrypt - cipher.encrypt(input, 0, output, iv.length() + 4); -} + // Encrypt + cipher.encrypt(input, 0, output, iv.length() + 4); +}*/ -static void DecryptBlob(const QByteArray& input, QByteArray& output) +/*static void DecryptBlob(const QByteArray& input, QByteArray& output) { - TwofishCipher cipher; - assert(input.length() >= cipher.blocksize() + 4); + TwofishCipher cipher; + assert(input.length() >= cipher.blocksize() + 4); - // Read IV - QByteArray iv(input.data(), cipher.blocksize()); + // Read IV + QByteArray iv(input.data(), cipher.blocksize()); - // Read original length - uint32_t lengthNbo; - memcpy(&lengthNbo, input.data() + iv.length(), 4); + // Read original length + uint32_t lengthNbo; + memcpy(&lengthNbo, input.data() + iv.length(), 4); - // Prepare output buffer - output.resize(input.length() - iv.length() - 4); + // Prepare output buffer + output.resize(input.length() - iv.length() - 4); - // Set IV - cipher.setIV(iv); + // Set IV + cipher.setIV(iv); - // Decrypt data - cipher.decrypt(input, iv.length() + 4, output, 0); + // Decrypt data + cipher.decrypt(input, iv.length() + 4, output, 0); - // Truncate to original size - output.truncate(ntohl(lengthNbo)); -} + // Truncate to original size + output.truncate(ntohl(lengthNbo)); +}*/ Storage::Storage() @@ -73,186 +73,186 @@ Storage::Storage() Storage::~Storage() { - close(); + close(); } QString Storage::path() { - return mPath; + return mPath; } void Storage::setPath(const QString &path) { - mPath = path; + mPath = path; } QString Storage::key() { - return mKey; + return mKey; } void Storage::setKey(const QString &key) { - mKey = key; + mKey = key; } SQLite::Database& Storage::database() { - return *mDatabase; + return *mDatabase; } bool Storage::create() { - assert(!mPath.isEmpty()); - try - { - mDatabase = QSharedPointer(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE )); - } - catch(std::exception& e) - { - return false; - } + assert(!mPath.isEmpty()); + try + { + mDatabase = QSharedPointer(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE )); + } + catch(std::exception& e) + { + return false; + } #ifdef USE_ENCRYPTED_DB - try - { - std::string keyQuery = "pragma key='" + mKey.toStdString() + "'"; - mDatabase->exec(keyQuery.c_str()); - mDatabase->exec("pragma locking_mode=EXCLUSIVE"); - mDatabase->exec("pragma journal_mode=MEMORY"); - mDatabase->exec("pragma temp_store=MEMORY"); - } - catch(std::exception& e) - { - return false; - } + try + { + std::string keyQuery = "pragma key='" + mKey.toStdString() + "'"; + mDatabase->exec(keyQuery.c_str()); + mDatabase->exec("pragma locking_mode=EXCLUSIVE"); + mDatabase->exec("pragma journal_mode=MEMORY"); + mDatabase->exec("pragma temp_store=MEMORY"); + } + catch(std::exception& e) + { + return false; + } #endif - try - { - // Timeline - mDatabase->exec("CREATE TABLE timeline (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, starttime TEXT, endtime TEXT, synctime TEXT)"); - mDatabase->exec("CREATE INDEX timeline_taskid_index ON timeline(taskid ASC)"); + try + { + // Timeline + mDatabase->exec("CREATE TABLE timeline (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, starttime TEXT, endtime TEXT, synctime TEXT)"); + mDatabase->exec("CREATE INDEX timeline_taskid_index ON timeline(taskid ASC)"); - // Tasks tree - mDatabase->exec("CREATE TABLE task (type INTEGER, removed INTEGER, modifytime TEXT, id INTEGER PRIMARY KEY, parentid INTEGER, orderid INTEGER, title TEXT, html TEXT, flags INTEGER, synctime1 TEXT, synctime2 TEXT)"); - mDatabase->exec("CREATE INDEX task_parentid_index ON task(parentid ASC)"); + // Tasks tree + mDatabase->exec("CREATE TABLE task (type INTEGER, removed INTEGER, modifytime TEXT, id INTEGER PRIMARY KEY, parentid INTEGER, orderid INTEGER, title TEXT, html TEXT, flags INTEGER, synctime1 TEXT, synctime2 TEXT)"); + mDatabase->exec("CREATE INDEX task_parentid_index ON task(parentid ASC)"); - // Attachments - mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, filename TEXT, content BLOB, orderid INTEGER, synctime TEXT)"); - mDatabase->exec("CREATE INDEX file_taskid_index ON file(taskid ASC)"); + // Attachments + mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, filename TEXT, content BLOB, orderid INTEGER, synctime TEXT)"); + mDatabase->exec("CREATE INDEX file_taskid_index ON file(taskid ASC)"); - // Sync status - mDatabase->exec("CREATE TABLE syncs (datetime TEXT, status TEXT)"); - } - catch(std::exception& e) - { - return false; - } + // Sync status + mDatabase->exec("CREATE TABLE syncs (datetime TEXT, status TEXT)"); + } + catch(std::exception& e) + { + return false; + } - return true; + return true; } bool Storage::open() { - assert(!mPath.isEmpty()); + assert(!mPath.isEmpty()); - try - { - mDatabase = QSharedPointer(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE)); - mDatabase->exec("pragma key='" + mKey.toStdString() + "'"); - SQLite::Statement q(*mDatabase, "select count(*) from sqlite_master"); - if (!q.executeStep()) - return false; - mDatabase->exec("pragma locking_mode=EXCLUSIVE"); - mDatabase->exec("pragma journal_mode=MEMORY"); - mDatabase->exec("pragma temp_store=MEMORY"); + try + { + mDatabase = QSharedPointer(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE)); + mDatabase->exec("pragma key='" + mKey.toStdString() + "'"); + SQLite::Statement q(*mDatabase, "select count(*) from sqlite_master"); + if (!q.executeStep()) + return false; + mDatabase->exec("pragma locking_mode=EXCLUSIVE"); + mDatabase->exec("pragma journal_mode=MEMORY"); + mDatabase->exec("pragma temp_store=MEMORY"); - } - catch(std::exception& e) - { - return false; - } + } + catch(std::exception& e) + { + return false; + } - loadTaskTree(); - return true; + loadTaskTree(); + return true; } void Storage::close() { - if (mDatabase) - { - mDatabase.clear(); - } + if (mDatabase) + { + mDatabase.clear(); + } } bool Storage::hasTable(const QString& tablename) { - SQLite::Statement tableQuery(*mDatabase, "SELECT name FROM sqlite_master WHERE type='table' AND name=:tablename"); - tableQuery.bind(":tablename", tablename.toStdString()); - return tableQuery.executeStep(); + SQLite::Statement tableQuery(*mDatabase, "SELECT name FROM sqlite_master WHERE type='table' AND name=:tablename"); + tableQuery.bind(":tablename", tablename.toStdString()); + return tableQuery.executeStep(); } bool Storage::upgrade() { - int currentVersion = 0; + int currentVersion = 0; - // Check if properties table is here - if (!mDatabase) - return false; + // Check if properties table is here + if (!mDatabase) + return false; - if (!hasTable("properties")) - { - // If not - create it and consider database version 0 - SQLite::Statement createQuery(*mDatabase, "create table properties(type INTEGER, value TEXT)"); - if (!createQuery.exec()) - return false; + if (!hasTable("properties")) + { + // If not - create it and consider database version 0 + SQLite::Statement createQuery(*mDatabase, "create table properties(type INTEGER, value TEXT)"); + if (!createQuery.exec()) + return false; - // Insert version 0 - SQLite::Statement insertVersionQuery(*mDatabase, "insert into properties(type, value) values(:proptype, :proptext)"); - insertVersionQuery.bind(":proptype", (int)DbProperty::Version); - insertVersionQuery.bind(":proptext", "0"); - } - else - { - // If yes - get database version from 'dbversion' key - SQLite::Statement versionQuery(*mDatabase, "select value from properties where type=:proptype"); - versionQuery.bind(":proptype", (int)DbProperty::Version); - if (!versionQuery.executeStep()) - return false; + // Insert version 0 + SQLite::Statement insertVersionQuery(*mDatabase, "insert into properties(type, value) values(:proptype, :proptext)"); + insertVersionQuery.bind(":proptype", (int)DbProperty::Version); + insertVersionQuery.bind(":proptext", "0"); + } + else + { + // If yes - get database version from 'dbversion' key + SQLite::Statement versionQuery(*mDatabase, "select value from properties where type=:proptype"); + versionQuery.bind(":proptype", (int)DbProperty::Version); + if (!versionQuery.executeStep()) + return false; - QString dbVersionText = versionQuery.getColumn(0).getText(""); - if (dbVersionText.isEmpty()) - return false; + QString dbVersionText = versionQuery.getColumn(0).getText(""); + if (dbVersionText.isEmpty()) + return false; - bool ok = false; - currentVersion = dbVersionText.toInt(&ok); - if (!ok) - return false; - } + bool ok = false; + currentVersion = dbVersionText.toInt(&ok); + if (!ok) + return false; + } - // Depending on obtained version upgrade database - switch (currentVersion) - { - case 0: - if (!upgradeFromVersion0()) - return false; - break; - } + // Depending on obtained version upgrade database + switch (currentVersion) + { + case 0: + if (!upgradeFromVersion0()) + return false; + break; + } - // Update version number after upgrade - SQLite::Statement updateVersion(*mDatabase, "update table properties set value=:proptext where type=:proptype"); - updateVersion.bind(":proptext", CURRENT_DBVERSION); - updateVersion.bind(":proptype", (int)DbProperty::Version); - updateVersion.exec(); + // Update version number after upgrade + SQLite::Statement updateVersion(*mDatabase, "update table properties set value=:proptext where type=:proptype"); + updateVersion.bind(":proptext", CURRENT_DBVERSION); + updateVersion.bind(":proptype", (int)DbProperty::Version); + updateVersion.exec(); - return true; + return true; } bool Storage::upgradeFromVersion0() { - /* + /* // Upgrade tasks table SQLite::Statement addEncryptedTitle(*mDatabase, "alter table task add column title_encrypted BLOB"); if (!addEncryptedTitle.exec()) @@ -287,94 +287,94 @@ bool Storage::upgradeFromVersion0() std::copy(task->children().begin(), task->children().end(), mTaskToUpgrade.begin() + tl); } */ - return true; + return true; } bool Storage::encryptTask(PTask task) { - return false; + return false; } bool Storage::encryptTaskContent(PTask task) { - + return false; } bool Storage::encryptTaskAttachment(PAttachment attachment) { - return false; + return false; } PTask Storage::createTask(int index) { - if (!mDatabase) - return PTask(); + if (!mDatabase) + return PTask(); - SQLite::Statement insertQuery(*mDatabase, "insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, NULL, :orderid, 0, 0)"); - insertQuery.bind(":title", ""); - insertQuery.bind(":html", ""); - insertQuery.bind(":orderid", index); - if (insertQuery.exec()) - { - PTask result(new Task()); - result->setId(database().getLastInsertRowid()); - result->setIndex(index); - mTaskModelIdMap[result->modelId()] = result; - mTaskIdMap[result->id()] = result; - if (index > mTopTasks.size()) - mTopTasks.push_back(result); - else + SQLite::Statement insertQuery(*mDatabase, "insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, NULL, :orderid, 0, 0)"); + insertQuery.bind(":title", ""); + insertQuery.bind(":html", ""); + insertQuery.bind(":orderid", index); + if (insertQuery.exec()) { - mTopTasks.insert(mTopTasks.begin() + index, result); - // Assign new indexes for top tasks - for (int i=0; isetIndex(i); - mTopTasks[i]->save(); - } + PTask result(new Task()); + result->setId(database().getLastInsertRowid()); + result->setIndex(index); + mTaskModelIdMap[result->modelId()] = result; + mTaskIdMap[result->id()] = result; + if (index > mTopTasks.size()) + mTopTasks.push_back(result); + else + { + mTopTasks.insert(mTopTasks.begin() + index, result); + // Assign new indexes for top tasks + for (int i=0; isetIndex(i); + mTopTasks[i]->save(); + } + } + return result; } - return result; - } - else - return PTask(); + else + return PTask(); } PTask Storage::createTask(PTask parent, int index) { - if (!parent) - return createTask(index); - if (!mDatabase) - return PTask(); + if (!parent) + return createTask(index); + if (!mDatabase) + return PTask(); - SQLite::Statement insertQuery(database(), "insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, :parentid, :orderid, 0, 0)"); - insertQuery.bind(":title", ""); - insertQuery.bind(":html", ""); - insertQuery.bind(":parentid", (sqlite3_int64)parent->id()); - insertQuery.bind(":orderid", index); - if (insertQuery.exec()) - { - PTask result(new Task()); - result->setId(database().getLastInsertRowid()); - result->setIndex(index); - result->setParent(parent); - if (index > parent->children().size()) - parent->children().push_back(result); - else + SQLite::Statement insertQuery(database(), "insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, :parentid, :orderid, 0, 0)"); + insertQuery.bind(":title", ""); + insertQuery.bind(":html", ""); + insertQuery.bind(":parentid", (sqlite3_int64)parent->id()); + insertQuery.bind(":orderid", index); + if (insertQuery.exec()) { - parent->children().insert(parent->children().begin() + index, result); - for (int i=0; ichildren().size(); i++) - { - parent->children()[i]->setIndex(i); - parent->children()[i]->save(); - } - } + PTask result(new Task()); + result->setId(database().getLastInsertRowid()); + result->setIndex(index); + result->setParent(parent); + if (index > parent->children().size()) + parent->children().push_back(result); + else + { + parent->children().insert(parent->children().begin() + index, result); + for (int i=0; ichildren().size(); i++) + { + parent->children()[i]->setIndex(i); + parent->children()[i]->save(); + } + } - mTaskModelIdMap[result->modelId()] = result; - mTaskIdMap[result->id()] = result; - return result; - } - return PTask(); + mTaskModelIdMap[result->modelId()] = result; + mTaskIdMap[result->id()] = result; + return result; + } + return PTask(); } /* PTask Storage::loadTask(Task::Id id, PTask parent) @@ -391,264 +391,264 @@ PTask Storage::loadTask(Task::Id id, PTask parent) void Storage::saveTask(PTask task, Depth depth) { - if (depth == depthSingleTask) - saveSingleTask(task); - else - { - saveSingleTask(task); - TaskArray& children = task->children(); - foreach(PTask child, children) - saveTask(child, depthRecursive); - } + if (depth == depthSingleTask) + saveSingleTask(task); + else + { + saveSingleTask(task); + TaskArray& children = task->children(); + foreach(PTask child, children) + saveTask(child, depthRecursive); + } } bool Storage::moveTask(PTask task, PTask newParent, int indexToInsert) { - bool result = false; - task->setParent(newParent); - if (newParent) - { - SQLite::Statement updateParent(database(), "update task set parentid=:parentid where id=:id"); - updateParent.bind(":parentid", (sqlite3_int64)newParent->id()); - updateParent.bind(":id", (sqlite3_int64)task->id()); - result = updateParent.exec(); - - // Insert into children list - TaskArray& children = newParent->children(); - if (indexToInsert >= children.size()) - children.push_back(task); - else - children.insert(indexToInsert, task); - for (int i=0; isetParent(newParent); + if (newParent) { - children[i]->setIndex(i); - children[i]->save(); + SQLite::Statement updateParent(database(), "update task set parentid=:parentid where id=:id"); + updateParent.bind(":parentid", (sqlite3_int64)newParent->id()); + updateParent.bind(":id", (sqlite3_int64)task->id()); + result = updateParent.exec(); + + // Insert into children list + TaskArray& children = newParent->children(); + if (indexToInsert >= children.size()) + children.push_back(task); + else + children.insert(indexToInsert, task); + for (int i=0; isetIndex(i); + children[i]->save(); + } } - } - else - { - SQLite::Statement updateToRoot(database(), "update task set parentid = NULL where id=:id"); - updateToRoot.bind(":id", (sqlite3_int64)task->id()); - result = updateToRoot.exec(); + else + { + SQLite::Statement updateToRoot(database(), "update task set parentid = NULL where id=:id"); + updateToRoot.bind(":id", (sqlite3_int64)task->id()); + result = updateToRoot.exec(); + if (result) + task->setParentId(0); + + // Insert into root list + if (topOfTaskTree().size() > indexToInsert) + topOfTaskTree().insert(indexToInsert, task); + else + topOfTaskTree().push_back(task); + + for (int i=0; isetIndex(i); + topOfTaskTree()[i]->save(); + } + } + if (result) - task->setParentId(0); - - // Insert into root list - if (topOfTaskTree().size() > indexToInsert) - topOfTaskTree().insert(indexToInsert, task); - else - topOfTaskTree().push_back(task); - - for (int i=0; isetIndex(i); - topOfTaskTree()[i]->save(); + task->save(); } - } - if (result) - { - task->save(); - } - - return result; + return result; } bool Storage::isOpened() { - return mDatabase != nullptr; + return mDatabase != nullptr; } void Storage::save() { - foreach (PTask task, mTopTasks) - saveTask(task, depthRecursive); + foreach (PTask task, mTopTasks) + saveTask(task, depthRecursive); - database().exec("delete from task where removed = 1"); - database().exec("delete from timeline where removed = 1"); - database().exec("delete from file where removed = 1"); - //database().exec("delete from change where removed = 1"); + database().exec("delete from task where removed = 1"); + database().exec("delete from timeline where removed = 1"); + database().exec("delete from file where removed = 1"); + //database().exec("delete from change where removed = 1"); } void Storage::saveSingleTask(PTask task) { - task->save(); + task->save(); } void Storage::loadTaskTree() { - mTopTasks.clear(); + mTopTasks.clear(); - SQLite::Statement q(database(), "select id, title, orderid, flags, (select count(*) from file where (file.taskid = task.id) and ((file.removed is null) or (file.removed = 0))) from task where (parentid is null) and ((removed != 1) or (removed is null)) order by orderid"); - int currentIndex = 0; - while(q.executeStep()) - { - PTask t(new Task()); - t->load(q); - t->setIndex(currentIndex++); - mTaskModelIdMap[t->modelId()] = t; - mTaskIdMap[t->id()] = t; - mTopTasks.push_back(t); - } + SQLite::Statement q(database(), "select id, title, orderid, flags, (select count(*) from file where (file.taskid = task.id) and ((file.removed is null) or (file.removed = 0))) from task where (parentid is null) and ((removed != 1) or (removed is null)) order by orderid"); + int currentIndex = 0; + while(q.executeStep()) + { + PTask t(new Task()); + t->load(q); + t->setIndex(currentIndex++); + mTaskModelIdMap[t->modelId()] = t; + mTaskIdMap[t->id()] = t; + mTopTasks.push_back(t); + } - foreach (PTask task, mTopTasks) - loadTaskChildren(task); + foreach (PTask task, mTopTasks) + loadTaskChildren(task); } void Storage::loadTaskChildren(PTask task) { - SQLite::Statement q(database(), "select id, title, orderid, flags, (select count(*) from file where (file.taskid = task.id) and ((file.removed is null) or (file.removed = 0))) from task where (task.parentid = :parentid) and ((task.removed != 1) or (task.removed is null)) order by task.orderid"); - q.bind(":parentid", (sqlite3_int64)task->id()); - int currentIndex = 0; - while (q.executeStep()) - { - PTask t(new Task()); - t->load(q); - t->setIndex(currentIndex++); - t->setParent(task, false); - loadTaskChildren(t); - mTaskModelIdMap[t->modelId()] = t; - mTaskIdMap[t->id()] = t; - task->children().push_back(t); - } + SQLite::Statement q(database(), "select id, title, orderid, flags, (select count(*) from file where (file.taskid = task.id) and ((file.removed is null) or (file.removed = 0))) from task where (task.parentid = :parentid) and ((task.removed != 1) or (task.removed is null)) order by task.orderid"); + q.bind(":parentid", (sqlite3_int64)task->id()); + int currentIndex = 0; + while (q.executeStep()) + { + PTask t(new Task()); + t->load(q); + t->setIndex(currentIndex++); + t->setParent(task, false); + loadTaskChildren(t); + mTaskModelIdMap[t->modelId()] = t; + mTaskIdMap[t->id()] = t; + task->children().push_back(t); + } } PTask Storage::findTaskByModelId(Task::ModelId id) { - auto taskIter = mTaskModelIdMap.find(id); - if (taskIter != mTaskModelIdMap.end()) - return *taskIter; - else - return PTask(); + auto taskIter = mTaskModelIdMap.find(id); + if (taskIter != mTaskModelIdMap.end()) + return *taskIter; + else + return PTask(); } PTask Storage::findTaskById(Task::Id id) { - auto taskIter = mTaskIdMap.find(id); - if (taskIter != mTaskIdMap.end()) - return *taskIter; - else - return PTask(); + auto taskIter = mTaskIdMap.find(id); + if (taskIter != mTaskIdMap.end()) + return *taskIter; + else + return PTask(); } int Storage::findTaskIndexInParent(PTask task) { - if (task->parent()) - return task->parent()->children().indexOf(task); - else - return topOfTaskTree().indexOf(task); + if (task->parent()) + return task->parent()->children().indexOf(task); + else + return topOfTaskTree().indexOf(task); } TaskArray& Storage::topOfTaskTree() { - return mTopTasks; + return mTopTasks; } void Storage::loadAttachments(PTask task, AttachmentArray& output) { - SQLite::Statement q(database(), "select id, filename, orderid from file where (taskid = :taskid) and ((removed != 1) or (removed is null)) order by orderid"); - q.bind(":taskid", (sqlite3_int64)task->id()); - while (q.executeStep()) - { - PAttachment att(new Attachment()); - att->setId(q.getColumn(0).getInt64()); - att->setFilename(q.getColumn(1).getText()); - att->setTaskId(task->id()); - att->setIndex(q.getColumn(2).getInt()); - output.push_back(att); - } + SQLite::Statement q(database(), "select id, filename, orderid from file where (taskid = :taskid) and ((removed != 1) or (removed is null)) order by orderid"); + q.bind(":taskid", (sqlite3_int64)task->id()); + while (q.executeStep()) + { + PAttachment att(new Attachment()); + att->setId(q.getColumn(0).getInt64()); + att->setFilename(q.getColumn(1).getText()); + att->setTaskId(task->id()); + att->setIndex(q.getColumn(2).getInt()); + output.push_back(att); + } } void Storage::deleteAttachment(PAttachment att) { - SQLite::Statement q(database(), "update file set removed=1 where id=:id"); - q.bind(":id", (sqlite3_int64)att->id()); - q.exec(); + SQLite::Statement q(database(), "update file set removed=1 where id=:id"); + q.bind(":id", (sqlite3_int64)att->id()); + q.exec(); } void Storage::undeleteAttachment(PAttachment att) { - SQLite::Statement q(database(), "update file set removed=0 where id=:id"); - q.bind(":id", (sqlite3_int64)att->id()); - q.exec(); + SQLite::Statement q(database(), "update file set removed=0 where id=:id"); + q.bind(":id", (sqlite3_int64)att->id()); + q.exec(); } Storage& Storage::instance() { - static Storage _instance; - return _instance; + static Storage _instance; + return _instance; } void Storage::deleteTask(PTask task, DeleteOption option) { - if (option != DeleteOption_FromParent) - { - // Remove from hash - removeTask(task); - } + if (option != DeleteOption_FromParent) + { + // Remove from hash + removeTask(task); + } - TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks; - siblings.removeOne(task); + TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks; + siblings.removeOne(task); - // Remove from database - if (option == DeleteOption_Total) - { - SQLite::Statement q(database(), "update task set removed = 1 where id = :id"); - q.bind(":id", (sqlite3_int64)task->id()); - q.exec(); - } + // Remove from database + if (option == DeleteOption_Total) + { + SQLite::Statement q(database(), "update task set removed = 1 where id = :id"); + q.bind(":id", (sqlite3_int64)task->id()); + q.exec(); + } #if 0 - int taskIndex = -1; - // Update indexes - for (int i=0; i task->index()) + int taskIndex = -1; + // Update indexes + for (int i=0; i= 0) - siblings.remove(taskIndex); + Task& t = *siblings[i]; + if (t.index() > task->index()) + { + t.setIndex(t.index() - 1); + t.save(); + } + } + + // Remove from tree + if (taskIndex >= 0) + siblings.remove(taskIndex); #endif } void Storage::undeleteTask(PTask task) { - SQLite::Statement q(database(), "update task set removed = 0 where id = :id"); - q.bind(":id", (sqlite3_int64)task->id()); - q.exec(); + SQLite::Statement q(database(), "update task set removed = 0 where id = :id"); + q.bind(":id", (sqlite3_int64)task->id()); + q.exec(); - // Find place where to insert task - TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks; + // Find place where to insert task + TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks; - TaskArray::iterator taskIter = std::lower_bound(siblings.begin(), siblings.end(), task->index(), [] (const PTask& t, int index) { return t->index() < index;}); - if (taskIter != siblings.end()) - siblings.insert(taskIter, task); - else - siblings.append(task); + TaskArray::iterator taskIter = std::lower_bound(siblings.begin(), siblings.end(), task->index(), [] (const PTask& t, int index) { return t->index() < index;}); + if (taskIter != siblings.end()) + siblings.insert(taskIter, task); + else + siblings.append(task); - mTaskModelIdMap[task->modelId()] = task; - mTaskIdMap[task->id()] = task; + mTaskModelIdMap[task->modelId()] = task; + mTaskIdMap[task->id()] = task; } void Storage::removeTask(PTask task) { - auto taskModelIter = mTaskModelIdMap.find(task->modelId()); - if (taskModelIter != mTaskModelIdMap.end()) - mTaskModelIdMap.erase(taskModelIter); + auto taskModelIter = mTaskModelIdMap.find(task->modelId()); + if (taskModelIter != mTaskModelIdMap.end()) + mTaskModelIdMap.erase(taskModelIter); - auto taskIter = mTaskIdMap.find(task->id()); - if (taskIter != mTaskIdMap.end()) - mTaskIdMap.erase(taskIter); + auto taskIter = mTaskIdMap.find(task->id()); + if (taskIter != mTaskIdMap.end()) + mTaskIdMap.erase(taskIter); } diff --git a/client/task.cpp b/client/task.cpp index 7eb54e4..5e5e7ae 100644 --- a/client/task.cpp +++ b/client/task.cpp @@ -1,8 +1,14 @@ #include "task.h" #include "storage.h" +#include "helper.h" + #include #include +#if defined(TARGET_OSX) || defined(TARGET_LINUX) +# include +#endif + // -------- WorldId ------ WorldId::WorldId() { @@ -10,11 +16,11 @@ WorldId::WorldId() WorldId::WorldId(const WorldId& src) { - mId = src.mId; + mId = src.mId; } -WorldId::WorldId(const QString &s) - :mId(s) +WorldId::WorldId(const std::string &s) + :mId(s) { } @@ -24,133 +30,146 @@ WorldId::~WorldId() WorldId& WorldId::operator = (const WorldId& src) { - mId = src.mId; - return *this; + mId = src.mId; + return *this; } bool WorldId::operator == (const WorldId& src) { - return mId.isNull() ? false : (mId == src.mId); + return mId == src.mId; } bool WorldId::operator < (const WorldId& src) { - return mId.isNull() ? true : (mId < src.mId); + return mId < src.mId; } -QString WorldId::asString() const +std::string WorldId::asString() const { - return mId.isNull() ? QString() : mId.toString(); + return mId; } WorldId WorldId::create() { - WorldId result; - result.mId = QUuid::createUuid(); - return result; + WorldId result; +#if defined(TARGET_OSX) || defined(TARGET_LINUX) + uuid_t t; + uuid_generate(t); + char buffer[128]; + uuid_unparse(t, buffer); + result.mId = buffer; +#endif + +#if defined(TARGET_WIN) +#endif + + return result; } // -------- TimeRecord ---------- TimeRecord::TimeRecord() - :mId(0), mTaskId(0), mSaved(false) + :mId(0), mTaskId(0), mSaved(false) {} -TimeRecord::TimeRecord(const QDateTime &startTime, const QDateTime &endTime, Id taskId) - :mId(0), mTaskId(taskId), mSaved(false), mStartTime(startTime), mEndTime(endTime) +TimeRecord::TimeRecord(const time_t &startTime, const time_t &endTime, Id taskId) + :mId(0), mTaskId(taskId), mSaved(false), mStartTime(startTime), mEndTime(endTime) { } TimeRecord::~TimeRecord() {} -QDateTime TimeRecord::endTime() const +time_t TimeRecord::endTime() const { - return mEndTime; + return mEndTime; } -void TimeRecord::setEndTime(const QDateTime &endTime) +void TimeRecord::setEndTime(const time_t &endTime) { - mEndTime = endTime; + mEndTime = endTime; } -QDateTime TimeRecord::startTime() const +time_t TimeRecord::startTime() const { - return mStartTime; + return mStartTime; } -void TimeRecord::setStartTime(const QDateTime &startTime) +void TimeRecord::setStartTime(const time_t &startTime) { - mStartTime = startTime; + mStartTime = startTime; } int TimeRecord::length() { - return mStartTime.secsTo(mEndTime); + return (int)(mEndTime - mStartTime); } Id TimeRecord::id() const { - return mId; + return mId; } void TimeRecord::setId(Id id) { - mId = id; + mId = id; } Id TimeRecord::taskId() const { - return mTaskId; + return mTaskId; } void TimeRecord::setTaskId(Id id) { - mTaskId = id; + mTaskId = id; } WorldId TimeRecord::worldId() const { - return mWorldId; + return mWorldId; } void TimeRecord::setWorldId(const WorldId& id) { - mWorldId = id; + mWorldId = id; } void TimeRecord::save() { - if (!mId) - { - SQLite::Statement q(Storage::instance().database(), "insert into timeline(id, starttime, endtime, taskid, removed) values (NULL, :starttime, :endtime, :taskid, :removed)"); - q.bind(":starttime", mStartTime.toString(Qt::ISODate).toStdString()); - q.bind(":endtime", mEndTime.toString(Qt::ISODate).toStdString()); - q.bind(":taskid", (sqlite3_int64)mTaskId); - q.bind(":removed", 0); - if (q.exec()) - mId = Storage::instance().database().getLastInsertRowid(); - } - else - { - SQLite::Statement q(Storage::instance().database(), "update timeline set starttime = :starttime, endtime = :endtime, taskid = :taskid, removed = 0 where id = :id"); - q.bind(":starttime", mStartTime.toString(Qt::ISODate).toStdString()); - q.bind(":endtime", mEndTime.toString(Qt::ISODate).toStdString()); - q.bind(":taskid", (sqlite3_int64)mTaskId); - q.bind(":id", (sqlite3_int64)mId); - q.exec(); - } + if (!mId) + { + SQLite::Statement q(Storage::instance().database(), "insert into timeline(id, starttime, endtime, taskid, removed) values (NULL, :starttime, :endtime, :taskid, :removed)"); + + + q.bind(":starttime", TimeHelper::timeToStr(mStartTime)); + q.bind(":endtime", TimeHelper::timeToStr(mEndTime)); + q.bind(":taskid", (sqlite3_int64)mTaskId); + q.bind(":removed", 0); + if (q.exec()) + mId = Storage::instance().database().getLastInsertRowid(); + } + else + { + SQLite::Statement q(Storage::instance().database(), + "update timeline set starttime = :starttime, endtime = :endtime, taskid = :taskid, removed = 0 where id = :id"); + q.bind(":starttime", TimeHelper::timeToStr(mStartTime)); + q.bind(":endtime", TimeHelper::timeToStr(mEndTime)); + q.bind(":taskid", (sqlite3_int64)mTaskId); + q.bind(":id", (sqlite3_int64)mId); + q.exec(); + } } void TimeRecord::deleteRecord() { - SQLite::Statement q(Storage::instance().database(), "update timeline set removed = 1 where id = :id"); - q.bind(":id", (sqlite3_int64)mId); - q.exec(); + SQLite::Statement q(Storage::instance().database(), "update timeline set removed = 1 where id = :id"); + q.bind(":id", (sqlite3_int64)mId); + q.exec(); } // -------------------- TimeLine -------------------- TimeLine::TimeLine() - :mTaskId(0), mActiveTimeRecord(nullptr), mTotalTime(0), mActive(false) + :mTaskId(0), mActiveTimeRecord(nullptr), mTotalTime(0), mActive(false) {} TimeLine::~TimeLine() @@ -158,595 +177,609 @@ TimeLine::~TimeLine() int TimeLine::findTotalTime() { - int result = 0; - for(auto timeIter = mData.begin(); timeIter != mData.end(); timeIter++) - { - int delta = timeIter->startTime().secsTo(timeIter->endTime()); - result += delta + 1; - } - return result; + int result = 0; + for(auto& t: mData.begin()) + { + int delta = t.endTime() - t.startTime(); + result += delta + 1; + } + return result; } int TimeLine::totalTime() { - return mTotalTime; + return mTotalTime; } bool TimeLine::active() { - return mActive; + return mActive; } void TimeLine::start() { - if (mActive) - return; + if (mActive) + return; - // Mark timeline as active - it means it records time interval now - mActive = true; + // Mark timeline as active - it means it records time interval now + mActive = true; - // Find current time in UTC format - QDateTime current = QDateTime::currentDateTimeUtc(); + // Find current time in UTC format + time_t current = time(nullptr); - // Check if current time point does not belong to any existing time interval - if (hasTimePoint(current)) - mActiveTimeRecord = nullptr; - else - mActiveTimeRecord = makeNewRecord(current, current); + // Check if current time point does not belong to any existing time interval + if (hasTimePoint(current)) + mActiveTimeRecord = nullptr; + else + mActiveTimeRecord = makeNewRecord(current, current); } -TimeRecord* TimeLine::makeNewRecord(const QDateTime& beginTime, const QDateTime& endTime) +TimeRecord* TimeLine::makeNewRecord(time_t beginTime, time_t endTime) { - TimeRecord tr; - tr.setStartTime(beginTime); - tr.setEndTime(endTime); - tr.setTaskId(mTaskId); - tr.save(); + TimeRecord tr; + tr.setStartTime(beginTime); + tr.setEndTime(endTime); + tr.setTaskId(mTaskId); + tr.save(); - mData.push_back(tr); - Id intervalId = tr.id(); - sortData(); + mData.push_back(tr); + Id intervalId = tr.id(); + sortData(); - std::reverse_iterator intervalIter = std::find_if(std::reverse_iterator(mData.end()), - std::reverse_iterator(mData.begin()), - [=] (const TimeRecord& tr) - {return tr.id() == intervalId;}); - if (intervalIter != std::reverse_iterator(mData.begin())) - return &(*intervalIter); - else - return nullptr; + std::reverse_iterator intervalIter = std::find_if(std::reverse_iterator(mData.end()), + std::reverse_iterator(mData.begin()), + [=] (const TimeRecord& tr) + { + return tr.id() == intervalId; + }); + + if (intervalIter != std::reverse_iterator(mData.begin())) + return &(*intervalIter); + else + return nullptr; } -TimeRecord* TimeLine::hasTimePoint(const QDateTime& t) +TimeRecord* TimeLine::hasTimePoint(time_t t) { - if (mData.empty()) - return nullptr; + if (mData.empty()) + return nullptr; - TimeRecord* tr = nullptr; - TimeArray::iterator result; - result = std::upper_bound(mData.begin(), mData.end(), t, - [](const QDateTime& t2, const TimeRecord& tr) - { return tr.startTime() > t2;}); + TimeRecord* tr = nullptr; + TimeArray::iterator result; + result = std::upper_bound(mData.begin(), mData.end(), t, + [](time_t t2, const TimeRecord& tr) + { + return tr.startTime() > t2; + }); - if (result == mData.end()) - tr = &mData.last(); - else - if (result != mData.begin()) - tr = &(*(--result)); - else - return nullptr; + if (result == mData.end()) + tr = &mData.last(); + else + if (result != mData.begin()) + tr = &(*(--result)); + else + return nullptr; - return (tr->startTime() <= t && tr->endTime() >= t) ? tr : nullptr; + return (tr->startTime() <= t && tr->endTime() >= t) ? tr : nullptr; } void TimeLine::sortData() { - std::sort(mData.begin(), mData.end(), [](const TimeRecord& lhs, const TimeRecord& rhs) { return lhs.startTime() < rhs.startTime();}); + std::sort(mData.begin(), mData.end(), [](const TimeRecord& lhs, const TimeRecord& rhs) { return lhs.startTime() < rhs.startTime();}); } bool TimeLine::hasIntersection(const TimeRecord &interval) { - if (mData.empty()) + if (mData.empty()) + return false; + + TimeArray::iterator result; + result = std::upper_bound(mData.begin(), mData.end(), interval.startTime(), + [](time_t t2, const TimeRecord& tr) + { return tr.startTime() > t2;}); + + if (result == mData.end()) + { + // There is time record which start point is lesser than interval.startTime() + return (mData.last().endTime() >= interval.startTime()); + } + else + if (result != mData.begin()) + { + TimeRecord& prev = *(result-1); + TimeRecord& next = *result; + if (prev.endTime() >= interval.startTime()) + return true; + if (next.startTime() <= interval.endTime()) + return true; + } + else + { + if (mData.front().startTime() <= interval.endTime()) + return true; + } return false; - - TimeArray::iterator result; - result = std::upper_bound(mData.begin(), mData.end(), interval.startTime(), - [](const QDateTime& t2, const TimeRecord& tr) - { return tr.startTime() > t2;}); - - if (result == mData.end()) - { - // There is time record which start point is lesser than interval.startTime() - return (mData.last().endTime() >= interval.startTime()); - } - else - if (result != mData.begin()) - { - TimeRecord& prev = *(result-1); - TimeRecord& next = *result; - if (prev.endTime() >= interval.startTime()) - return true; - if (next.startTime() <= interval.endTime()) - return true; - } - else - { - if (mData.front().startTime() <= interval.endTime()) - return true; - } - return false; } void TimeLine::insertInterval(const TimeRecord &interval) { - mData.push_back(interval); - mData.last().save(); + mData.push_back(interval); + mData.last().save(); - sortData(); + sortData(); } bool TimeLine::removeInterval(const TimeRecord &interval) { - // Find interval by binary search - if (mData.empty()) + // Find interval by binary search + if (mData.empty()) + return false; + + TimeArray::iterator result; + result = std::upper_bound(mData.begin(), mData.end(), interval.startTime(), + [](time_t t2, const TimeRecord& tr) + { return tr.startTime() > t2;}); + + if (result == mData.end()) + { + // There is time record which start point is lesser than interval.startTime() + if (mData.last().id() == interval.id()) + { + // Remove from DB + mData.back().deleteRecord(); + + // Remove from memory + mData.erase(mData.begin() + mData.size() - 1); + + return true; + } + } + else + if (result != mData.begin()) + { + result--; + if (result->id() == interval.id()) + { + // Remove from DB + result->deleteRecord(); + + // Remove from memory + mData.erase(result); + + return true; + } + } return false; - - TimeArray::iterator result; - result = std::upper_bound(mData.begin(), mData.end(), interval.startTime(), - [](const QDateTime& t2, const TimeRecord& tr) - { return tr.startTime() > t2;}); - - if (result == mData.end()) - { - // There is time record which start point is lesser than interval.startTime() - if (mData.last().id() == interval.id()) - { - // Remove from DB - mData.back().deleteRecord(); - - // Remove from memory - mData.erase(mData.begin() + mData.size() - 1); - - return true; - } - } - else - if (result != mData.begin()) - { - result--; - if (result->id() == interval.id()) - { - // Remove from DB - result->deleteRecord(); - - // Remove from memory - mData.erase(result); - - return true; - } - } - return false; } void TimeLine::cutInterval(const TimeRecord& interval) { - // Find interval that startTime() time is greater than interval.endTime() - TimeArray::iterator iter; - iter = std::upper_bound(mData.begin(), mData.end(), interval.endTime(), - [](const QDateTime& t2, const TimeRecord& tr) - { return tr.startTime() > t2;}); + // Find interval that startTime() time is greater than interval.endTime() + TimeArray::iterator iter; + iter = std::upper_bound(mData.begin(), mData.end(), interval.endTime(), + [](time_t t2, const TimeRecord& tr) + { return tr.startTime() > t2;}); - // If all intervals begins later() than requested interval - exit silently - if (iter == mData.begin()) - return; - - // Go to previous interval - it is first interval to check - if (iter == mData.end()) - iter = mData.begin() + mData.size() - 1; - else - iter--; - - bool done = false; - // Loop while current interval end time is greater than cut interval start time - while (iter->endTime() >= interval.startTime()) - { - if (iter->endTime() > interval.endTime()) - { - if (iter->startTime() >= interval.startTime()) - { - iter->setStartTime(iter->endTime().addSecs(1)); // Current interval starts in [interval.startTime(), interval.endTime()], but finishes later - iter->save(); - } - else - { - // Current interval starts before interval.startTime() and finishes later - // So cut interval will split it to 2 new intervals - // Also this operation will end loop - TimeRecord toInsert(*iter); // Backup current interval - iter->setEndTime(interval.startTime().addSecs(-1)); - iter->save(); - toInsert.setStartTime(interval.endTime().addSecs(1)); - mData.insert(++iter, toInsert); - toInsert.save(); - done = true; - break; - } - } - else - { - if (iter->startTime() >= interval.startTime()) - { - iter->deleteRecord(); - iter = mData.erase(iter); // Current interval fits into cut interval - } - else - { - // Current interval starts before cut interval but finishes in cut interval - iter->setEndTime(interval.startTime().addSecs(-1)); - iter->save(); - done = true; - break; - } - } + // If all intervals begins later() than requested interval - exit silently if (iter == mData.begin()) - break; - iter--; - } + return; - // Look for exact the same interval as specified one - if (!done) - { - iter = std::find_if(mData.begin(), mData.end(), [=] (const TimeRecord& tr) { return tr.id() == interval.id();}); - if (iter != mData.end()) - mData.erase(iter); - } + // Go to previous interval - it is first interval to check + if (iter == mData.end()) + iter = mData.begin() + mData.size() - 1; + else + iter--; + + bool done = false; + // Loop while current interval end time is greater than cut interval start time + while (iter->endTime() >= interval.startTime()) + { + if (iter->endTime() > interval.endTime()) + { + if (iter->startTime() >= interval.startTime()) + { + iter->setStartTime(iter->endTime() + 1); // Current interval starts in [interval.startTime(), interval.endTime()], but finishes later + iter->save(); + } + else + { + // Current interval starts before interval.startTime() and finishes later + // So cut interval will split it to 2 new intervals + // Also this operation will end loop + TimeRecord toInsert(*iter); // Backup current interval + iter->setEndTime(interval.startTime() - 1); + iter->save(); + toInsert.setStartTime(interval.endTime() + 1); + mData.insert(++iter, toInsert); + toInsert.save(); + done = true; + break; + } + } + else + { + if (iter->startTime() >= interval.startTime()) + { + iter->deleteRecord(); + iter = mData.erase(iter); // Current interval fits into cut interval + } + else + { + // Current interval starts before cut interval but finishes in cut interval + iter->setEndTime(interval.startTime() - 1); + iter->save(); + done = true; + break; + } + } + if (iter == mData.begin()) + break; + iter--; + } + + // Look for exact the same interval as specified one + if (!done) + { + iter = std::find_if(mData.begin(), mData.end(), + [=] (const TimeRecord& tr) + { return tr.id() == interval.id();}); + if (iter != mData.end()) + mData.erase(iter); + } } TimeRecord* TimeLine::findIntervalById(Id id) { - TimeArray::iterator iter = std::find_if(mData.begin(), mData.end(), [=] (const TimeRecord& tr) { return tr.id() == id;}); - if (iter == mData.end()) - return nullptr; - return &(*iter); + TimeArray::iterator iter = std::find_if(mData.begin(), mData.end(), + [=] (const TimeRecord& tr) + { return tr.id() == id;}); + if (iter == mData.end()) + return nullptr; + return &(*iter); } void TimeLine::stop(bool updateTimeline) { - if (!mActive) - return; + if (!mActive) + return; - if (updateTimeline) - flush(true, QDateTime::currentDateTimeUtc()); + if (updateTimeline) + flush(true, time(nullptr)); - mActive = false; - mActiveTimeRecord = nullptr; + mActive = false; + mActiveTimeRecord = nullptr; } -void TimeLine::flush(bool saveToDb, const QDateTime& currentUtc) +void TimeLine::flush(bool saveToDb, time_t currentUtc) { - if (!mActive) - return; + if (!mActive) + return; - if (mActiveTimeRecord) - { - int delta = mActiveTimeRecord->endTime().secsTo(currentUtc); - mActiveTimeRecord->setEndTime(currentUtc); - - TimeRecord* tr = hasTimePoint(currentUtc); - if (tr && tr != mActiveTimeRecord) + if (mActiveTimeRecord) { - mActiveTimeRecord->setEndTime(currentUtc); - mActiveTimeRecord->save(); - mActiveTimeRecord = nullptr; - } - if (saveToDb && mActiveTimeRecord) - mActiveTimeRecord->save(); + int delta = currentUtc - mActiveTimeRecord->endTime(); + mActiveTimeRecord->setEndTime(currentUtc); - mTotalTime += delta; - } - else - { - if (!hasTimePoint(currentUtc)) - { - // Start new record here - mActiveTimeRecord = makeNewRecord(currentUtc, currentUtc); + TimeRecord* tr = hasTimePoint(currentUtc); + if (tr && tr != mActiveTimeRecord) + { + mActiveTimeRecord->setEndTime(currentUtc); + mActiveTimeRecord->save(); + mActiveTimeRecord = nullptr; + } + if (saveToDb && mActiveTimeRecord) + mActiveTimeRecord->save(); + + mTotalTime += delta; + } + else + { + if (!hasTimePoint(currentUtc)) + { + // Start new record here + mActiveTimeRecord = makeNewRecord(currentUtc, currentUtc); + } } - } } void TimeLine::load() { - SQLite::Statement q(Storage::instance().database(), "select id, starttime, endtime from timeline where (taskid = :taskid) and ((removed is null) or (removed != 1)) order by id asc"); - q.bind(":taskid", (sqlite3_int64)mTaskId); - while (q.executeStep()) - { - QDateTime start = QDateTime::fromString(q.getColumn(1).getText(), Qt::ISODate); - start.setTimeSpec(Qt::UTC); - QDateTime stop = QDateTime::fromString(q.getColumn(2).getText(), Qt::ISODate); - stop.setTimeSpec(Qt::UTC); + SQLite::Statement q(Storage::instance().database(), "select id, starttime, endtime from timeline where (taskid = :taskid) and ((removed is null) or (removed != 1)) order by id asc"); + q.bind(":taskid", (sqlite3_int64)mTaskId); + while (q.executeStep()) + { + time_t start = TimeHelper::strToTime(q.getColumn(1).getText()); + time_t stop = TimeHelper::strToTime(q.getColumn(2).getText()); - TimeRecord tr; - tr.setId(q.getColumn(0).getInt64()); - tr.setStartTime(start); - tr.setEndTime(stop); - tr.setTaskId(mTaskId); - mData.push_back(tr); - } + TimeRecord tr; + tr.setId(q.getColumn(0).getInt64()); + tr.setStartTime(start); + tr.setEndTime(stop); + tr.setTaskId(mTaskId); + mData.push_back(tr); + } - // Sort time intervals - sortData(); + // Sort time intervals + sortData(); - // Find current total time length - mTotalTime = findTotalTime(); + // Find current total time length + mTotalTime = findTotalTime(); } void TimeLine::save() { - // No need to save anything here - everything is saved right on creation/update + // No need to save anything here - everything is saved right on creation/update } Id TimeLine::taskId() { - return mTaskId; + return mTaskId; } void TimeLine::setTaskId(Id id) { - mTaskId = id; + mTaskId = id; } TimeArray& TimeLine::data() { - return mData; + return mData; } void TimeLine::getYears(std::set& years) { - if (mData.empty()) - return; + if (mData.empty()) + return; - // Find lower bound of years - it is first time record - int year1 = mData.front().startTime().toLocalTime().date().year(); + struct tm t1, t2; + localtime(&mData.front().startTime(), &t1); + localtime(&mData.back().startTime(), &t2); - years.insert(year1); + // Find lower bound of years - it is first time record + int year1 = t1.tm_year + 1900; + years.insert(year1); - // Find higher bound of years - it is last time record - int year2 = mData.back().endTime().toLocalTime().date().year(); + // Find higher bound of years - it is last time record + int year2 = t2.tm_year + 1900; - if (year1 == year2) - return; + if (year1 == year2) + return; - // Try to find next year by binary search - for (int year = year1+1; year <= year2; year++) - { - QDate yearStart(year, 1, 1); - TimeArray::iterator iter = std::lower_bound(mData.begin(), mData.end(), yearStart, [&](const TimeRecord& lhs, const QDate& rhs) { return lhs.endTime().toLocalTime().date() < rhs; }); - if (iter != mData.end()) + // Try to find next year by binary search + for (int year = year1+1; year <= year2; year++) { - if (iter->startTime().toLocalTime().date().year() <= year) - years.insert(year); + QDate yearStart(year, 1, 1); + TimeArray::iterator iter = std::lower_bound(mData.begin(), mData.end(), yearStart, + [&](const TimeRecord& lhs, time_t rhs) + { return lhs.endTime() < rhs; }); + if (iter != mData.end()) + { + // Get current year + struct tm ct; localtime(&iter->startTime(), &ct); + if (ct.tm_year + 1900 <= year) + years.insert(year); + } } - } } void TimeLine::getMonthes(int year, std::set& result) { - for (int month = 1; month <= 12; month++) - { - QDate monthBegin(year, month, 1); - QDate monthEnd(year, month, monthBegin.daysInMonth()); - - // Find range for every month in year [lowest, higher) - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), monthBegin, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); - //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), monthEnd, [] (const TimeRecord& tr, const QDate& d) { return tr.startTime().toLocalTime().date() < d;}); - if (lowest != mData.end()) + for (int month = 1; month <= 12; month++) { - int currentYear = lowest->startTime().toLocalTime().date().year(); - int currentMonth = lowest->startTime().toLocalTime().date().month(); - if (currentYear > year) - continue; - if (currentYear == year && currentMonth > month) - continue; + QDate monthBegin(year, month, 1); + QDate monthEnd(year, month, monthBegin.daysInMonth()); - result.insert(month); + // Find range for every month in year [lowest, higher) + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), monthBegin, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), monthEnd, [] (const TimeRecord& tr, const QDate& d) { return tr.startTime().toLocalTime().date() < d;}); + if (lowest != mData.end()) + { + int currentYear = lowest->startTime().toLocalTime().date().year(); + int currentMonth = lowest->startTime().toLocalTime().date().month(); + if (currentYear > year) + continue; + if (currentYear == year && currentMonth > month) + continue; + + result.insert(month); + } } - } } void TimeLine::getDays(int year, int month, std::set& result) { - QDate monthBegin(year, month, 1); - for (int day = 1; day <= monthBegin.daysInMonth(); day++) - { - QDate currentDay(year, month, day); - - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), currentDay, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); - if (lowest != mData.end()) + QDate monthBegin(year, month, 1); + for (int day = 1; day <= monthBegin.daysInMonth(); day++) { - int startYear = lowest->startTime().toLocalTime().date().year(); - int startMonth = lowest->startTime().toLocalTime().date().month(); - int startDay = lowest->startTime().toLocalTime().date().day(); - if (startYear > year) - continue; - if (startYear == year && startMonth > month) - continue; - if (startYear == year && startMonth == month && startDay > day) - continue; + QDate currentDay(year, month, day); - result.insert(day); + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), currentDay, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + if (lowest != mData.end()) + { + int startYear = lowest->startTime().toLocalTime().date().year(); + int startMonth = lowest->startTime().toLocalTime().date().month(); + int startDay = lowest->startTime().toLocalTime().date().day(); + if (startYear > year) + continue; + if (startYear == year && startMonth > month) + continue; + if (startYear == year && startMonth == month && startDay > day) + continue; + + result.insert(day); + } } - } } int TimeLine::getTime(int year, int month, int day, std::vector* intervals) { - int result = 0; - QDate d(year, month, day); + int result = 0; + QDate d(year, month, day); - // Find range of related records [lowest, higher) - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), d, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); - //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), d, [] (const QDate& d, const TimeRecord& tr) { return tr.startTime().toLocalTime().date() < d;}); + // Find range of related records [lowest, higher) + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), d, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), d, [] (const QDate& d, const TimeRecord& tr) { return tr.startTime().toLocalTime().date() < d;}); - for (;lowest != mData.end();/*&& lowest != higher;*/ lowest++) - { - TimeRecord& tr = *lowest; - if (tr.startTime().toLocalTime().date().year() > year) - break; - if (tr.startTime().toLocalTime().date().year() == year && tr.startTime().toLocalTime().date().month() > month) - break; - - if (tr.startTime().toLocalTime().date().year() == year && - tr.startTime().toLocalTime().date().month() == month && - tr.startTime().toLocalTime().date().day() > day) - break; - - QDateTime dayBegin(d, QTime(0, 0, 0)); - QDateTime dayEnd(d, QTime(23, 59, 59)); - - if (tr.startTime().toLocalTime().secsTo(dayBegin) < 0) - dayBegin = tr.startTime().toLocalTime(); // Time record begin is later than begin of the day - - if (tr.endTime().toLocalTime().secsTo(dayEnd) > 0) - dayEnd = tr.endTime().toLocalTime(); - - if (intervals) + for (;lowest != mData.end();/*&& lowest != higher;*/ lowest++) { - TimeRecord resultingRecord; - resultingRecord.setStartTime(dayBegin); - resultingRecord.setEndTime(dayEnd); - resultingRecord.setId(tr.id()); - resultingRecord.setTaskId(tr.taskId()); - intervals->push_back(resultingRecord); + TimeRecord& tr = *lowest; + if (tr.startTime().toLocalTime().date().year() > year) + break; + if (tr.startTime().toLocalTime().date().year() == year && tr.startTime().toLocalTime().date().month() > month) + break; + + if (tr.startTime().toLocalTime().date().year() == year && + tr.startTime().toLocalTime().date().month() == month && + tr.startTime().toLocalTime().date().day() > day) + break; + + QDateTime dayBegin(d, QTime(0, 0, 0)); + QDateTime dayEnd(d, QTime(23, 59, 59)); + + if (tr.startTime().toLocalTime().secsTo(dayBegin) < 0) + dayBegin = tr.startTime().toLocalTime(); // Time record begin is later than begin of the day + + if (tr.endTime().toLocalTime().secsTo(dayEnd) > 0) + dayEnd = tr.endTime().toLocalTime(); + + if (intervals) + { + TimeRecord resultingRecord; + resultingRecord.setStartTime(dayBegin); + resultingRecord.setEndTime(dayEnd); + resultingRecord.setId(tr.id()); + resultingRecord.setTaskId(tr.taskId()); + intervals->push_back(resultingRecord); + } + result++; } - result++; - } - return result; + return result; } int TimeLine::today() { - int result = 0; + int result = 0; - TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), QDate::currentDate(), - [] (const TimeRecord& lhs, const QDate& rhs) - { - return lhs.endTime().toLocalTime().date() < rhs; - }); - - for (;lowIter != mData.end(); lowIter++) - { - if (lowIter->startTime().toLocalTime().date() > QDate::currentDate()) - break; // quit the loop - - if (lowIter->endTime().toLocalTime().date() >= QDate::currentDate()) + TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), QDate::currentDate(), + [] (const TimeRecord& lhs, const QDate& rhs) { - QDateTime dayBegin(QDate::currentDate(), QTime(0,0)); - QDateTime dayEnd(QDate::currentDate(), QTime(23, 59, 59)); + return lhs.endTime().toLocalTime().date() < rhs; + }); - int secondsTo = dayBegin.secsTo(lowIter->startTime().toLocalTime()); - if (secondsTo > 0) - dayBegin = lowIter->startTime(); + for (;lowIter != mData.end(); lowIter++) + { + if (lowIter->startTime().toLocalTime().date() > QDate::currentDate()) + break; // quit the loop - int secondsFrom = dayEnd.secsTo(lowIter->endTime().toLocalTime()); - if (secondsFrom < 0) - dayEnd = lowIter->endTime().toLocalTime(); + if (lowIter->endTime().toLocalTime().date() >= QDate::currentDate()) + { + QDateTime dayBegin(QDate::currentDate(), QTime(0,0)); + QDateTime dayEnd(QDate::currentDate(), QTime(23, 59, 59)); - int secondsSpent = dayBegin.secsTo(dayEnd); - result += secondsSpent; + int secondsTo = dayBegin.secsTo(lowIter->startTime().toLocalTime()); + if (secondsTo > 0) + dayBegin = lowIter->startTime(); + + int secondsFrom = dayEnd.secsTo(lowIter->endTime().toLocalTime()); + if (secondsFrom < 0) + dayEnd = lowIter->endTime().toLocalTime(); + + int secondsSpent = dayBegin.secsTo(dayEnd); + result += secondsSpent; + } } - } - return result; + return result; } int TimeLine::month() { - int result = 0; + int result = 0; - // Find first day of month - QDate currentMonth = QDate::currentDate(); - currentMonth.setDate(currentMonth.year(), currentMonth.month(), 1); + // Find first day of month + QDate currentMonth = QDate::currentDate(); + currentMonth.setDate(currentMonth.year(), currentMonth.month(), 1); - // Find position in time record array close to month begin - TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), currentMonth, [] (const TimeRecord& lhs, const QDate& rhs) { return lhs.endTime().toLocalTime().date() < rhs; }); + // Find position in time record array close to month begin + TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), currentMonth, [] (const TimeRecord& lhs, const QDate& rhs) { return lhs.endTime().toLocalTime().date() < rhs; }); - for (;lowIter != mData.end(); lowIter++) - { - // See if start of current time period is later than current month - if (lowIter->startTime().toLocalTime().date().month() > currentMonth.month()) - break; // quit the loop - - if (lowIter->endTime().toLocalTime().date().month() >= currentMonth.month()) + for (;lowIter != mData.end(); lowIter++) { - QDateTime monthBegin(currentMonth, QTime(0,0)); - QDateTime monthEnd(QDate(currentMonth.year(), currentMonth.month(), currentMonth.daysInMonth()), QTime(23, 59, 59)); + // See if start of current time period is later than current month + if (lowIter->startTime().toLocalTime().date().month() > currentMonth.month()) + break; // quit the loop - int secondsTo = monthBegin.secsTo(lowIter->startTime().toLocalTime()); - if (secondsTo > 0) - monthBegin = lowIter->startTime(); + if (lowIter->endTime().toLocalTime().date().month() >= currentMonth.month()) + { + QDateTime monthBegin(currentMonth, QTime(0,0)); + QDateTime monthEnd(QDate(currentMonth.year(), currentMonth.month(), currentMonth.daysInMonth()), QTime(23, 59, 59)); - int secondsFrom = monthEnd.secsTo(lowIter->endTime().toLocalTime()); - if (secondsFrom < 0) - monthEnd = lowIter->endTime().toLocalTime(); + int secondsTo = monthBegin.secsTo(lowIter->startTime().toLocalTime()); + if (secondsTo > 0) + monthBegin = lowIter->startTime(); - int secondsSpent = monthBegin.secsTo(monthEnd); - result += secondsSpent; + int secondsFrom = monthEnd.secsTo(lowIter->endTime().toLocalTime()); + if (secondsFrom < 0) + monthEnd = lowIter->endTime().toLocalTime(); + + int secondsSpent = monthBegin.secsTo(monthEnd); + result += secondsSpent; + } } - } - return result; + return result; } int TimeLine::getSum(const QDate& start, const QDate& finish) { - int result = 0; + int result = 0; - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), start, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); - //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), finish, [] (const QDate& d, const TimeRecord& tr) { return tr.startTime().toLocalTime().date() < d;}); + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), start, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), finish, [] (const QDate& d, const TimeRecord& tr) { return tr.startTime().toLocalTime().date() < d;}); - for (;lowest != mData.end(); lowest++) - { - TimeRecord& tr = *lowest; - if (tr.startTime().toLocalTime().date() > finish) - break; + for (;lowest != mData.end(); lowest++) + { + TimeRecord& tr = *lowest; + if (tr.startTime().toLocalTime().date() > finish) + break; - QDateTime dayBegin(start, QTime(0, 0, 0)); - QDateTime dayEnd(finish, QTime(23, 59, 59)); + QDateTime dayBegin(start, QTime(0, 0, 0)); + QDateTime dayEnd(finish, QTime(23, 59, 59)); - if (tr.startTime().toLocalTime().secsTo(dayBegin) < 0) - dayBegin = tr.startTime().toLocalTime(); // Time record begin is later than begin of the interval + if (tr.startTime().toLocalTime().secsTo(dayBegin) < 0) + dayBegin = tr.startTime().toLocalTime(); // Time record begin is later than begin of the interval - if (tr.endTime().toLocalTime().secsTo(dayEnd) > 0) - dayEnd = tr.endTime().toLocalTime(); + if (tr.endTime().toLocalTime().secsTo(dayEnd) > 0) + dayEnd = tr.endTime().toLocalTime(); - result += dayBegin.secsTo(dayEnd); - } - return result; + result += dayBegin.secsTo(dayEnd); + } + return result; } bool TimeLine::duplicateDetected() const { - return mActiveTimeRecord == nullptr; + return mActiveTimeRecord == nullptr; } void TimeLine::putDebugRecord() { - QDateTime current = QDateTime::currentDateTimeUtc(); - QDateTime end = current.addSecs(600); - TimeRecord* r = makeNewRecord(current, end); - r->save(); + QDateTime current = QDateTime::currentDateTimeUtc(); + QDateTime end = current.addSecs(600); + TimeRecord* r = makeNewRecord(current, end); + r->save(); } // -------------------- Task -------------------------- static Task::ModelId ModelIdGenerator = 0; Task::Task() - :mId(0), mTitleModified(false), mHtmlModified(false), mHtmlLoaded(false), mModelId(++ModelIdGenerator), - mParentId(-1), mIndex(-1), mChecked(false), mIndexModified(false), - mParentModified(false), mReportedTime(0), mChildrenReportedTime(0), mAttachmentCount(0), - mDocument(nullptr), mCursorPosition(0) + :mId(0), mTitleModified(false), mHtmlModified(false), mHtmlLoaded(false), mModelId(++ModelIdGenerator), + mParentId(-1), mIndex(-1), mChecked(false), mIndexModified(false), + mParentModified(false), mReportedTime(0), mChildrenReportedTime(0), mAttachmentCount(0), + mDocument(nullptr), mCursorPosition(0) { } @@ -756,136 +789,136 @@ Task::~Task() void Task::load(SQLite::Statement &q) { - mId = q.getColumn(0).getInt64(); - mTitle = q.getColumn(1).getText(); - mIndex = q.getColumn(2).getInt(); - mFlags = q.getColumn(3).getInt(); - mAttachmentCount = q.getColumn(4).getInt(); + mId = q.getColumn(0).getInt64(); + mTitle = q.getColumn(1).getText(); + mIndex = q.getColumn(2).getInt(); + mFlags = q.getColumn(3).getInt(); + mAttachmentCount = q.getColumn(4).getInt(); } Task::Id Task::id() const { - return mId; + return mId; } void Task::setId(Id id) { - mId = id; + mId = id; } Task::Id Task::parentId() const { - return mParentId; + return mParentId; } void Task::setParentId(Id id) { - if (mParentId != id) - { - mParentModified = true; - mParentId = id; - } + if (mParentId != id) + { + mParentModified = true; + mParentId = id; + } } WorldId Task::worldId() const { - return mWorldId; + return mWorldId; } void Task::setWorldId(const WorldId& id) { - mWorldId = id; + mWorldId = id; } Task::ModelId Task::modelId() const { - return mModelId; + return mModelId; } void Task::setModelId(ModelId id) { - mModelId = id; + mModelId = id; } int Task::index() const { - return mIndex; + return mIndex; } void Task::setIndex(int index, bool modified) { - if (index != mIndex) - { - if (modified) - mIndexModified = true; - mIndex = index; - } + if (index != mIndex) + { + if (modified) + mIndexModified = true; + mIndex = index; + } } QString Task::html() const { - return mHtml; + return mHtml; } void Task::setHtml(const QString &html) { - if (mHtml != html) - { - mHtml = html; - mHtmlModified = true; - } + if (mHtml != html) + { + mHtml = html; + mHtmlModified = true; + } } QString Task::title() const { - return mTitle; + return mTitle; } void Task::setTitle(const QString &title, bool modified) { - if (mTitle != title) - { - mTitle = title; - if (modified) - mTitleModified = true; - } + if (mTitle != title) + { + mTitle = title; + if (modified) + mTitleModified = true; + } } void Task::save(SaveOptions options) { - if (!mTitleModified && !mHtmlModified && !mIndexModified && !mParentModified && options == Save_Automatic) - return; + if (!mTitleModified && !mHtmlModified && !mIndexModified && !mParentModified && options == Save_Automatic) + return; - const char* queryText = NULL; + const char* queryText = NULL; - if (mTitleModified && mHtmlModified) - queryText = "update task set parentid = :parentid, flags = :flags, title = :title, html = :html, orderid = :orderid where id = :id"; - else - if (mTitleModified) - queryText = "update task set parentid = :parentid, flags = :flags, title = :title, orderid = :orderid where id = :id"; - else - if (mHtmlModified) - queryText = "update task set parentid = :parentid, flags = :flags, html = :html, orderid = :orderid where id = :id"; - else - queryText = "update task set parentid = :parentid, flags = :flags, orderid = :orderid where id = :id"; + if (mTitleModified && mHtmlModified) + queryText = "update task set parentid = :parentid, flags = :flags, title = :title, html = :html, orderid = :orderid where id = :id"; + else + if (mTitleModified) + queryText = "update task set parentid = :parentid, flags = :flags, title = :title, orderid = :orderid where id = :id"; + else + if (mHtmlModified) + queryText = "update task set parentid = :parentid, flags = :flags, html = :html, orderid = :orderid where id = :id"; + else + queryText = "update task set parentid = :parentid, flags = :flags, orderid = :orderid where id = :id"; - SQLite::Statement q(Storage::instance().database(), queryText); - if (mParent) - q.bind(":parentid", (sqlite3_int64)mParent->id()); - else - q.bind(":parentid"); + SQLite::Statement q(Storage::instance().database(), queryText); + if (mParent) + q.bind(":parentid", (sqlite3_int64)mParent->id()); + else + q.bind(":parentid"); - q.bind(":flags", mFlags); + q.bind(":flags", mFlags); - if (mTitleModified) - q.bind(":title", mTitle.toStdString()); - if (mHtmlModified) - q.bind(":html", mHtml.toStdString()); + if (mTitleModified) + q.bind(":title", mTitle.toStdString()); + if (mHtmlModified) + q.bind(":html", mHtml.toStdString()); - q.bind(":id", (sqlite3_int64)mId); - q.bind(":orderid", mIndex); - q.exec(); - mIndexModified = mTitleModified = mHtmlModified = false; + q.bind(":id", (sqlite3_int64)mId); + q.bind(":orderid", mIndex); + q.exec(); + mIndexModified = mTitleModified = mHtmlModified = false; } /*void Task::load() @@ -907,145 +940,145 @@ void Task::save(SaveOptions options) QString Task::path() const { - QString result = this->title(); - if (parent()) - result.insert(0, parent()->path() + " / "); + QString result = this->title(); + if (parent()) + result.insert(0, parent()->path() + " / "); - return result; + return result; } PTask Task::parent() const { - return mParent; + return mParent; } void Task::setParent(PTask task, bool modified) { - if (mParent != task) - { - mParent = task; - if (task) - mParentId = task->id(); - else - mParentId = -1; - if (modified) - mTitleModified = true; // To force save() - } + if (mParent != task) + { + mParent = task; + if (task) + mParentId = task->id(); + else + mParentId = -1; + if (modified) + mTitleModified = true; // To force save() + } } TaskArray& Task::children() { - return mChildren; + return mChildren; } void Task::loadContent() { - SQLite::Statement htmlQuery(Storage::instance().database(), "select html from task where id = :id"); - htmlQuery.bind(":id", (sqlite3_int64)mId); - if (htmlQuery.executeStep()) - { - mHtml = htmlQuery.getColumn(0).getText(); - mHtmlLoaded = true; - mHtmlModified = false; - } - - if (!mTimeLine) - { - mTimeLine = PTimeLine(new TimeLine()); - mTimeLine->setTaskId(mId); - mTimeLine->load(); - } + SQLite::Statement htmlQuery(Storage::instance().database(), "select html from task where id = :id"); + htmlQuery.bind(":id", (sqlite3_int64)mId); + if (htmlQuery.executeStep()) + { + mHtml = htmlQuery.getColumn(0).getText(); + mHtmlLoaded = true; + mHtmlModified = false; + } + + if (!mTimeLine) + { + mTimeLine = PTimeLine(new TimeLine()); + mTimeLine->setTaskId(mId); + mTimeLine->load(); + } } bool Task::isContentLoaded() const { - return mHtmlLoaded; + return mHtmlLoaded; } void Task::unloadContent(bool includeTimeline) { - mHtml.clear(); - mHtmlModified = mHtmlLoaded = false; - if (includeTimeline) - mTimeLine.clear(); + mHtml.clear(); + mHtmlModified = mHtmlLoaded = false; + if (includeTimeline) + mTimeLine.clear(); } PTimeLine Task::timeline() { - return mTimeLine; + return mTimeLine; } int Task::getAttachmentCount() { - return mAttachmentCount; + return mAttachmentCount; } void Task::setAttachmentCount(int count) { - mAttachmentCount = count; + mAttachmentCount = count; } int Task::checkAttachments() { - SQLite::Statement q(Storage::instance().database(), "select count(*) from file where (taskid = :taskid) and ((removed = 0) or (removed is null))"); - q.bind(":taskid", (sqlite3_int64)mId); - if (q.executeStep()) - mAttachmentCount = q.getColumn(0).getInt(); - else - mAttachmentCount = 0; + SQLite::Statement q(Storage::instance().database(), "select count(*) from file where (taskid = :taskid) and ((removed = 0) or (removed is null))"); + q.bind(":taskid", (sqlite3_int64)mId); + if (q.executeStep()) + mAttachmentCount = q.getColumn(0).getInt(); + else + mAttachmentCount = 0; - return mAttachmentCount; + return mAttachmentCount; } bool Task::isChecked() const { - return mChecked; + return mChecked; } void Task::setChecked(bool checked) { - mChecked = checked; + mChecked = checked; } int Task::getReportedTime() const { - return mReportedTime; + return mReportedTime; } void Task::setReportedTime(int t) { - mReportedTime = t; + mReportedTime = t; } int Task::getChildrenReportedTime() const { - return mChildrenReportedTime; + return mChildrenReportedTime; } void Task::setChildrenReportedTime(int t) { - mChildrenReportedTime = t; + mChildrenReportedTime = t; } int Task::flags() const { - return mFlags; + return mFlags; } void Task::setFlags(int value) { - mFlags = value; - save(Save_Forced); + mFlags = value; + save(Save_Forced); } int Task::cursorPosition() const { - return mCursorPosition; + return mCursorPosition; } void Task::setCursorPosition(int position) { - mCursorPosition = position; + mCursorPosition = position; } /* @@ -1063,7 +1096,7 @@ void Task::setTextDocument(QTextDocument* doc) */ // ----------- Attachment ------------- Attachment::Attachment() - :mId(0), mTaskId(0) + :mId(0), mTaskId(0) { } @@ -1075,99 +1108,99 @@ Attachment::~Attachment() Task::Id Attachment::id() { - return mId; + return mId; } void Attachment::setId(Task::Id id) { - mId = id; + mId = id; } Task::Id Attachment::taskId() { - return mTaskId; + return mTaskId; } void Attachment::setTaskId(Task::Id id) { - mTaskId = id; + mTaskId = id; } WorldId Attachment::worldId() const { - return mWorldId; + return mWorldId; } void Attachment::setWorldId(const WorldId& id) { - mWorldId = id; + mWorldId = id; } int Attachment::index() { - return mIndex; + return mIndex; } void Attachment::setIndex(int index) { - //TODO: introduce mIndexModified field and corresponding login in save() - mIndex = index; + //TODO: introduce mIndexModified field and corresponding login in save() + mIndex = index; } QByteArray Attachment::loadContent() { - SQLite::Statement q(Storage::instance().database(), "select content from file where id = :id"); - q.bind(":id", (sqlite3_int64)mId); - if (q.executeStep()) - return QByteArray((const char*)q.getColumn(0).getBlob(), q.getColumn(0).size()); - else - return QByteArray(); + SQLite::Statement q(Storage::instance().database(), "select content from file where id = :id"); + q.bind(":id", (sqlite3_int64)mId); + if (q.executeStep()) + return QByteArray((const char*)q.getColumn(0).getBlob(), q.getColumn(0).size()); + else + return QByteArray(); } void Attachment::saveContent(const QByteArray& content) { - SQLite::Statement q(Storage::instance().database(), "update file set content = :content where id = :id"); - q.bind(":content", content.data(), content.size()); - q.bind(":id", (sqlite3_int64)mId); - if (q.exec()) - ; + SQLite::Statement q(Storage::instance().database(), "update file set content = :content where id = :id"); + q.bind(":content", content.data(), content.size()); + q.bind(":id", (sqlite3_int64)mId); + if (q.exec()) + ; } void Attachment::setFilename(const QString& filename) { - mFilename = filename; + mFilename = filename; } QString Attachment::filename() { - return mFilename; + return mFilename; } // mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, filename TEXT, content BLOB, orderid INTEGER, synctime TEXT)"); void Attachment::save() { - if (mId) - { - SQLite::Statement q(Storage::instance().database(), "update file set filename = :filename, orderid = :orderid where id = :id"); - q.bind(":filename", mFilename.toStdString().c_str()); - q.bind(":orderid", mIndex); - q.bind(":id", (sqlite3_int64)mId); - - if (q.exec()) - ; - } - else - { - SQLite::Statement q(Storage::instance().database(), "insert into file (filename, taskid, orderid, removed) values(:filename, :taskid, :orderid, 0)"); - q.bind(":filename", mFilename.toStdString().c_str()); - q.bind(":taskid", (sqlite3_int64)mTaskId); - q.bind(":orderid", mIndex); - if (q.exec()) + if (mId) { - mId = Storage::instance().database().getLastInsertRowid(); + SQLite::Statement q(Storage::instance().database(), "update file set filename = :filename, orderid = :orderid where id = :id"); + q.bind(":filename", mFilename.toStdString().c_str()); + q.bind(":orderid", mIndex); + q.bind(":id", (sqlite3_int64)mId); + + if (q.exec()) + ; + } + else + { + SQLite::Statement q(Storage::instance().database(), "insert into file (filename, taskid, orderid, removed) values(:filename, :taskid, :orderid, 0)"); + q.bind(":filename", mFilename.toStdString().c_str()); + q.bind(":taskid", (sqlite3_int64)mTaskId); + q.bind(":orderid", mIndex); + if (q.exec()) + { + mId = Storage::instance().database().getLastInsertRowid(); + } } - } } void Attachment::load() diff --git a/client/task.h b/client/task.h index 347d5b6..91369be 100644 --- a/client/task.h +++ b/client/task.h @@ -12,55 +12,55 @@ #include #include "SQLiteCpp/Database.h" -typedef qulonglong Id; +typedef uint64_t Id; class WorldId { public: - WorldId(); - WorldId(const WorldId& src); - WorldId(const QString& s); - ~WorldId(); - WorldId& operator = (const WorldId& src); - bool operator == (const WorldId& src); - bool operator < (const WorldId& src); + WorldId(); + WorldId(const WorldId& src); + WorldId(const std::string& s); + ~WorldId(); + WorldId& operator = (const WorldId& src); + bool operator == (const WorldId& src); + bool operator < (const WorldId& src); - QString asString() const; - static WorldId create(); + std::string asString() const; + static WorldId create(); protected: - QUuid mId; + std::string mId; }; class TimeRecord { public: - TimeRecord(); - TimeRecord(const QDateTime& startTime, const QDateTime& endTime, Id taskId); - ~TimeRecord(); + TimeRecord(); + TimeRecord(const time_t& startTime, const time_t& endTime, Id taskId); + ~TimeRecord(); - QDateTime startTime() const; - void setStartTime(const QDateTime& startTime); - QDateTime endTime() const; - void setEndTime(const QDateTime& endTime); - int length(); - Id id() const; - void setId(Id id); - Id taskId() const; - void setTaskId(Id id); - WorldId worldId() const; - void setWorldId(const WorldId& id); + time_t startTime() const; + void setStartTime(const time_t& startTime); + time_t endTime() const; + void setEndTime(const time_t& endTime); + int length(); + Id id() const; + void setId(Id id); + Id taskId() const; + void setTaskId(Id id); + WorldId worldId() const; + void setWorldId(const WorldId& id); - // Save record to DB. If record is new - id() property will be set after this call. - void save(); + // Save record to DB. If record is new - id() property will be set after this call. + void save(); - // Remove record from DB. - void deleteRecord(); + // Remove record from DB. + void deleteRecord(); protected: - Id mId, mTaskId; - WorldId mWorldId; - QDateTime mStartTime, mEndTime; - bool mSaved; + Id mId, mTaskId; + WorldId mWorldId; + time_t mStartTime, mEndTime; + bool mSaved; }; typedef QVector TimeArray; @@ -73,73 +73,73 @@ typedef std::map > YearsMap; class TimeLine { public: - TimeLine(); - ~TimeLine(); + TimeLine(); + ~TimeLine(); - TimeArray& data(); + TimeArray& data(); - // Returns total time in seconds - int totalTime(); - bool active(); - void start(); - void stop(bool updateTimeline = true); - void flush(bool saveToDb, const QDateTime& currentUtc); + // Returns total time in seconds + int totalTime(); + bool active(); + void start(); + void stop(bool updateTimeline = true); + void flush(bool saveToDb, time_t currentUtc); - void load(); - void save(); + void load(); + void save(); - Id taskId(); - void setTaskId(Id id); + Id taskId(); + void setTaskId(Id id); - // These methods work with local time - void getYears(std::set& result); - void getMonthes(int year, std::set& result); - void getDays(int year, int month, std::set& result); - int getTime(int year, int month, int day, std::vector* intervals); + // These methods work with local time + void getYears(std::set& result); + void getMonthes(int year, std::set& result); + void getDays(int year, int month, std::set& result); + int getTime(int year, int month, int day, std::vector* intervals); - int today(); - int month(); - int getSum(const QDate& start, const QDate& finish); - bool duplicateDetected() const; + int today(); + int month(); + int getSum(const QDate& start, const QDate& finish); + bool duplicateDetected() const; - // Checks if specified interval has intersection - bool hasIntersection(const TimeRecord& interval); + // Checks if specified interval has intersection + bool hasIntersection(const TimeRecord& interval); - // Inserts new interval to timeline. Saves new interval to DB. - void insertInterval(const TimeRecord& interval); + // Inserts new interval to timeline. Saves new interval to DB. + void insertInterval(const TimeRecord& interval); - // Attempts to find & remove from timeline specified interval. Returns true if succeeded. Removes interval from DB too. - // Search is made using start/finish time interval value - not id() value. Only whole TimeRecord can be deleted. - bool removeInterval(const TimeRecord& interval); + // Attempts to find & remove from timeline specified interval. Returns true if succeeded. Removes interval from DB too. + // Search is made using start/finish time interval value - not id() value. Only whole TimeRecord can be deleted. + bool removeInterval(const TimeRecord& interval); - // Attempts to find & cut interval from timeline. - // It does not mean whole TimeRecord will be removed. Depending on interval bounds existing TimeRecord can be removed/modified/add new records even. - void cutInterval(const TimeRecord& interval); + // Attempts to find & cut interval from timeline. + // It does not mean whole TimeRecord will be removed. Depending on interval bounds existing TimeRecord can be removed/modified/add new records even. + void cutInterval(const TimeRecord& interval); - // Searches time interval by its id. Can return NULL if search failed. - TimeRecord* findIntervalById(Id id); + // Searches time interval by its id. Can return NULL if search failed. + TimeRecord* findIntervalById(Id id); - // Adds 10 minutes interval starting from current time. For debug/test purposes. - void putDebugRecord(); + // Adds 10 minutes interval starting from current time. For debug/test purposes. + void putDebugRecord(); protected: - TimeArray mData; - Id mTaskId; - bool mActive; - TimeRecord* mActiveTimeRecord; - int mTotalTime; + TimeArray mData; + Id mTaskId; + bool mActive; + TimeRecord* mActiveTimeRecord; + int mTotalTime; - // Sums total time in timeline - int findTotalTime(); + // Sums total time in timeline + int findTotalTime(); - // Builds new time interval record with specified start/finish time - TimeRecord* makeNewRecord(const QDateTime& begin, const QDateTime& end); + // Builds new time interval record with specified start/finish time + TimeRecord* makeNewRecord(time_t begin, time_t end); - // Looks for time record that includes specified time point - TimeRecord* hasTimePoint(const QDateTime& t); + // Looks for time record that includes specified time point + TimeRecord* hasTimePoint(time_t t); - // Sorts records in mData by startTime() value - void sortData(); + // Sorts records in mData by startTime() value + void sortData(); }; @@ -151,139 +151,139 @@ typedef QVector TaskArray; class Task: public QObject { - Q_OBJECT + Q_OBJECT public: - typedef qulonglong Id; - typedef quint32 ModelId; - enum Flag - { - Flag_NoTimeTracking = 1 - }; + typedef uint64_t Id; + typedef uint32_t ModelId; + enum Flag + { + Flag_NoTimeTracking = 1 + }; - Task(); - ~Task(); - - void load(SQLite::Statement& q); + Task(); + ~Task(); - Id id() const; - void setId(Id id); + void load(SQLite::Statement& q); - Id parentId() const; - void setParentId(Id id); + Id id() const; + void setId(Id id); - WorldId worldId() const; - void setWorldId(const WorldId& id); + Id parentId() const; + void setParentId(Id id); - ModelId modelId() const; - void setModelId(ModelId id); + WorldId worldId() const; + void setWorldId(const WorldId& id); - int index() const; - void setIndex(int index, bool modified = true); + ModelId modelId() const; + void setModelId(ModelId id); - enum SaveOptions - { - Save_Automatic, - Save_Forced - }; + int index() const; + void setIndex(int index, bool modified = true); - void save(SaveOptions options = Save_Automatic); + enum SaveOptions + { + Save_Automatic, + Save_Forced + }; - QString html() const; - void setHtml(const QString& html); - QString title() const; - void setTitle(const QString& title, bool modified = true); + void save(SaveOptions options = Save_Automatic); - QString path() const; + QString html() const; + void setHtml(const QString& html); + QString title() const; + void setTitle(const QString& title, bool modified = true); - PTask parent() const; - void setParent(PTask task, bool modified = true); + QString path() const; - TaskArray& children(); - - // Loads html and timeline - void loadContent(); - bool isContentLoaded() const; + PTask parent() const; + void setParent(PTask task, bool modified = true); - // Unloads html and timeline - void unloadContent(bool includeTimeline = true); + TaskArray& children(); - PTimeLine timeline(); + // Loads html and timeline + void loadContent(); + bool isContentLoaded() const; - // Returns true if task has attachments - int getAttachmentCount(); - void setAttachmentCount(int count); - int checkAttachments(); + // Unloads html and timeline + void unloadContent(bool includeTimeline = true); - // Service properties used in time reporting - bool isChecked() const; - void setChecked(bool checked); + PTimeLine timeline(); - int getReportedTime() const; - void setReportedTime(int t); + // Returns true if task has attachments + int getAttachmentCount(); + void setAttachmentCount(int count); + int checkAttachments(); - int getChildrenReportedTime() const; - void setChildrenReportedTime(int t); + // Service properties used in time reporting + bool isChecked() const; + void setChecked(bool checked); - int flags() const; - void setFlags(int value); + int getReportedTime() const; + void setReportedTime(int t); - int cursorPosition() const; - void setCursorPosition(int position); + int getChildrenReportedTime() const; + void setChildrenReportedTime(int t); - /* + int flags() const; + void setFlags(int value); + + int cursorPosition() const; + void setCursorPosition(int position); + + /* QUndoStack* getUndoStack() const; void setUndoStack(QUndoStack* doc); */ protected: - QString mHtml, mTitle; - PTimeLine mTimeLine; - Id mId, mParentId; - WorldId mWorldId; - ModelId mModelId; - bool mTitleModified, mHtmlModified, mHtmlLoaded, mIndexModified, mParentModified; - PTask mParent; - TaskArray mChildren; - int mIndex; - bool mChecked; - int mReportedTime, mChildrenReportedTime; - int mAttachmentCount; - int mFlags; - QTextDocument* mDocument; - int mCursorPosition; + QString mHtml, mTitle; + PTimeLine mTimeLine; + Id mId, mParentId; + WorldId mWorldId; + ModelId mModelId; + bool mTitleModified, mHtmlModified, mHtmlLoaded, mIndexModified, mParentModified; + PTask mParent; + TaskArray mChildren; + int mIndex; + bool mChecked; + int mReportedTime, mChildrenReportedTime; + int mAttachmentCount; + int mFlags; + QTextDocument* mDocument; + int mCursorPosition; }; class Attachment { public: - Attachment(); - ~Attachment(); + Attachment(); + ~Attachment(); - Task::Id id(); - void setId(Task::Id id); + Task::Id id(); + void setId(Task::Id id); - Task::Id taskId(); - void setTaskId(Task::Id id); + Task::Id taskId(); + void setTaskId(Task::Id id); - WorldId worldId() const; - void setWorldId(const WorldId& id); + WorldId worldId() const; + void setWorldId(const WorldId& id); - int index(); - void setIndex(int index); + int index(); + void setIndex(int index); - QByteArray loadContent(); - void saveContent(const QByteArray& content); + QByteArray loadContent(); + void saveContent(const QByteArray& content); - void setFilename(const QString& filename); - QString filename(); + void setFilename(const QString& filename); + QString filename(); - void save(); - void load(); + void save(); + void load(); protected: - Id mId, mTaskId; - WorldId mWorldId; - QString mFilename; - int mIndex; + Id mId, mTaskId; + WorldId mWorldId; + QString mFilename; + int mIndex; }; typedef QSharedPointer PAttachment;