- all storage related logic moved to Storage class + fix non removed time intervals

This commit is contained in:
Dmytro Bogovych 2021-06-05 18:13:42 +03:00
parent 45d27bf48d
commit fc0d501c05
15 changed files with 817 additions and 684 deletions

View File

@ -180,23 +180,23 @@ void AttachmentsList::importFile()
QByteArray compressed = qCompress(content); QByteArray compressed = qCompress(content);
// Put it to Attachment instance // Put it to Attachment instance
PAttachment att(new Attachment());
att->setTaskId(mTask->id());
att->setIndex(mModel->rowCount());
QFileInfo fi(filename); QFileInfo fi(filename);
att->setFilename(fi.fileName()); PAttachment att(new Attachment());
att->setTaskId(mTask->id())
.setIndex(mModel->rowCount())
.setFilename(fi.fileName());
// Save everything // Save everything
att->save(); att->saveMetadata()
att->saveContent(compressed); .saveContent(compressed);
mModel->addItem(att); mModel->addItem(att);
} }
f.close(); f.close();
} }
// Refresh hasAttachments property on owner task // Refresh AttachmentsCount property on owner task
mTask->checkAttachments(); mTask->preloadAttachmentCount();
} }
void AttachmentsList::exportFile() void AttachmentsList::exportFile()
@ -242,13 +242,13 @@ void AttachmentsList::deleteFile()
for (int row = index.row() + 1; row < mModel->rowCount(); row++) for (int row = index.row() + 1; row < mModel->rowCount(); row++)
{ {
Attachment& att = *mModel->itemAt(row); Attachment& att = *mModel->itemAt(row);
att.setIndex(att.index() - 1); att.setIndex(att.index() - 1)
att.save(); .saveMetadata();
} }
} }
// Refresh hasAttachment property value on task // Refresh hasAttachment property value on task
mTask->checkAttachments(); mTask->preloadAttachmentCount();
} }
void AttachmentsList::renameFile() void AttachmentsList::renameFile()

View File

@ -1,2 +1,2 @@
// Auto generated file ! Please do not edit ! // Auto generated file ! Please do not edit !
#define APP_BUILD_NUMBER 177 #define APP_BUILD_NUMBER 214

View File

@ -310,13 +310,15 @@ bool EscapeKeyEventFilter::eventFilter(QObject *obj, QEvent * event)
return false; return false;
} }
QString password::load() QString password::loadFromKeychain()
{ {
QKeychain::ReadPasswordJob job(APPNAME); QKeychain::ReadPasswordJob job(APPNAME);
job.setKey(KEY_PASSWORD); job.setKey(KEY_PASSWORD);
job.setAutoDelete(false); job.setAutoDelete(false);
QEventLoop loop; QEventLoop loop;
job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
// Start job
job.start(); job.start();
loop.exec(); loop.exec();
if (job.error()) if (job.error())
@ -325,12 +327,14 @@ QString password::load()
return job.textData(); return job.textData();
} }
bool password::save(const QString& password) bool password::saveToKeychain(const QString &password)
{ {
QKeychain::WritePasswordJob job(APPNAME); QKeychain::WritePasswordJob job(APPNAME);
job.setKey(KEY_PASSWORD); job.setKey(KEY_PASSWORD);
job.setAutoDelete(false); job.setAutoDelete(false);
job.setTextData(password); job.setTextData(password);
// Start job
QEventLoop loop; QEventLoop loop;
job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
job.start(); job.start();

View File

@ -94,11 +94,12 @@ namespace helper
}; };
// Keychain helper class
class password class password
{ {
public: public:
static QString load(); static QString loadFromKeychain();
static bool save(const QString& password); static bool saveToKeychain(const QString& password);
}; };
extern std::string app_version(); extern std::string app_version();

View File

@ -18,37 +18,6 @@ int main(int argc, char *argv[])
QString folder = QFileInfo(path).absoluteDir().path(); QString folder = QFileInfo(path).absoluteDir().path();
Storage::instance().setPath(path); Storage::instance().setPath(path);
// Check if file exists at all
/*if (!QFile::exists(path))
{
// Show dialog that requests database path
}
else
{
// Check if password is available
if (Settings::instance().data()[KEY_AUTOSAVE_PASSWORD].toBool())
{
QString password = helper::password::load();
if (password.isEmpty())
{
// Ask about password
}
else
{
Storage::instance().setKey(password);
if (!Storage::instance().open())
{
askDbPassword(tr("Invalid password, please try again."));
}
else
QApplication::postEvent(this, new UiInitEvent());
}
}
else
askDbPassword(QString());
}*/
MainWindow w; MainWindow w;
w.layout()->invalidate(); w.layout()->invalidate();

View File

@ -91,19 +91,11 @@ void MainWindow::attachDatabase()
{ {
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool()) if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
{ {
QString password = helper::password::load(); QString password = helper::password::loadFromKeychain();
if (password.isEmpty()) if (password.isEmpty())
askDbPassword(); askDbPassword();
else else
{ onDbPasswordEntered(password);
Storage::instance().setKey(password);
if (!Storage::instance().open())
{
askDbPassword(tr("Invalid password, please try again."));
}
else
QApplication::postEvent(this, new ClientEvent<UiInitId>());
}
} }
else else
askDbPassword(QString()); askDbPassword(QString());
@ -465,7 +457,7 @@ void MainWindow::preferences()
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool() == false) if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool() == false)
{ {
// Reset password in keychain // Reset password in keychain
helper::password::save(QString("")); helper::password::saveToKeychain(QString(""));
//mSettings->data()[KEY_PASSWORD] = NOPASSWORDSTRING; //mSettings->data()[KEY_PASSWORD] = NOPASSWORDSTRING;
mSettings->save(); mSettings->save();
@ -1672,7 +1664,7 @@ void MainWindow::onDbPasswordEntered(const QString& password)
{ {
// Save password to keychain if needed // Save password to keychain if needed
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool()) if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
helper::password::save(password); helper::password::saveToKeychain(password);
// Try to open database // Try to open database
Storage::instance().setKey(password); Storage::instance().setKey(password);
@ -1694,7 +1686,7 @@ void MainWindow::onNewDbPasswordEntered(const QString& password)
{ {
// Save password to keychain if needed // Save password to keychain if needed
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool()) if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
helper::password::save(password); helper::password::saveToKeychain(password);
// Configure storage // Configure storage
Storage::instance().setKey(password); Storage::instance().setKey(password);

View File

@ -75,7 +75,7 @@ void PreferencesDlg::accepted()
{ {
bool savePassword = ui->mAutosavePasswordCheckbox->isChecked(); bool savePassword = ui->mAutosavePasswordCheckbox->isChecked();
mSettings.data()[KEY_AUTOSAVE_PASSWORD] = savePassword; mSettings.data()[KEY_AUTOSAVE_PASSWORD] = savePassword;
helper::password::save(savePassword ? Storage::instance().key() : QString("")); helper::password::saveToKeychain(savePassword ? Storage::instance().key() : QString(""));
mSettings.data()[KEY_SHOW_SECONDS] = ui->mShowSecondsCheckbox->isChecked(); mSettings.data()[KEY_SHOW_SECONDS] = ui->mShowSecondsCheckbox->isChecked();
mSettings.data()[KEY_ASK_BEFORE_DELETE] = ui->mAskBeforeDeleteCheckbox->isChecked(); mSettings.data()[KEY_ASK_BEFORE_DELETE] = ui->mAskBeforeDeleteCheckbox->isChecked();

View File

@ -135,36 +135,68 @@ bool Storage::create()
// Synctime & timestamp are always milliseconds from the start of UNIX epoch. // Synctime & timestamp are always milliseconds from the start of UNIX epoch.
// Timeline // Timeline
mDatabase->exec("CREATE TABLE timeline (id INTEGER PRIMARY KEY, removed INTEGER, " mDatabase->exec("CREATE TABLE timeline ("
"taskid INTEGER, starttime TEXT, endtime TEXT, timestamp INTEGER)"); "id INTEGER PRIMARY KEY, "
"removed INTEGER, "
"taskid INTEGER, "
"starttime TEXT, "
"endtime TEXT, "
"timestamp INTEGER)");
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, " mDatabase->exec("CREATE TABLE task ("
"id INTEGER PRIMARY KEY, parentid INTEGER, orderid INTEGER, title TEXT, html TEXT, " "type INTEGER, "
"flags INTEGER, timestamp INTEGER)"); "removed INTEGER, "
"id INTEGER PRIMARY KEY, "
"parentid INTEGER, "
"orderid INTEGER, "
"title TEXT, "
"html TEXT, "
"flags INTEGER, "
"timestamp INTEGER)");
mDatabase->exec("CREATE INDEX task_parentid_index ON task(parentid ASC)"); mDatabase->exec("CREATE INDEX task_parentid_index ON task(parentid ASC)");
// Tasks history // Tasks history
mDatabase->exec("CREATE TABLE history_task (removed_old INTEGER, removed_new INTEGER, " mDatabase->exec("CREATE TABLE history_task ("
"timestamp_old INTEGER, timestamp_new INTEGER, " "removed_old INTEGER, "
"id INTEGER PRIMARY KEY, " "removed_new INTEGER, "
"parent_id_old INTEGER, parent_id_new INTEGER, " "timestamp_old INTEGER, "
"order_id_old INTEGER, order_id_new INTEGER, " "timestamp_new INTEGER, "
"diff_title TEXT, diff_html TEXT, " "id INTEGER PRIMARY KEY, "
"flags_old INTEGER, flags_new INTEGER)"); "parent_id_old INTEGER, "
"parent_id_new INTEGER, "
"order_id_old INTEGER, "
"order_id_new INTEGER, "
"diff_title TEXT, "
"diff_html TEXT, "
"flags_old INTEGER, "
"flags_new INTEGER)");
// Attachments // Attachments
mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, " mDatabase->exec("CREATE TABLE file ("
"filename TEXT, content BLOB, orderid INTEGER, timestamp INTEGER)"); "id INTEGER PRIMARY KEY, "
"removed INTEGER, "
"taskid INTEGER, "
"filename TEXT, "
"content BLOB, "
"orderid INTEGER, "
"timestamp INTEGER)");
mDatabase->exec("CREATE TABLE history_file (id INTEGER PRIMARY KEY, " mDatabase->exec("CREATE TABLE history_file ("
"removed_old INTEGER, removed_new INTEGER, " "id INTEGER PRIMARY KEY, "
"taskid_old INTEGER, taskid_new INTEGER, " "removed_old INTEGER, "
"filename_old TEXT, filename_new TEXT," "removed_new INTEGER, "
"content BLOB, " "taskid_old INTEGER, "
"order_id_old INTEGER, order_id_new INTEGER, " "taskid_new INTEGER, "
"timestamp_old INTEGER, timestamp_new INTEGER)"); "filename_old TEXT, "
"filename_new TEXT,"
"content BLOB, "
"order_id_old INTEGER, "
"order_id_new INTEGER, "
"timestamp_old INTEGER, "
"timestamp_new INTEGER)");
mDatabase->exec("CREATE INDEX file_taskid_index ON file(taskid ASC)"); mDatabase->exec("CREATE INDEX file_taskid_index ON file(taskid ASC)");
@ -319,17 +351,17 @@ bool Storage::upgradeFromVersion0()
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; return false;
} }
bool Storage::encryptTaskAttachment(PAttachment attachment) bool Storage::encryptTaskAttachment(PAttachment /*attachment*/)
{ {
return false; return false;
} }
@ -347,8 +379,9 @@ PTask Storage::createTask(int index)
if (insertQuery.exec()) if (insertQuery.exec())
{ {
PTask result(new Task()); PTask result(new Task());
result->setId(database().getLastInsertRowid()); result->setId(database().getLastInsertRowid())
result->setIndex(index); .setIndex(index);
mTaskModelIdMap[result->modelId()] = result; mTaskModelIdMap[result->modelId()] = result;
mTaskIdMap[result->id()] = result; mTaskIdMap[result->id()] = result;
if (index > mTopTasks.size()) if (index > mTopTasks.size())
@ -359,8 +392,8 @@ PTask Storage::createTask(int index)
// Assign new indexes for top tasks // Assign new indexes for top tasks
for (int i=0; i<mTopTasks.size(); i++) for (int i=0; i<mTopTasks.size(); i++)
{ {
mTopTasks[i]->setIndex(i); mTopTasks[i]->setIndex(i)
mTopTasks[i]->save(); .save();
} }
} }
return result; return result;
@ -369,7 +402,7 @@ PTask Storage::createTask(int index)
return PTask(); return PTask();
} }
PTask Storage::createTask(PTask parent, int index) PTask Storage::createTask(const PTask& parent, int index)
{ {
if (!parent) if (!parent)
return createTask(index); return createTask(index);
@ -394,8 +427,8 @@ PTask Storage::createTask(PTask parent, int index)
parent->children().insert(parent->children().begin() + index, result); parent->children().insert(parent->children().begin() + index, result);
for (int i=0; i<parent->children().size(); i++) for (int i=0; i<parent->children().size(); i++)
{ {
parent->children()[i]->setIndex(i); parent->children()[i]->setIndex(i)
parent->children()[i]->save(); .save();
} }
} }
@ -470,8 +503,8 @@ bool Storage::moveTask(PTask task, PTask newParent, int indexToInsert)
for (int i=0; i<topOfTaskTree().size(); i++) for (int i=0; i<topOfTaskTree().size(); i++)
{ {
topOfTaskTree()[i]->setIndex(i); topOfTaskTree()[i]->setIndex(i)
topOfTaskTree()[i]->save(); .save();
} }
} }
@ -504,6 +537,15 @@ void Storage::saveSingleTask(PTask task)
task->save(); task->save();
} }
void Storage::loadTaskRecord(Task& t, SQLite::Statement& q)
{
t.mId = q.getColumn(0).getInt64();
t.mTitle = q.getColumn(1).getText();
t.mIndex = q.getColumn(2).getInt();
t.mFlags = q.getColumn(3).getInt();
t.mAttachmentCount = q.getColumn(4).getInt();
}
void Storage::loadTaskTree() void Storage::loadTaskTree()
{ {
mTopTasks.clear(); mTopTasks.clear();
@ -513,7 +555,7 @@ void Storage::loadTaskTree()
while(q.executeStep()) while(q.executeStep())
{ {
PTask t(new Task()); PTask t(new Task());
t->load(q); loadTaskRecord(*t, q);
t->setIndex(currentIndex++); t->setIndex(currentIndex++);
mTaskModelIdMap[t->modelId()] = t; mTaskModelIdMap[t->modelId()] = t;
mTaskIdMap[t->id()] = t; mTaskIdMap[t->id()] = t;
@ -532,9 +574,10 @@ void Storage::loadTaskChildren(PTask task)
while (q.executeStep()) while (q.executeStep())
{ {
PTask t(new Task()); PTask t(new Task());
t->load(q); loadTaskRecord(*t, q);
t->setIndex(currentIndex++); t->setIndex(currentIndex++)
t->setParent(task, false); .setParent(task, false);
loadTaskChildren(t); loadTaskChildren(t);
mTaskModelIdMap[t->modelId()] = t; mTaskModelIdMap[t->modelId()] = t;
mTaskIdMap[t->id()] = t; mTaskIdMap[t->id()] = t;
@ -580,10 +623,10 @@ void Storage::loadAttachments(PTask task, AttachmentArray& output)
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()); .setFilename(q.getColumn(1).getText())
att->setTaskId(task->id()); .setTaskId(task->id())
att->setIndex(q.getColumn(2).getInt()); .setIndex(q.getColumn(2).getInt());
output.push_back(att); output.push_back(att);
} }
} }
@ -602,6 +645,114 @@ void Storage::undeleteAttachment(PAttachment att)
q.exec(); q.exec();
} }
Id Storage::saveMetadata(const Attachment &a)
{
if (a.mId)
{
SQLite::Statement q(Storage::instance().database(), "update file set filename = :filename, orderid = :orderid where id = :id");
q.bind(":filename", a.mFilename.toStdString().c_str());
q.bind(":orderid", a.mIndex);
q.bind(":id", (sqlite3_int64)a.mId);
if (q.exec())
return a.mId;
}
else
{
SQLite::Statement q(Storage::instance().database(), "insert into file (filename, taskid, orderid, removed) values(:filename, :taskid, :orderid, 0)");
q.bind(":filename", a.mFilename.toStdString().c_str());
q.bind(":taskid", (sqlite3_int64)a.mTaskId);
q.bind(":orderid", a.mIndex);
if (q.exec())
{
return Storage::instance().database().getLastInsertRowid();
}
}
return Id(0);
}
void Storage::saveContent(const Attachment &a, const QByteArray& content)
{
SQLite::Statement q(Storage::instance().database(), "update file set content = :content where id = :id");
q.bind(":content", content.data(), content.size());
q.bind(":id", (sqlite3_int64)a.mId);
if (q.exec())
;
}
QByteArray Storage::loadContent(const Attachment &a)
{
SQLite::Statement q(Storage::instance().database(), "select content from file where id = :id");
q.bind(":id", (sqlite3_int64)a.mId);
if (q.executeStep())
return QByteArray((const char*)q.getColumn(0).getBlob(), q.getColumn(0).size());
else
return QByteArray();
}
Id Storage::saveTimeRecord(const TimeRecord &r)
{
if (!r.id())
{
SQLite::Statement q(Storage::instance().database(), "insert into timeline(id, starttime, endtime, taskid, removed) values (NULL, :starttime, :endtime, :taskid, :removed)");
q.bind(":starttime", helper::chrono::timeToStr(r.startTime()));
q.bind(":endtime", helper::chrono::timeToStr(r.endTime()));
q.bind(":taskid", static_cast<sqlite3_int64>(r.taskId()));
q.bind(":removed", 0);
if (q.exec())
return Storage::instance().database().getLastInsertRowid();
}
else
{
SQLite::Statement q(Storage::instance().database(),
"update timeline set starttime = :starttime, endtime = :endtime, taskid = :taskid, removed = 0 where id = :id");
q.bind(":starttime", helper::chrono::timeToStr(r.startTime()));
q.bind(":endtime", helper::chrono::timeToStr(r.endTime()));
q.bind(":taskid", static_cast<sqlite3_int64>(r.taskId()));
q.bind(":id", static_cast<sqlite3_int64>(r.id()));
if (q.exec())
return r.id();
}
return (Id)0;
}
void Storage::deleteTimeRecord(const TimeRecord &r)
{
SQLite::Statement q(Storage::instance().database(), "update timeline set removed = 1 where id = :id");
q.bind(":id", static_cast<sqlite3_int64>(r.id()));
q.exec();
}
void Storage::loadTimeLine(TimeLine& l)
{
SQLite::Statement q(Storage::instance().database(), "select id, starttime, endtime from timeline where (taskid = :taskid) and ((removed is null) or (removed != 1)) order by id asc");
q.bind(":taskid", static_cast<sqlite3_int64>(l.taskId()));
while (q.executeStep())
{
time_t start = helper::chrono::strToTime(q.getColumn(1).getText());
time_t stop = helper::chrono::strToTime(q.getColumn(2).getText());
TimeRecord tr;
tr.setId(q.getColumn(0).getInt64())
.setStartTime(start)
.setEndTime(stop)
.setTaskId(l.taskId());
l.data().push_back(tr);
}
// Sort time intervals
l.sortData();
// Find current total time length
l.mTotalTime = l.findTotalTime();
}
void Storage::saveTimeLime(const TimeLine& l)
{
}
Storage& Storage::instance() Storage& Storage::instance()
{ {
@ -610,7 +761,7 @@ Storage& Storage::instance()
} }
void Storage::deleteTask(PTask task, DeleteOption option) void Storage::deleteTask(const PTask& task, DeleteOption option)
{ {
if (option != DeleteOption_FromParent) if (option != DeleteOption_FromParent)
{ {
@ -651,7 +802,7 @@ void Storage::deleteTask(PTask task, DeleteOption option)
#endif #endif
} }
void Storage::undeleteTask(PTask task) void Storage::undeleteTask(const 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());
@ -670,7 +821,7 @@ void Storage::undeleteTask(PTask task)
mTaskIdMap[task->id()] = task; mTaskIdMap[task->id()] = task;
} }
void Storage::removeTask(PTask task) void Storage::removeTask(const PTask& task)
{ {
auto taskModelIter = mTaskModelIdMap.find(task->modelId()); auto taskModelIter = mTaskModelIdMap.find(task->modelId());
if (taskModelIter != mTaskModelIdMap.end()) if (taskModelIter != mTaskModelIdMap.end())
@ -681,3 +832,69 @@ void Storage::removeTask(PTask task)
mTaskIdMap.erase(taskIter); mTaskIdMap.erase(taskIter);
} }
void Storage::loadTaskContent(Task& task)
{
SQLite::Statement htmlQuery(Storage::instance().database(), "select html from task where id = :id");
htmlQuery.bind(":id", (sqlite3_int64)task.mId);
if (htmlQuery.executeStep())
{
task.mHtml = htmlQuery.getColumn(0).getText();
task.mHtmlLoaded = true;
task.mHtmlModified = false;
}
if (!task.mTimeLine)
{
task.mTimeLine = PTimeLine(new TimeLine());
task.mTimeLine->setTaskId(task.mId);
task.mTimeLine->load();
}
}
void Storage::saveTask(const Task& task, SaveOptions options)
{
const Task& t = task;
if (!t.mTitleModified && !t.mHtmlModified && !t.mIndexModified && !t.mParentModified && options == Save_Automatic)
return;
const char* queryText = nullptr;
// Minimize changes to DB
if (t.mTitleModified && t.mHtmlModified)
queryText = "update task set parentid = :parentid, flags = :flags, title = :title, html = :html, orderid = :orderid where id = :id";
else
if (t.mTitleModified)
queryText = "update task set parentid = :parentid, flags = :flags, title = :title, orderid = :orderid where id = :id";
else
if (t.mHtmlModified)
queryText = "update task set parentid = :parentid, flags = :flags, html = :html, orderid = :orderid where id = :id";
else
queryText = "update task set parentid = :parentid, flags = :flags, orderid = :orderid where id = :id";
SQLite::Statement q(Storage::instance().database(), queryText);
if (t.mParent)
q.bind(":parentid", (sqlite3_int64)t.mParent->id());
else
q.bind(":parentid");
q.bind(":flags", t.mFlags);
if (t.mTitleModified)
q.bind(":title", t.mTitle.toStdString());
if (t.mHtmlModified)
q.bind(":html", t.mHtml.toStdString());
q.bind(":id", (sqlite3_int64)t.mId);
q.bind(":orderid", t.mIndex);
q.exec();
}
int Storage::findAttachmentCountOnTask(const Task &t)
{
SQLite::Statement q(Storage::instance().database(), "select count(*) from file where (taskid = :taskid) and ((removed = 0) or (removed is null))");
q.bind(":taskid", (sqlite3_int64)t.id());
if (q.executeStep())
return q.getColumn(0).getInt();
else
return 0;
}

View File

@ -9,86 +9,117 @@
class Storage class Storage
{ {
public: public:
Storage(); Storage();
~Storage(); ~Storage();
QString path(); QString path();
void setPath(const QString& path); void setPath(const QString& path);
QString key(); QString key();
void setKey(const QString& key); void setKey(const QString& key);
SQLite::Database& database(); SQLite::Database& database();
// New database. Returns true or false depending on success of call. // New database. Returns true or false depending on success of call.
bool create(); bool create();
// Open database, preload task tree. Returns true or false depending on success of operation. // Open database, preload task tree. Returns true or false depending on success of operation.
bool open(); bool open();
// Close database // Close database
void close(); void close();
// Ensure database is latest version // Ensure database is latest version
bool upgrade(); bool upgrade();
PTask createTask(int index); PTask createTask(int index);
PTask createTask(PTask parent, int index); PTask createTask(const PTask& parent, int index);
PTask loadTask(Task::Id id, PTask parent); // PTask loadTask(Task::Id id, PTask parent);
// Remove task from database // Remove task from database
enum DeleteOption enum DeleteOption
{ {
DeleteOption_Total, DeleteOption_Total,
DeleteOption_FromParentAndHash, DeleteOption_FromParentAndHash,
DeleteOption_FromParent DeleteOption_FromParent
}; };
void deleteTask(PTask task, DeleteOption option); void deleteTask(const PTask& task, DeleteOption option);
// Undelete task // Undelete task
void undeleteTask(PTask task); void undeleteTask(const PTask& task);
// Remove task from internal dictionaries - it makes task unadressable by its id/model id // Remove task from internal dictionaries - it makes task unadressable by its id/model id
void removeTask(PTask task); void removeTask(const PTask& task);
enum Depth void loadTaskContent(Task& task);
{ void loadTaskMetadata(Task& task);
depthSingleTask,
depthRecursive
};
void saveTask(PTask task, Depth depth);
bool moveTask(PTask task, PTask newParent, int indexToInsert);
bool isOpened();
void save();
TaskArray& topOfTaskTree();
PTask findTaskByModelId(Task::ModelId id);
PTask findTaskById(Task::Id id);
int findTaskIndexInParent(PTask task);
void loadAttachments(PTask task, AttachmentArray& output);
void deleteAttachment(PAttachment att);
void undeleteAttachment(PAttachment att);
static Storage& instance(); // Save changes in single task
enum SaveOptions
{
Save_Automatic,
Save_Forced
};
void saveTask(const Task& task, SaveOptions options = Save_Automatic);
// Save Task hierarchy
enum Depth
{
depthSingleTask,
depthRecursive
};
void saveTask(PTask task, Depth depth);
int findAttachmentCountOnTask(const Task& t);
// Move task
bool moveTask(PTask task, PTask newParent, int indexToInsert);
bool isOpened();
void save();
TaskArray& topOfTaskTree();
PTask findTaskByModelId(Task::ModelId id);
PTask findTaskById(Task::Id id);
int findTaskIndexInParent(PTask task);
// Attachments
void loadAttachments(PTask task, AttachmentArray& output);
void deleteAttachment(PAttachment att);
void undeleteAttachment(PAttachment att);
Id saveMetadata(const Attachment& attachment);
void saveContent(const Attachment& attachment, const QByteArray& content);
QByteArray loadContent(const Attachment& attachment);
// Timeline
Id saveTimeRecord(const TimeRecord& r);
void deleteTimeRecord(const TimeRecord& r);
void loadTimeLine(TimeLine& l);
void saveTimeLime(const TimeLine& l);
static Storage& instance();
protected: protected:
QString mPath, mKey; QString mPath, mKey;
QSharedPointer<SQLite::Database> mDatabase; QSharedPointer<SQLite::Database> mDatabase;
TaskArray mTopTasks; TaskArray mTopTasks;
typedef QMap<Task::ModelId, PTask> TaskModelIdMap; typedef QMap<Task::ModelId, PTask> TaskModelIdMap;
TaskModelIdMap mTaskModelIdMap; TaskModelIdMap mTaskModelIdMap;
typedef QMap<Task::Id, PTask> TaskIdMap; typedef QMap<Task::Id, PTask> TaskIdMap;
TaskIdMap mTaskIdMap; TaskIdMap mTaskIdMap;
TaskArray mTaskToUpgrade; TaskArray mTaskToUpgrade;
bool hasTable(const QString& tablename); bool hasTable(const QString& tablename);
void saveSingleTask(PTask task); void saveSingleTask(PTask task);
void loadTaskTree(); void loadTaskRecord(Task& t, SQLite::Statement& q);
void loadTaskChildren(PTask task); void loadTaskTree();
bool upgradeFromVersion0(); void loadTaskChildren(PTask task);
bool encryptTask(PTask task); bool upgradeFromVersion0();
bool encryptTaskContent(PTask task); bool encryptTask(PTask task);
bool encryptTaskAttachment(PAttachment attachment); bool encryptTaskContent(PTask task);
bool encryptTaskAttachment(PAttachment attachment);
}; };
#endif // STORAGE_H #endif // STORAGE_H

View File

@ -86,9 +86,10 @@ time_t TimeRecord::endTime() const
return mEndTime; return mEndTime;
} }
void TimeRecord::setEndTime(const time_t &endTime) TimeRecord& TimeRecord::setEndTime(const time_t &endTime)
{ {
mEndTime = endTime; mEndTime = endTime;
return *this;
} }
time_t TimeRecord::startTime() const time_t TimeRecord::startTime() const
@ -96,9 +97,10 @@ time_t TimeRecord::startTime() const
return mStartTime; return mStartTime;
} }
void TimeRecord::setStartTime(const time_t &startTime) TimeRecord& TimeRecord::setStartTime(const time_t &startTime)
{ {
mStartTime = startTime; mStartTime = startTime;
return *this;
} }
int TimeRecord::length() int TimeRecord::length()
@ -111,9 +113,10 @@ Id TimeRecord::id() const
return mId; return mId;
} }
void TimeRecord::setId(Id id) TimeRecord& TimeRecord::setId(Id id)
{ {
mId = id; mId = id;
return *this;
} }
Id TimeRecord::taskId() const Id TimeRecord::taskId() const
@ -121,9 +124,10 @@ Id TimeRecord::taskId() const
return mTaskId; return mTaskId;
} }
void TimeRecord::setTaskId(Id id) TimeRecord& TimeRecord::setTaskId(Id id)
{ {
mTaskId = id; mTaskId = id;
return *this;
} }
WorldId TimeRecord::worldId() const WorldId TimeRecord::worldId() const
@ -131,41 +135,22 @@ WorldId TimeRecord::worldId() const
return mWorldId; return mWorldId;
} }
void TimeRecord::setWorldId(const WorldId& id) TimeRecord& TimeRecord::setWorldId(const WorldId& id)
{ {
mWorldId = id; mWorldId = id;
return *this;
} }
void TimeRecord::save() void TimeRecord::save()
{ {
if (!mId) auto id = Storage::instance().saveTimeRecord(*this);
{ if (id)
SQLite::Statement q(Storage::instance().database(), "insert into timeline(id, starttime, endtime, taskid, removed) values (NULL, :starttime, :endtime, :taskid, :removed)"); mId = id;
q.bind(":starttime", helper::chrono::timeToStr(mStartTime));
q.bind(":endtime", helper::chrono::timeToStr(mEndTime));
q.bind(":taskid", static_cast<sqlite3_int64>(mTaskId));
q.bind(":removed", 0);
if (q.exec())
mId = Storage::instance().database().getLastInsertRowid();
}
else
{
SQLite::Statement q(Storage::instance().database(),
"update timeline set starttime = :starttime, endtime = :endtime, taskid = :taskid, removed = 0 where id = :id");
q.bind(":starttime", helper::chrono::timeToStr(mStartTime));
q.bind(":endtime", helper::chrono::timeToStr(mEndTime));
q.bind(":taskid", static_cast<sqlite3_int64>(mTaskId));
q.bind(":id", static_cast<sqlite3_int64>(mId));
q.exec();
}
} }
void TimeRecord::deleteRecord() void TimeRecord::deleteRecord()
{ {
SQLite::Statement q(Storage::instance().database(), "update timeline set removed = 1 where id = :id"); Storage::instance().deleteTimeRecord(*this);
q.bind(":id", static_cast<sqlite3_int64>(mId));
q.exec();
} }
// -------------------- TimeLine -------------------- // -------------------- TimeLine --------------------
@ -218,10 +203,10 @@ void TimeLine::start()
TimeRecord* TimeLine::makeNewRecord(time_t beginTime, time_t endTime) TimeRecord* TimeLine::makeNewRecord(time_t beginTime, time_t endTime)
{ {
TimeRecord tr; TimeRecord tr;
tr.setStartTime(beginTime); tr.setStartTime(beginTime)
tr.setEndTime(endTime); .setEndTime(endTime)
tr.setTaskId(mTaskId); .setTaskId(mTaskId)
tr.save(); .save();
mData.push_back(tr); mData.push_back(tr);
Id intervalId = tr.id(); Id intervalId = tr.id();
@ -388,11 +373,13 @@ void TimeLine::cutInterval(const TimeRecord& interval)
// So cut interval will split it to 2 new intervals // So cut interval will split it to 2 new intervals
// Also this operation will end loop // Also this operation will end loop
TimeRecord toInsert(*iter); // Backup current interval TimeRecord toInsert(*iter); // Backup current interval
iter->setEndTime(interval.startTime() - 1); iter->setEndTime(interval.startTime() - 1)
iter->save(); .save();
toInsert.setStartTime(interval.endTime() + 1);
toInsert.setStartTime(interval.endTime() + 1)
.save();
mData.insert(++iter, toInsert); mData.insert(++iter, toInsert);
toInsert.save();
done = true; done = true;
break; break;
} }
@ -407,8 +394,8 @@ void TimeLine::cutInterval(const TimeRecord& interval)
else else
{ {
// Current interval starts before cut interval but finishes in cut interval // Current interval starts before cut interval but finishes in cut interval
iter->setEndTime(interval.startTime() - 1); iter->setEndTime(interval.startTime() - 1)
iter->save(); .save();
done = true; done = true;
break; break;
} }
@ -425,7 +412,10 @@ void TimeLine::cutInterval(const TimeRecord& interval)
[=] (const TimeRecord& tr) [=] (const TimeRecord& tr)
{ return tr.id() == interval.id();}); { return tr.id() == interval.id();});
if (iter != mData.end()) if (iter != mData.end())
{
Storage::instance().deleteTimeRecord(*iter);
mData.erase(iter); mData.erase(iter);
}
} }
} }
@ -465,8 +455,8 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc)
TimeRecord* tr = hasTimePoint(currentUtc); TimeRecord* tr = hasTimePoint(currentUtc);
if (tr && tr != mActiveTimeRecord) if (tr && tr != mActiveTimeRecord)
{ {
mActiveTimeRecord->setEndTime(currentUtc); mActiveTimeRecord->setEndTime(currentUtc)
mActiveTimeRecord->save(); .save();
mActiveTimeRecord = nullptr; mActiveTimeRecord = nullptr;
} }
if (saveToDb && mActiveTimeRecord) if (saveToDb && mActiveTimeRecord)
@ -486,26 +476,7 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc)
void TimeLine::load() void TimeLine::load()
{ {
SQLite::Statement q(Storage::instance().database(), "select id, starttime, endtime from timeline where (taskid = :taskid) and ((removed is null) or (removed != 1)) order by id asc"); Storage::instance().loadTimeLine(*this);
q.bind(":taskid", static_cast<sqlite3_int64>(mTaskId));
while (q.executeStep())
{
time_t start = helper::chrono::strToTime(q.getColumn(1).getText());
time_t stop = helper::chrono::strToTime(q.getColumn(2).getText());
TimeRecord tr;
tr.setId(q.getColumn(0).getInt64());
tr.setStartTime(start);
tr.setEndTime(stop);
tr.setTaskId(mTaskId);
mData.push_back(tr);
}
// Sort time intervals
sortData();
// Find current total time length
mTotalTime = findTotalTime();
} }
void TimeLine::save() void TimeLine::save()
@ -518,9 +489,10 @@ Id TimeLine::taskId()
return mTaskId; return mTaskId;
} }
void TimeLine::setTaskId(Id id) TimeLine& TimeLine::setTaskId(Id id)
{ {
mTaskId = id; mTaskId = id;
return *this;
} }
TimeArray& TimeLine::data() TimeArray& TimeLine::data()
@ -668,10 +640,10 @@ int TimeLine::getTime(int year, int month, int day, std::vector<TimeRecord>* int
if (intervals) if (intervals)
{ {
TimeRecord resultingRecord; TimeRecord resultingRecord;
resultingRecord.setStartTime(dayBegin); resultingRecord.setStartTime(dayBegin)
resultingRecord.setEndTime(dayEnd); .setEndTime(dayEnd)
resultingRecord.setId(tr.id()); .setId(tr.id())
resultingRecord.setTaskId(tr.taskId()); .setTaskId(tr.taskId());
intervals->push_back(resultingRecord); intervals->push_back(resultingRecord);
} }
result++; result++;
@ -822,26 +794,17 @@ Task::Task()
} }
Task::~Task() Task::~Task()
{ {}
}
void Task::load(SQLite::Statement &q)
{
mId = q.getColumn(0).getInt64();
mTitle = q.getColumn(1).getText();
mIndex = q.getColumn(2).getInt();
mFlags = q.getColumn(3).getInt();
mAttachmentCount = q.getColumn(4).getInt();
}
Task::Id Task::id() const Task::Id Task::id() const
{ {
return mId; return mId;
} }
void Task::setId(Id id) Task& Task::setId(Id id)
{ {
mId = id; mId = id;
return *this;
} }
Task::Id Task::parentId() const Task::Id Task::parentId() const
@ -849,13 +812,14 @@ Task::Id Task::parentId() const
return mParentId; return mParentId;
} }
void Task::setParentId(Id id) Task& Task::setParentId(Id id)
{ {
if (mParentId != id) if (mParentId != id)
{ {
mParentModified = true; mParentModified = true;
mParentId = id; mParentId = id;
} }
return *this;
} }
WorldId Task::worldId() const WorldId Task::worldId() const
@ -863,9 +827,10 @@ WorldId Task::worldId() const
return mWorldId; return mWorldId;
} }
void Task::setWorldId(const WorldId& id) Task& Task::setWorldId(const WorldId& id)
{ {
mWorldId = id; mWorldId = id;
return *this;
} }
Task::ModelId Task::modelId() const Task::ModelId Task::modelId() const
@ -873,9 +838,10 @@ Task::ModelId Task::modelId() const
return mModelId; return mModelId;
} }
void Task::setModelId(ModelId id) Task& Task::setModelId(ModelId id)
{ {
mModelId = id; mModelId = id;
return *this;
} }
int Task::index() const int Task::index() const
@ -883,7 +849,7 @@ int Task::index() const
return mIndex; return mIndex;
} }
void Task::setIndex(int index, bool modified) Task& Task::setIndex(int index, bool modified)
{ {
if (index != mIndex) if (index != mIndex)
{ {
@ -891,6 +857,7 @@ void Task::setIndex(int index, bool modified)
mIndexModified = true; mIndexModified = true;
mIndex = index; mIndex = index;
} }
return *this;
} }
QString Task::html() const QString Task::html() const
@ -898,13 +865,14 @@ QString Task::html() const
return mHtml; return mHtml;
} }
void Task::setHtml(const QString &html) Task& Task::setHtml(const QString &html)
{ {
if (mHtml != html) if (mHtml != html)
{ {
mHtml = html; mHtml = html;
mHtmlModified = true; mHtmlModified = true;
} }
return *this;
} }
QString Task::title() const QString Task::title() const
@ -912,7 +880,7 @@ QString Task::title() const
return mTitle; return mTitle;
} }
void Task::setTitle(const QString &title, bool modified) Task& Task::setTitle(const QString &title, bool modified)
{ {
if (mTitle != title) if (mTitle != title)
{ {
@ -920,61 +888,20 @@ void Task::setTitle(const QString &title, bool modified)
if (modified) if (modified)
mTitleModified = true; mTitleModified = true;
} }
return *this;
} }
void Task::save(SaveOptions options) void Task::save()
{ {
if (!mTitleModified && !mHtmlModified && !mIndexModified && !mParentModified && options == Save_Automatic) Storage::instance().saveTask(*this);
return;
const char* queryText = NULL;
if (mTitleModified && mHtmlModified)
queryText = "update task set parentid = :parentid, flags = :flags, title = :title, html = :html, orderid = :orderid where id = :id";
else
if (mTitleModified)
queryText = "update task set parentid = :parentid, flags = :flags, title = :title, orderid = :orderid where id = :id";
else
if (mHtmlModified)
queryText = "update task set parentid = :parentid, flags = :flags, html = :html, orderid = :orderid where id = :id";
else
queryText = "update task set parentid = :parentid, flags = :flags, orderid = :orderid where id = :id";
SQLite::Statement q(Storage::instance().database(), queryText);
if (mParent)
q.bind(":parentid", (sqlite3_int64)mParent->id());
else
q.bind(":parentid");
q.bind(":flags", mFlags);
if (mTitleModified)
q.bind(":title", mTitle.toStdString());
if (mHtmlModified)
q.bind(":html", mHtml.toStdString());
q.bind(":id", (sqlite3_int64)mId);
q.bind(":orderid", mIndex);
q.exec();
mIndexModified = mTitleModified = mHtmlModified = false; mIndexModified = mTitleModified = mHtmlModified = false;
} }
/*void Task::load() void Task::saveAnyway()
{ {
SQLite::Statement q(Storage::instance().database(), "select parentid, title, html, orderid from task where (id = :id) and (type = 0)"); Storage::instance().saveTask(*this, Storage::Save_Forced);
q.bind(":id", (sqlite3_int64)mId); mIndexModified = mTitleModified = mHtmlModified = false;
if (q.executeStep()) }
{
mParentId = q.getColumn(0).getInt64();
mTitle = q.getColumn(1).getText();
mHtml = q.getColumn(2).getText();
mIndex = q.getColumn(3).getInt();
}
mTitleModified = mHtmlModified = false;
mHtmlLoaded = true;
checkAttachments();
}*/
QString Task::path() const QString Task::path() const
{ {
@ -990,7 +917,7 @@ PTask Task::parent() const
return mParent; return mParent;
} }
void Task::setParent(PTask task, bool modified) Task& Task::setParent(PTask task, bool modified)
{ {
if (mParent != task) if (mParent != task)
{ {
@ -1002,6 +929,7 @@ void Task::setParent(PTask task, bool modified)
if (modified) if (modified)
mTitleModified = true; // To force save() mTitleModified = true; // To force save()
} }
return *this;
} }
TaskArray& Task::children() TaskArray& Task::children()
@ -1011,21 +939,7 @@ TaskArray& Task::children()
void Task::loadContent() void Task::loadContent()
{ {
SQLite::Statement htmlQuery(Storage::instance().database(), "select html from task where id = :id"); Storage::instance().loadTaskContent(*this);
htmlQuery.bind(":id", (sqlite3_int64)mId);
if (htmlQuery.executeStep())
{
mHtml = htmlQuery.getColumn(0).getText();
mHtmlLoaded = true;
mHtmlModified = false;
}
if (!mTimeLine)
{
mTimeLine = PTimeLine(new TimeLine());
mTimeLine->setTaskId(mId);
mTimeLine->load();
}
} }
bool Task::isContentLoaded() const bool Task::isContentLoaded() const
@ -1051,20 +965,15 @@ int Task::getAttachmentCount()
return mAttachmentCount; return mAttachmentCount;
} }
void Task::setAttachmentCount(int count) Task& Task::setAttachmentCount(int count)
{ {
mAttachmentCount = count; mAttachmentCount = count;
return *this;
} }
int Task::checkAttachments() int Task::preloadAttachmentCount()
{ {
SQLite::Statement q(Storage::instance().database(), "select count(*) from file where (taskid = :taskid) and ((removed = 0) or (removed is null))"); mAttachmentCount = Storage::instance().findAttachmentCountOnTask(*this);
q.bind(":taskid", (sqlite3_int64)mId);
if (q.executeStep())
mAttachmentCount = q.getColumn(0).getInt();
else
mAttachmentCount = 0;
return mAttachmentCount; return mAttachmentCount;
} }
@ -1073,9 +982,10 @@ bool Task::isChecked() const
return mChecked; return mChecked;
} }
void Task::setChecked(bool checked) Task& Task::setChecked(bool checked)
{ {
mChecked = checked; mChecked = checked;
return *this;
} }
int Task::getReportedTime() const int Task::getReportedTime() const
@ -1083,9 +993,10 @@ int Task::getReportedTime() const
return mReportedTime; return mReportedTime;
} }
void Task::setReportedTime(int t) Task& Task::setReportedTime(int t)
{ {
mReportedTime = t; mReportedTime = t;
return *this;
} }
int Task::getChildrenReportedTime() const int Task::getChildrenReportedTime() const
@ -1093,9 +1004,10 @@ int Task::getChildrenReportedTime() const
return mChildrenReportedTime; return mChildrenReportedTime;
} }
void Task::setChildrenReportedTime(int t) Task& Task::setChildrenReportedTime(int t)
{ {
mChildrenReportedTime = t; mChildrenReportedTime = t;
return *this;
} }
int Task::flags() const int Task::flags() const
@ -1103,10 +1015,12 @@ int Task::flags() const
return mFlags; return mFlags;
} }
void Task::setFlags(int value) Task& Task::setFlags(int value)
{ {
mFlags = value; mFlags = value;
save(Save_Forced); saveAnyway();
return *this;
} }
int Task::cursorPosition() const int Task::cursorPosition() const
@ -1114,24 +1028,12 @@ int Task::cursorPosition() const
return mCursorPosition; return mCursorPosition;
} }
void Task::setCursorPosition(int position) Task& Task::setCursorPosition(int position)
{ {
mCursorPosition = position; mCursorPosition = position;
return *this;
} }
/*
QTextDocument* Task::getTextDocument() const
{
return mDocument;
}
void Task::setTextDocument(QTextDocument* doc)
{
mDocument = doc;
if (mDocument)
mDocument->setParent(this);
}
*/
// ----------- Attachment ------------- // ----------- Attachment -------------
Attachment::Attachment() Attachment::Attachment()
:mId(0), mTaskId(0) :mId(0), mTaskId(0)
@ -1149,9 +1051,10 @@ Task::Id Attachment::id()
return mId; return mId;
} }
void Attachment::setId(Task::Id id) Attachment& Attachment::setId(Task::Id id)
{ {
mId = id; mId = id;
return *this;
} }
Task::Id Attachment::taskId() Task::Id Attachment::taskId()
@ -1159,9 +1062,10 @@ Task::Id Attachment::taskId()
return mTaskId; return mTaskId;
} }
void Attachment::setTaskId(Task::Id id) Attachment& Attachment::setTaskId(Task::Id id)
{ {
mTaskId = id; mTaskId = id;
return *this;
} }
WorldId Attachment::worldId() const WorldId Attachment::worldId() const
@ -1169,9 +1073,10 @@ WorldId Attachment::worldId() const
return mWorldId; return mWorldId;
} }
void Attachment::setWorldId(const WorldId& id) Attachment& Attachment::setWorldId(const WorldId& id)
{ {
mWorldId = id; mWorldId = id;
return *this;
} }
int Attachment::index() int Attachment::index()
@ -1179,34 +1084,28 @@ int Attachment::index()
return mIndex; return mIndex;
} }
void Attachment::setIndex(int index) Attachment& Attachment::setIndex(int index)
{ {
//TODO: introduce mIndexModified field and corresponding login in save() //TODO: introduce mIndexModified field and corresponding login in save()
mIndex = index; mIndex = index;
return *this;
} }
QByteArray Attachment::loadContent() QByteArray Attachment::loadContent()
{ {
SQLite::Statement q(Storage::instance().database(), "select content from file where id = :id"); return Storage::instance().loadContent(*this);
q.bind(":id", (sqlite3_int64)mId);
if (q.executeStep())
return QByteArray((const char*)q.getColumn(0).getBlob(), q.getColumn(0).size());
else
return QByteArray();
} }
void Attachment::saveContent(const QByteArray& content) Attachment& Attachment::saveContent(const QByteArray& content)
{ {
SQLite::Statement q(Storage::instance().database(), "update file set content = :content where id = :id"); Storage::instance().saveContent(*this, content);
q.bind(":content", content.data(), content.size()); return *this;
q.bind(":id", (sqlite3_int64)mId);
if (q.exec())
;
} }
void Attachment::setFilename(const QString& filename) Attachment& Attachment::setFilename(const QString& filename)
{ {
mFilename = filename; mFilename = filename;
return *this;
} }
QString Attachment::filename() QString Attachment::filename()
@ -1214,33 +1113,16 @@ QString Attachment::filename()
return mFilename; return mFilename;
} }
// mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, filename TEXT, content BLOB, orderid INTEGER, synctime TEXT)"); Attachment& Attachment::saveMetadata()
void Attachment::save()
{ {
if (mId) auto id = Storage::instance().saveMetadata(*this);
{ if (id != 0)
SQLite::Statement q(Storage::instance().database(), "update file set filename = :filename, orderid = :orderid where id = :id"); mId = id;
q.bind(":filename", mFilename.toStdString().c_str());
q.bind(":orderid", mIndex);
q.bind(":id", (sqlite3_int64)mId);
if (q.exec()) return *this;
;
}
else
{
SQLite::Statement q(Storage::instance().database(), "insert into file (filename, taskid, orderid, removed) values(:filename, :taskid, :orderid, 0)");
q.bind(":filename", mFilename.toStdString().c_str());
q.bind(":taskid", (sqlite3_int64)mTaskId);
q.bind(":orderid", mIndex);
if (q.exec())
{
mId = Storage::instance().database().getLastInsertRowid();
}
}
} }
void Attachment::load() Attachment& Attachment::loadMetadata()
{ {
return *this;
} }

View File

@ -40,16 +40,21 @@ public:
~TimeRecord(); ~TimeRecord();
time_t startTime() const; time_t startTime() const;
void setStartTime(const time_t& startTime); TimeRecord& setStartTime(const time_t& startTime);
time_t endTime() const; time_t endTime() const;
void setEndTime(const time_t& endTime); TimeRecord& setEndTime(const time_t& endTime);
int length(); int length();
Id id() const; Id id() const;
void setId(Id id); TimeRecord& setId(Id id);
Id taskId() const; Id taskId() const;
void setTaskId(Id id); TimeRecord& setTaskId(Id id);
WorldId worldId() const; WorldId worldId() const;
void setWorldId(const WorldId& id); TimeRecord& 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();
@ -74,6 +79,7 @@ typedef std::map<int, QSharedPointer<MonthesMap> > YearsMap;
class TimeLine class TimeLine
{ {
friend class Storage;
public: public:
TimeLine(); TimeLine();
~TimeLine(); ~TimeLine();
@ -91,7 +97,7 @@ public:
void save(); void save();
Id taskId(); Id taskId();
void setTaskId(Id id); TimeLine& setTaskId(Id id);
// These methods work with local time // These methods work with local time
// Returns set of available years in timeline // Returns set of available years in timeline
@ -168,6 +174,8 @@ class Task: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
friend class Storage;
typedef uint64_t Id; typedef uint64_t Id;
typedef uint32_t ModelId; typedef uint32_t ModelId;
enum Flag enum Flag
@ -181,37 +189,32 @@ public:
void load(SQLite::Statement& q); void load(SQLite::Statement& q);
Id id() const; Id id() const;
void setId(Id id); Task& setId(Id id);
Id parentId() const; Id parentId() const;
void setParentId(Id id); Task& setParentId(Id id);
WorldId worldId() const; WorldId worldId() const;
void setWorldId(const WorldId& id); Task& setWorldId(const WorldId& id);
ModelId modelId() const; ModelId modelId() const;
void setModelId(ModelId id); Task& setModelId(ModelId id);
int index() const; int index() const;
void setIndex(int index, bool modified = true); Task& setIndex(int index, bool modified = true);
enum SaveOptions void save();
{ void saveAnyway();
Save_Automatic,
Save_Forced
};
void save(SaveOptions options = Save_Automatic);
QString html() const; QString html() const;
void setHtml(const QString& html); Task& setHtml(const QString& html);
QString title() const; QString title() const;
void setTitle(const QString& title, bool modified = true); Task& setTitle(const QString& title, bool modified = true);
QString path() const; QString path() const;
PTask parent() const; PTask parent() const;
void setParent(PTask task, bool modified = true); Task& setParent(PTask task, bool modified = true);
TaskArray& children(); TaskArray& children();
@ -226,24 +229,24 @@ public:
// Returns true if task has attachments // Returns true if task has attachments
int getAttachmentCount(); int getAttachmentCount();
void setAttachmentCount(int count); Task& setAttachmentCount(int count);
int checkAttachments(); int preloadAttachmentCount();
// Service properties used in time reporting // Service properties used in time reporting
bool isChecked() const; bool isChecked() const;
void setChecked(bool checked); Task& setChecked(bool checked);
int getReportedTime() const; int getReportedTime() const;
void setReportedTime(int t); Task& setReportedTime(int t);
int getChildrenReportedTime() const; int getChildrenReportedTime() const;
void setChildrenReportedTime(int t); Task& setChildrenReportedTime(int t);
int flags() const; int flags() const;
void setFlags(int value); Task& setFlags(int value);
int cursorPosition() const; int cursorPosition() const;
void setCursorPosition(int position); Task& setCursorPosition(int position);
/* /*
QUndoStack* getUndoStack() const; QUndoStack* getUndoStack() const;
@ -270,30 +273,31 @@ protected:
class Attachment class Attachment
{ {
friend class Storage;
public: public:
Attachment(); Attachment();
~Attachment(); ~Attachment();
Task::Id id(); Task::Id id();
void setId(Task::Id id); Attachment& setId(Task::Id id);
Task::Id taskId(); Task::Id taskId();
void setTaskId(Task::Id id); Attachment& setTaskId(Task::Id id);
WorldId worldId() const; WorldId worldId() const;
void setWorldId(const WorldId& id); Attachment& setWorldId(const WorldId& id);
int index(); int index();
void setIndex(int index); Attachment& setIndex(int index);
QByteArray loadContent(); QByteArray loadContent();
void saveContent(const QByteArray& content); Attachment& saveContent(const QByteArray& content);
void setFilename(const QString& filename); Attachment& setFilename(const QString& filename);
QString filename(); QString filename();
void save(); Attachment& saveMetadata();
void load(); Attachment& loadMetadata();
protected: protected:
Id mId, mTaskId; Id mId, mTaskId;
WorldId mWorldId; WorldId mWorldId;

View File

@ -7,7 +7,7 @@
#include <QByteArray> #include <QByteArray>
TaskAction::TaskAction(PTask task) TaskAction::TaskAction(PTask task)
:mTask(task) :mTask(task)
{ {
} }
@ -17,7 +17,7 @@ TaskAction::~TaskAction()
PTask TaskAction::task() const PTask TaskAction::task() const
{ {
return mTask; return mTask;
} }
// ------------ History ------------------ // ------------ History ------------------
@ -33,161 +33,158 @@ ChangesHistory::~ChangesHistory()
void ChangesHistory::setTaskTreeModel(TaskTreeModel* model) void ChangesHistory::setTaskTreeModel(TaskTreeModel* model)
{ {
mTaskModel = model; mTaskModel = model;
} }
void ChangesHistory::setAttachmentsModel(AttachmentsListModel* model) void ChangesHistory::setAttachmentsModel(AttachmentsListModel* model)
{ {
mAttachmentsModel = model; mAttachmentsModel = model;
} }
TaskTreeModel* ChangesHistory::taskTreeModel() const TaskTreeModel* ChangesHistory::taskTreeModel() const
{ {
return mTaskModel; return mTaskModel;
} }
AttachmentsListModel* ChangesHistory::attachmentsModel() const AttachmentsListModel* ChangesHistory::attachmentsModel() const
{ {
return mAttachmentsModel; return mAttachmentsModel;
} }
void ChangesHistory::add(PTaskAction action) void ChangesHistory::add(PTaskAction action)
{ {
// See if there are "undo"ed actions in the list now // See if there are "undo"ed actions in the list now
if (mRollbackCount > 0) if (mRollbackCount > 0)
{ {
// Just delete from list - these changes are not in DB already // Just delete from list - these changes are not in DB already
mActionList.erase(mActionList.begin() + mActionList.size() - mRollbackCount, mActionList.end()); mActionList.erase(mActionList.begin() + mActionList.size() - mRollbackCount, mActionList.end());
mRollbackCount = 0; mRollbackCount = 0;
} }
// Apply change // Apply change
if (action->commit(mTaskModel, mAttachmentsModel)) if (action->commit(mTaskModel, mAttachmentsModel))
mActionList.push_back(action); mActionList.push_back(action);
} }
void ChangesHistory::undo() void ChangesHistory::undo()
{ {
// If there are actions that can be "undo" // If there are actions that can be "undo"
if (mActionList.size() - mRollbackCount > 0) if (mActionList.size() - mRollbackCount > 0)
{
mRollbackCount++;
PTaskAction& action = mActionList[mActionList.size() - mRollbackCount];
if (action->rollback(mTaskModel, mAttachmentsModel))
{ {
mRollbackCount++;
PTaskAction& action = mActionList[mActionList.size() - mRollbackCount];
if (action->rollback(mTaskModel, mAttachmentsModel))
{
}
} }
}
} }
void ChangesHistory::redo() void ChangesHistory::redo()
{ {
if (mRollbackCount > 0) if (mRollbackCount > 0)
{
PTaskAction& action = mActionList[mActionList.size() - mRollbackCount];
mRollbackCount--;
if (action->commit(mTaskModel, mAttachmentsModel))
{ {
PTaskAction& action = mActionList[mActionList.size() - mRollbackCount];
mRollbackCount--;
if (action->commit(mTaskModel, mAttachmentsModel))
{
}
} }
}
} }
bool ChangesHistory::canRedo() const bool ChangesHistory::canRedo() const
{ {
return mRollbackCount > 0; return mRollbackCount > 0;
} }
bool ChangesHistory::canUndo() const bool ChangesHistory::canUndo() const
{ {
return mRollbackCount < mActionList.size(); return mRollbackCount < mActionList.size();
} }
static ChangesHistory HistoryStaticInstance; static ChangesHistory HistoryStaticInstance;
ChangesHistory& ChangesHistory::instance() ChangesHistory& ChangesHistory::instance()
{ {
return HistoryStaticInstance; return HistoryStaticInstance;
} }
// ------------ ImportAttachmentAction ---- // ------------ ImportAttachmentAction ----
#define ImportAttachmentId int(1) #define ImportAttachmentId int(1)
ImportAttachmentAction::ImportAttachmentAction(PTask task, const QString &path, int index) ImportAttachmentAction::ImportAttachmentAction(PTask task, const QString &path, int index)
:TaskAction(task), mPath(path), mIndex(index) :TaskAction(task), mPath(path), mIndex(index)
{} {}
ImportAttachmentAction::~ImportAttachmentAction() ImportAttachmentAction::~ImportAttachmentAction()
{} {}
bool ImportAttachmentAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel) bool ImportAttachmentAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
QFile f(mPath); QFile f(mPath);
f.open(QFile::ReadOnly); f.open(QFile::ReadOnly);
if (!f.isOpen()) if (!f.isOpen())
return false; return false;
// Get data from file // Get data from file
QByteArray content = f.readAll(); QByteArray content = f.readAll();
// Compress them // Compress them
QByteArray compressed = qCompress(content); QByteArray compressed = qCompress(content);
// Put it to Attachment instance // Put it to Attachment instance
mAttachment = PAttachment(new Attachment()); QFileInfo fi(mPath);
mAttachment->setTaskId(mTask->id()); mAttachment = PAttachment(new Attachment());
mAttachment->setIndex(mIndex); mAttachment->setTaskId(mTask->id())
.setIndex(mIndex)
.setFilename(fi.fileName())
.saveMetadata()
.saveContent(compressed);
QFileInfo fi(mPath); return true;
mAttachment->setFilename(fi.fileName());
// Save everything
mAttachment->save();
mAttachment->saveContent(compressed);
return true;
} }
bool ImportAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel) bool ImportAttachmentAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
Storage::instance().deleteAttachment(mAttachment); Storage::instance().deleteAttachment(mAttachment);
return true; return true;
} }
PAttachment ImportAttachmentAction::attachment() const PAttachment ImportAttachmentAction::attachment() const
{ {
return mAttachment; return mAttachment;
} }
// ------------ RenameAttachmentAction --- // ------------ RenameAttachmentAction ---
#define RenameAttachmentId int(2) #define RenameAttachmentId int(2)
RenameAttachmentAction::RenameAttachmentAction(PTask task, PAttachment attachment, const QString &newname) RenameAttachmentAction::RenameAttachmentAction(PTask task, PAttachment attachment, const QString &newname)
:TaskAction(task), mAttachment(attachment), mNewName(newname) :TaskAction(task), mAttachment(attachment), mNewName(newname)
{ {
mName = mAttachment->filename(); mName = mAttachment->filename();
} }
RenameAttachmentAction::~RenameAttachmentAction() RenameAttachmentAction::~RenameAttachmentAction()
{ {
} }
bool RenameAttachmentAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel) bool RenameAttachmentAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* attModel)
{ {
mAttachment->setFilename(mNewName); mAttachment->setFilename(mNewName)
mAttachment->save(); .saveMetadata();
if (attModel) if (attModel)
{ {
int row = attModel->findRow(mAttachment); int row = attModel->findRow(mAttachment);
QModelIndex index = attModel->index(row, 0); QModelIndex index = attModel->index(row, 0);
attModel->dataChanged(index, index); attModel->dataChanged(index, index);
} }
return true; return true;
} }
bool RenameAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel) bool RenameAttachmentAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
mAttachment->setFilename(mName); mAttachment->setFilename(mName)
mAttachment->save(); .saveMetadata();
return true; return true;
} }
// ------------ DeleteAttachmentAction --- // ------------ DeleteAttachmentAction ---
@ -195,70 +192,68 @@ bool RenameAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListM
#define DeleteAttachmentId int(3) #define DeleteAttachmentId int(3)
DeleteAttachmentAction::DeleteAttachmentAction(PTask task, QModelIndexList& mil) DeleteAttachmentAction::DeleteAttachmentAction(PTask task, QModelIndexList& mil)
:TaskAction(task), mIndexList(mil) :TaskAction(task), mIndexList(mil)
{} {}
DeleteAttachmentAction::~DeleteAttachmentAction() DeleteAttachmentAction::~DeleteAttachmentAction()
{} {}
bool DeleteAttachmentAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel) bool DeleteAttachmentAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* attModel)
{ {
if (!attModel) if (!attModel)
return false; return false;
mAttachments.clear(); mAttachments.clear();
foreach (const QModelIndex& index, mIndexList) foreach (const QModelIndex& index, mIndexList)
{ {
if (!index.isValid()) if (!index.isValid())
continue; continue;
PAttachment att = attModel->itemAt(index.row()); PAttachment att = attModel->itemAt(index.row());
// Remove from DB // Remove from DB
Storage::instance().deleteAttachment(att); Storage::instance().deleteAttachment(att);
// Remove from model // Remove from model
attModel->removeItem(index.row()); attModel->removeItem(index.row());
mAttachments.push_back(att); mAttachments.push_back(att);
} }
// Iterate other items and decrease their DB table's orderid field // Iterate other items and decrease their DB table's orderid field
for (int row = 0; row < attModel->rowCount(); row++) for (int row = 0; row < attModel->rowCount(); row++)
{ {
Attachment& att = *attModel->itemAt(row); Attachment& att = *attModel->itemAt(row);
att.setIndex(row); att.setIndex(row)
att.save(); .saveMetadata();
} }
return true; return true;
} }
bool DeleteAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel) bool DeleteAttachmentAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* attModel)
{ {
while (!mAttachments.isEmpty()) while (!mAttachments.isEmpty())
{ {
auto iter = std::min_element(mAttachments.begin(), mAttachments.end(), [] (const PAttachment& a, const PAttachment& b) { return a->index() < b->index();}); auto iter = std::min_element(mAttachments.begin(), mAttachments.end(), [] (const PAttachment& a, const PAttachment& b) { return a->index() < b->index();});
PAttachment attToUndelete = *iter; PAttachment attToUndelete = *iter;
// Restore attachment in database // Restore attachment in database
Storage::instance().undeleteAttachment(attToUndelete); Storage::instance().undeleteAttachment(attToUndelete);
attModel->addItem(attToUndelete); attModel->addItem(attToUndelete);
}
} return true;
//Storage::instance().undeleteAttachment(mAttachment);
return true;
} }
// ------------ NewTaskAction ------------ // ------------ NewTaskAction ------------
#define NewTaskId int(4) #define NewTaskId int(4)
NewTaskAction::NewTaskAction(PTask parent, int index, const QString &title) NewTaskAction::NewTaskAction(PTask parent, int index, const QString &title)
:TaskAction(PTask()), mParent(parent), mIndex(index), mTitle(title) :TaskAction(PTask()), mParent(parent), mIndex(index), mTitle(title)
{} {}
NewTaskAction::~NewTaskAction() NewTaskAction::~NewTaskAction()
@ -266,50 +261,50 @@ NewTaskAction::~NewTaskAction()
bool NewTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool NewTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mParent); QModelIndex parentIndex = taskModel->getIndex(mParent);
taskModel->beginInsertRows(parentIndex, mIndex, mIndex); taskModel->beginInsertRows(parentIndex, mIndex, mIndex);
//taskModel->rowCount(parentIndex), taskModel->rowCount(parentIndex)); //taskModel->rowCount(parentIndex), taskModel->rowCount(parentIndex));
} }
if (!mTask) if (!mTask)
{ {
mTask = Storage::instance().createTask(mParent, mIndex); mTask = Storage::instance().createTask(mParent, mIndex);
mTask->setTitle(mTitle); mTask->setTitle(mTitle);
mTask->save(); mTask->save();
} }
else else
Storage::instance().undeleteTask(mTask); Storage::instance().undeleteTask(mTask);
if (taskModel) if (taskModel)
taskModel->endInsertRows(); taskModel->endInsertRows();
return true; return true;
} }
bool NewTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool NewTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
if (taskModel) if (taskModel)
{ {
// Get parent index // Get parent index
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
int row = Storage::instance().findTaskIndexInParent(mTask); int row = Storage::instance().findTaskIndexInParent(mTask);
taskModel->beginRemoveRows(parentIndex, row, row); taskModel->beginRemoveRows(parentIndex, row, row);
} }
Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total); Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total);
if (taskModel) if (taskModel)
{ {
taskModel->endRemoveRows(); taskModel->endRemoveRows();
} }
return true; return true;
} }
// ------------ RenameTaskAction --------- // ------------ RenameTaskAction ---------
RenameTaskAction::RenameTaskAction(PTask task, const QString &newTitle) RenameTaskAction::RenameTaskAction(PTask task, const QString &newTitle)
:TaskAction(task), mNewTitle(newTitle) :TaskAction(task), mNewTitle(newTitle)
{ {
mTitle = mTask->title(); mTitle = mTask->title();
} }
RenameTaskAction::~RenameTaskAction() RenameTaskAction::~RenameTaskAction()
@ -319,38 +314,38 @@ RenameTaskAction::~RenameTaskAction()
bool RenameTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool RenameTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
mTask->setTitle(mNewTitle); mTask->setTitle(mNewTitle);
mTask->save(); mTask->save();
if (taskModel) if (taskModel)
{ {
QModelIndex index = taskModel->getIndex(mTask); QModelIndex index = taskModel->getIndex(mTask);
if (index.isValid()) if (index.isValid())
taskModel->dataChanged(index ,index); taskModel->dataChanged(index ,index);
} }
return true; return true;
} }
bool RenameTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool RenameTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
mTask->setTitle(mTitle); mTask->setTitle(mTitle);
mTask->save(); mTask->save();
if (taskModel) if (taskModel)
{ {
QModelIndex index = taskModel->getIndex(mTask); QModelIndex index = taskModel->getIndex(mTask);
if (index.isValid()) if (index.isValid())
taskModel->dataChanged(index, index); taskModel->dataChanged(index, index);
} }
return true; return true;
} }
// ------------ MoveTaskAction ------------ // ------------ MoveTaskAction ------------
MoveTaskAction::MoveTaskAction(PTask task, PTask newParent, int newIndex) MoveTaskAction::MoveTaskAction(PTask task, PTask newParent, int newIndex)
:TaskAction(task), mNewIndex(newIndex), mNewParent(newParent) :TaskAction(task), mNewIndex(newIndex), mNewParent(newParent)
{ {
mIndex = mTask->index(); mIndex = mTask->index();
mParent = mTask->parent(); mParent = mTask->parent();
} }
MoveTaskAction::~MoveTaskAction() MoveTaskAction::~MoveTaskAction()
@ -359,75 +354,75 @@ MoveTaskAction::~MoveTaskAction()
bool MoveTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool MoveTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
taskModel->layoutAboutToBeChanged(); taskModel->layoutAboutToBeChanged();
// Detach it from old parent (if exists) // Detach it from old parent (if exists)
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
int row = Storage::instance().findTaskIndexInParent(mTask); int row = Storage::instance().findTaskIndexInParent(mTask);
taskModel->beginRemoveRows(parentIndex, row, row); taskModel->beginRemoveRows(parentIndex, row, row);
} }
// Remove task from memory structures only // Remove task from memory structures only
Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent); Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent);
if (taskModel) if (taskModel)
taskModel->endRemoveRows(); taskModel->endRemoveRows();
// Move task // Move task
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mNewParent); QModelIndex parentIndex = taskModel->getIndex(mNewParent);
taskModel->beginInsertRows(parentIndex, mNewIndex, mNewIndex); taskModel->beginInsertRows(parentIndex, mNewIndex, mNewIndex);
} }
Storage::instance().moveTask(mTask, mNewParent, mNewIndex); Storage::instance().moveTask(mTask, mNewParent, mNewIndex);
if (taskModel) if (taskModel)
taskModel->endInsertRows(); taskModel->endInsertRows();
taskModel->layoutChanged(); taskModel->layoutChanged();
return true; return true;
} }
bool MoveTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool MoveTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mNewParent); QModelIndex parentIndex = taskModel->getIndex(mNewParent);
// Tell about removing of row // Tell about removing of row
taskModel->beginRemoveRows(parentIndex, mNewIndex, mNewIndex); taskModel->beginRemoveRows(parentIndex, mNewIndex, mNewIndex);
} }
// Delete from parent // Delete from parent
Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent); Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent);
if (taskModel) if (taskModel)
taskModel->endRemoveRows(); taskModel->endRemoveRows();
// Reload task if needed // Reload task if needed
mTask->loadContent(); mTask->loadContent();
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mParent); QModelIndex parentIndex = taskModel->getIndex(mParent);
taskModel->beginInsertRows(parentIndex, mIndex, mIndex); taskModel->beginInsertRows(parentIndex, mIndex, mIndex);
} }
// Move task back // Move task back
Storage::instance().moveTask(mTask, mParent, mIndex); Storage::instance().moveTask(mTask, mParent, mIndex);
if (taskModel) if (taskModel)
taskModel->endInsertRows(); taskModel->endInsertRows();
return true; return true;
} }
// ------- IncreaseLevelAction ----------- // ------- IncreaseLevelAction -----------
IncreaseLevelAction::IncreaseLevelAction(PTask task) IncreaseLevelAction::IncreaseLevelAction(PTask task)
:TaskAction(task) :TaskAction(task)
{ {
mOldParent = task->parent(); mOldParent = task->parent();
mOldIndex = task->index(); mOldIndex = task->index();
} }
IncreaseLevelAction::~IncreaseLevelAction() IncreaseLevelAction::~IncreaseLevelAction()
@ -435,9 +430,9 @@ IncreaseLevelAction::~IncreaseLevelAction()
} }
bool IncreaseLevelAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool IncreaseLevelAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
/* if (taskModel) /* if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
int row = Storage::instance().findTaskIndexInParent(mTask); int row = Storage::instance().findTaskIndexInParent(mTask);
@ -448,12 +443,12 @@ bool IncreaseLevelAction::commit(TaskTreeModel* taskModel, AttachmentsListModel*
if (taskModel) if (taskModel)
taskModel->endRemoveRows(); taskModel->endRemoveRows();
*/ */
return true; return true;
} }
bool IncreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool IncreaseLevelAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
/* /*
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
@ -463,12 +458,12 @@ bool IncreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListMode
if (taskModel) if (taskModel)
taskModel->endInsertRows(); taskModel->endInsertRows();
*/ */
return true; return true;
} }
// ------- DecreaseLevelAction ----------- // ------- DecreaseLevelAction -----------
DecreaseLevelAction::DecreaseLevelAction(PTask task) DecreaseLevelAction::DecreaseLevelAction(PTask task)
:TaskAction(task) :TaskAction(task)
{ {
} }
@ -478,9 +473,9 @@ DecreaseLevelAction::~DecreaseLevelAction()
} }
bool DecreaseLevelAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool DecreaseLevelAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
/* if (taskModel) /* if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
int row = Storage::instance().findTaskIndexInParent(mTask); int row = Storage::instance().findTaskIndexInParent(mTask);
@ -491,12 +486,12 @@ bool DecreaseLevelAction::commit(TaskTreeModel* taskModel, AttachmentsListModel*
if (taskModel) if (taskModel)
taskModel->endRemoveRows(); taskModel->endRemoveRows();
*/ */
return true; return true;
} }
bool DecreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool DecreaseLevelAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{ {
/* /*
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
@ -506,14 +501,14 @@ bool DecreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListMode
if (taskModel) if (taskModel)
taskModel->endInsertRows(); taskModel->endInsertRows();
*/ */
return true; return true;
} }
// ------- DeleteTaskAction --------------- // ------- DeleteTaskAction ---------------
DeleteTaskAction::DeleteTaskAction(PTask task) DeleteTaskAction::DeleteTaskAction(PTask task)
:TaskAction(task) :TaskAction(task)
{ {
mIndex = Storage::instance().findTaskIndexInParent(mTask); mIndex = Storage::instance().findTaskIndexInParent(mTask);
} }
DeleteTaskAction::~DeleteTaskAction() DeleteTaskAction::~DeleteTaskAction()
@ -522,31 +517,52 @@ DeleteTaskAction::~DeleteTaskAction()
bool DeleteTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool DeleteTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
int row = Storage::instance().findTaskIndexInParent(mTask); int row = Storage::instance().findTaskIndexInParent(mTask);
taskModel->beginRemoveRows(parentIndex, row, row); taskModel->beginRemoveRows(parentIndex, row, row);
} }
Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total); Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total);
if (taskModel) if (taskModel)
taskModel->endRemoveRows(); taskModel->endRemoveRows();
return true; return true;
} }
bool DeleteTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) bool DeleteTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/)
{ {
if (taskModel) if (taskModel)
{ {
QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); QModelIndex parentIndex = taskModel->getIndex(mTask->parent());
taskModel->beginInsertRows(parentIndex, mIndex, mIndex); taskModel->beginInsertRows(parentIndex, mIndex, mIndex);
} }
Storage::instance().undeleteTask(mTask); Storage::instance().undeleteTask(mTask);
if (taskModel) if (taskModel)
taskModel->endInsertRows(); taskModel->endInsertRows();
return true; return true;
} }
// ---------------- SaveTaskAction --------------
SaveTaskAction::SaveTaskAction(const PTask& task)
:TaskAction(task)
{}
SaveTaskAction::~SaveTaskAction()
{}
bool SaveTaskAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{
if (mTask)
mTask->save();
return true;
}
bool SaveTaskAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/)
{
return false;
}

View File

@ -1,7 +1,7 @@
#ifndef TASKACTION_H #ifndef TASKACTION_H
#define TASKACTION_H #define TASKACTION_H
#include <task.h> #include "task.h"
#include <QString> #include <QString>
#include <QDataStream> #include <QDataStream>
#include <QModelIndexList> #include <QModelIndexList>
@ -9,12 +9,15 @@
class TaskTreeModel; class TaskTreeModel;
class AttachmentsListModel; class AttachmentsListModel;
// Base class for all actions
class TaskAction class TaskAction
{ {
friend class ChangesHistory; friend class ChangesHistory;
public: public:
TaskAction(PTask task); TaskAction(PTask task);
virtual ~TaskAction(); virtual ~TaskAction();
// Associated task
PTask task() const; PTask task() const;
protected: protected:
@ -45,10 +48,10 @@ public:
static ChangesHistory& instance(); static ChangesHistory& instance();
protected: protected:
TaskTreeModel* mTaskModel; TaskTreeModel* mTaskModel = nullptr;
AttachmentsListModel* mAttachmentsModel; AttachmentsListModel* mAttachmentsModel = nullptr;
QVector<PTaskAction> mActionList; QVector<PTaskAction> mActionList;
int mRollbackCount; int mRollbackCount = 0;
}; };
#define MAKE_ACTION(X) ChangesHistory::instance().add(PTaskAction(X)) #define MAKE_ACTION(X) ChangesHistory::instance().add(PTaskAction(X))
@ -181,4 +184,15 @@ protected:
bool rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel); bool rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel);
}; };
class SaveTaskAction: public TaskAction
{
public:
SaveTaskAction(const PTask& task);
~SaveTaskAction() override;
protected:
bool commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel);
bool rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel);
};
#endif // TASKACTION_H #endif // TASKACTION_H

View File

@ -11,9 +11,7 @@
TaskTreeView::TaskTreeView(QWidget *widget) TaskTreeView::TaskTreeView(QWidget *widget)
:QTreeView(widget) :QTreeView(widget)
{ {}
}
void TaskTreeView::dragMoveEvent(QDragMoveEvent* event) void TaskTreeView::dragMoveEvent(QDragMoveEvent* event)
{ {
@ -270,15 +268,17 @@ QVariant TaskTreeModel::headerData(int /*section*/, Qt::Orientation /*orientatio
{ {
return ""; return "";
/*if (orientation != Qt::Horizontal) /*
return QVariant(); if (orientation != Qt::Horizontal)
return QVariant();
switch (role) switch (role)
{ {
case Qt::DisplayRole: case Qt::DisplayRole:
return QString(tr("Tasks")); return QString(tr("Tasks"));
} }
return QVariant();*/ return QVariant();
*/
} }
Qt::DropActions TaskTreeModel::supportedDropActions() const Qt::DropActions TaskTreeModel::supportedDropActions() const
@ -419,7 +419,8 @@ bool TaskTreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction actio
return true; return true;
} }
bool TaskTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const bool TaskTreeModel::canDropMimeData(const QMimeData */*data*/, Qt::DropAction /*action*/,
int /*row*/, int /*column*/, const QModelIndex &/*parent*/) const
{ {
//qDebug() << "TaskTreeModel::canDropMimeData() called"; //qDebug() << "TaskTreeModel::canDropMimeData() called";
return true; return true;
@ -432,7 +433,7 @@ bool TaskTreeModel::removeRows(int row, int count, const QModelIndex &parent)
return true; return true;
// Called during drag and drop // Called during drag and drop
if (parent.isValid()) /* if (parent.isValid())
{ {
PTask parentTask = getTask(parent); PTask parentTask = getTask(parent);
for (int i=0; i<count; i++) for (int i=0; i<count; i++)
@ -461,6 +462,7 @@ bool TaskTreeModel::removeRows(int row, int count, const QModelIndex &parent)
} }
return true; return true;
*/
} }
bool TaskTreeModel::insertRows(int /*row*/, int /*count*/, const QModelIndex& /*parent*/) bool TaskTreeModel::insertRows(int /*row*/, int /*count*/, const QModelIndex& /*parent*/)

View File

@ -396,6 +396,7 @@ void TimeTreeModel::cutInterval(const QModelIndex& index)
beginRemoveRows(index.parent(), index.row(), index.row()); beginRemoveRows(index.parent(), index.row(), index.row());
mTimeLine->cutInterval(t); mTimeLine->cutInterval(t);
mTimeLine->save();
endRemoveRows(); endRemoveRows();
} }