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

View File

@ -1,2 +1,2 @@
// 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;
}
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();

View File

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

View File

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

View File

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

View File

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

View File

@ -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; i<mTopTasks.size(); i++)
{
mTopTasks[i]->setIndex(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; i<parent->children().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; i<topOfTaskTree().size(); i++)
{
topOfTaskTree()[i]->setIndex(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<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()
{
@ -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;
}

View File

@ -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<SQLite::Database> mDatabase;
TaskArray mTopTasks;
typedef QMap<Task::ModelId, PTask> TaskModelIdMap;
TaskModelIdMap mTaskModelIdMap;
typedef QMap<Task::Id, PTask> TaskIdMap;
TaskIdMap mTaskIdMap;
QString mPath, mKey;
QSharedPointer<SQLite::Database> mDatabase;
TaskArray mTopTasks;
typedef QMap<Task::ModelId, PTask> TaskModelIdMap;
TaskModelIdMap mTaskModelIdMap;
typedef QMap<Task::Id, PTask> 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

View File

@ -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<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();
}
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<sqlite3_int64>(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<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();
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<TimeRecord>* 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;
}

View File

@ -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<int, QSharedPointer<MonthesMap> > 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;

View File

@ -7,7 +7,7 @@
#include <QByteArray>
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;
}

View File

@ -1,7 +1,7 @@
#ifndef TASKACTION_H
#define TASKACTION_H
#include <task.h>
#include "task.h"
#include <QString>
#include <QDataStream>
#include <QModelIndexList>
@ -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<PTaskAction> 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

View File

@ -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; i<count; i++)
@ -461,6 +462,7 @@ bool TaskTreeModel::removeRows(int row, int count, const QModelIndex &parent)
}
return true;
*/
}
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());
mTimeLine->cutInterval(t);
mTimeLine->save();
endRemoveRows();
}