diff --git a/client/attachmentslist.cpp b/client/attachmentslist.cpp index 57e62e2..9f3fdc4 100644 --- a/client/attachmentslist.cpp +++ b/client/attachmentslist.cpp @@ -180,23 +180,23 @@ void AttachmentsList::importFile() QByteArray compressed = qCompress(content); // Put it to Attachment instance - PAttachment att(new Attachment()); - att->setTaskId(mTask->id()); - att->setIndex(mModel->rowCount()); QFileInfo fi(filename); - att->setFilename(fi.fileName()); + PAttachment att(new Attachment()); + att->setTaskId(mTask->id()) + .setIndex(mModel->rowCount()) + .setFilename(fi.fileName()); // Save everything - att->save(); - att->saveContent(compressed); + att->saveMetadata() + .saveContent(compressed); mModel->addItem(att); } f.close(); } - // Refresh hasAttachments property on owner task - mTask->checkAttachments(); + // Refresh AttachmentsCount property on owner task + mTask->preloadAttachmentCount(); } void AttachmentsList::exportFile() @@ -242,13 +242,13 @@ void AttachmentsList::deleteFile() for (int row = index.row() + 1; row < mModel->rowCount(); row++) { Attachment& att = *mModel->itemAt(row); - att.setIndex(att.index() - 1); - att.save(); + att.setIndex(att.index() - 1) + .saveMetadata(); } } // Refresh hasAttachment property value on task - mTask->checkAttachments(); + mTask->preloadAttachmentCount(); } void AttachmentsList::renameFile() diff --git a/client/build-number/build_number.h b/client/build-number/build_number.h index a0f2ae3..dd461d4 100644 --- a/client/build-number/build_number.h +++ b/client/build-number/build_number.h @@ -1,2 +1,2 @@ // Auto generated file ! Please do not edit ! -#define APP_BUILD_NUMBER 177 \ No newline at end of file +#define APP_BUILD_NUMBER 214 \ No newline at end of file diff --git a/client/helper.cpp b/client/helper.cpp index 5c692dd..5284b8d 100644 --- a/client/helper.cpp +++ b/client/helper.cpp @@ -310,13 +310,15 @@ bool EscapeKeyEventFilter::eventFilter(QObject *obj, QEvent * event) return false; } -QString password::load() +QString password::loadFromKeychain() { QKeychain::ReadPasswordJob job(APPNAME); job.setKey(KEY_PASSWORD); job.setAutoDelete(false); QEventLoop loop; job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); + + // Start job job.start(); loop.exec(); if (job.error()) @@ -325,12 +327,14 @@ QString password::load() return job.textData(); } -bool password::save(const QString& password) +bool password::saveToKeychain(const QString &password) { QKeychain::WritePasswordJob job(APPNAME); job.setKey(KEY_PASSWORD); job.setAutoDelete(false); job.setTextData(password); + + // Start job QEventLoop loop; job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); job.start(); diff --git a/client/helper.h b/client/helper.h index a4439f2..11cc39f 100644 --- a/client/helper.h +++ b/client/helper.h @@ -94,11 +94,12 @@ namespace helper }; + // Keychain helper class class password { public: - static QString load(); - static bool save(const QString& password); + static QString loadFromKeychain(); + static bool saveToKeychain(const QString& password); }; extern std::string app_version(); diff --git a/client/main.cpp b/client/main.cpp index 7cb3586..6dc5034 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -18,37 +18,6 @@ int main(int argc, char *argv[]) QString folder = QFileInfo(path).absoluteDir().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; w.layout()->invalidate(); diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 4752889..d512c9b 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -91,19 +91,11 @@ void MainWindow::attachDatabase() { if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool()) { - QString password = helper::password::load(); + QString password = helper::password::loadFromKeychain(); if (password.isEmpty()) askDbPassword(); else - { - Storage::instance().setKey(password); - if (!Storage::instance().open()) - { - askDbPassword(tr("Invalid password, please try again.")); - } - else - QApplication::postEvent(this, new ClientEvent()); - } + onDbPasswordEntered(password); } else askDbPassword(QString()); @@ -465,7 +457,7 @@ void MainWindow::preferences() if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool() == false) { // Reset password in keychain - helper::password::save(QString("")); + helper::password::saveToKeychain(QString("")); //mSettings->data()[KEY_PASSWORD] = NOPASSWORDSTRING; mSettings->save(); @@ -1672,7 +1664,7 @@ void MainWindow::onDbPasswordEntered(const QString& password) { // Save password to keychain if needed if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool()) - helper::password::save(password); + helper::password::saveToKeychain(password); // Try to open database Storage::instance().setKey(password); @@ -1694,7 +1686,7 @@ void MainWindow::onNewDbPasswordEntered(const QString& password) { // Save password to keychain if needed if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool()) - helper::password::save(password); + helper::password::saveToKeychain(password); // Configure storage Storage::instance().setKey(password); diff --git a/client/preferencesdlg.cpp b/client/preferencesdlg.cpp index 85b3563..2debfee 100644 --- a/client/preferencesdlg.cpp +++ b/client/preferencesdlg.cpp @@ -75,7 +75,7 @@ void PreferencesDlg::accepted() { bool savePassword = ui->mAutosavePasswordCheckbox->isChecked(); 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_ASK_BEFORE_DELETE] = ui->mAskBeforeDeleteCheckbox->isChecked(); diff --git a/client/storage.cpp b/client/storage.cpp index 52c8b41..2f45b91 100644 --- a/client/storage.cpp +++ b/client/storage.cpp @@ -135,36 +135,68 @@ bool Storage::create() // Synctime & timestamp are always milliseconds from the start of UNIX epoch. // Timeline - mDatabase->exec("CREATE TABLE timeline (id INTEGER PRIMARY KEY, removed INTEGER, " - "taskid INTEGER, starttime TEXT, endtime TEXT, timestamp INTEGER)"); + mDatabase->exec("CREATE TABLE timeline (" + "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)"); // Tasks tree - mDatabase->exec("CREATE TABLE task (type INTEGER, removed INTEGER, " - "id INTEGER PRIMARY KEY, parentid INTEGER, orderid INTEGER, title TEXT, html TEXT, " - "flags INTEGER, timestamp INTEGER)"); + mDatabase->exec("CREATE TABLE task (" + "type 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)"); // Tasks history - mDatabase->exec("CREATE TABLE history_task (removed_old INTEGER, removed_new INTEGER, " - "timestamp_old INTEGER, timestamp_new INTEGER, " - "id INTEGER PRIMARY KEY, " - "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)"); + mDatabase->exec("CREATE TABLE history_task (" + "removed_old INTEGER, " + "removed_new INTEGER, " + "timestamp_old INTEGER, " + "timestamp_new INTEGER, " + "id INTEGER PRIMARY KEY, " + "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 - mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, " - "filename TEXT, content BLOB, orderid INTEGER, timestamp INTEGER)"); + mDatabase->exec("CREATE TABLE file (" + "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, " - "removed_old INTEGER, removed_new INTEGER, " - "taskid_old INTEGER, taskid_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 TABLE history_file (" + "id INTEGER PRIMARY KEY, " + "removed_old INTEGER, " + "removed_new INTEGER, " + "taskid_old INTEGER, " + "taskid_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)"); @@ -319,17 +351,17 @@ bool Storage::upgradeFromVersion0() return true; } -bool Storage::encryptTask(PTask task) +bool Storage::encryptTask(PTask /*task*/) { 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; } @@ -347,8 +379,9 @@ PTask Storage::createTask(int index) if (insertQuery.exec()) { PTask result(new Task()); - result->setId(database().getLastInsertRowid()); - result->setIndex(index); + result->setId(database().getLastInsertRowid()) + .setIndex(index); + mTaskModelIdMap[result->modelId()] = result; mTaskIdMap[result->id()] = result; if (index > mTopTasks.size()) @@ -359,8 +392,8 @@ PTask Storage::createTask(int index) // Assign new indexes for top tasks for (int i=0; isetIndex(i); - mTopTasks[i]->save(); + mTopTasks[i]->setIndex(i) + .save(); } } return result; @@ -369,7 +402,7 @@ PTask Storage::createTask(int index) return PTask(); } -PTask Storage::createTask(PTask parent, int index) +PTask Storage::createTask(const PTask& parent, int index) { if (!parent) return createTask(index); @@ -394,8 +427,8 @@ PTask Storage::createTask(PTask parent, int index) parent->children().insert(parent->children().begin() + index, result); for (int i=0; ichildren().size(); i++) { - parent->children()[i]->setIndex(i); - parent->children()[i]->save(); + parent->children()[i]->setIndex(i) + .save(); } } @@ -470,8 +503,8 @@ bool Storage::moveTask(PTask task, PTask newParent, int indexToInsert) for (int i=0; isetIndex(i); - topOfTaskTree()[i]->save(); + topOfTaskTree()[i]->setIndex(i) + .save(); } } @@ -504,6 +537,15 @@ void Storage::saveSingleTask(PTask task) 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() { mTopTasks.clear(); @@ -513,7 +555,7 @@ void Storage::loadTaskTree() while(q.executeStep()) { PTask t(new Task()); - t->load(q); + loadTaskRecord(*t, q); t->setIndex(currentIndex++); mTaskModelIdMap[t->modelId()] = t; mTaskIdMap[t->id()] = t; @@ -532,9 +574,10 @@ void Storage::loadTaskChildren(PTask task) while (q.executeStep()) { PTask t(new Task()); - t->load(q); - t->setIndex(currentIndex++); - t->setParent(task, false); + loadTaskRecord(*t, q); + t->setIndex(currentIndex++) + .setParent(task, false); + loadTaskChildren(t); mTaskModelIdMap[t->modelId()] = t; mTaskIdMap[t->id()] = t; @@ -580,10 +623,10 @@ void Storage::loadAttachments(PTask task, AttachmentArray& output) while (q.executeStep()) { PAttachment att(new Attachment()); - att->setId(q.getColumn(0).getInt64()); - att->setFilename(q.getColumn(1).getText()); - att->setTaskId(task->id()); - att->setIndex(q.getColumn(2).getInt()); + att->setId(q.getColumn(0).getInt64()) + .setFilename(q.getColumn(1).getText()) + .setTaskId(task->id()) + .setIndex(q.getColumn(2).getInt()); output.push_back(att); } } @@ -602,6 +645,114 @@ void Storage::undeleteAttachment(PAttachment att) 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(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(r.taskId())); + q.bind(":id", static_cast(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(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(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() { @@ -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) { @@ -651,7 +802,7 @@ void Storage::deleteTask(PTask task, DeleteOption option) #endif } -void Storage::undeleteTask(PTask task) +void Storage::undeleteTask(const PTask& task) { SQLite::Statement q(database(), "update task set removed = 0 where id = :id"); q.bind(":id", (sqlite3_int64)task->id()); @@ -670,7 +821,7 @@ void Storage::undeleteTask(PTask task) mTaskIdMap[task->id()] = task; } -void Storage::removeTask(PTask task) +void Storage::removeTask(const PTask& task) { auto taskModelIter = mTaskModelIdMap.find(task->modelId()); if (taskModelIter != mTaskModelIdMap.end()) @@ -681,3 +832,69 @@ void Storage::removeTask(PTask task) 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; +} diff --git a/client/storage.h b/client/storage.h index 712f5f0..a276514 100644 --- a/client/storage.h +++ b/client/storage.h @@ -9,86 +9,117 @@ class Storage { public: - Storage(); - ~Storage(); + Storage(); + ~Storage(); - QString path(); - void setPath(const QString& path); - QString key(); - void setKey(const QString& key); + QString path(); + void setPath(const QString& path); + QString key(); + void setKey(const QString& key); - SQLite::Database& database(); + SQLite::Database& database(); - // New database. Returns true or false depending on success of call. - bool create(); + // New database. Returns true or false depending on success of call. + bool create(); - // Open database, preload task tree. Returns true or false depending on success of operation. - bool open(); + // Open database, preload task tree. Returns true or false depending on success of operation. + bool open(); - // Close database - void close(); + // Close database + void close(); - // Ensure database is latest version - bool upgrade(); + // Ensure database is latest version + bool upgrade(); - PTask createTask(int index); - PTask createTask(PTask parent, int index); - PTask loadTask(Task::Id id, PTask parent); + PTask createTask(int index); + PTask createTask(const PTask& parent, int index); + // PTask loadTask(Task::Id id, PTask parent); - // Remove task from database - enum DeleteOption - { - DeleteOption_Total, - DeleteOption_FromParentAndHash, - DeleteOption_FromParent - }; + // Remove task from database + enum DeleteOption + { + DeleteOption_Total, + DeleteOption_FromParentAndHash, + DeleteOption_FromParent + }; - void deleteTask(PTask task, DeleteOption option); + void deleteTask(const PTask& task, DeleteOption option); - // Undelete task - void undeleteTask(PTask task); + // Undelete task + void undeleteTask(const PTask& task); - // Remove task from internal dictionaries - it makes task unadressable by its id/model id - void removeTask(PTask task); + // Remove task from internal dictionaries - it makes task unadressable by its id/model id + void removeTask(const PTask& task); - enum Depth - { - 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); + void loadTaskContent(Task& task); + void loadTaskMetadata(Task& task); - 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: - QString mPath, mKey; - QSharedPointer mDatabase; - TaskArray mTopTasks; - typedef QMap TaskModelIdMap; - TaskModelIdMap mTaskModelIdMap; - typedef QMap TaskIdMap; - TaskIdMap mTaskIdMap; + QString mPath, mKey; + QSharedPointer mDatabase; + TaskArray mTopTasks; + typedef QMap TaskModelIdMap; + TaskModelIdMap mTaskModelIdMap; + typedef QMap TaskIdMap; + TaskIdMap mTaskIdMap; - TaskArray mTaskToUpgrade; + TaskArray mTaskToUpgrade; - bool hasTable(const QString& tablename); - void saveSingleTask(PTask task); - void loadTaskTree(); - void loadTaskChildren(PTask task); - bool upgradeFromVersion0(); - bool encryptTask(PTask task); - bool encryptTaskContent(PTask task); - bool encryptTaskAttachment(PAttachment attachment); + bool hasTable(const QString& tablename); + void saveSingleTask(PTask task); + void loadTaskRecord(Task& t, SQLite::Statement& q); + void loadTaskTree(); + void loadTaskChildren(PTask task); + bool upgradeFromVersion0(); + bool encryptTask(PTask task); + bool encryptTaskContent(PTask task); + bool encryptTaskAttachment(PAttachment attachment); }; #endif // STORAGE_H diff --git a/client/task.cpp b/client/task.cpp index a5ff009..4a60a65 100644 --- a/client/task.cpp +++ b/client/task.cpp @@ -86,9 +86,10 @@ time_t TimeRecord::endTime() const return mEndTime; } -void TimeRecord::setEndTime(const time_t &endTime) +TimeRecord& TimeRecord::setEndTime(const time_t &endTime) { mEndTime = endTime; + return *this; } time_t TimeRecord::startTime() const @@ -96,9 +97,10 @@ time_t TimeRecord::startTime() const return mStartTime; } -void TimeRecord::setStartTime(const time_t &startTime) +TimeRecord& TimeRecord::setStartTime(const time_t &startTime) { mStartTime = startTime; + return *this; } int TimeRecord::length() @@ -111,9 +113,10 @@ Id TimeRecord::id() const return mId; } -void TimeRecord::setId(Id id) +TimeRecord& TimeRecord::setId(Id id) { mId = id; + return *this; } Id TimeRecord::taskId() const @@ -121,9 +124,10 @@ Id TimeRecord::taskId() const return mTaskId; } -void TimeRecord::setTaskId(Id id) +TimeRecord& TimeRecord::setTaskId(Id id) { mTaskId = id; + return *this; } WorldId TimeRecord::worldId() const @@ -131,41 +135,22 @@ WorldId TimeRecord::worldId() const return mWorldId; } -void TimeRecord::setWorldId(const WorldId& id) +TimeRecord& TimeRecord::setWorldId(const WorldId& id) { mWorldId = id; + return *this; } void TimeRecord::save() { - if (!mId) - { - SQLite::Statement q(Storage::instance().database(), "insert into timeline(id, starttime, endtime, taskid, removed) values (NULL, :starttime, :endtime, :taskid, :removed)"); - - q.bind(":starttime", helper::chrono::timeToStr(mStartTime)); - q.bind(":endtime", helper::chrono::timeToStr(mEndTime)); - q.bind(":taskid", static_cast(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(mTaskId)); - q.bind(":id", static_cast(mId)); - q.exec(); - } + auto id = Storage::instance().saveTimeRecord(*this); + if (id) + mId = id; } void TimeRecord::deleteRecord() { - SQLite::Statement q(Storage::instance().database(), "update timeline set removed = 1 where id = :id"); - q.bind(":id", static_cast(mId)); - q.exec(); + Storage::instance().deleteTimeRecord(*this); } // -------------------- TimeLine -------------------- @@ -218,10 +203,10 @@ void TimeLine::start() TimeRecord* TimeLine::makeNewRecord(time_t beginTime, time_t endTime) { TimeRecord tr; - tr.setStartTime(beginTime); - tr.setEndTime(endTime); - tr.setTaskId(mTaskId); - tr.save(); + tr.setStartTime(beginTime) + .setEndTime(endTime) + .setTaskId(mTaskId) + .save(); mData.push_back(tr); Id intervalId = tr.id(); @@ -388,11 +373,13 @@ void TimeLine::cutInterval(const TimeRecord& interval) // So cut interval will split it to 2 new intervals // Also this operation will end loop TimeRecord toInsert(*iter); // Backup current interval - iter->setEndTime(interval.startTime() - 1); - iter->save(); - toInsert.setStartTime(interval.endTime() + 1); + iter->setEndTime(interval.startTime() - 1) + .save(); + + toInsert.setStartTime(interval.endTime() + 1) + .save(); mData.insert(++iter, toInsert); - toInsert.save(); + done = true; break; } @@ -407,8 +394,8 @@ void TimeLine::cutInterval(const TimeRecord& interval) else { // Current interval starts before cut interval but finishes in cut interval - iter->setEndTime(interval.startTime() - 1); - iter->save(); + iter->setEndTime(interval.startTime() - 1) + .save(); done = true; break; } @@ -425,7 +412,10 @@ void TimeLine::cutInterval(const TimeRecord& interval) [=] (const TimeRecord& tr) { return tr.id() == interval.id();}); if (iter != mData.end()) + { + Storage::instance().deleteTimeRecord(*iter); mData.erase(iter); + } } } @@ -465,8 +455,8 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc) TimeRecord* tr = hasTimePoint(currentUtc); if (tr && tr != mActiveTimeRecord) { - mActiveTimeRecord->setEndTime(currentUtc); - mActiveTimeRecord->save(); + mActiveTimeRecord->setEndTime(currentUtc) + .save(); mActiveTimeRecord = nullptr; } if (saveToDb && mActiveTimeRecord) @@ -486,26 +476,7 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc) void TimeLine::load() { - SQLite::Statement q(Storage::instance().database(), "select id, starttime, endtime from timeline where (taskid = :taskid) and ((removed is null) or (removed != 1)) order by id asc"); - q.bind(":taskid", static_cast(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(); + Storage::instance().loadTimeLine(*this); } void TimeLine::save() @@ -518,9 +489,10 @@ Id TimeLine::taskId() return mTaskId; } -void TimeLine::setTaskId(Id id) +TimeLine& TimeLine::setTaskId(Id id) { mTaskId = id; + return *this; } TimeArray& TimeLine::data() @@ -668,10 +640,10 @@ int TimeLine::getTime(int year, int month, int day, std::vector* int if (intervals) { TimeRecord resultingRecord; - resultingRecord.setStartTime(dayBegin); - resultingRecord.setEndTime(dayEnd); - resultingRecord.setId(tr.id()); - resultingRecord.setTaskId(tr.taskId()); + resultingRecord.setStartTime(dayBegin) + .setEndTime(dayEnd) + .setId(tr.id()) + .setTaskId(tr.taskId()); intervals->push_back(resultingRecord); } result++; @@ -822,26 +794,17 @@ 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 { return mId; } -void Task::setId(Id id) +Task& Task::setId(Id id) { mId = id; + return *this; } Task::Id Task::parentId() const @@ -849,13 +812,14 @@ Task::Id Task::parentId() const return mParentId; } -void Task::setParentId(Id id) +Task& Task::setParentId(Id id) { if (mParentId != id) { mParentModified = true; mParentId = id; } + return *this; } WorldId Task::worldId() const @@ -863,9 +827,10 @@ WorldId Task::worldId() const return mWorldId; } -void Task::setWorldId(const WorldId& id) +Task& Task::setWorldId(const WorldId& id) { mWorldId = id; + return *this; } Task::ModelId Task::modelId() const @@ -873,9 +838,10 @@ Task::ModelId Task::modelId() const return mModelId; } -void Task::setModelId(ModelId id) +Task& Task::setModelId(ModelId id) { mModelId = id; + return *this; } int Task::index() const @@ -883,7 +849,7 @@ int Task::index() const return mIndex; } -void Task::setIndex(int index, bool modified) +Task& Task::setIndex(int index, bool modified) { if (index != mIndex) { @@ -891,6 +857,7 @@ void Task::setIndex(int index, bool modified) mIndexModified = true; mIndex = index; } + return *this; } QString Task::html() const @@ -898,13 +865,14 @@ QString Task::html() const return mHtml; } -void Task::setHtml(const QString &html) +Task& Task::setHtml(const QString &html) { if (mHtml != html) { mHtml = html; mHtmlModified = true; } + return *this; } QString Task::title() const @@ -912,7 +880,7 @@ QString Task::title() const return mTitle; } -void Task::setTitle(const QString &title, bool modified) +Task& Task::setTitle(const QString &title, bool modified) { if (mTitle != title) { @@ -920,61 +888,20 @@ void Task::setTitle(const QString &title, bool modified) if (modified) mTitleModified = true; } + return *this; } -void Task::save(SaveOptions options) +void Task::save() { - if (!mTitleModified && !mHtmlModified && !mIndexModified && !mParentModified && options == Save_Automatic) - 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(); + Storage::instance().saveTask(*this); 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)"); - q.bind(":id", (sqlite3_int64)mId); - 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(); -}*/ + Storage::instance().saveTask(*this, Storage::Save_Forced); + mIndexModified = mTitleModified = mHtmlModified = false; +} QString Task::path() const { @@ -990,7 +917,7 @@ PTask Task::parent() const return mParent; } -void Task::setParent(PTask task, bool modified) +Task& Task::setParent(PTask task, bool modified) { if (mParent != task) { @@ -1002,6 +929,7 @@ void Task::setParent(PTask task, bool modified) if (modified) mTitleModified = true; // To force save() } + return *this; } TaskArray& Task::children() @@ -1011,21 +939,7 @@ TaskArray& Task::children() void Task::loadContent() { - SQLite::Statement htmlQuery(Storage::instance().database(), "select html from task where id = :id"); - htmlQuery.bind(":id", (sqlite3_int64)mId); - if (htmlQuery.executeStep()) - { - mHtml = htmlQuery.getColumn(0).getText(); - mHtmlLoaded = true; - mHtmlModified = false; - } - - if (!mTimeLine) - { - mTimeLine = PTimeLine(new TimeLine()); - mTimeLine->setTaskId(mId); - mTimeLine->load(); - } + Storage::instance().loadTaskContent(*this); } bool Task::isContentLoaded() const @@ -1051,20 +965,15 @@ int Task::getAttachmentCount() return mAttachmentCount; } -void Task::setAttachmentCount(int count) +Task& Task::setAttachmentCount(int 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))"); - q.bind(":taskid", (sqlite3_int64)mId); - if (q.executeStep()) - mAttachmentCount = q.getColumn(0).getInt(); - else - mAttachmentCount = 0; - + mAttachmentCount = Storage::instance().findAttachmentCountOnTask(*this); return mAttachmentCount; } @@ -1073,9 +982,10 @@ bool Task::isChecked() const return mChecked; } -void Task::setChecked(bool checked) +Task& Task::setChecked(bool checked) { mChecked = checked; + return *this; } int Task::getReportedTime() const @@ -1083,9 +993,10 @@ int Task::getReportedTime() const return mReportedTime; } -void Task::setReportedTime(int t) +Task& Task::setReportedTime(int t) { mReportedTime = t; + return *this; } int Task::getChildrenReportedTime() const @@ -1093,9 +1004,10 @@ int Task::getChildrenReportedTime() const return mChildrenReportedTime; } -void Task::setChildrenReportedTime(int t) +Task& Task::setChildrenReportedTime(int t) { mChildrenReportedTime = t; + return *this; } int Task::flags() const @@ -1103,10 +1015,12 @@ int Task::flags() const return mFlags; } -void Task::setFlags(int value) +Task& Task::setFlags(int value) { mFlags = value; - save(Save_Forced); + saveAnyway(); + + return *this; } int Task::cursorPosition() const @@ -1114,24 +1028,12 @@ int Task::cursorPosition() const return mCursorPosition; } -void Task::setCursorPosition(int position) +Task& Task::setCursorPosition(int 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() :mId(0), mTaskId(0) @@ -1149,9 +1051,10 @@ Task::Id Attachment::id() return mId; } -void Attachment::setId(Task::Id id) +Attachment& Attachment::setId(Task::Id id) { mId = id; + return *this; } Task::Id Attachment::taskId() @@ -1159,9 +1062,10 @@ Task::Id Attachment::taskId() return mTaskId; } -void Attachment::setTaskId(Task::Id id) +Attachment& Attachment::setTaskId(Task::Id id) { mTaskId = id; + return *this; } WorldId Attachment::worldId() const @@ -1169,9 +1073,10 @@ WorldId Attachment::worldId() const return mWorldId; } -void Attachment::setWorldId(const WorldId& id) +Attachment& Attachment::setWorldId(const WorldId& id) { mWorldId = id; + return *this; } int Attachment::index() @@ -1179,34 +1084,28 @@ int Attachment::index() return mIndex; } -void Attachment::setIndex(int index) +Attachment& Attachment::setIndex(int index) { //TODO: introduce mIndexModified field and corresponding login in save() mIndex = index; + return *this; } QByteArray Attachment::loadContent() { - SQLite::Statement q(Storage::instance().database(), "select content from file where id = :id"); - q.bind(":id", (sqlite3_int64)mId); - if (q.executeStep()) - return QByteArray((const char*)q.getColumn(0).getBlob(), q.getColumn(0).size()); - else - return QByteArray(); + return Storage::instance().loadContent(*this); } -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"); - q.bind(":content", content.data(), content.size()); - q.bind(":id", (sqlite3_int64)mId); - if (q.exec()) - ; + Storage::instance().saveContent(*this, content); + return *this; } -void Attachment::setFilename(const QString& filename) +Attachment& Attachment::setFilename(const QString& filename) { mFilename = filename; + return *this; } QString Attachment::filename() @@ -1214,33 +1113,16 @@ QString Attachment::filename() return mFilename; } -// mDatabase->exec("CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, filename TEXT, content BLOB, orderid INTEGER, synctime TEXT)"); - -void Attachment::save() +Attachment& Attachment::saveMetadata() { - if (mId) - { - SQLite::Statement q(Storage::instance().database(), "update file set filename = :filename, orderid = :orderid where id = :id"); - q.bind(":filename", mFilename.toStdString().c_str()); - q.bind(":orderid", mIndex); - q.bind(":id", (sqlite3_int64)mId); + auto id = Storage::instance().saveMetadata(*this); + if (id != 0) + mId = id; - if (q.exec()) - ; - } - else - { - SQLite::Statement q(Storage::instance().database(), "insert into file (filename, taskid, orderid, removed) values(:filename, :taskid, :orderid, 0)"); - q.bind(":filename", mFilename.toStdString().c_str()); - q.bind(":taskid", (sqlite3_int64)mTaskId); - q.bind(":orderid", mIndex); - if (q.exec()) - { - mId = Storage::instance().database().getLastInsertRowid(); - } - } + return *this; } -void Attachment::load() +Attachment& Attachment::loadMetadata() { + return *this; } diff --git a/client/task.h b/client/task.h index c8956ed..5e79bb3 100644 --- a/client/task.h +++ b/client/task.h @@ -40,16 +40,21 @@ public: ~TimeRecord(); time_t startTime() const; - void setStartTime(const time_t& startTime); + TimeRecord& setStartTime(const time_t& startTime); + time_t endTime() const; - void setEndTime(const time_t& endTime); + TimeRecord& setEndTime(const time_t& endTime); + int length(); + Id id() const; - void setId(Id id); + TimeRecord& setId(Id id); + Id taskId() const; - void setTaskId(Id id); + TimeRecord& setTaskId(Id id); + 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. void save(); @@ -74,6 +79,7 @@ typedef std::map > YearsMap; class TimeLine { + friend class Storage; public: TimeLine(); ~TimeLine(); @@ -91,7 +97,7 @@ public: void save(); Id taskId(); - void setTaskId(Id id); + TimeLine& setTaskId(Id id); // These methods work with local time // Returns set of available years in timeline @@ -168,6 +174,8 @@ class Task: public QObject { Q_OBJECT public: + friend class Storage; + typedef uint64_t Id; typedef uint32_t ModelId; enum Flag @@ -181,37 +189,32 @@ public: void load(SQLite::Statement& q); Id id() const; - void setId(Id id); + Task& setId(Id id); Id parentId() const; - void setParentId(Id id); + Task& setParentId(Id id); WorldId worldId() const; - void setWorldId(const WorldId& id); + Task& setWorldId(const WorldId& id); ModelId modelId() const; - void setModelId(ModelId id); + Task& setModelId(ModelId id); int index() const; - void setIndex(int index, bool modified = true); + Task& setIndex(int index, bool modified = true); - enum SaveOptions - { - Save_Automatic, - Save_Forced - }; - - void save(SaveOptions options = Save_Automatic); + void save(); + void saveAnyway(); QString html() const; - void setHtml(const QString& html); + Task& setHtml(const QString& html); QString title() const; - void setTitle(const QString& title, bool modified = true); + Task& setTitle(const QString& title, bool modified = true); QString path() const; PTask parent() const; - void setParent(PTask task, bool modified = true); + Task& setParent(PTask task, bool modified = true); TaskArray& children(); @@ -226,24 +229,24 @@ public: // Returns true if task has attachments int getAttachmentCount(); - void setAttachmentCount(int count); - int checkAttachments(); + Task& setAttachmentCount(int count); + int preloadAttachmentCount(); // Service properties used in time reporting bool isChecked() const; - void setChecked(bool checked); + Task& setChecked(bool checked); int getReportedTime() const; - void setReportedTime(int t); + Task& setReportedTime(int t); int getChildrenReportedTime() const; - void setChildrenReportedTime(int t); + Task& setChildrenReportedTime(int t); int flags() const; - void setFlags(int value); + Task& setFlags(int value); int cursorPosition() const; - void setCursorPosition(int position); + Task& setCursorPosition(int position); /* QUndoStack* getUndoStack() const; @@ -270,30 +273,31 @@ protected: class Attachment { + friend class Storage; public: Attachment(); ~Attachment(); Task::Id id(); - void setId(Task::Id id); + Attachment& setId(Task::Id id); Task::Id taskId(); - void setTaskId(Task::Id id); + Attachment& setTaskId(Task::Id id); WorldId worldId() const; - void setWorldId(const WorldId& id); + Attachment& setWorldId(const WorldId& id); int index(); - void setIndex(int index); + Attachment& setIndex(int index); QByteArray loadContent(); - void saveContent(const QByteArray& content); + Attachment& saveContent(const QByteArray& content); - void setFilename(const QString& filename); + Attachment& setFilename(const QString& filename); QString filename(); - void save(); - void load(); + Attachment& saveMetadata(); + Attachment& loadMetadata(); protected: Id mId, mTaskId; WorldId mWorldId; diff --git a/client/taskaction.cpp b/client/taskaction.cpp index f95581f..961ba6f 100644 --- a/client/taskaction.cpp +++ b/client/taskaction.cpp @@ -7,7 +7,7 @@ #include TaskAction::TaskAction(PTask task) - :mTask(task) + :mTask(task) { } @@ -17,7 +17,7 @@ TaskAction::~TaskAction() PTask TaskAction::task() const { - return mTask; + return mTask; } // ------------ History ------------------ @@ -33,161 +33,158 @@ ChangesHistory::~ChangesHistory() void ChangesHistory::setTaskTreeModel(TaskTreeModel* model) { - mTaskModel = model; + mTaskModel = model; } void ChangesHistory::setAttachmentsModel(AttachmentsListModel* model) { - mAttachmentsModel = model; + mAttachmentsModel = model; } TaskTreeModel* ChangesHistory::taskTreeModel() const { - return mTaskModel; + return mTaskModel; } AttachmentsListModel* ChangesHistory::attachmentsModel() const { - return mAttachmentsModel; + return mAttachmentsModel; } void ChangesHistory::add(PTaskAction action) { - // See if there are "undo"ed actions in the list now - if (mRollbackCount > 0) - { - // Just delete from list - these changes are not in DB already - mActionList.erase(mActionList.begin() + mActionList.size() - mRollbackCount, mActionList.end()); - mRollbackCount = 0; - } + // See if there are "undo"ed actions in the list now + if (mRollbackCount > 0) + { + // Just delete from list - these changes are not in DB already + mActionList.erase(mActionList.begin() + mActionList.size() - mRollbackCount, mActionList.end()); + mRollbackCount = 0; + } - // Apply change - if (action->commit(mTaskModel, mAttachmentsModel)) - mActionList.push_back(action); + // Apply change + if (action->commit(mTaskModel, mAttachmentsModel)) + mActionList.push_back(action); } void ChangesHistory::undo() { - // If there are actions that can be "undo" - if (mActionList.size() - mRollbackCount > 0) - { - mRollbackCount++; - PTaskAction& action = mActionList[mActionList.size() - mRollbackCount]; - if (action->rollback(mTaskModel, mAttachmentsModel)) + // If there are actions that can be "undo" + if (mActionList.size() - mRollbackCount > 0) { + mRollbackCount++; + PTaskAction& action = mActionList[mActionList.size() - mRollbackCount]; + if (action->rollback(mTaskModel, mAttachmentsModel)) + { + } } - } } void ChangesHistory::redo() { - if (mRollbackCount > 0) - { - PTaskAction& action = mActionList[mActionList.size() - mRollbackCount]; - mRollbackCount--; - if (action->commit(mTaskModel, mAttachmentsModel)) + if (mRollbackCount > 0) { + PTaskAction& action = mActionList[mActionList.size() - mRollbackCount]; + mRollbackCount--; + if (action->commit(mTaskModel, mAttachmentsModel)) + { + } } - } } bool ChangesHistory::canRedo() const { - return mRollbackCount > 0; + return mRollbackCount > 0; } bool ChangesHistory::canUndo() const { - return mRollbackCount < mActionList.size(); + return mRollbackCount < mActionList.size(); } static ChangesHistory HistoryStaticInstance; ChangesHistory& ChangesHistory::instance() { - return HistoryStaticInstance; + return HistoryStaticInstance; } // ------------ ImportAttachmentAction ---- #define ImportAttachmentId int(1) ImportAttachmentAction::ImportAttachmentAction(PTask task, const QString &path, int index) - :TaskAction(task), mPath(path), mIndex(index) + :TaskAction(task), mPath(path), mIndex(index) {} ImportAttachmentAction::~ImportAttachmentAction() {} -bool ImportAttachmentAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel) +bool ImportAttachmentAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/) { - QFile f(mPath); - f.open(QFile::ReadOnly); - if (!f.isOpen()) - return false; + QFile f(mPath); + f.open(QFile::ReadOnly); + if (!f.isOpen()) + return false; - // Get data from file - QByteArray content = f.readAll(); + // Get data from file + QByteArray content = f.readAll(); - // Compress them - QByteArray compressed = qCompress(content); + // Compress them + QByteArray compressed = qCompress(content); - // Put it to Attachment instance - mAttachment = PAttachment(new Attachment()); - mAttachment->setTaskId(mTask->id()); - mAttachment->setIndex(mIndex); + // Put it to Attachment instance + QFileInfo fi(mPath); + mAttachment = PAttachment(new Attachment()); + mAttachment->setTaskId(mTask->id()) + .setIndex(mIndex) + .setFilename(fi.fileName()) + .saveMetadata() + .saveContent(compressed); - QFileInfo fi(mPath); - mAttachment->setFilename(fi.fileName()); - - // Save everything - mAttachment->save(); - mAttachment->saveContent(compressed); - - return true; + return true; } -bool ImportAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel) +bool ImportAttachmentAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/) { - Storage::instance().deleteAttachment(mAttachment); - return true; + Storage::instance().deleteAttachment(mAttachment); + return true; } PAttachment ImportAttachmentAction::attachment() const { - return mAttachment; + return mAttachment; } // ------------ RenameAttachmentAction --- #define RenameAttachmentId int(2) 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() { } -bool RenameAttachmentAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel) +bool RenameAttachmentAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* attModel) { - mAttachment->setFilename(mNewName); - mAttachment->save(); + mAttachment->setFilename(mNewName) + .saveMetadata(); - if (attModel) - { - int row = attModel->findRow(mAttachment); - QModelIndex index = attModel->index(row, 0); - attModel->dataChanged(index, index); - } - return true; + if (attModel) + { + int row = attModel->findRow(mAttachment); + QModelIndex index = attModel->index(row, 0); + attModel->dataChanged(index, index); + } + return true; } -bool RenameAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel) +bool RenameAttachmentAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/) { - mAttachment->setFilename(mName); - mAttachment->save(); - return true; + mAttachment->setFilename(mName) + .saveMetadata(); + return true; } // ------------ DeleteAttachmentAction --- @@ -195,70 +192,68 @@ bool RenameAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListM #define DeleteAttachmentId int(3) DeleteAttachmentAction::DeleteAttachmentAction(PTask task, QModelIndexList& mil) - :TaskAction(task), mIndexList(mil) + :TaskAction(task), mIndexList(mil) {} DeleteAttachmentAction::~DeleteAttachmentAction() {} -bool DeleteAttachmentAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* attModel) +bool DeleteAttachmentAction::commit(TaskTreeModel* /*taskModel*/, AttachmentsListModel* attModel) { - if (!attModel) - return false; + if (!attModel) + return false; - mAttachments.clear(); + mAttachments.clear(); - foreach (const QModelIndex& index, mIndexList) - { - if (!index.isValid()) - continue; + foreach (const QModelIndex& index, mIndexList) + { + if (!index.isValid()) + continue; - PAttachment att = attModel->itemAt(index.row()); + PAttachment att = attModel->itemAt(index.row()); - // Remove from DB - Storage::instance().deleteAttachment(att); + // Remove from DB + Storage::instance().deleteAttachment(att); - // Remove from model - attModel->removeItem(index.row()); + // Remove from model + attModel->removeItem(index.row()); - mAttachments.push_back(att); - } + mAttachments.push_back(att); + } - // Iterate other items and decrease their DB table's orderid field - for (int row = 0; row < attModel->rowCount(); row++) - { - Attachment& att = *attModel->itemAt(row); - att.setIndex(row); - att.save(); - } + // Iterate other items and decrease their DB table's orderid field + for (int row = 0; row < attModel->rowCount(); row++) + { + Attachment& att = *attModel->itemAt(row); + att.setIndex(row) + .saveMetadata(); + } - return true; + return true; } -bool DeleteAttachmentAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* attModel) +bool DeleteAttachmentAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* attModel) { - while (!mAttachments.isEmpty()) - { - auto iter = std::min_element(mAttachments.begin(), mAttachments.end(), [] (const PAttachment& a, const PAttachment& b) { return a->index() < b->index();}); - PAttachment attToUndelete = *iter; + while (!mAttachments.isEmpty()) + { + auto iter = std::min_element(mAttachments.begin(), mAttachments.end(), [] (const PAttachment& a, const PAttachment& b) { return a->index() < b->index();}); + PAttachment attToUndelete = *iter; - // Restore attachment in database - Storage::instance().undeleteAttachment(attToUndelete); + // Restore attachment in database + Storage::instance().undeleteAttachment(attToUndelete); - attModel->addItem(attToUndelete); + attModel->addItem(attToUndelete); + } - } - - //Storage::instance().undeleteAttachment(mAttachment); - return true; + return true; } // ------------ NewTaskAction ------------ #define NewTaskId int(4) 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() @@ -266,50 +261,50 @@ NewTaskAction::~NewTaskAction() bool NewTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mParent); - taskModel->beginInsertRows(parentIndex, mIndex, mIndex); - //taskModel->rowCount(parentIndex), taskModel->rowCount(parentIndex)); - } - if (!mTask) - { - mTask = Storage::instance().createTask(mParent, mIndex); - mTask->setTitle(mTitle); - mTask->save(); - } - else - Storage::instance().undeleteTask(mTask); + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mParent); + taskModel->beginInsertRows(parentIndex, mIndex, mIndex); + //taskModel->rowCount(parentIndex), taskModel->rowCount(parentIndex)); + } + if (!mTask) + { + mTask = Storage::instance().createTask(mParent, mIndex); + mTask->setTitle(mTitle); + mTask->save(); + } + else + Storage::instance().undeleteTask(mTask); - if (taskModel) - taskModel->endInsertRows(); + if (taskModel) + taskModel->endInsertRows(); - return true; + return true; } bool NewTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - if (taskModel) - { - // Get parent index - QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); - int row = Storage::instance().findTaskIndexInParent(mTask); - taskModel->beginRemoveRows(parentIndex, row, row); - } + if (taskModel) + { + // Get parent index + QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); + int row = Storage::instance().findTaskIndexInParent(mTask); + taskModel->beginRemoveRows(parentIndex, row, row); + } - Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total); - if (taskModel) - { - taskModel->endRemoveRows(); - } - return true; + Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total); + if (taskModel) + { + taskModel->endRemoveRows(); + } + return true; } // ------------ RenameTaskAction --------- RenameTaskAction::RenameTaskAction(PTask task, const QString &newTitle) - :TaskAction(task), mNewTitle(newTitle) + :TaskAction(task), mNewTitle(newTitle) { - mTitle = mTask->title(); + mTitle = mTask->title(); } RenameTaskAction::~RenameTaskAction() @@ -319,38 +314,38 @@ RenameTaskAction::~RenameTaskAction() bool RenameTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - mTask->setTitle(mNewTitle); - mTask->save(); + mTask->setTitle(mNewTitle); + mTask->save(); - if (taskModel) - { - QModelIndex index = taskModel->getIndex(mTask); - if (index.isValid()) - taskModel->dataChanged(index ,index); - } - return true; + if (taskModel) + { + QModelIndex index = taskModel->getIndex(mTask); + if (index.isValid()) + taskModel->dataChanged(index ,index); + } + return true; } bool RenameTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - mTask->setTitle(mTitle); - mTask->save(); + mTask->setTitle(mTitle); + mTask->save(); - if (taskModel) - { - QModelIndex index = taskModel->getIndex(mTask); - if (index.isValid()) - taskModel->dataChanged(index, index); - } - return true; + if (taskModel) + { + QModelIndex index = taskModel->getIndex(mTask); + if (index.isValid()) + taskModel->dataChanged(index, index); + } + return true; } // ------------ MoveTaskAction ------------ MoveTaskAction::MoveTaskAction(PTask task, PTask newParent, int newIndex) - :TaskAction(task), mNewIndex(newIndex), mNewParent(newParent) + :TaskAction(task), mNewIndex(newIndex), mNewParent(newParent) { - mIndex = mTask->index(); - mParent = mTask->parent(); + mIndex = mTask->index(); + mParent = mTask->parent(); } MoveTaskAction::~MoveTaskAction() @@ -359,75 +354,75 @@ MoveTaskAction::~MoveTaskAction() bool MoveTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - taskModel->layoutAboutToBeChanged(); + taskModel->layoutAboutToBeChanged(); - // Detach it from old parent (if exists) - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); - int row = Storage::instance().findTaskIndexInParent(mTask); - taskModel->beginRemoveRows(parentIndex, row, row); - } + // Detach it from old parent (if exists) + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); + int row = Storage::instance().findTaskIndexInParent(mTask); + taskModel->beginRemoveRows(parentIndex, row, row); + } - // Remove task from memory structures only - Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent); + // Remove task from memory structures only + Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent); - if (taskModel) - taskModel->endRemoveRows(); + if (taskModel) + taskModel->endRemoveRows(); - // Move task - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mNewParent); - taskModel->beginInsertRows(parentIndex, mNewIndex, mNewIndex); - } - Storage::instance().moveTask(mTask, mNewParent, mNewIndex); - if (taskModel) - taskModel->endInsertRows(); + // Move task + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mNewParent); + taskModel->beginInsertRows(parentIndex, mNewIndex, mNewIndex); + } + Storage::instance().moveTask(mTask, mNewParent, mNewIndex); + if (taskModel) + taskModel->endInsertRows(); - taskModel->layoutChanged(); + taskModel->layoutChanged(); - return true; + return true; } bool MoveTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mNewParent); + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mNewParent); - // Tell about removing of row - taskModel->beginRemoveRows(parentIndex, mNewIndex, mNewIndex); - } + // Tell about removing of row + taskModel->beginRemoveRows(parentIndex, mNewIndex, mNewIndex); + } - // Delete from parent - Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent); - if (taskModel) - taskModel->endRemoveRows(); + // Delete from parent + Storage::instance().deleteTask(mTask, Storage::DeleteOption_FromParent); + if (taskModel) + taskModel->endRemoveRows(); - // Reload task if needed - mTask->loadContent(); + // Reload task if needed + mTask->loadContent(); - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mParent); - taskModel->beginInsertRows(parentIndex, mIndex, mIndex); - } + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mParent); + taskModel->beginInsertRows(parentIndex, mIndex, mIndex); + } - // Move task back - Storage::instance().moveTask(mTask, mParent, mIndex); - if (taskModel) - taskModel->endInsertRows(); + // Move task back + Storage::instance().moveTask(mTask, mParent, mIndex); + if (taskModel) + taskModel->endInsertRows(); - return true; + return true; } // ------- IncreaseLevelAction ----------- IncreaseLevelAction::IncreaseLevelAction(PTask task) - :TaskAction(task) + :TaskAction(task) { - mOldParent = task->parent(); - mOldIndex = task->index(); + mOldParent = task->parent(); + mOldIndex = task->index(); } 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()); int row = Storage::instance().findTaskIndexInParent(mTask); @@ -448,12 +443,12 @@ bool IncreaseLevelAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* if (taskModel) taskModel->endRemoveRows(); */ - return true; + return true; } -bool IncreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) +bool IncreaseLevelAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/) { - /* + /* if (taskModel) { QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); @@ -463,12 +458,12 @@ bool IncreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListMode if (taskModel) taskModel->endInsertRows(); */ - return true; + return true; } // ------- DecreaseLevelAction ----------- 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()); int row = Storage::instance().findTaskIndexInParent(mTask); @@ -491,12 +486,12 @@ bool DecreaseLevelAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* if (taskModel) taskModel->endRemoveRows(); */ - return true; + return true; } -bool DecreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) +bool DecreaseLevelAction::rollback(TaskTreeModel* /*taskModel*/, AttachmentsListModel* /*attModel*/) { - /* + /* if (taskModel) { QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); @@ -506,14 +501,14 @@ bool DecreaseLevelAction::rollback(TaskTreeModel* taskModel, AttachmentsListMode if (taskModel) taskModel->endInsertRows(); */ - return true; + return true; } // ------- DeleteTaskAction --------------- DeleteTaskAction::DeleteTaskAction(PTask task) - :TaskAction(task) + :TaskAction(task) { - mIndex = Storage::instance().findTaskIndexInParent(mTask); + mIndex = Storage::instance().findTaskIndexInParent(mTask); } DeleteTaskAction::~DeleteTaskAction() @@ -522,31 +517,52 @@ DeleteTaskAction::~DeleteTaskAction() bool DeleteTaskAction::commit(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); - int row = Storage::instance().findTaskIndexInParent(mTask); - taskModel->beginRemoveRows(parentIndex, row, row); - } + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); + int row = Storage::instance().findTaskIndexInParent(mTask); + taskModel->beginRemoveRows(parentIndex, row, row); + } - Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total); - if (taskModel) - taskModel->endRemoveRows(); + Storage::instance().deleteTask(mTask, Storage::DeleteOption_Total); + if (taskModel) + taskModel->endRemoveRows(); - return true; + return true; } bool DeleteTaskAction::rollback(TaskTreeModel* taskModel, AttachmentsListModel* /*attModel*/) { - if (taskModel) - { - QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); - taskModel->beginInsertRows(parentIndex, mIndex, mIndex); - } - Storage::instance().undeleteTask(mTask); - if (taskModel) - taskModel->endInsertRows(); - return true; + if (taskModel) + { + QModelIndex parentIndex = taskModel->getIndex(mTask->parent()); + taskModel->beginInsertRows(parentIndex, mIndex, mIndex); + } + Storage::instance().undeleteTask(mTask); + if (taskModel) + taskModel->endInsertRows(); + 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; +} diff --git a/client/taskaction.h b/client/taskaction.h index 026c43a..a2caa21 100644 --- a/client/taskaction.h +++ b/client/taskaction.h @@ -1,7 +1,7 @@ #ifndef TASKACTION_H #define TASKACTION_H -#include +#include "task.h" #include #include #include @@ -9,12 +9,15 @@ class TaskTreeModel; class AttachmentsListModel; +// Base class for all actions class TaskAction { friend class ChangesHistory; public: TaskAction(PTask task); virtual ~TaskAction(); + + // Associated task PTask task() const; protected: @@ -45,10 +48,10 @@ public: static ChangesHistory& instance(); protected: - TaskTreeModel* mTaskModel; - AttachmentsListModel* mAttachmentsModel; + TaskTreeModel* mTaskModel = nullptr; + AttachmentsListModel* mAttachmentsModel = nullptr; QVector mActionList; - int mRollbackCount; + int mRollbackCount = 0; }; #define MAKE_ACTION(X) ChangesHistory::instance().add(PTaskAction(X)) @@ -181,4 +184,15 @@ protected: 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 diff --git a/client/tasktreemodel.cpp b/client/tasktreemodel.cpp index e4c4baf..af6bf0b 100644 --- a/client/tasktreemodel.cpp +++ b/client/tasktreemodel.cpp @@ -11,9 +11,7 @@ TaskTreeView::TaskTreeView(QWidget *widget) :QTreeView(widget) -{ - -} +{} void TaskTreeView::dragMoveEvent(QDragMoveEvent* event) { @@ -270,15 +268,17 @@ QVariant TaskTreeModel::headerData(int /*section*/, Qt::Orientation /*orientatio { return ""; - /*if (orientation != Qt::Horizontal) - return QVariant(); + /* + if (orientation != Qt::Horizontal) + return QVariant(); - switch (role) - { - case Qt::DisplayRole: - return QString(tr("Tasks")); - } - return QVariant();*/ + switch (role) + { + case Qt::DisplayRole: + return QString(tr("Tasks")); + } + return QVariant(); + */ } Qt::DropActions TaskTreeModel::supportedDropActions() const @@ -419,7 +419,8 @@ bool TaskTreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction actio 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"; return true; @@ -432,7 +433,7 @@ bool TaskTreeModel::removeRows(int row, int count, const QModelIndex &parent) return true; // Called during drag and drop - if (parent.isValid()) +/* if (parent.isValid()) { PTask parentTask = getTask(parent); for (int i=0; icutInterval(t); + mTimeLine->save(); endRemoveRows(); }