diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index b4f0677..ba58e91 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -123,7 +123,7 @@ if (TARGET_OSX) endif() # This will create you executable -set (EXE_NAME litt_outliner) +set (EXE_NAME Litt) add_executable(${EXE_NAME} ${ADDITIONAL_EXE_OPTIONS} diff --git a/client/browserwidget.cpp b/client/browserwidget.cpp new file mode 100644 index 0000000..8e7e476 --- /dev/null +++ b/client/browserwidget.cpp @@ -0,0 +1,137 @@ +#include "browserwidget.h" +#include "ui_browserwidget.h" + +BrowserWidget::BrowserWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::BrowserWidget) +{ + ui->setupUi(this); +} + +BrowserWidget::~BrowserWidget() +{ + delete ui; +} + +void BrowserWidget::newRootTask() +{ + PTask rootTask = mTaskTreeModel->addTask(QModelIndex(), Storage::instance().topOfTaskTree().size()); + QModelIndex rootIndex = mTaskTreeModel->getIndex(rootTask); + ui->mTaskTree->setCurrentIndex(rootIndex); + ui->mTaskTree->edit(rootIndex); +} + +void BrowserWidget::newTask() +{ + QModelIndex index = ui->mTaskTree->currentIndex(); + if (index.isValid()) + ui->mTaskTree->expand(index); + PTask parent = mTaskTreeModel->getTask(index); + if (!parent) + return; + + PTask child = mTaskTreeModel->addTask(index, parent->children().size()); + if (!child) + return; + + QModelIndex childIndex = mTaskTreeModel->getIndex(child); + ui->mTaskTree->setCurrentIndex(childIndex); + ui->mTaskTree->edit(childIndex); +} + +void BrowserWidget::newSibling() +{ + QModelIndex index = ui->mTaskTree->currentIndex(); + if (!index.isValid()) + return; + PTask currentTask = mTaskTreeModel->getTask(index); + if (!currentTask) + return; + + PTask t = mTaskTreeModel->addTask(index.parent(), index.row() + 1); + QModelIndex i = mTaskTreeModel->getIndex(t); + + ui->mTaskTree->setCurrentIndex(i); + ui->mTaskTree->edit(i); +} + +void BrowserWidget::moveUp() +{ + QModelIndex index = ui->mTaskTree->currentIndex(); + if (!index.isValid()) + return; + + PTask currentTask = mTaskTreeModel->getTask(index); + if (!currentTask) + return; + + if (index.row() == 0) + return; + + mTaskTreeModel->moveTask(currentTask, -1); + ui->mTaskTree->setCurrentIndex(mTaskTreeModel->getIndex(currentTask)); +} + +void BrowserWidget::moveDown() +{ + QModelIndex index = ui->mTaskTree->currentIndex(); + if (!index.isValid()) + return; + + PTask currentTask = mTaskTreeModel->getTask(index); + if (!currentTask) + return; + + if (currentTask->parent()) + { + if (index.row() == currentTask->parent()->children().size() - 1) + return; + } + else + if (index.row() == Storage::instance().topOfTaskTree().size() - 1) + return; + + mTaskTreeModel->moveTask(currentTask, +1); + ui->mTaskTree->setCurrentIndex(mTaskTreeModel->getIndex(currentTask)); +} + +void BrowserWidget::renameTask() +{ + QModelIndex index = ui->mTaskTree->currentIndex(); + if (index.isValid()) + ui->mTaskTree->edit(index); +} + +void BrowserWidget::deleteTask() +{ + QModelIndex index = ui->mTaskTree->currentIndex(); + if (!index.isValid()) + return; + + PTask t = mTaskTreeModel->getTask(index); + if (!t) + return; + + // Check if deleted task + if (mCurrentTask == t) + { + alertBox(tr("Problem"), tr("Impossible to delete active task. Please stop tracking before task delete."), AlertType_Warning); + } + else + { + if (mSettings->data()[KEY_ASK_BEFORE_DELETE].toBool()) + { + auto reply = QMessageBox::question(this, + tr("Are you sure?"), + tr("Are you sure you want to delete ") + t->title(), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) + return; + } + + if (mAutomaticTask == t) + mAutomaticTask.reset(); + + mTaskTreeModel->deleteTask(ui->mTaskTree->currentIndex()); + } +} diff --git a/client/browserwidget.h b/client/browserwidget.h new file mode 100644 index 0000000..2926bba --- /dev/null +++ b/client/browserwidget.h @@ -0,0 +1,32 @@ +#ifndef BROWSERWIDGET_H +#define BROWSERWIDGET_H + +#include + +namespace Ui { +class BrowserWidget; +} + +class BrowserWidget : public QWidget +{ + Q_OBJECT + +public: + explicit BrowserWidget(QWidget *parent = 0); + ~BrowserWidget(); + +private: + Ui::BrowserWidget *ui; + +public slots: + void onNewRootTask(); + void onNewTask(); + void onNewSibling(); + void onMoveUp(); + void onMoveDown(); + void onRenameTask(); + void onDeleteTask(); + +}; + +#endif // BROWSERWIDGET_H diff --git a/client/browserwidget.ui b/client/browserwidget.ui new file mode 100644 index 0000000..4f2804b --- /dev/null +++ b/client/browserwidget.ui @@ -0,0 +1,235 @@ + + + BrowserWidget + + + + 0 + 0 + 989 + 566 + + + + Form + + + + + + + 0 + 0 + + + + + 600 + 200 + + + + Qt::Horizontal + + + + Qt::CustomContextMenu + + + true + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + 0 + + + Qt::Vertical + + + + QFrame::StyledPanel + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + false + + + background-color:white; + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Find: + + + + + + + + + + + + + + + 16777215 + 70 + + + + QFrame::StyledPanel + + + + + + Today: + + + + + + + 0 hours 0 minutes + + + + + + + This month: + + + + + + + 0 hours 0 minutes + + + + + + + + + + + + + + + + TaskTreeView + QTreeView +
tasktreemodel.h
+
+ + QMarkdownTextEdit + QPlainTextEdit +
qmarkdowntextedit/qmarkdowntextedit.h
+
+
+ + +
diff --git a/client/build-number/build_number.h b/client/build-number/build_number.h index 53a3d79..7e788f0 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 8 \ No newline at end of file +#define APP_BUILD_NUMBER 38 \ No newline at end of file diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 16f4cdc..7b88132 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -37,6 +37,10 @@ #define SETTINGS mSettings->data() +const int ViewIndex_Main = 0; +const int ViewIndex_OpenOrCreateDb = 1; +const int ViewIndex_DbPassword = 2; + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), mPasswordFailed(false), mFindInTasksDlg(this), mDockRecentMenu(nullptr) @@ -59,13 +63,17 @@ MainWindow::MainWindow(QWidget *parent) : loadGeometry(); // Now check if database is already configured - QWidget* centralWidget = new QWidget(this); - setCentralWidget(centralWidget); - centralWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + mStackedViews = new QStackedWidget(this); + setCentralWidget(mStackedViews); + mStackedViews->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + buildMainView(); + buildOpenOrCreateView(); + buildPasswordView(); - // Check if database file exists + // Find default database file exists QString path = helper::path::pathToDatabase(); + // Find optional custom path to database if (mSettings->data()[KEY_DB_FILENAME_SPECIFIED].toBool()) path = mSettings->data()[KEY_DB_FILENAME].toString(); @@ -591,6 +599,16 @@ void MainWindow::deleteTask() } else { + if (mSettings->data()[KEY_ASK_BEFORE_DELETE].toBool()) + { + auto reply = QMessageBox::question(this, + tr("Are you sure?"), + tr("Are you sure you want to delete ") + t->title(), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) + return; + } + if (mAutomaticTask == t) mAutomaticTask.reset(); @@ -811,9 +829,6 @@ void MainWindow::updateAttachmentsLabel(PTask t) void MainWindow::setupMainUi() { - // Detach old widget - setCentralWidget(nullptr); - // Construct main UI ui = new Ui::MainWindow(); ui->setupUi(this); @@ -833,31 +848,41 @@ void MainWindow::setupMainUi() QApplication::postEvent(this, new AttachDatabaseEvent()); } +void MainWindow::buildPasswordView() +{ + if (!mConnectDbWidget) + { + mConnectDbWidget = new ConnectDbWidget(message, mStackedViews); + connect(mConnectDbWidget, SIGNAL(passwordEntered(QString)), this, SLOT(onDbPasswordEntered(QString))); + connect(mConnectDbWidget, SIGNAL(cancelled()), this, SLOT(onDbPasswordCancelled())); + int index = mStackedViews->addWidget(mConnectDbWidget); + assert (index == ViewIndex_DbPassword); + } +} + +void MainWindow::buildOpenOrCreateView() +{ + if (!mOpenOrCreateDbWidget) + { + mOpenOrCreateDbWidget = new OpenOrCreateDbWidget(mStackedViews); + connect(w, &OpenOrCreateDbWidget::databaseChanged, + [this](const QString& path) { onDatabaseChanged(path); }); + connect(w, &OpenOrCreateDbWidget::passwordEntered, + [this](const QString& password) { onNewDbPasswordEntered(password); }); + int index = mStackedViews->addWidget(mOpenOrCreateDbWidget); + assert (index == ViewIndex_OpenOrCreateDb); + } +} + // Ask password void MainWindow::askDbPassword(const QString& message) { - setCentralWidget(nullptr); setCentralWidget(new QWidget(this)); - auto cdw = new ConnectDbWidget(message, centralWidget()); - connect(cdw, SIGNAL(passwordEntered(QString)), this, SLOT(onDbPasswordEntered(QString))); - connect(cdw, SIGNAL(cancelled()), this, SLOT(onDbPasswordCancelled())); - - QVBoxLayout* l = new QVBoxLayout(centralWidget()); - l->addWidget(cdw); - l->setAlignment(Qt::AlignCenter); - centralWidget()->setLayout(l); + mStackedViews->setCurrentIndex(0); } void MainWindow::askNewDbPassword() { - setCentralWidget(nullptr); setCentralWidget(new QWidget(this)); - auto w = new OpenOrCreateDbWidget(centralWidget()); - connect(w, &OpenOrCreateDbWidget::databaseChanged, [this](const QString& path) { onDatabaseChanged(path); }); - connect(w, &OpenOrCreateDbWidget::passwordEntered, [this](const QString& password) { onNewDbPasswordEntered(password); }); - - auto l = new QVBoxLayout(centralWidget()); - l->addWidget(w); - l->setAlignment(Qt::AlignCenter); - centralWidget()->setLayout(l); + mStackedViews->setCurrentIndex(1); } void MainWindow::startOrStopTracking() diff --git a/client/mainwindow.h b/client/mainwindow.h index 7bbc36e..838efd5 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "tasktreemodel.h" #include "settings.h" @@ -16,6 +17,9 @@ #include "logger.h" #include "platforms/hidtracker.h" #include "finddialog.h" +#include "connectdb_widget.h" +#include "openorcreatedb_widget.h" + #include #ifdef TARGET_OSX @@ -91,7 +95,9 @@ private: QMenu* mDockRecentMenu; QDialog* mTrayWindow = nullptr; QString mPassword = NOPASSWORDSTRING; + ConnectDbWidget* mConnectDbWidget = nullptr; + QStackedWidget* mStackedViews = nullptr; void saveGeometry(); void loadGeometry(); @@ -148,6 +154,10 @@ private: // Show UI about fatal alert & button to quit app void showFatal(const QString& message); + void buildPasswordView(); + void buildOpenOrCreateView(); + void buildMainView(); + signals: void onTimeFormatChanged(); void onTimeChanged(); diff --git a/client/platforms/hidtracker.cpp b/client/platforms/hidtracker.cpp index b712a3e..c3f1ead 100644 --- a/client/platforms/hidtracker.cpp +++ b/client/platforms/hidtracker.cpp @@ -7,7 +7,7 @@ #endif HIDActivityTracker::HIDActivityTracker() - :mInterval(600), mTrackerActive(false), mState(None) + :mInterval(600), mTrackerActive(false), mState(None), mImpl(nullptr) { #ifdef TARGET_OSX mImpl = new HIDTrackerImplOSX(); diff --git a/client/preferencesdlg.cpp b/client/preferencesdlg.cpp index 8465570..3cccc7c 100644 --- a/client/preferencesdlg.cpp +++ b/client/preferencesdlg.cpp @@ -28,6 +28,9 @@ PreferencesDlg::PreferencesDlg(QWidget *parent, Settings& settings) : // Show seconds or not? ui->mShowSecondsCheckbox->setChecked(settings.data().value(KEY_SHOW_SECONDS).toBool()); + // Ask confirmation before node delete + ui->mAskBeforeDeleteCheckbox->setChecked(settings.data().value(KEY_ASK_BEFORE_DELETE).toBool()); + // Dark theme ? ui->mDarkThemeCheckbox->setChecked(settings.data().value(KEY_DARK_THEME).toBool()); @@ -79,13 +82,13 @@ void PreferencesDlg::accepted() helper::password::save(savePassword ? Storage::instance().key() : QString("")); mSettings.data()[KEY_SHOW_SECONDS] = ui->mShowSecondsCheckbox->isChecked(); + mSettings.data()[KEY_ASK_BEFORE_DELETE] = ui->mAskBeforeDeleteCheckbox->isChecked(); mSettings.data()[KEY_DB_FILENAME_SPECIFIED] = ui->mCustomDatabaseFileCheckbox->isChecked(); mSettings.data()[KEY_DB_FILENAME] = ui->mDatabaseLocation->text(); mSettings.data()[KEY_SMART_STOP] = ui->mSmartStopTracking->isChecked(); mSettings.data()[KEY_SMART_STOP_MINUTES] = ui->mSmartStopIntervalInMinutes->text().toInt(); mSettings.data()[KEY_SMART_START] = ui->mSmartStartTracking->isChecked(); mSettings.data()[KEY_SHOW_TRAY_ICON] = ui->mShowTrayIconCheckbox->isChecked(); - //mSettings.data()[KEY_ASK_START] = ui->mAskQuestionOnStartRadiobutton->isChecked(); mSettings.data()[KEY_ASK_STOP] = ui->mAskQuestionOnStopRadiobutton->isChecked(); if (mSettings.data()[KEY_DARK_THEME].toBool() != ui->mDarkThemeCheckbox->isChecked()) { diff --git a/client/preferencesdlg.ui b/client/preferencesdlg.ui index 515367f..55e57d4 100644 --- a/client/preferencesdlg.ui +++ b/client/preferencesdlg.ui @@ -6,8 +6,8 @@ 0 0 - 444 - 374 + 586 + 452 @@ -34,6 +34,16 @@ + + + + Ask before delete + + + true + + + diff --git a/client/settings.cpp b/client/settings.cpp index c2cc9e4..7699520 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -2,6 +2,9 @@ #include "helper.h" #include #include +#include +#include +#include Settings::Settings() { @@ -30,6 +33,16 @@ void Settings::save() void Settings::load() { + // Path to settings file + QString path = helper::path::pathToSettings(); + + // Check if directory exists at all + QString dir = QFileInfo(path).absoluteDir().path(); + + if (!QDir(dir).exists()) + QDir().mkdir(dir); + + // Load data itself QSettings settings(helper::path::pathToSettings(), QSettings::IniFormat); mData.clear(); const QStringList keys = settings.allKeys(); diff --git a/client/settings.h b/client/settings.h index 5f75017..d59b290 100644 --- a/client/settings.h +++ b/client/settings.h @@ -6,42 +6,43 @@ #include #include -#define KEY_AUTOSAVE_PASSWORD "AutosavePassword" -#define KEY_PASSWORD "Password" -#define KEY_SHOW_SECONDS "ShowSeconds" -#define KEY_DB_FILENAME "DbFilename" -#define KEY_DB_FILENAME_SPECIFIED "DbFilenameSpecified" -#define KEY_TIMECOUNTER_TYPE "TimecounterType" +#define KEY_AUTOSAVE_PASSWORD "AutosavePassword" +#define KEY_PASSWORD "Password" +#define KEY_SHOW_SECONDS "ShowSeconds" +#define KEY_DB_FILENAME "DbFilename" +#define KEY_DB_FILENAME_SPECIFIED "DbFilenameSpecified" +#define KEY_TIMECOUNTER_TYPE "TimecounterType" -#define KEY_LEFT "Left" -#define KEY_TOP "Top" -#define KEY_WIDTH "Width" -#define KEY_HEIGHT "Height" -#define KEY_MAXIMIZED "Maximized" -#define KEY_SPLITTEROFFSET1 "SplitterOffset1" -#define KEY_SPLITTEROFFSET2 "SplitterOffset2" +#define KEY_LEFT "Left" +#define KEY_TOP "Top" +#define KEY_WIDTH "Width" +#define KEY_HEIGHT "Height" +#define KEY_MAXIMIZED "Maximized" +#define KEY_SPLITTEROFFSET1 "SplitterOffset1" +#define KEY_SPLITTEROFFSET2 "SplitterOffset2" -#define KEY_TIMESPLITTER_OFFSET1 "TimeSplitterOffset1" -#define KEY_TIMESPLITTER_OFFSET2 "TimeSplitterOffset2" +#define KEY_TIMESPLITTER_OFFSET1 "TimeSplitterOffset1" +#define KEY_TIMESPLITTER_OFFSET2 "TimeSplitterOffset2" -#define VALUE_TIMECOUNTER_THISDAY "ThisDay" -#define VALUE_TIMECOUNTER_THISSESSION "ThisSession" -#define VALUE_TIMECOUNTER_ALLTIME "AllTime" +#define VALUE_TIMECOUNTER_THISDAY "ThisDay" +#define VALUE_TIMECOUNTER_THISSESSION "ThisSession" +#define VALUE_TIMECOUNTER_ALLTIME "AllTime" -#define KEY_SMART_START "SmartStart" -#define KEY_SMART_STOP "SmartStop" -#define KEY_SMART_STOP_MINUTES "SmartStopIntervalInMinutes" -#define KEY_ASK_START "AskStart" -#define KEY_ASK_STOP "AskStop" +#define KEY_SMART_START "SmartStart" +#define KEY_SMART_STOP "SmartStop" +#define KEY_SMART_STOP_MINUTES "SmartStopIntervalInMinutes" +#define KEY_ASK_START "AskStart" +#define KEY_ASK_STOP "AskStop" -#define KEY_SHOW_TRAY_ICON "ShowTrayIcon" -#define KEY_CUMULATIVE_REPORT "CumulativeReport" +#define KEY_SHOW_TRAY_ICON "ShowTrayIcon" +#define KEY_CUMULATIVE_REPORT "CumulativeReport" -#define KEY_SELECTED_TASKS "SelectedTasks" -#define KEY_EXPANDED_TASKS "ExpandedTasks" -#define KEY_RECENT_TASKS "RecentTasks" +#define KEY_SELECTED_TASKS "SelectedTasks" +#define KEY_EXPANDED_TASKS "ExpandedTasks" +#define KEY_RECENT_TASKS "RecentTasks" -#define KEY_DARK_THEME "DarkTheme" +#define KEY_DARK_THEME "DarkTheme" +#define KEY_ASK_BEFORE_DELETE "AskBeforeDelete" class Settings {