diff options
author | Louie S <louie@example.com> | 2024-07-23 20:37:48 -0400 |
---|---|---|
committer | Louie S <louie@example.com> | 2024-07-23 20:37:48 -0400 |
commit | c34930425fadfc4083067b9306159cd8e8ecf6c6 (patch) | |
tree | d9c09709f1b9cf7e9bcf098d48c7036044098a7d /src/frontend/qtwidgets | |
parent | f820f439910ef0243e6b9aded293190341b058ac (diff) |
Rearrange source file locations
Diffstat (limited to 'src/frontend/qtwidgets')
34 files changed, 2061 insertions, 0 deletions
diff --git a/src/frontend/qtwidgets/addEntryForm.cpp b/src/frontend/qtwidgets/addEntryForm.cpp new file mode 100644 index 0000000..7402167 --- /dev/null +++ b/src/frontend/qtwidgets/addEntryForm.cpp @@ -0,0 +1,49 @@ +#include <QDate> +#include <QMessageBox> +#include <QString> + +#include "addEntryForm.h" +#include "../../backend/db_sqlite.h" + +AddEntryForm::AddEntryForm(const Group &g) : + parent_group(g) +{ + // load uic + ui.setupUi(this); + + // set titles + this->setWindowTitle("Add Entry"); + ui.title->setText("Add Entry"); + + // widgets setup + ui.entry_due->setDate(QDate::currentDate()); +} + +void AddEntryForm::accept() { + QString desc_text = ui.entry_desc->text(); + QDateTime due_text = ui.entry_due->date().startOfDay(); + QString due_alt_text = ui.entry_due_alt->text(); + QString link_text = ui.entry_link->text(); + QString color_text = ui.entry_color->text(); + QString highlight_text = ui.entry_highlight->text(); + QMessageBox error_message; + BackendDB database; + int new_id; + + // Check that the new entry is not blank + if(desc_text.isEmpty()) { + error_message.setIcon(QMessageBox::Warning); + error_message.setWindowTitle("Error Message"); + error_message.setText("Description cannot be blank"); + error_message.exec(); + return; + } + + // replace due_text with null date if due_checkbox is unchecked + if(!ui.entry_due_checkbox->isChecked()) + due_text = QDateTime(); + + new_id = database.insertEntry(Entry(0, this->parent_group.id, desc_text, due_text, due_alt_text, link_text, color_text, highlight_text)); + + QDialog::accept(); +} diff --git a/src/frontend/qtwidgets/addEntryForm.h b/src/frontend/qtwidgets/addEntryForm.h new file mode 100644 index 0000000..81535f1 --- /dev/null +++ b/src/frontend/qtwidgets/addEntryForm.h @@ -0,0 +1,24 @@ +#ifndef ADDENTRYFORM_H +#define ADDENTRYFORM_H + +#include <QObject> + +#include "group.h" +#include "ui_entryForm.h" + +class AddEntryForm : public QDialog { + Q_OBJECT + + public: + AddEntryForm(const Group &g); + + private: + Ui::entryDialog ui; + + Group parent_group; + + private slots: + void accept(); +}; + +#endif diff --git a/src/frontend/qtwidgets/addGroupForm.cpp b/src/frontend/qtwidgets/addGroupForm.cpp new file mode 100644 index 0000000..c7d5faa --- /dev/null +++ b/src/frontend/qtwidgets/addGroupForm.cpp @@ -0,0 +1,34 @@ +#include <QMessageBox> + +#include "addGroupForm.h" +#include "../../backend/db_sqlite.h" + +AddGroupForm::AddGroupForm() { + // load uic + ui.setupUi(this); + + // set titles + this->setWindowTitle("Add Group"); + ui.title->setText("Add Group"); +} + +void AddGroupForm::accept() { + QString name_text = ui.group_name->text(); + Group::Column column_value = Group::Column(ui.group_column->currentIndex()); + QString link_text = ui.group_link->text(); + QMessageBox error_message; + BackendDB database; + int new_id; + + if(name_text.isEmpty()) { + error_message.setIcon(QMessageBox::Warning); + error_message.setWindowTitle("Error Message"); + error_message.setText("Name cannot be blank"); + error_message.exec(); + return; + } + + new_id = database.insertGroup(Group(0, name_text, column_value, link_text)); + + QDialog::accept(); +} diff --git a/src/frontend/qtwidgets/addGroupForm.h b/src/frontend/qtwidgets/addGroupForm.h new file mode 100644 index 0000000..b181408 --- /dev/null +++ b/src/frontend/qtwidgets/addGroupForm.h @@ -0,0 +1,21 @@ +#ifndef ADDGROUPFORM_H +#define ADDGROUPFORM_H + +#include <QObject> + +#include "ui_groupForm.h" + +class AddGroupForm : public QDialog { + Q_OBJECT + + public: + AddGroupForm(); + + private: + Ui::groupDialog ui; + + private slots: + void accept(); +}; + +#endif diff --git a/src/frontend/qtwidgets/assignmentList.cpp b/src/frontend/qtwidgets/assignmentList.cpp new file mode 100644 index 0000000..dc89544 --- /dev/null +++ b/src/frontend/qtwidgets/assignmentList.cpp @@ -0,0 +1,169 @@ +#include <algorithm> +#include <QAction> +#include <QApplication> +#include <QCoreApplication> +#include <QDate> +#include <QDir> +#include <QFile> +#include <QMessageBox> +#include <QObject> +#include <QSettings> +#include <QStandardPaths> +#include <QUiLoader> + +#include <QDebug> +#include <QErrorMessage> + +#include "../../../config.h" +#include "addGroupForm.h" +#include "assignmentList.h" +#include "../../backend/db_sqlite.h" +#include "entryLayout.h" +#include "groupLayout.h" +#include "lib.h" +#include "preferencesDialog.h" + +AssignmentList::AssignmentList() { + // set QSettings information + QCoreApplication::setOrganizationName(ORGANIZATION_NAME); + QCoreApplication::setApplicationName(PROJECT_NAME); + + // ensure QSettings location exists + this->initializeSettings(); + + // load everything from database into static global variables + this->initializeGlobals(); + + // load uic + ui.setupUi(this); + this->initializeUI(); +} + +void AssignmentList::initializeSettings() { + QSettings settings; + QDir local_data_dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + + settings.beginGroup("paths"); + if(!settings.contains("db_path")) { + qDebug() << "Setting default db_path"; + settings.setValue("db_path", local_data_dir.filePath("data.db")); + } + settings.endGroup(); +} + +// load from database into static QList variables +void AssignmentList::initializeGlobals() { + BackendDB database; + + Group::groups = database.loadGroups(); + Entry::entries = database.loadEntries(); + Rule::rules = database.loadRules(); +} + +void AssignmentList::initializeUI() { + // create menu connections + QObject::connect(ui.actionPreferences, &QAction::triggered, this, &AssignmentList::preferences); + QObject::connect(ui.actionReload, &QAction::triggered, this, &AssignmentList::reload); + QObject::connect(ui.actionExit, &QAction::triggered, this, QApplication::quit); + QObject::connect(ui.actionAdd_Group, &QAction::triggered, this, &AssignmentList::addGroup); + QObject::connect(ui.actionClean_Hidden, &QAction::triggered, this, &AssignmentList::cleanHidden); + QObject::connect(ui.actionAbout, &QAction::triggered, this, &AssignmentList::aboutDialog); + + // create toolbar + ui.toolBar->addAction(ui.actionAdd_Group); + + this->displayDate(); + this->show(); + this->displayWidgets(); +} + +void AssignmentList::displayDate() { + QDate today = QDate::currentDate(); + ui.title->setText(today.toString("dddd, MMM d yyyy")); +} + +void AssignmentList::displayWidgets() { + QVBoxLayout *column_left = new QVBoxLayout(); + QVBoxLayout *column_right = new QVBoxLayout(); + BackendDB database; + GroupLayout *new_group_layout; + int i; + + // clear out old layouts if they exist + recursiveClear(ui.groups_layout); + + for(i = 0; i < Group::groups.size(); ++i) { + if(Group::groups[i].hidden) continue; + new_group_layout = new GroupLayout(Group::groups[i]); + new_group_layout->addLayout(this->drawEntries(Group::groups[i].id)); // add entries to layout + if(Group::groups[i].column == Group::left) column_left->addLayout(new_group_layout); + else column_right->addLayout(new_group_layout); + } + + column_left->addStretch(); + column_right->addStretch(); + + ui.groups_layout->addLayout(column_left, 0, 0); + ui.groups_layout->addLayout(column_right, 0, 1); +} + +QVBoxLayout *AssignmentList::drawEntries(int parent_id) { + BackendDB database; + QVBoxLayout *output = new QVBoxLayout; + int i; + + // styling + output->setContentsMargins(5, 0, 0, 0); + + // sort entries + std::sort(Entry::entries.begin(), Entry::entries.end(), Entry::compare); + + for(i = 0; i < Entry::entries.size(); ++i) { + if(Entry::entries[i].parent_id != parent_id) continue; + // skip if this entry is set to hidden + if(Entry::entries[i].hidden) continue; + output->addLayout(new EntryLayout(Entry::entries[i])); + } + + return output; +} + +// Open the 'addGroup' form +void AssignmentList::addGroup() { + AddGroupForm create_new_group_dialog; + if(create_new_group_dialog.exec() == QDialog::Accepted) + this->displayWidgets(); +} + +void AssignmentList::preferences() { + PreferencesDialog preferences_dialog; + if(preferences_dialog.exec() == QDialog::Accepted) + this->displayWidgets(); +} + +void AssignmentList::reload() { + this->initializeGlobals(); + this->displayWidgets(); +} + +void AssignmentList::cleanHidden() { + QMessageBox::StandardButton are_you_sure; + BackendDB database; + + // create 'are you sure?' dialog before going forward + are_you_sure = QMessageBox::question(this, "Are You Sure?", + "Are you sure? All removed groups and entries will be permanently deleted from the database.", + QMessageBox::Yes|QMessageBox::No); + + if(are_you_sure == QMessageBox::Yes) + database.cleanHidden(); +} + +void AssignmentList::aboutDialog() { + QMessageBox about; + QString title("About " + QString(PROJECT_TITLE)); + QString text( + QString(PROJECT_TITLE) + " " + QString(VERSION) + "\n" + + "Created by Louie S. - 2024"); + about.about(this, title, text); +} diff --git a/src/frontend/qtwidgets/assignmentList.h b/src/frontend/qtwidgets/assignmentList.h new file mode 100644 index 0000000..4b792fa --- /dev/null +++ b/src/frontend/qtwidgets/assignmentList.h @@ -0,0 +1,43 @@ +#ifndef ASSIGNMENTLIST_H +#define ASSIGNMENTLIST_H + +#include <QMainWindow> +#include <QSettings> +#include <QVBoxLayout> + +#include "ui_assignmentList.h" + +class AssignmentList : public QMainWindow { + Q_OBJECT + + public: + QSettings configuration; + + AssignmentList(); + void initializeGlobals(); + void displayWidgets(); + + private: + Ui::MainWindow ui; + + void initializeSettings(); + void initializeUI(); + void displayDate(); + QVBoxLayout *drawEntries(int parent_id); + void editEntry(int id); + void toggleDoneEntry(int id); + void removeEntry(int id); + void editRules(int id); + void entryContextMenu(int entry_id); + void drawGroups(); + void drawEntries(); + + private slots: + void preferences(); + void reload(); + void addGroup(); + void cleanHidden(); + void aboutDialog(); +}; + +#endif diff --git a/src/frontend/qtwidgets/assignmentList.ui b/src/frontend/qtwidgets/assignmentList.ui new file mode 100644 index 0000000..60c66da --- /dev/null +++ b/src/frontend/qtwidgets/assignmentList.ui @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle"> + <string>Assignment List</string> + </property> + <property name="windowIcon"> + <iconset resource="../resources.qrc"> + <normaloff>:/data/assignment-list.svg</normaloff>:/data/assignment-list.svg</iconset> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents_3"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>620</width> + <height>421</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="v_box"> + <item> + <layout class="QHBoxLayout" name="title_h_box"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="title"> + <property name="font"> + <font> + <family>Arial</family> + <pointsize>17</pointsize> + </font> + </property> + <property name="text"> + <string>[DATE]</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" name="groups_layout"> + <property name="leftMargin"> + <number>20</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>20</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>22</height> + </rect> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="actionPreferences"/> + <addaction name="actionReload"/> + <addaction name="separator"/> + <addaction name="actionExit"/> + </widget> + <widget class="QMenu" name="menuEdit"> + <property name="title"> + <string>Edit</string> + </property> + <addaction name="actionAdd_Group"/> + <addaction name="separator"/> + <addaction name="actionClean_Hidden"/> + </widget> + <widget class="QMenu" name="menuHelp"> + <property name="title"> + <string>Help</string> + </property> + <addaction name="actionAbout"/> + </widget> + <addaction name="menuFile"/> + <addaction name="menuEdit"/> + <addaction name="menuHelp"/> + </widget> + <widget class="QToolBar" name="toolBar"> + <property name="windowTitle"> + <string>toolBar</string> + </property> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> + </widget> + <action name="actionPreferences"> + <property name="text"> + <string>Preferences</string> + </property> + <property name="shortcut"> + <string>Alt+Return</string> + </property> + </action> + <action name="actionReload"> + <property name="text"> + <string>Reload</string> + </property> + <property name="shortcut"> + <string>F5</string> + </property> + </action> + <action name="actionExit"> + <property name="text"> + <string>Exit</string> + </property> + <property name="shortcut"> + <string>Ctrl+Q</string> + </property> + </action> + <action name="actionAdd_Group"> + <property name="text"> + <string>Add Group</string> + </property> + </action> + <action name="actionClean_Hidden"> + <property name="text"> + <string>Permanently Delete Removed Groups and Entries</string> + </property> + </action> + <action name="actionAbout"> + <property name="text"> + <string>About</string> + </property> + </action> + </widget> + <resources> + <include location="../resources.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/frontend/qtwidgets/editEntryForm.cpp b/src/frontend/qtwidgets/editEntryForm.cpp new file mode 100644 index 0000000..ca788ca --- /dev/null +++ b/src/frontend/qtwidgets/editEntryForm.cpp @@ -0,0 +1,55 @@ +#include <QMessageBox> + +#include "../../backend/db_sqlite.h" +#include "editEntryForm.h" + +EditEntryForm::EditEntryForm(const Entry &e) : + entry(e) +{ + // load uic + ui.setupUi(this); + + // set titles + this->setWindowTitle("Edit Entry"); + ui.title->setText("Edit Entry"); + + // widgets setup + ui.entry_desc->setText(this->entry.desc); + ui.entry_due->setDate(QDate::currentDate()); + if(this->entry.due.isValid()) { + ui.entry_due->setDate(entry.due.date()); + ui.entry_due_checkbox->setChecked(true); + } + else + ui.entry_due_checkbox->setChecked(false); + ui.entry_due_alt->setText(this->entry.due_alt); + ui.entry_link->setText(this->entry.link.toString()); + ui.entry_color->setText(this->entry.color); + ui.entry_highlight->setText(this->entry.highlight); +} + +void EditEntryForm::accept() { + this->entry.desc = ui.entry_desc->text(); + if(ui.entry_due_checkbox->isChecked()) + this->entry.due = ui.entry_due->dateTime(); + else + this->entry.due = QDateTime(); + this->entry.due_alt = ui.entry_due_alt->text(); + this->entry.link = ui.entry_link->text(); + this->entry.color = ui.entry_color->text(); + this->entry.highlight = ui.entry_highlight->text(); + QMessageBox error_message; + BackendDB database; + + if(this->entry.desc.isEmpty()) { + error_message.setIcon(QMessageBox::Warning); + error_message.setWindowTitle("Error Message"); + error_message.setText("Name cannot be blank"); + error_message.exec(); + return; + } + + database.updateEntry(this->entry); + + QDialog::accept(); +} diff --git a/src/frontend/qtwidgets/editEntryForm.h b/src/frontend/qtwidgets/editEntryForm.h new file mode 100644 index 0000000..6231478 --- /dev/null +++ b/src/frontend/qtwidgets/editEntryForm.h @@ -0,0 +1,25 @@ +#ifndef EDITENTRYFORM_H +#define EDITENTRYFORM_H + +#include <QDialog> + +#include "entry.h" +#include "ui_entryForm.h" + +// form to edit/update an entry +class EditEntryForm : public QDialog { + Q_OBJECT + + public: + EditEntryForm(const Entry &e); + + private: + Ui::entryDialog ui; + + Entry entry; + + private slots: + void accept(); +}; + +#endif diff --git a/src/frontend/qtwidgets/editGroupForm.cpp b/src/frontend/qtwidgets/editGroupForm.cpp new file mode 100644 index 0000000..fa17467 --- /dev/null +++ b/src/frontend/qtwidgets/editGroupForm.cpp @@ -0,0 +1,40 @@ +#include <QMessageBox> + +#include "../../backend/db_sqlite.h" +#include "editGroupForm.h" + +EditGroupForm::EditGroupForm(const Group &g) : + group(g) +{ + // load uic + ui.setupUi(this); + + // set titles + this->setWindowTitle("Edit Group"); + ui.title->setText("Edit Group"); + + // widgets setup + ui.group_name->setText(this->group.name); + ui.group_column->setCurrentIndex(this->group.column); + ui.group_link->setText(this->group.link); +} + +void EditGroupForm::accept() { + this->group.name = ui.group_name->text(); + this->group.column = Group::Column(ui.group_column->currentIndex()); + this->group.link = ui.group_link->text(); + QMessageBox error_message; + BackendDB database; + + if(this->group.name.isEmpty()) { + error_message.setIcon(QMessageBox::Warning); + error_message.setWindowTitle("Error Message"); + error_message.setText("Name cannot be blank"); + error_message.exec(); + return; + } + + database.updateGroup(this->group); + + QDialog::accept(); +} diff --git a/src/frontend/qtwidgets/editGroupForm.h b/src/frontend/qtwidgets/editGroupForm.h new file mode 100644 index 0000000..e04b46e --- /dev/null +++ b/src/frontend/qtwidgets/editGroupForm.h @@ -0,0 +1,25 @@ +#ifndef EDITGROUPFORM_H +#define EDITGROUPFORM_H + +#include <QDialog> + +#include "group.h" +#include "ui_groupForm.h" + +// form to edit/update a group +class EditGroupForm : public QDialog { + Q_OBJECT + + public: + EditGroupForm(const Group &g); + + private: + Ui::groupDialog ui; + + Group group; + + private slots: + void accept(); +}; + +#endif diff --git a/src/frontend/qtwidgets/entry.cpp b/src/frontend/qtwidgets/entry.cpp new file mode 100644 index 0000000..c95bbb0 --- /dev/null +++ b/src/frontend/qtwidgets/entry.cpp @@ -0,0 +1,36 @@ +#include <QList> + +#include "entry.h" + +Entry::Entry(int id, int parent_id, QString desc, QDateTime due, QString due_alt, QUrl link, QString color, QString highlight, bool done, bool hidden) : + id(id), + parent_id(parent_id), + desc(desc), + due(due), + due_alt(due_alt), + link(link), + color(color), + highlight(highlight), + done(done), + hidden(hidden) +{ +} + +QList<Entry> Entry::entries; + +bool Entry::compare(Entry a, Entry b) { + // 1st level: sort not done before done + if(a.done != b.done) + return a.done < b.done; + // next level: sort on due date + if(a.due != b.due) + return a.due < b.due; + // next level: sort on alt due date + if(a.due_alt != b.due_alt) + return a.due_alt < b.due_alt; + // next level: sort on description + if(a.desc != b.desc) + return a.desc < b.desc; + // otherwise, sort on id + return a.id < b.id; +} diff --git a/src/frontend/qtwidgets/entry.h b/src/frontend/qtwidgets/entry.h new file mode 100644 index 0000000..294df7b --- /dev/null +++ b/src/frontend/qtwidgets/entry.h @@ -0,0 +1,39 @@ +#ifndef ENTRY_H +#define ENTRY_H + +#include <QDateTime> +#include <QString> +#include <QUrl> + +struct Entry { + int id; + int parent_id; + QString desc; + QDateTime due; + QString due_alt; + QUrl link; + QString color; // consider making this a QColor instead + QString highlight; // see color comment + bool done; + bool hidden; + + Entry( + int id, + int parent_id, + QString desc, + QDateTime due = QDateTime(), + QString due_alt = "", + QUrl link = QUrl(), + QString color = "", + QString highlight = "", + bool done = false, + bool hidden = false + ); + + static QList<Entry> entries; + + // function for sorting algorithm + static bool compare(Entry a, Entry b); +}; + +#endif diff --git a/src/frontend/qtwidgets/entryForm.ui b/src/frontend/qtwidgets/entryForm.ui new file mode 100644 index 0000000..bce8ce3 --- /dev/null +++ b/src/frontend/qtwidgets/entryForm.ui @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>entryDialog</class> + <widget class="QDialog" name="entryDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>266</height> + </rect> + </property> + <property name="windowTitle"> + <string>Add Entry</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="1" column="0"> + <widget class="QLabel" name="descriptionLabel"> + <property name="text"> + <string>Description: </string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="entry_desc"/> + </item> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="title"> + <property name="font"> + <font> + <family>Arial</family> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string>[TITLE]</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="dueDateLabel"> + <property name="text"> + <string>Due Date: </string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QWidget" name="due_hbox" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QDateTimeEdit" name="entry_due"> + <property name="displayFormat"> + <string>MM/dd/yyyy</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="entry_due_checkbox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="dueDateAltLabel"> + <property name="text"> + <string>Due Date (Alt):</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="entry_due_alt"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="linkLabel"> + <property name="text"> + <string>Link:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="entry_link"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="colorLabel"> + <property name="text"> + <string>Color:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="entry_color"/> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="highlightLabel"> + <property name="text"> + <string>Highlight:</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QLineEdit" name="entry_highlight"/> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + <property name="centerButtons"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>entryDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>entryDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/frontend/qtwidgets/entryLayout.cpp b/src/frontend/qtwidgets/entryLayout.cpp new file mode 100644 index 0000000..578ec23 --- /dev/null +++ b/src/frontend/qtwidgets/entryLayout.cpp @@ -0,0 +1,177 @@ +#include <QLabel> +#include <QMenu> + +#include <QDebug> + +#include "../../backend/db_sqlite.h" +#include "editEntryForm.h" +#include "entryLayout.h" +#include "lib.h" +#include "rulesDialog.h" + +EntryLayout::EntryLayout(const Entry &e) : + entry(e) +{ + QLabel *bullet = new QLabel(); + QLabel *body = new QLabel(); + int i; + + // set styling + this->setContentsMargins(2, 2, 2, 2); + + bullet->setFont(QFont("Arial", 11)); + bullet->setMaximumWidth(15); + + body->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); + body->setFont(QFont("Arial", 11)); + body->setWordWrap(true); + body->setToolTip("Right-Click for actions"); + body->setContextMenuPolicy(Qt::CustomContextMenu); + + // set context menu + body->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(body, + SIGNAL(customContextMenuRequested(const QPoint &)), + SLOT(showContextMenu())); + + // Check rules + QList<Rule> rules = this->loadRules(); + for(i = 0; i < rules.size(); ++i) { + if( + (rules[i].when == Rule::before && + rules[i].date > QDateTime::currentDateTime()) || + (rules[i].when == Rule::after && + rules[i].date <= QDateTime::currentDateTime()) + ) { + if(!rules[i].color.isEmpty()) + this->entry.color = rules[i].color; + if(!rules[i].highlight.isEmpty()) + this->entry.highlight = rules[i].highlight; + } + } + + // set conditional styling + if(this->entry.done) { + bullet->setText("\u2713"); + bullet->setStyleSheet( + "QLabel {" + " color: green;" + "}" + ); + } + else + bullet->setText("- "); + this->addWidget(bullet); + + if(!this->entry.due.isNull()) + body->setText(this->entry.due.toString("dddd, MM/dd/yyyy: ")); + else if(!this->entry.due_alt.isEmpty()) + body->setText(this->entry.due_alt + ": "); + + if(!this->entry.link.isEmpty()) { + body->setOpenExternalLinks(true); + body->setText(body->text() + "<a href=\"" + this->entry.link.toString() + "\" " "style=\"color: "); + if(this->entry.done) + body->setText(body->text() + "green"); + else if(this->entry.color.isEmpty()) + body->setText(body->text() + "default"); + else + body->setText(body->text() + this->entry.color); + body->setText(body->text() + ";\">"); + } + body->setText(body->text() + this->entry.desc); + if(!this->entry.link.isEmpty()) { + body->setText(body->text() + "</a>"); + body->setToolTip(this->entry.link.toString()); + } + + if(this->entry.done) { + QFont body_font = body->font(); + body_font.setStrikeOut(true); + body->setFont(body_font); + body->setStyleSheet( + "QLabel {" + " color: green" + "}" + ); + } + else { + body->setStyleSheet( + "QLabel {" + " color: " + (this->entry.color.isEmpty() ? "default" : this->entry.color) + ";" + " background-color: " + (this->entry.highlight.isEmpty() ? "none" : this->entry.highlight) + ";" + " font-weight: " + (this->entry.due.isValid() && this->entry.due <= QDateTime::currentDateTime() ? "bold" : "normal") + ";" + "}" + ); + } + + this->addWidget(body); +} + +QList<Rule> EntryLayout::loadRules() { + BackendDB database; + + return database.loadRules(this->entry.id); +} + +void EntryLayout::showContextMenu() { + QMenu menu; + + QAction *edit_entry_act = new QAction("Edit Entry"); + QObject::connect(edit_entry_act, &QAction::triggered, this, &EntryLayout::editEntry); + menu.addAction(edit_entry_act); + + QAction *set_rules_act = new QAction("Rules"); + QObject::connect(set_rules_act, &QAction::triggered, this, &EntryLayout::setRules); + menu.addAction(set_rules_act); + + QAction *toggle_done_act = new QAction("Done"); + toggle_done_act->setCheckable(true); + if(this->entry.done) toggle_done_act->setChecked(true); + QObject::connect(toggle_done_act, &QAction::triggered, this, &EntryLayout::toggleDone); + menu.addAction(toggle_done_act); + + QAction *clone_entry_act = new QAction("Clone Entry"); + QObject::connect(clone_entry_act, &QAction::triggered, this, &EntryLayout::cloneEntry); + menu.addAction(clone_entry_act); + + QAction *del_entry_act = new QAction("Remove Entry"); + QObject::connect(del_entry_act, &QAction::triggered, this, &EntryLayout::removeEntry); + menu.addAction(del_entry_act); + + menu.exec(QCursor::pos()); +} + +void EntryLayout::editEntry() { + EditEntryForm edit_entry_dialog(this->entry); + if(edit_entry_dialog.exec() == QDialog::Accepted) + getMainWindow()->displayWidgets(); +} + +void EntryLayout::setRules() { + RulesDialog rules_dialog(this->entry); + if(rules_dialog.exec() == QDialog::Accepted) + getMainWindow()->displayWidgets(); +} + +void EntryLayout::toggleDone() { + BackendDB database; + + this->entry.done = !this->entry.done; + database.updateEntry(this->entry); + getMainWindow()->displayWidgets(); +} + +void EntryLayout::cloneEntry() { + BackendDB database; + + database.insertEntry(this->entry); + getMainWindow()->displayWidgets(); +} + +void EntryLayout::removeEntry() { + BackendDB database; + + if(database.removeEntry(this->entry) > 0) + getMainWindow()->displayWidgets(); +} diff --git a/src/frontend/qtwidgets/entryLayout.h b/src/frontend/qtwidgets/entryLayout.h new file mode 100644 index 0000000..54e523f --- /dev/null +++ b/src/frontend/qtwidgets/entryLayout.h @@ -0,0 +1,32 @@ +#ifndef ENTRYLAYOUT_H +#define ENTRYLAYOUT_H + +#include <QDateTime> +#include <QHBoxLayout> +#include <QString> +#include <QUrl> + +#include "entry.h" +#include "rule.h" + +class EntryLayout : public QHBoxLayout { + Q_OBJECT + + public: + Entry entry; + + EntryLayout(const Entry &e); + + private: + QList<Rule> loadRules(); + + private slots: + void showContextMenu(); + void editEntry(); + void setRules(); + void toggleDone(); + void cloneEntry(); + void removeEntry(); +}; + +#endif diff --git a/src/frontend/qtwidgets/group.cpp b/src/frontend/qtwidgets/group.cpp new file mode 100644 index 0000000..ddb3b13 --- /dev/null +++ b/src/frontend/qtwidgets/group.cpp @@ -0,0 +1,14 @@ +#include <QList> + +#include "group.h" + +Group::Group(int id, QString name, Group::Column column, QString link, bool hidden) : + id(id), + name(name), + column(column), + link(link), + hidden(hidden) +{ +} + +QList<Group> Group::groups; diff --git a/src/frontend/qtwidgets/group.h b/src/frontend/qtwidgets/group.h new file mode 100644 index 0000000..9c7bfa4 --- /dev/null +++ b/src/frontend/qtwidgets/group.h @@ -0,0 +1,25 @@ +#ifndef GROUP_H +#define GROUP_H + +#include <QString> + +struct Group { + enum Column { left, right }; + int id; + QString name; + Column column; + QString link; + bool hidden; + + Group( + int id, + QString name, + Column column = left, + QString link = "", + bool hidden = false + ); + + static QList<Group> groups; +}; + +#endif diff --git a/src/frontend/qtwidgets/groupForm.ui b/src/frontend/qtwidgets/groupForm.ui new file mode 100644 index 0000000..4f58e1d --- /dev/null +++ b/src/frontend/qtwidgets/groupForm.ui @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>groupDialog</class> + <widget class="QDialog" name="groupDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>172</height> + </rect> + </property> + <property name="windowTitle"> + <string>Group Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="title"> + <property name="font"> + <font> + <family>Arial</family> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string>[TITLE]</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="descriptionLabel"> + <property name="text"> + <string>Name:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="group_name"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="dueDateLabel"> + <property name="text"> + <string>Column:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="group_column"> + <item> + <property name="text"> + <string>Left</string> + </property> + </item> + <item> + <property name="text"> + <string>Right</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="linkLabel"> + <property name="text"> + <string>Link:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="group_link"/> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + <property name="centerButtons"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>groupDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>groupDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/frontend/qtwidgets/groupLayout.cpp b/src/frontend/qtwidgets/groupLayout.cpp new file mode 100644 index 0000000..e67d3a5 --- /dev/null +++ b/src/frontend/qtwidgets/groupLayout.cpp @@ -0,0 +1,68 @@ +#include <QLabel> +#include <QMenu> +#include <QString> + +#include <QDebug> + +#include "addEntryForm.h" +#include "../../backend/db_sqlite.h" +#include "editGroupForm.h" +#include "groupLayout.h" +#include "lib.h" + +GroupLayout::GroupLayout(const Group &g) : + group(g) +{ + this->setContentsMargins(0, 10, 0, 10); + + QLabel *name_label = new QLabel(this->group.name); + name_label->setTextInteractionFlags(Qt::TextSelectableByMouse); + name_label->setToolTip("Right-Click for actions"); + name_label->setContextMenuPolicy(Qt::CustomContextMenu); + //name_label->customContextMenuRequested(const QPoint &pos) + QObject::connect(name_label, + SIGNAL(customContextMenuRequested(const QPoint &)), + SLOT(showContextMenu())); + + QFont name_font = QFont("Arial", 13); + name_font.setUnderline(true); + name_label->setFont(name_font); + + this->addWidget(name_label); +} + +void GroupLayout::showContextMenu() { + QMenu menu; + + QAction *add_entry_act = new QAction("Add Entry"); + QObject::connect(add_entry_act, &QAction::triggered, this, &GroupLayout::addEntry); + menu.addAction(add_entry_act); + + QAction *edit_group_act = new QAction("Edit Group"); + QObject::connect(edit_group_act, &QAction::triggered, this, &GroupLayout::editGroup); + menu.addAction(edit_group_act); + + QAction *del_group_act = new QAction("Remove Group"); + QObject::connect(del_group_act, &QAction::triggered, this, &GroupLayout::removeGroup); + menu.addAction(del_group_act); + + menu.exec(QCursor::pos()); +} + +void GroupLayout::addEntry() { + AddEntryForm create_new_entry_dialog(this->group); + if(create_new_entry_dialog.exec() == QDialog::Accepted) + getMainWindow()->displayWidgets(); +} + +void GroupLayout::editGroup() { + EditGroupForm edit_group_dialog(this->group); + if(edit_group_dialog.exec() == QDialog::Accepted) + getMainWindow()->displayWidgets(); +} + +void GroupLayout::removeGroup() { + BackendDB database; + if(database.removeGroup(this->group) > 0) + getMainWindow()->displayWidgets(); +} diff --git a/src/frontend/qtwidgets/groupLayout.h b/src/frontend/qtwidgets/groupLayout.h new file mode 100644 index 0000000..cfd33cf --- /dev/null +++ b/src/frontend/qtwidgets/groupLayout.h @@ -0,0 +1,23 @@ +#ifndef GROUPLAYOUT_H +#define GROUPLAYOUT_H + +#include <QVBoxLayout> + +#include "group.h" + +class GroupLayout : public QVBoxLayout { + Q_OBJECT + + public: + Group group; + + GroupLayout(const Group &g); + + private slots: + void showContextMenu(); + void addEntry(); + void editGroup(); + void removeGroup(); +}; + +#endif diff --git a/src/frontend/qtwidgets/lib.cpp b/src/frontend/qtwidgets/lib.cpp new file mode 100644 index 0000000..2290472 --- /dev/null +++ b/src/frontend/qtwidgets/lib.cpp @@ -0,0 +1,22 @@ +#include <QApplication> +#include <QMainWindow> +#include <QWidget> + +#include "lib.h" + +// shamelessly ripped from https://stackoverflow.com/a/46456214 +AssignmentList *getMainWindow() { + foreach(QWidget *w, QApplication::topLevelWidgets()) + if(AssignmentList *mainWin = qobject_cast<AssignmentList*>(w)) + return mainWin; + return nullptr; +} + +void recursiveClear(QLayout *layout) { + QLayoutItem *child; + while((child = layout->takeAt(0)) != nullptr) { + if(child->layout()) recursiveClear(child->layout()); + delete child->widget(); + delete child; + } +} diff --git a/src/frontend/qtwidgets/lib.h b/src/frontend/qtwidgets/lib.h new file mode 100644 index 0000000..14f63c1 --- /dev/null +++ b/src/frontend/qtwidgets/lib.h @@ -0,0 +1,12 @@ +/* Helper functions that various classes utilize */ + +#include <QMainWindow> + +#include "assignmentList.h" + +AssignmentList *getMainWindow(); + +QDialog *getParentDialog(); + +// used to clear out a layout when a refresh occurs +void recursiveClear(QLayout *layout); diff --git a/src/frontend/qtwidgets/main.cpp b/src/frontend/qtwidgets/main.cpp new file mode 100644 index 0000000..f7d69ee --- /dev/null +++ b/src/frontend/qtwidgets/main.cpp @@ -0,0 +1,12 @@ +#include <QApplication> +#include <QPushButton> + +#include "assignmentList.h" + +int main(int argc, char **argv) { + QApplication app (argc, argv); + + AssignmentList window; + + return app.exec(); +} diff --git a/src/frontend/qtwidgets/preferencesDialog.cpp b/src/frontend/qtwidgets/preferencesDialog.cpp new file mode 100644 index 0000000..d3b3dff --- /dev/null +++ b/src/frontend/qtwidgets/preferencesDialog.cpp @@ -0,0 +1,59 @@ +#include <QFileDialog> +#include <QSettings> + +#include "preferencesDialog.h" + +PreferencesDialog::PreferencesDialog() { + // load uic + ui.setupUi(this); + + // display widgets + // TODO make this a scollable window + // FIXME could use some work on sizing + this->setupPathsTab(); + + // create button connections + QObject::connect(ui.close_button, &QPushButton::released, this, &PreferencesDialog::close); + QObject::connect(ui.apply_button, &QPushButton::released, this, &PreferencesDialog::apply); + QObject::connect(ui.reload_button, &QPushButton::released, this, &PreferencesDialog::reload); +} + +void PreferencesDialog::setupPathsTab() { + QSettings settings; + settings.beginGroup("paths"); + + ui.db_path_edit->setText(settings.value("db_path").toString()); + QObject::connect(ui.db_path_button, &QPushButton::released, this, &PreferencesDialog::dbPathDialog); +} + +void PreferencesDialog::dbPathDialog() { + QFileDialog file_dialog; + // TODO create filter to only allow selecting .db files + QString new_path = file_dialog.getOpenFileName(this, "Open File"); + + if(!new_path.isEmpty()) + ui.db_path_edit->setText(new_path); +} + +// close the dialog, returning our ret_value +void PreferencesDialog::close() { + this->done(ret_value); +} + +// update the configuration in the config file +void PreferencesDialog::apply() { + QSettings settings; + + // save paths + settings.beginGroup("paths"); + settings.setValue("db_path", ui.db_path_edit->text()); + + // set the return value to accepted + this->ret_value = QDialog::Accepted; +} + +// update, reload, and close the window +void PreferencesDialog::reload() { + this->apply(); + this->accept(); +} diff --git a/src/frontend/qtwidgets/preferencesDialog.h b/src/frontend/qtwidgets/preferencesDialog.h new file mode 100644 index 0000000..ac9b88e --- /dev/null +++ b/src/frontend/qtwidgets/preferencesDialog.h @@ -0,0 +1,28 @@ +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include <QDialog> + +#include "ui_preferencesDialog.h" + +// set configuration options in the program +class PreferencesDialog : public QDialog { + Q_OBJECT + + public: + PreferencesDialog(); + + private: + Ui::preferencesDialog ui; + QDialog::DialogCode ret_value = QDialog::Rejected; + + void setupPathsTab(); + + private slots: + void dbPathDialog(); + void close(); + void apply(); + void reload(); +}; + +#endif diff --git a/src/frontend/qtwidgets/preferencesDialog.ui b/src/frontend/qtwidgets/preferencesDialog.ui new file mode 100644 index 0000000..343ba33 --- /dev/null +++ b/src/frontend/qtwidgets/preferencesDialog.ui @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>preferencesDialog</class> + <widget class="QDialog" name="preferencesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>320</height> + </rect> + </property> + <property name="windowTitle"> + <string>Preferences</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QVBoxLayout" name="main_layout"> + <item> + <widget class="QTabWidget" name="tab_bar"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Paths</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QFormLayout" name="paths_tab_layout"> + <item row="0" column="0"> + <widget class="QLabel" name="databaseFileLabel"> + <property name="text"> + <string>Database File:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QWidget" name="db_path_hbox" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLineEdit" name="db_path_edit"/> + </item> + <item> + <widget class="QPushButton" name="db_path_button"> + <property name="maximumSize"> + <size> + <width>25</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttons_hbox"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="close_button"> + <property name="text"> + <string>Close</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="apply_button"> + <property name="text"> + <string>Apply</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="reload_button"> + <property name="text"> + <string>Reload</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/frontend/qtwidgets/rule.cpp b/src/frontend/qtwidgets/rule.cpp new file mode 100644 index 0000000..85da13a --- /dev/null +++ b/src/frontend/qtwidgets/rule.cpp @@ -0,0 +1,15 @@ +#include <QList> + +#include "rule.h" + +Rule::Rule(int id, int entry_id, When when, QDateTime date, QString color, QString highlight) : + id(id), + entry_id(entry_id), + when(when), + date(date), + color(color), + highlight(highlight) +{ +} + +QList<Rule> Rule::rules; diff --git a/src/frontend/qtwidgets/rule.h b/src/frontend/qtwidgets/rule.h new file mode 100644 index 0000000..cf9abfe --- /dev/null +++ b/src/frontend/qtwidgets/rule.h @@ -0,0 +1,28 @@ +#ifndef RULE_H +#define RULE_H + +#include <QDateTime> +#include <QString> + +struct Rule { + enum When { before, after }; + int id; + int entry_id; + When when; + QDateTime date; + QString color; // consider making this a QColor instead + QString highlight; // see color comment + + Rule( + int id, + int entry_id, + When when, + QDateTime date = QDateTime::currentDateTime(), + QString color = "", + QString highlight = "" + ); + + static QList<Rule> rules; +}; + +#endif diff --git a/src/frontend/qtwidgets/ruleLayout.cpp b/src/frontend/qtwidgets/ruleLayout.cpp new file mode 100644 index 0000000..11abf80 --- /dev/null +++ b/src/frontend/qtwidgets/ruleLayout.cpp @@ -0,0 +1,40 @@ + +#include "lib.h" +#include "ruleLayout.h" + +RuleLayout::RuleLayout(const Rule &r) : + rule(r) +{ + this->when_widget = new QComboBox; + this->date_widget = new QDateTimeEdit(QDate::currentDate()); + this->color_widget = new QLineEdit; + this->highlight_widget = new QLineEdit; + this->del_button = new QPushButton; + + QStringList when_options; + when_options.append("Before"); + when_options.append("After"); + this->when_widget->addItems(when_options); + this->when_widget->setCurrentIndex(this->rule.when); + this->addWidget(this->when_widget); + + this->date_widget->setDisplayFormat("MM/dd/yyyy"); + this->date_widget->setDateTime(this->rule.date); + this->addWidget(this->date_widget); + + this->addStretch(); + + this->color_widget->setPlaceholderText("Color"); + if(!this->rule.color.isEmpty()) + this->color_widget->setText(this->rule.color); + this->addWidget(this->color_widget); + + this->highlight_widget->setPlaceholderText("Highlight"); + if(!this->rule.highlight.isEmpty()) + this->highlight_widget->setText(this->rule.highlight); + this->addWidget(this->highlight_widget); + + this->del_button->setText("Delete"); + // connection needs to be made in rulesDialog.cpp + this->addWidget(this->del_button); +} diff --git a/src/frontend/qtwidgets/ruleLayout.h b/src/frontend/qtwidgets/ruleLayout.h new file mode 100644 index 0000000..29a4deb --- /dev/null +++ b/src/frontend/qtwidgets/ruleLayout.h @@ -0,0 +1,27 @@ +#ifndef RULELAYOUT_H +#define RULELAYOUT_H + +#include <QComboBox> +#include <QDate> +#include <QDateTimeEdit> +#include <QHBoxLayout> +#include <QLineEdit> +#include <QPushButton> + +#include "rule.h" + +class RuleLayout : public QHBoxLayout { + Q_OBJECT + + public: + Rule rule; + QComboBox *when_widget; + QDateTimeEdit *date_widget; + QLineEdit *color_widget; // TODO consider making this a color selector widget + QLineEdit *highlight_widget; // TODO consider making this a color selector widget + QPushButton *del_button; + + RuleLayout(const Rule &r); +}; + +#endif diff --git a/src/frontend/qtwidgets/rulesDialog.cpp b/src/frontend/qtwidgets/rulesDialog.cpp new file mode 100644 index 0000000..cd62342 --- /dev/null +++ b/src/frontend/qtwidgets/rulesDialog.cpp @@ -0,0 +1,107 @@ +#include <QPushButton> +#include <QScrollArea> +#include <QVBoxLayout> + +#include <QDebug> + +#include "../../backend/db_sqlite.h" +#include "lib.h" +#include "ruleLayout.h" +#include "rulesDialog.h" + +RulesDialog::RulesDialog(const Entry &entry) { + BackendDB database; + + this->entry_id = entry.id; + + // load uic + ui.setupUi(this); + + // load rules into object member + this->rules = database.loadRules(entry_id); + + // display widgets + this->drawRules(); + + // set connections + QObject::connect(ui.add_rule_button, &QPushButton::released, this, &RulesDialog::addRule); +} + +// for maintaining values when adding/removing rules +void RulesDialog::updateRulesList() { + int i; + + for(i = 0; i < ui.rules_layout->children().size(); ++i) { + this->rules[i].when = Rule::When(qobject_cast<RuleLayout *>(ui.rules_layout->children()[i])->when_widget->currentIndex()); + this->rules[i].date = qobject_cast<RuleLayout *>(ui.rules_layout->children()[i])->date_widget->dateTime(); + this->rules[i].color = qobject_cast<RuleLayout *>(ui.rules_layout->children()[i])->color_widget->text(); + this->rules[i].highlight = qobject_cast<RuleLayout *>(ui.rules_layout->children()[i])->highlight_widget->text(); + } +} + +void RulesDialog::drawRules() { + RuleLayout *new_layout; + int i; + + + // remove all children from layout + recursiveClear(ui.rules_layout); + + // Draw each rule + for(i = 0; i < this->rules.size(); ++i) { + new_layout = new RuleLayout(rules[i]); + + // connect delete button + QObject::connect(new_layout->del_button, &QPushButton::released, this, [=](){ this->deleteRule(i); }); + + ui.rules_layout->addLayout(new_layout); + } +} + +void RulesDialog::addRule() { + Rule new_rule = Rule(0, this->entry_id, Rule::before); + RuleLayout *new_layout = new RuleLayout(new_rule); + + // add new rule to the member variable + this->rules.append(new_rule); + + // redraw rules + this->updateRulesList(); + this->drawRules(); +} + +void RulesDialog::deleteRule(int i) { + if(i < 0) return; + if(i >= this->rules.size()) return; + + this->updateRulesList(); + // mark as needing to be deleted from database if id > 0 + if(this->rules[i].id > 0) + this->deleted_rules.append(this->rules[i]); + this->rules.removeAt(i); + this->drawRules(); +} + +void RulesDialog::accept() { + int i; + BackendDB database; + + this->updateRulesList(); + + // update rules in database + for(i = 0; i < this->rules.size(); ++i) { + // insert - this is a new rule + if(this->rules[i].id == 0) + database.insertRule(this->rules[i]); + // update - this is an existing rule + else + database.updateRule(this->rules[i]); + } + + // delete deleted rules in database + for(i = 0; i < this->deleted_rules.size(); ++i) { + database.removeRule(this->deleted_rules[i]); + } + + QDialog::accept(); +} diff --git a/src/frontend/qtwidgets/rulesDialog.h b/src/frontend/qtwidgets/rulesDialog.h new file mode 100644 index 0000000..7e74dc2 --- /dev/null +++ b/src/frontend/qtwidgets/rulesDialog.h @@ -0,0 +1,32 @@ +#ifndef RULESDIALOG_H +#define RULESDIALOG_H + +#include <QDialog> + +#include "entry.h" +#include "rule.h" +#include "ui_rulesDialog.h" + +// show the list of rules associated with an entry +class RulesDialog : public QDialog { + Q_OBJECT + + public: + RulesDialog(const Entry &entry); + void drawRules(); + + private: + Ui::rulesDialog ui; + int entry_id; + QList<Rule> rules; + QList<Rule> deleted_rules; + + void updateRulesList(); + + private slots: + void addRule(); + void deleteRule(int i); + void accept(); +}; + +#endif diff --git a/src/frontend/qtwidgets/rulesDialog.ui b/src/frontend/qtwidgets/rulesDialog.ui new file mode 100644 index 0000000..c362887 --- /dev/null +++ b/src/frontend/qtwidgets/rulesDialog.ui @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>rulesDialog</class> + <widget class="QDialog" name="rulesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>320</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>269</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QVBoxLayout" name="rules_layout"/> + </item> + <item> + <layout class="QHBoxLayout" name="rules_button_box"> + <item> + <widget class="QPushButton" name="add_rule_button"> + <property name="text"> + <string>Add Rule</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>rulesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>rulesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> |