- refactoring started
This commit is contained in:
parent
e346222c6e
commit
f8d51cf95f
@ -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(×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
|
#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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1293
client/task.cpp
1293
client/task.cpp
File diff suppressed because it is too large
Load Diff
350
client/task.h
350
client/task.h
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user