- 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
#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(&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
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;

View File

@ -5,46 +5,59 @@
#include <QObject>
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

View File

@ -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();

View File

@ -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<SQLite::Database>(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<SQLite::Database>(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<SQLite::Database>(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<SQLite::Database>(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; i<mTopTasks.size(); i++)
{
mTopTasks[i]->setIndex(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; 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)
{
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; i<parent->children().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; i<parent->children().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; i<children.size(); i++)
bool result = false;
task->setParent(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; i<children.size(); i++)
{
children[i]->setIndex(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; i<topOfTaskTree().size(); i++)
{
topOfTaskTree()[i]->setIndex(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; i<topOfTaskTree().size(); i++)
{
topOfTaskTree()[i]->setIndex(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<siblings.size(); i++)
{
if (siblings[i] == task)
taskIndex = i;
Task& t = *siblings[i];
if (t.index() > task->index())
int taskIndex = -1;
// Update indexes
for (int i=0; i<siblings.size(); i++)
{
t.setIndex(t.index() - 1);
t.save();
}
}
if (siblings[i] == task)
taskIndex = i;
// Remove from tree
if (taskIndex >= 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);
}

File diff suppressed because it is too large Load Diff

View File

@ -12,55 +12,55 @@
#include <map>
#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<TimeRecord> TimeArray;
@ -73,73 +73,73 @@ typedef std::map<int, QSharedPointer<MonthesMap> > 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<int>& result);
void getMonthes(int year, 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);
// These methods work with local time
void getYears(std::set<int>& result);
void getMonthes(int year, 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 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<PTask> 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<Attachment> PAttachment;