- refactoring started

This commit is contained in:
dmytro.bogovych 2018-12-21 23:11:26 +02:00
parent e346222c6e
commit f8d51cf95f
6 changed files with 1298 additions and 1231 deletions

View File

@ -24,7 +24,10 @@ char* __strlcat_chk (char* dest, const char* src, int len, int destcapacity)
#endif #endif
#include "settings.h" #include "settings.h"
void ThemeHelper::applyCurrentTheme(Settings& settings)
using namespace helper;
void theme::applyCurrent(Settings& settings)
{ {
// Dark theme // Dark theme
if (settings.data()[KEY_DARK_THEME].toBool()) 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 hours = seconds / 3600;
int minutes = (seconds % 3600) / 60; int minutes = (seconds % 3600) / 60;
int secs = seconds % 60; int secs = seconds % 60;
char r[32];
if (showSeconds) 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 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(&timestamp));
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 #if QT_VERSION >= 0x050000
QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
@ -64,7 +85,7 @@ QString PathHelper::pathToSettings()
return path; return path;
} }
QString PathHelper::pathToDatabase() QString path::pathToDatabase()
{ {
#if QT_VERSION >= 0x050000 #if QT_VERSION >= 0x050000
QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
@ -75,7 +96,7 @@ QString PathHelper::pathToDatabase()
return path; return path;
} }
QString PathHelper::pathToDesktop() QString path::pathToDesktop()
{ {
#if QT_VERSION >= 0x050000 #if QT_VERSION >= 0x050000
QString folder = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString folder = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
@ -85,7 +106,7 @@ QString PathHelper::pathToDesktop()
return folder; return folder;
} }
QString PathHelper::pathToDatabaseTemplate() QString path::pathToDatabaseTemplate()
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
return QCoreApplication::applicationDirPath() + "/" + DATABASENAME; return QCoreApplication::applicationDirPath() + "/" + DATABASENAME;
@ -96,7 +117,7 @@ QString PathHelper::pathToDatabaseTemplate()
#endif #endif
} }
QString PathHelper::pathToLog() QString path::pathToLog()
{ {
#if QT_VERSION >= 0x050000 #if QT_VERSION >= 0x050000
QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString folder = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
@ -106,7 +127,7 @@ QString PathHelper::pathToLog()
return folder + "/" + LOGNAME; return folder + "/" + LOGNAME;
} }
bool ActivityTrackerHelper::ensureSmartTrackingIsPossible() bool activityTracker::ensureSmartTrackingIsPossible()
{ {
bool result = false; bool result = false;
HIDActivityTracker tracker; HIDActivityTracker tracker;

View File

@ -5,46 +5,59 @@
#include <QObject> #include <QObject>
class Settings; class Settings;
class ThemeHelper namespace helper
{ {
public: class theme
static void applyCurrentTheme(Settings& settings); {
}; public:
static void applyCurrent(Settings& settings);
};
class TimeHelper struct date
{ {
public: int mYear = -1, mMonth = -1, mDay = -1;
static QString secondsToDisplay(int seconds, bool showSeconds);
}; 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 class path
{ {
public: public:
static QString pathToDatabase(); static QString pathToDatabase();
static QString pathToDesktop(); static QString pathToDesktop();
static QString pathToSettings(); static QString pathToSettings();
static QString pathToDatabaseTemplate(); static QString pathToDatabaseTemplate();
static QString pathToLog(); static QString pathToLog();
}; };
class ActivityTrackerHelper class activityTracker
{ {
public: public:
static bool ensureSmartTrackingIsPossible(); static bool ensureSmartTrackingIsPossible();
}; };
class EscapeKeyEventFilter: public QObject class EscapeKeyEventFilter: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit EscapeKeyEventFilter(QObject* parent = 0); explicit EscapeKeyEventFilter(QObject* parent = 0);
bool eventFilter(QObject *obj, QEvent * event); bool eventFilter(QObject *obj, QEvent * event);
signals: signals:
void escapePressed(QObject* obj); void escapePressed(QObject* obj);
}; };
}
#endif #endif

View File

@ -74,7 +74,7 @@ MainWindow::~MainWindow()
void MainWindow::attachDatabase() void MainWindow::attachDatabase()
{ {
// Open database // Open database
QString path = PathHelper::pathToDatabase(); QString path = helper::path::pathToDatabase();
if (mSettings->data()[KEY_DB_FILENAME_SPECIFIED].toBool()) if (mSettings->data()[KEY_DB_FILENAME_SPECIFIED].toBool())
path = mSettings->data()[KEY_DB_FILENAME].toString(); path = mSettings->data()[KEY_DB_FILENAME].toString();

View File

@ -8,63 +8,63 @@
#endif #endif
enum class DbProperty enum class DbProperty
{ {
Version = 1, Version = 1,
InternalCipher = 2 InternalCipher = 2
}; };
#define CURRENT_DBVERSION "0" #define CURRENT_DBVERSION "0"
// Function to make encryption. Input is raw content blob, output is blob with IV, original length and encrypted data. // 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 // Generate 16 bytes IV
QByteArray iv(16, 0); QByteArray iv(16, 0);
IV::Generate(iv); IV::Generate(iv);
cipher.setIV(iv); cipher.setIV(iv);
// Make padding // Make padding
int padded = (input.length() % cipher.blocksize()) ? (cipher.blocksize() - input.length() % cipher.blocksize()) : 0; int padded = (input.length() % cipher.blocksize()) ? (cipher.blocksize() - input.length() % cipher.blocksize()) : 0;
// Prepare output buffer // Prepare output buffer
output.resize(iv.length() + 4 + input.length() + padded); output.resize(iv.length() + 4 + input.length() + padded);
// Copy IV // Copy IV
memcpy(output.data(), iv.data(), iv.length()); memcpy(output.data(), iv.data(), iv.length());
// Copy original length in network byte order // Copy original length in network byte order
uint32_t lengthNbo = htonl(input.length()); uint32_t lengthNbo = htonl(input.length());
memcpy(output.data() + iv.length(), &lengthNbo, 4); memcpy(output.data() + iv.length(), &lengthNbo, 4);
// Encrypt // Encrypt
cipher.encrypt(input, 0, output, iv.length() + 4); 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; TwofishCipher cipher;
assert(input.length() >= cipher.blocksize() + 4); assert(input.length() >= cipher.blocksize() + 4);
// Read IV // Read IV
QByteArray iv(input.data(), cipher.blocksize()); QByteArray iv(input.data(), cipher.blocksize());
// Read original length // Read original length
uint32_t lengthNbo; uint32_t lengthNbo;
memcpy(&lengthNbo, input.data() + iv.length(), 4); memcpy(&lengthNbo, input.data() + iv.length(), 4);
// Prepare output buffer // Prepare output buffer
output.resize(input.length() - iv.length() - 4); output.resize(input.length() - iv.length() - 4);
// Set IV // Set IV
cipher.setIV(iv); cipher.setIV(iv);
// Decrypt data // Decrypt data
cipher.decrypt(input, iv.length() + 4, output, 0); cipher.decrypt(input, iv.length() + 4, output, 0);
// Truncate to original size // Truncate to original size
output.truncate(ntohl(lengthNbo)); output.truncate(ntohl(lengthNbo));
} }*/
Storage::Storage() Storage::Storage()
@ -73,186 +73,186 @@ Storage::Storage()
Storage::~Storage() Storage::~Storage()
{ {
close(); close();
} }
QString Storage::path() QString Storage::path()
{ {
return mPath; return mPath;
} }
void Storage::setPath(const QString &path) void Storage::setPath(const QString &path)
{ {
mPath = path; mPath = path;
} }
QString Storage::key() QString Storage::key()
{ {
return mKey; return mKey;
} }
void Storage::setKey(const QString &key) void Storage::setKey(const QString &key)
{ {
mKey = key; mKey = key;
} }
SQLite::Database& Storage::database() SQLite::Database& Storage::database()
{ {
return *mDatabase; return *mDatabase;
} }
bool Storage::create() bool Storage::create()
{ {
assert(!mPath.isEmpty()); assert(!mPath.isEmpty());
try try
{ {
mDatabase = QSharedPointer<SQLite::Database>(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE )); mDatabase = QSharedPointer<SQLite::Database>(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ));
} }
catch(std::exception& e) catch(std::exception& e)
{ {
return false; return false;
} }
#ifdef USE_ENCRYPTED_DB #ifdef USE_ENCRYPTED_DB
try try
{ {
std::string keyQuery = "pragma key='" + mKey.toStdString() + "'"; std::string keyQuery = "pragma key='" + mKey.toStdString() + "'";
mDatabase->exec(keyQuery.c_str()); mDatabase->exec(keyQuery.c_str());
mDatabase->exec("pragma locking_mode=EXCLUSIVE"); mDatabase->exec("pragma locking_mode=EXCLUSIVE");
mDatabase->exec("pragma journal_mode=MEMORY"); mDatabase->exec("pragma journal_mode=MEMORY");
mDatabase->exec("pragma temp_store=MEMORY"); mDatabase->exec("pragma temp_store=MEMORY");
} }
catch(std::exception& e) catch(std::exception& e)
{ {
return false; return false;
} }
#endif #endif
try try
{ {
// Timeline // Timeline
mDatabase->exec("CREATE TABLE timeline (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, starttime TEXT, endtime TEXT, synctime TEXT)"); 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)"); mDatabase->exec("CREATE INDEX timeline_taskid_index ON timeline(taskid ASC)");
// Tasks tree // 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 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)"); mDatabase->exec("CREATE INDEX task_parentid_index ON task(parentid ASC)");
// Attachments // 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 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)"); mDatabase->exec("CREATE INDEX file_taskid_index ON file(taskid ASC)");
// Sync status // Sync status
mDatabase->exec("CREATE TABLE syncs (datetime TEXT, status TEXT)"); mDatabase->exec("CREATE TABLE syncs (datetime TEXT, status TEXT)");
} }
catch(std::exception& e) catch(std::exception& e)
{ {
return false; return false;
} }
return true; return true;
} }
bool Storage::open() bool Storage::open()
{ {
assert(!mPath.isEmpty()); assert(!mPath.isEmpty());
try try
{ {
mDatabase = QSharedPointer<SQLite::Database>(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE)); mDatabase = QSharedPointer<SQLite::Database>(new SQLite::Database(mPath.toStdString().c_str(), SQLITE_OPEN_READWRITE));
mDatabase->exec("pragma key='" + mKey.toStdString() + "'"); mDatabase->exec("pragma key='" + mKey.toStdString() + "'");
SQLite::Statement q(*mDatabase, "select count(*) from sqlite_master"); SQLite::Statement q(*mDatabase, "select count(*) from sqlite_master");
if (!q.executeStep()) if (!q.executeStep())
return false; return false;
mDatabase->exec("pragma locking_mode=EXCLUSIVE"); mDatabase->exec("pragma locking_mode=EXCLUSIVE");
mDatabase->exec("pragma journal_mode=MEMORY"); mDatabase->exec("pragma journal_mode=MEMORY");
mDatabase->exec("pragma temp_store=MEMORY"); mDatabase->exec("pragma temp_store=MEMORY");
} }
catch(std::exception& e) catch(std::exception& e)
{ {
return false; return false;
} }
loadTaskTree(); loadTaskTree();
return true; return true;
} }
void Storage::close() void Storage::close()
{ {
if (mDatabase) if (mDatabase)
{ {
mDatabase.clear(); mDatabase.clear();
} }
} }
bool Storage::hasTable(const QString& tablename) bool Storage::hasTable(const QString& tablename)
{ {
SQLite::Statement tableQuery(*mDatabase, "SELECT name FROM sqlite_master WHERE type='table' AND name=:tablename"); SQLite::Statement tableQuery(*mDatabase, "SELECT name FROM sqlite_master WHERE type='table' AND name=:tablename");
tableQuery.bind(":tablename", tablename.toStdString()); tableQuery.bind(":tablename", tablename.toStdString());
return tableQuery.executeStep(); return tableQuery.executeStep();
} }
bool Storage::upgrade() bool Storage::upgrade()
{ {
int currentVersion = 0; int currentVersion = 0;
// Check if properties table is here // Check if properties table is here
if (!mDatabase) if (!mDatabase)
return false; return false;
if (!hasTable("properties")) if (!hasTable("properties"))
{ {
// If not - create it and consider database version 0 // If not - create it and consider database version 0
SQLite::Statement createQuery(*mDatabase, "create table properties(type INTEGER, value TEXT)"); SQLite::Statement createQuery(*mDatabase, "create table properties(type INTEGER, value TEXT)");
if (!createQuery.exec()) if (!createQuery.exec())
return false; return false;
// Insert version 0 // Insert version 0
SQLite::Statement insertVersionQuery(*mDatabase, "insert into properties(type, value) values(:proptype, :proptext)"); SQLite::Statement insertVersionQuery(*mDatabase, "insert into properties(type, value) values(:proptype, :proptext)");
insertVersionQuery.bind(":proptype", (int)DbProperty::Version); insertVersionQuery.bind(":proptype", (int)DbProperty::Version);
insertVersionQuery.bind(":proptext", "0"); insertVersionQuery.bind(":proptext", "0");
} }
else else
{ {
// If yes - get database version from 'dbversion' key // If yes - get database version from 'dbversion' key
SQLite::Statement versionQuery(*mDatabase, "select value from properties where type=:proptype"); SQLite::Statement versionQuery(*mDatabase, "select value from properties where type=:proptype");
versionQuery.bind(":proptype", (int)DbProperty::Version); versionQuery.bind(":proptype", (int)DbProperty::Version);
if (!versionQuery.executeStep()) if (!versionQuery.executeStep())
return false; return false;
QString dbVersionText = versionQuery.getColumn(0).getText(""); QString dbVersionText = versionQuery.getColumn(0).getText("");
if (dbVersionText.isEmpty()) if (dbVersionText.isEmpty())
return false; return false;
bool ok = false; bool ok = false;
currentVersion = dbVersionText.toInt(&ok); currentVersion = dbVersionText.toInt(&ok);
if (!ok) if (!ok)
return false; return false;
} }
// Depending on obtained version upgrade database // Depending on obtained version upgrade database
switch (currentVersion) switch (currentVersion)
{ {
case 0: case 0:
if (!upgradeFromVersion0()) if (!upgradeFromVersion0())
return false; return false;
break; break;
} }
// Update version number after upgrade // Update version number after upgrade
SQLite::Statement updateVersion(*mDatabase, "update table properties set value=:proptext where type=:proptype"); SQLite::Statement updateVersion(*mDatabase, "update table properties set value=:proptext where type=:proptype");
updateVersion.bind(":proptext", CURRENT_DBVERSION); updateVersion.bind(":proptext", CURRENT_DBVERSION);
updateVersion.bind(":proptype", (int)DbProperty::Version); updateVersion.bind(":proptype", (int)DbProperty::Version);
updateVersion.exec(); updateVersion.exec();
return true; return true;
} }
bool Storage::upgradeFromVersion0() bool Storage::upgradeFromVersion0()
{ {
/* /*
// Upgrade tasks table // Upgrade tasks table
SQLite::Statement addEncryptedTitle(*mDatabase, "alter table task add column title_encrypted BLOB"); SQLite::Statement addEncryptedTitle(*mDatabase, "alter table task add column title_encrypted BLOB");
if (!addEncryptedTitle.exec()) if (!addEncryptedTitle.exec())
@ -287,94 +287,94 @@ bool Storage::upgradeFromVersion0()
std::copy(task->children().begin(), task->children().end(), mTaskToUpgrade.begin() + tl); std::copy(task->children().begin(), task->children().end(), mTaskToUpgrade.begin() + tl);
} }
*/ */
return true; return true;
} }
bool Storage::encryptTask(PTask task) bool Storage::encryptTask(PTask task)
{ {
return false; return false;
} }
bool Storage::encryptTaskContent(PTask task) bool Storage::encryptTaskContent(PTask task)
{ {
return false;
} }
bool Storage::encryptTaskAttachment(PAttachment attachment) bool Storage::encryptTaskAttachment(PAttachment attachment)
{ {
return false; return false;
} }
PTask Storage::createTask(int index) PTask Storage::createTask(int index)
{ {
if (!mDatabase) if (!mDatabase)
return PTask(); return PTask();
SQLite::Statement insertQuery(*mDatabase, "insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, NULL, :orderid, 0, 0)"); 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(":title", "");
insertQuery.bind(":html", ""); insertQuery.bind(":html", "");
insertQuery.bind(":orderid", index); insertQuery.bind(":orderid", index);
if (insertQuery.exec()) 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
{ {
mTopTasks.insert(mTopTasks.begin() + index, result); PTask result(new Task());
// Assign new indexes for top tasks result->setId(database().getLastInsertRowid());
for (int i=0; i<mTopTasks.size(); i++) result->setIndex(index);
{ mTaskModelIdMap[result->modelId()] = result;
mTopTasks[i]->setIndex(i); mTaskIdMap[result->id()] = result;
mTopTasks[i]->save(); 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; i<mTopTasks.size(); i++)
{
mTopTasks[i]->setIndex(i);
mTopTasks[i]->save();
}
}
return result;
} }
return result; else
} return PTask();
else
return PTask();
} }
PTask Storage::createTask(PTask parent, int index) PTask Storage::createTask(PTask parent, int index)
{ {
if (!parent) if (!parent)
return createTask(index); return createTask(index);
if (!mDatabase) if (!mDatabase)
return PTask(); return PTask();
SQLite::Statement insertQuery(database(), "insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, :parentid, :orderid, 0, 0)"); 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(":title", "");
insertQuery.bind(":html", ""); insertQuery.bind(":html", "");
insertQuery.bind(":parentid", (sqlite3_int64)parent->id()); insertQuery.bind(":parentid", (sqlite3_int64)parent->id());
insertQuery.bind(":orderid", index); insertQuery.bind(":orderid", index);
if (insertQuery.exec()) 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
{ {
parent->children().insert(parent->children().begin() + index, result); PTask result(new Task());
for (int i=0; i<parent->children().size(); i++) result->setId(database().getLastInsertRowid());
{ result->setIndex(index);
parent->children()[i]->setIndex(i); result->setParent(parent);
parent->children()[i]->save(); if (index > parent->children().size())
} parent->children().push_back(result);
} else
{
parent->children().insert(parent->children().begin() + index, result);
for (int i=0; i<parent->children().size(); i++)
{
parent->children()[i]->setIndex(i);
parent->children()[i]->save();
}
}
mTaskModelIdMap[result->modelId()] = result; mTaskModelIdMap[result->modelId()] = result;
mTaskIdMap[result->id()] = result; mTaskIdMap[result->id()] = result;
return result; return result;
} }
return PTask(); return PTask();
} }
/* /*
PTask Storage::loadTask(Task::Id id, PTask parent) 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) void Storage::saveTask(PTask task, Depth depth)
{ {
if (depth == depthSingleTask) if (depth == depthSingleTask)
saveSingleTask(task); saveSingleTask(task);
else else
{ {
saveSingleTask(task); saveSingleTask(task);
TaskArray& children = task->children(); TaskArray& children = task->children();
foreach(PTask child, children) foreach(PTask child, children)
saveTask(child, depthRecursive); saveTask(child, depthRecursive);
} }
} }
bool Storage::moveTask(PTask task, PTask newParent, int indexToInsert) bool Storage::moveTask(PTask task, PTask newParent, int indexToInsert)
{ {
bool result = false; bool result = false;
task->setParent(newParent); task->setParent(newParent);
if (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; i<children.size(); i++)
{ {
children[i]->setIndex(i); SQLite::Statement updateParent(database(), "update task set parentid=:parentid where id=:id");
children[i]->save(); 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; i<children.size(); i++)
{
children[i]->setIndex(i);
children[i]->save();
}
} }
} else
else {
{ SQLite::Statement updateToRoot(database(), "update task set parentid = NULL where id=:id");
SQLite::Statement updateToRoot(database(), "update task set parentid = NULL where id=:id"); updateToRoot.bind(":id", (sqlite3_int64)task->id());
updateToRoot.bind(":id", (sqlite3_int64)task->id()); result = updateToRoot.exec();
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; i<topOfTaskTree().size(); i++)
{
topOfTaskTree()[i]->setIndex(i);
topOfTaskTree()[i]->save();
}
}
if (result) 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; i<topOfTaskTree().size(); i++)
{ {
topOfTaskTree()[i]->setIndex(i); task->save();
topOfTaskTree()[i]->save();
} }
}
if (result) return result;
{
task->save();
}
return result;
} }
bool Storage::isOpened() bool Storage::isOpened()
{ {
return mDatabase != nullptr; return mDatabase != nullptr;
} }
void Storage::save() void Storage::save()
{ {
foreach (PTask task, mTopTasks) foreach (PTask task, mTopTasks)
saveTask(task, depthRecursive); saveTask(task, depthRecursive);
database().exec("delete from task where removed = 1"); database().exec("delete from task where removed = 1");
database().exec("delete from timeline where removed = 1"); database().exec("delete from timeline where removed = 1");
database().exec("delete from file where removed = 1"); database().exec("delete from file where removed = 1");
//database().exec("delete from change where removed = 1"); //database().exec("delete from change where removed = 1");
} }
void Storage::saveSingleTask(PTask task) void Storage::saveSingleTask(PTask task)
{ {
task->save(); task->save();
} }
void Storage::loadTaskTree() 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"); 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; int currentIndex = 0;
while(q.executeStep()) while(q.executeStep())
{ {
PTask t(new Task()); PTask t(new Task());
t->load(q); t->load(q);
t->setIndex(currentIndex++); t->setIndex(currentIndex++);
mTaskModelIdMap[t->modelId()] = t; mTaskModelIdMap[t->modelId()] = t;
mTaskIdMap[t->id()] = t; mTaskIdMap[t->id()] = t;
mTopTasks.push_back(t); mTopTasks.push_back(t);
} }
foreach (PTask task, mTopTasks) foreach (PTask task, mTopTasks)
loadTaskChildren(task); loadTaskChildren(task);
} }
void Storage::loadTaskChildren(PTask 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"); 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()); q.bind(":parentid", (sqlite3_int64)task->id());
int currentIndex = 0; int currentIndex = 0;
while (q.executeStep()) while (q.executeStep())
{ {
PTask t(new Task()); PTask t(new Task());
t->load(q); t->load(q);
t->setIndex(currentIndex++); t->setIndex(currentIndex++);
t->setParent(task, false); t->setParent(task, false);
loadTaskChildren(t); loadTaskChildren(t);
mTaskModelIdMap[t->modelId()] = t; mTaskModelIdMap[t->modelId()] = t;
mTaskIdMap[t->id()] = t; mTaskIdMap[t->id()] = t;
task->children().push_back(t); task->children().push_back(t);
} }
} }
PTask Storage::findTaskByModelId(Task::ModelId id) PTask Storage::findTaskByModelId(Task::ModelId id)
{ {
auto taskIter = mTaskModelIdMap.find(id); auto taskIter = mTaskModelIdMap.find(id);
if (taskIter != mTaskModelIdMap.end()) if (taskIter != mTaskModelIdMap.end())
return *taskIter; return *taskIter;
else else
return PTask(); return PTask();
} }
PTask Storage::findTaskById(Task::Id id) PTask Storage::findTaskById(Task::Id id)
{ {
auto taskIter = mTaskIdMap.find(id); auto taskIter = mTaskIdMap.find(id);
if (taskIter != mTaskIdMap.end()) if (taskIter != mTaskIdMap.end())
return *taskIter; return *taskIter;
else else
return PTask(); return PTask();
} }
int Storage::findTaskIndexInParent(PTask task) int Storage::findTaskIndexInParent(PTask task)
{ {
if (task->parent()) if (task->parent())
return task->parent()->children().indexOf(task); return task->parent()->children().indexOf(task);
else else
return topOfTaskTree().indexOf(task); return topOfTaskTree().indexOf(task);
} }
TaskArray& Storage::topOfTaskTree() TaskArray& Storage::topOfTaskTree()
{ {
return mTopTasks; return mTopTasks;
} }
void Storage::loadAttachments(PTask task, AttachmentArray& output) 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"); 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()); q.bind(":taskid", (sqlite3_int64)task->id());
while (q.executeStep()) while (q.executeStep())
{ {
PAttachment att(new Attachment()); PAttachment att(new Attachment());
att->setId(q.getColumn(0).getInt64()); att->setId(q.getColumn(0).getInt64());
att->setFilename(q.getColumn(1).getText()); att->setFilename(q.getColumn(1).getText());
att->setTaskId(task->id()); att->setTaskId(task->id());
att->setIndex(q.getColumn(2).getInt()); att->setIndex(q.getColumn(2).getInt());
output.push_back(att); output.push_back(att);
} }
} }
void Storage::deleteAttachment(PAttachment att) void Storage::deleteAttachment(PAttachment att)
{ {
SQLite::Statement q(database(), "update file set removed=1 where id=:id"); SQLite::Statement q(database(), "update file set removed=1 where id=:id");
q.bind(":id", (sqlite3_int64)att->id()); q.bind(":id", (sqlite3_int64)att->id());
q.exec(); q.exec();
} }
void Storage::undeleteAttachment(PAttachment att) void Storage::undeleteAttachment(PAttachment att)
{ {
SQLite::Statement q(database(), "update file set removed=0 where id=:id"); SQLite::Statement q(database(), "update file set removed=0 where id=:id");
q.bind(":id", (sqlite3_int64)att->id()); q.bind(":id", (sqlite3_int64)att->id());
q.exec(); q.exec();
} }
Storage& Storage::instance() Storage& Storage::instance()
{ {
static Storage _instance; static Storage _instance;
return _instance; return _instance;
} }
void Storage::deleteTask(PTask task, DeleteOption option) void Storage::deleteTask(PTask task, DeleteOption option)
{ {
if (option != DeleteOption_FromParent) if (option != DeleteOption_FromParent)
{ {
// Remove from hash // Remove from hash
removeTask(task); removeTask(task);
} }
TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks; TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks;
siblings.removeOne(task); siblings.removeOne(task);
// Remove from database // Remove from database
if (option == DeleteOption_Total) if (option == DeleteOption_Total)
{ {
SQLite::Statement q(database(), "update task set removed = 1 where id = :id"); SQLite::Statement q(database(), "update task set removed = 1 where id = :id");
q.bind(":id", (sqlite3_int64)task->id()); q.bind(":id", (sqlite3_int64)task->id());
q.exec(); q.exec();
} }
#if 0 #if 0
int taskIndex = -1; int taskIndex = -1;
// Update indexes // Update indexes
for (int i=0; i<siblings.size(); i++) for (int i=0; i<siblings.size(); i++)
{
if (siblings[i] == task)
taskIndex = i;
Task& t = *siblings[i];
if (t.index() > task->index())
{ {
t.setIndex(t.index() - 1); if (siblings[i] == task)
t.save(); taskIndex = i;
}
}
// Remove from tree Task& t = *siblings[i];
if (taskIndex >= 0) if (t.index() > task->index())
siblings.remove(taskIndex); {
t.setIndex(t.index() - 1);
t.save();
}
}
// Remove from tree
if (taskIndex >= 0)
siblings.remove(taskIndex);
#endif #endif
} }
void Storage::undeleteTask(PTask task) void Storage::undeleteTask(PTask task)
{ {
SQLite::Statement q(database(), "update task set removed = 0 where id = :id"); SQLite::Statement q(database(), "update task set removed = 0 where id = :id");
q.bind(":id", (sqlite3_int64)task->id()); q.bind(":id", (sqlite3_int64)task->id());
q.exec(); q.exec();
// Find place where to insert task // Find place where to insert task
TaskArray& siblings = task->parent() ? task->parent()->children() : mTopTasks; 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;}); 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()) if (taskIter != siblings.end())
siblings.insert(taskIter, task); siblings.insert(taskIter, task);
else else
siblings.append(task); siblings.append(task);
mTaskModelIdMap[task->modelId()] = task; mTaskModelIdMap[task->modelId()] = task;
mTaskIdMap[task->id()] = task; mTaskIdMap[task->id()] = task;
} }
void Storage::removeTask(PTask task) void Storage::removeTask(PTask task)
{ {
auto taskModelIter = mTaskModelIdMap.find(task->modelId()); auto taskModelIter = mTaskModelIdMap.find(task->modelId());
if (taskModelIter != mTaskModelIdMap.end()) if (taskModelIter != mTaskModelIdMap.end())
mTaskModelIdMap.erase(taskModelIter); mTaskModelIdMap.erase(taskModelIter);
auto taskIter = mTaskIdMap.find(task->id()); auto taskIter = mTaskIdMap.find(task->id());
if (taskIter != mTaskIdMap.end()) if (taskIter != mTaskIdMap.end())
mTaskIdMap.erase(taskIter); mTaskIdMap.erase(taskIter);
} }

File diff suppressed because it is too large Load Diff

View File

@ -12,55 +12,55 @@
#include <map> #include <map>
#include "SQLiteCpp/Database.h" #include "SQLiteCpp/Database.h"
typedef qulonglong Id; typedef uint64_t Id;
class WorldId class WorldId
{ {
public: public:
WorldId(); WorldId();
WorldId(const WorldId& src); WorldId(const WorldId& src);
WorldId(const QString& s); WorldId(const std::string& s);
~WorldId(); ~WorldId();
WorldId& operator = (const WorldId& src); WorldId& operator = (const WorldId& src);
bool operator == (const WorldId& src); bool operator == (const WorldId& src);
bool operator < (const WorldId& src); bool operator < (const WorldId& src);
QString asString() const; std::string asString() const;
static WorldId create(); static WorldId create();
protected: protected:
QUuid mId; std::string mId;
}; };
class TimeRecord class TimeRecord
{ {
public: public:
TimeRecord(); TimeRecord();
TimeRecord(const QDateTime& startTime, const QDateTime& endTime, Id taskId); TimeRecord(const time_t& startTime, const time_t& endTime, Id taskId);
~TimeRecord(); ~TimeRecord();
QDateTime startTime() const; time_t startTime() const;
void setStartTime(const QDateTime& startTime); void setStartTime(const time_t& startTime);
QDateTime endTime() const; time_t endTime() const;
void setEndTime(const QDateTime& endTime); void setEndTime(const time_t& endTime);
int length(); int length();
Id id() const; Id id() const;
void setId(Id id); void setId(Id id);
Id taskId() const; Id taskId() const;
void setTaskId(Id id); void setTaskId(Id id);
WorldId worldId() const; WorldId worldId() const;
void setWorldId(const WorldId& id); void setWorldId(const WorldId& id);
// Save record to DB. If record is new - id() property will be set after this call. // Save record to DB. If record is new - id() property will be set after this call.
void save(); void save();
// Remove record from DB. // Remove record from DB.
void deleteRecord(); void deleteRecord();
protected: protected:
Id mId, mTaskId; Id mId, mTaskId;
WorldId mWorldId; WorldId mWorldId;
QDateTime mStartTime, mEndTime; time_t mStartTime, mEndTime;
bool mSaved; bool mSaved;
}; };
typedef QVector<TimeRecord> TimeArray; typedef QVector<TimeRecord> TimeArray;
@ -73,73 +73,73 @@ typedef std::map<int, QSharedPointer<MonthesMap> > YearsMap;
class TimeLine class TimeLine
{ {
public: public:
TimeLine(); TimeLine();
~TimeLine(); ~TimeLine();
TimeArray& data(); TimeArray& data();
// Returns total time in seconds // Returns total time in seconds
int totalTime(); int totalTime();
bool active(); bool active();
void start(); void start();
void stop(bool updateTimeline = true); void stop(bool updateTimeline = true);
void flush(bool saveToDb, const QDateTime& currentUtc); void flush(bool saveToDb, time_t currentUtc);
void load(); void load();
void save(); void save();
Id taskId(); Id taskId();
void setTaskId(Id id); void setTaskId(Id id);
// These methods work with local time // These methods work with local time
void getYears(std::set<int>& result); void getYears(std::set<int>& result);
void getMonthes(int year, std::set<int>& result); void getMonthes(int year, std::set<int>& result);
void getDays(int year, int month, std::set<int>& result); void getDays(int year, int month, std::set<int>& result);
int getTime(int year, int month, int day, std::vector<TimeRecord>* intervals); int getTime(int year, int month, int day, std::vector<TimeRecord>* intervals);
int today(); int today();
int month(); int month();
int getSum(const QDate& start, const QDate& finish); int getSum(const QDate& start, const QDate& finish);
bool duplicateDetected() const; bool duplicateDetected() const;
// Checks if specified interval has intersection // Checks if specified interval has intersection
bool hasIntersection(const TimeRecord& interval); bool hasIntersection(const TimeRecord& interval);
// Inserts new interval to timeline. Saves new interval to DB. // Inserts new interval to timeline. Saves new interval to DB.
void insertInterval(const TimeRecord& interval); void insertInterval(const TimeRecord& interval);
// Attempts to find & remove from timeline specified interval. Returns true if succeeded. Removes interval from DB too. // 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. // Search is made using start/finish time interval value - not id() value. Only whole TimeRecord can be deleted.
bool removeInterval(const TimeRecord& interval); bool removeInterval(const TimeRecord& interval);
// Attempts to find & cut interval from timeline. // 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. // 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); void cutInterval(const TimeRecord& interval);
// Searches time interval by its id. Can return NULL if search failed. // Searches time interval by its id. Can return NULL if search failed.
TimeRecord* findIntervalById(Id id); TimeRecord* findIntervalById(Id id);
// Adds 10 minutes interval starting from current time. For debug/test purposes. // Adds 10 minutes interval starting from current time. For debug/test purposes.
void putDebugRecord(); void putDebugRecord();
protected: protected:
TimeArray mData; TimeArray mData;
Id mTaskId; Id mTaskId;
bool mActive; bool mActive;
TimeRecord* mActiveTimeRecord; TimeRecord* mActiveTimeRecord;
int mTotalTime; int mTotalTime;
// Sums total time in timeline // Sums total time in timeline
int findTotalTime(); int findTotalTime();
// Builds new time interval record with specified start/finish time // Builds new time interval record with specified start/finish time
TimeRecord* makeNewRecord(const QDateTime& begin, const QDateTime& end); TimeRecord* makeNewRecord(time_t begin, time_t end);
// Looks for time record that includes specified time point // Looks for time record that includes specified time point
TimeRecord* hasTimePoint(const QDateTime& t); TimeRecord* hasTimePoint(time_t t);
// Sorts records in mData by startTime() value // Sorts records in mData by startTime() value
void sortData(); void sortData();
}; };
@ -151,139 +151,139 @@ typedef QVector<PTask> TaskArray;
class Task: public QObject class Task: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
typedef qulonglong Id; typedef uint64_t Id;
typedef quint32 ModelId; typedef uint32_t ModelId;
enum Flag enum Flag
{ {
Flag_NoTimeTracking = 1 Flag_NoTimeTracking = 1
}; };
Task(); Task();
~Task(); ~Task();
void load(SQLite::Statement& q);
Id id() const; void load(SQLite::Statement& q);
void setId(Id id);
Id parentId() const; Id id() const;
void setParentId(Id id); void setId(Id id);
WorldId worldId() const; Id parentId() const;
void setWorldId(const WorldId& id); void setParentId(Id id);
ModelId modelId() const; WorldId worldId() const;
void setModelId(ModelId id); void setWorldId(const WorldId& id);
int index() const; ModelId modelId() const;
void setIndex(int index, bool modified = true); void setModelId(ModelId id);
enum SaveOptions int index() const;
{ void setIndex(int index, bool modified = true);
Save_Automatic,
Save_Forced
};
void save(SaveOptions options = Save_Automatic); enum SaveOptions
{
Save_Automatic,
Save_Forced
};
QString html() const; void save(SaveOptions options = Save_Automatic);
void setHtml(const QString& html);
QString title() const;
void setTitle(const QString& title, bool modified = true);
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; QString path() const;
void setParent(PTask task, bool modified = true);
TaskArray& children(); PTask parent() const;
void setParent(PTask task, bool modified = true);
// Loads html and timeline
void loadContent();
bool isContentLoaded() const;
// Unloads html and timeline TaskArray& children();
void unloadContent(bool includeTimeline = true);
PTimeLine timeline(); // Loads html and timeline
void loadContent();
bool isContentLoaded() const;
// Returns true if task has attachments // Unloads html and timeline
int getAttachmentCount(); void unloadContent(bool includeTimeline = true);
void setAttachmentCount(int count);
int checkAttachments();
// Service properties used in time reporting PTimeLine timeline();
bool isChecked() const;
void setChecked(bool checked);
int getReportedTime() const; // Returns true if task has attachments
void setReportedTime(int t); int getAttachmentCount();
void setAttachmentCount(int count);
int checkAttachments();
int getChildrenReportedTime() const; // Service properties used in time reporting
void setChildrenReportedTime(int t); bool isChecked() const;
void setChecked(bool checked);
int flags() const; int getReportedTime() const;
void setFlags(int value); void setReportedTime(int t);
int cursorPosition() const; int getChildrenReportedTime() const;
void setCursorPosition(int position); void setChildrenReportedTime(int t);
/* int flags() const;
void setFlags(int value);
int cursorPosition() const;
void setCursorPosition(int position);
/*
QUndoStack* getUndoStack() const; QUndoStack* getUndoStack() const;
void setUndoStack(QUndoStack* doc); void setUndoStack(QUndoStack* doc);
*/ */
protected: protected:
QString mHtml, mTitle; QString mHtml, mTitle;
PTimeLine mTimeLine; PTimeLine mTimeLine;
Id mId, mParentId; Id mId, mParentId;
WorldId mWorldId; WorldId mWorldId;
ModelId mModelId; ModelId mModelId;
bool mTitleModified, mHtmlModified, mHtmlLoaded, mIndexModified, mParentModified; bool mTitleModified, mHtmlModified, mHtmlLoaded, mIndexModified, mParentModified;
PTask mParent; PTask mParent;
TaskArray mChildren; TaskArray mChildren;
int mIndex; int mIndex;
bool mChecked; bool mChecked;
int mReportedTime, mChildrenReportedTime; int mReportedTime, mChildrenReportedTime;
int mAttachmentCount; int mAttachmentCount;
int mFlags; int mFlags;
QTextDocument* mDocument; QTextDocument* mDocument;
int mCursorPosition; int mCursorPosition;
}; };
class Attachment class Attachment
{ {
public: public:
Attachment(); Attachment();
~Attachment(); ~Attachment();
Task::Id id(); Task::Id id();
void setId(Task::Id id); void setId(Task::Id id);
Task::Id taskId(); Task::Id taskId();
void setTaskId(Task::Id id); void setTaskId(Task::Id id);
WorldId worldId() const; WorldId worldId() const;
void setWorldId(const WorldId& id); void setWorldId(const WorldId& id);
int index(); int index();
void setIndex(int index); void setIndex(int index);
QByteArray loadContent(); QByteArray loadContent();
void saveContent(const QByteArray& content); void saveContent(const QByteArray& content);
void setFilename(const QString& filename); void setFilename(const QString& filename);
QString filename(); QString filename();
void save(); void save();
void load(); void load();
protected: protected:
Id mId, mTaskId; Id mId, mTaskId;
WorldId mWorldId; WorldId mWorldId;
QString mFilename; QString mFilename;
int mIndex; int mIndex;
}; };
typedef QSharedPointer<Attachment> PAttachment; typedef QSharedPointer<Attachment> PAttachment;