Files
TaskBoard/src/frames/mainwindow.cpp

757 lines
23 KiB
C++

#include "mainwindow.h"
#include "ui_mainwindow.h"
#define PRIORITIES_KEY "priorities"
#define STATUS_KEY "status"
#define BOARDS_KEY "boards"
#define FILTERS_KEY "filters"
#include <QUuid>
#include <QColor>
#include <QJsonArray>
#include <QJsonObject>
#include <QMessageBox>
#include "prefdialog.h"
#include "aboutdialog.h"
#include "namedialog.h"
#include "taskdialog.h"
#include "filterdialog.h"
#include "boardconfigdialog.h"
#include "../tools.h"
#include "../services/taskstateservice.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->boardStatus->setVisible(false);
this->menuSelectedBoardItem = nullptr;
this->menuSelectedFilterItem = nullptr;
this->menuSelectedTaskItem = nullptr;
init();
this->selectedBoardIndex = -1;
// Change "name" column size
ui->taskList->header()->resizeSection(0, 520);
// Change "status" column size
ui->taskList->header()->resizeSection(1, 150);
connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::openPreferences);
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::openAbout);
connect(ui->actionNew, &QAction::triggered, this, &MainWindow::onNewBoardClick);
connect(ui->actionNew_task, &QAction::triggered, this, &MainWindow::onNewTaskClick);
connect(ui->actionNew_filter, &QAction::triggered, this, &MainWindow::onNewFilterClick);
connect(ui->boardList, &QListWidget::currentRowChanged, this, &MainWindow::onBoardSelected);
connect(ui->boardList, &QListWidget::customContextMenuRequested, this, &MainWindow::prepareBoardMenu);
connect(ui->filterListWidget, &QListWidget::currentRowChanged, this, &MainWindow::onFilterSelected);
connect(ui->filterListWidget, &QListWidget::customContextMenuRequested, this, &MainWindow::prepareFilterMenu);
connect(ui->taskList, &QTreeWidget::itemDoubleClicked, this, &MainWindow::onEditTask);
connect(ui->taskList, &QTreeWidget::customContextMenuRequested, this, &MainWindow::prepareTaskMenu);
ui->boardList->setContextMenuPolicy(Qt::CustomContextMenu);
ui->taskList->setContextMenuPolicy(Qt::CustomContextMenu);
ui->filterListWidget->setContextMenuPolicy(Qt::CustomContextMenu);
}
MainWindow::~MainWindow()
{
for (uint16_t i = 0; i < boards.count(); i++)
{
Board *b = boards.takeAt(i);
delete b;
}
delete ui;
}
void MainWindow::openPreferences()
{
PrefDialog dialog(this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
TaskStateService::getInstance()->updatePriorities(dialog.getPriorities());
TaskStateService::getInstance()->updateStatuses(dialog.getStatus());
save();
redrawTaskTree();
}
}
void MainWindow::openAbout()
{
AboutDialog dialog(this);
dialog.exec();
}
void MainWindow::prepareBoardMenu(const QPoint &pos)
{
QMenu menu(this);
this->menuSelectedBoardItem = ui->boardList->itemAt(pos);
if (this->menuSelectedBoardItem != nullptr) {
QAction *renameAction = new QAction(tr("Edit board"), this);
connect(renameAction, &QAction::triggered, this, &MainWindow::onEditNameBoardMenu);
menu.addAction(renameAction);
QAction *deleteAction = new QAction(tr("Delete the board and all the tasks"), this);
connect(deleteAction, &QAction::triggered, this, &MainWindow::onRemoveBoardMenu);
menu.addAction(deleteAction);
menu.addSeparator();
}
QAction *addAction = new QAction(tr("New board"), this);
connect(addAction, &QAction::triggered, this, &MainWindow::onNewBoardClick);
menu.addAction(addAction);
menu.exec(ui->boardList->mapToGlobal(pos));
}
void MainWindow::prepareTaskMenu(const QPoint &pos)
{
bool show = false;
QMenu menu(this);
this->menuSelectedTaskItem = ui->taskList->itemAt(pos);
if (menuSelectedTaskItem != nullptr) {
show = true;
QAction *renameAction = new QAction(tr("Edit the task"), this);
connect(renameAction, &QAction::triggered, this, &MainWindow::onEditNameTaskMenu);
menu.addAction(renameAction);
if (selectedBoardIndex > -1)
{
QAction *deleteAction = new QAction(tr("Delete from the board"), this);
connect(deleteAction, &QAction::triggered, this, &MainWindow::onRemoveTaskMenu);
menu.addAction(deleteAction);
}
menu.addSeparator();
}
if (selectedBoardIndex > -1)
{
show = true;
QAction *addAction = new QAction(tr("New task"), this);
connect(addAction, &QAction::triggered, this, &MainWindow::onNewTaskClick);
menu.addAction(addAction);
}
if (show)
{
menu.exec(ui->taskList->mapToGlobal(pos));
}
}
void MainWindow::onNewBoardClick()
{
NameDialog dialog("Create a board", "New empty board", "", this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
QString name = dialog.getChoosenName();
QString desc = dialog.getDescription();
Board *b = new Board(name, desc);
boards.append(b);
QListWidgetItem *item = new QListWidgetItem(name);
item->setStatusTip(desc);
ui->boardList->addItem(item);
save();
}
}
void MainWindow::onNewTaskClick()
{
if (selectedBoardIndex > -1)
{
TaskDialog dialog(this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
Task t = dialog.getTask();
Board *b = boards[selectedBoardIndex];
b->add(t);
QTreeWidgetItem *item = new QTreeWidgetItem();
updateTaskRow(item, t);
ui->taskList->addTopLevelItem(item);
save();
}
}
}
void MainWindow::onNewFilterClick()
{
FilterDialog dialog("New filter", boards, this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
Filter f = dialog.getFilter();
filters.append(f);
QListWidgetItem *item = new QListWidgetItem(f.getName());
item->setToolTip(f.getDescription());
ui->filterListWidget->addItem(item);
save();
}
}
void MainWindow::onBoardSelected(int i)
{
selectedBoardIndex = i;
selectedFilterIndex = -1;
ui->filterListWidget->blockSignals(true);
ui->filterListWidget->setCurrentIndex(ui->filterListWidget->currentIndex().sibling(-1, -1));
ui->filterListWidget->blockSignals(false);
filterResult.clear();
if (selectedBoardIndex > -1)
{
Board *b = boards[selectedBoardIndex];
ui->label->setText(b->getName());
if (b->getDescription().length() > 0)
{
ui->boardDescription->setText(b->getDescription());
ui->boardDescription->setEnabled(true);
}
else
{
ui->boardDescription->setText("No description");
ui->boardDescription->setEnabled(false);
}
ui->actionNew_task->setDisabled(false);
}
else
{
ui->label->setText("No board selected");
ui->boardDescription->clear();
ui->actionNew_task->setDisabled(true);
}
redrawTaskTree();
}
void MainWindow::onFilterSelected(int i)
{
selectedFilterIndex = i;
selectedBoardIndex = -1;
ui->boardList->blockSignals(true);
ui->boardList->setCurrentIndex(ui->boardList->currentIndex().sibling(-1, -1));
ui->boardList->blockSignals(false);
filterResult.clear();
ui->actionNew_task->setDisabled(true);
if (selectedFilterIndex > -1)
{
Filter f = filters[selectedFilterIndex];
ui->label->setText(f.getName());
if (f.getDescription().length() > 0)
{
ui->boardDescription->setText(f.getDescription());
ui->boardDescription->setEnabled(true);
}
else
{
ui->boardDescription->setText("No description");
ui->boardDescription->setEnabled(false);
}
}
else
{
ui->label->setText("No board selected");
ui->boardDescription->clear();
}
redrawTaskTree();
}
void MainWindow::onEditTask(QTreeWidgetItem *item)
{
Task *t = getSelectedTask();
if (t != nullptr)
{
TaskDialog dialog(t, this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
Task editedTask = dialog.getTask();
t->update(editedTask);
updateTaskRow(item, editedTask);
save();
redrawBoardStatus();
if (selectedFilterIndex > -1)
{
redrawTaskTree();
}
}
}
}
void MainWindow::onRemoveBoardMenu()
{
if (menuSelectedBoardItem != nullptr)
{
int i = ui->boardList->indexFromItem(menuSelectedBoardItem).row();
if (i == -1)
{
return;
}
QMessageBox::StandardButton result = QMessageBox::question(this, "Delete a board", "Do you want to delete this board?");
if (result == QMessageBox::Yes)
{
boards.removeAt(i);
delete ui->boardList->takeItem(i);
if (selectedBoardIndex == i)
{
selectedBoardIndex = -1;
}
redrawTaskTree();
save();
}
}
}
void MainWindow::onRemoveTaskMenu()
{
if (selectedBoardIndex > -1 && ui->taskList->selectedItems().length() == 1)
{
QMessageBox::StandardButton result = QMessageBox::question(this, "Delete a task", "Do you want to delete this task?");
if (result == QMessageBox::Yes)
{
int16_t i = ui->taskList->indexOfTopLevelItem(ui->taskList->currentItem());
Board *b = boards[selectedBoardIndex];
b->remove(i);
redrawTaskTree();
save();
}
}
}
void MainWindow::onRemoveFilterMenu()
{
if (menuSelectedFilterItem != nullptr)
{
int i = ui->filterListWidget->indexFromItem(menuSelectedFilterItem).row();
if (i == -1)
{
return;
}
QMessageBox::StandardButton result = QMessageBox::question(this, "Delete a filter", "Do you want to delete this filter?");
if (result == QMessageBox::Yes)
{
filters.removeAt(i);
delete ui->filterListWidget->takeItem(i);
if (selectedFilterIndex == i)
{
selectedFilterIndex = -1;
}
redrawTaskTree();
save();
}
}
}
void MainWindow::onEditNameBoardMenu()
{
if (menuSelectedBoardItem != nullptr)
{
int i = ui->boardList->indexFromItem(menuSelectedBoardItem).row();
if (i == -1)
{
return;
}
Board *b = boards.at(i);
BoardConfigDialog dialog(b, this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
QString newName = dialog.getName();
QString newDesc = dialog.getDescription();
b->setName(newName);
b->setDescription(newDesc);
b->setShowingStatus(dialog.isShowingStatus());
if (!dialog.isAutoStatus())
{
std::optional<Status> status = TaskStateService::getInstance()->getStatusByUUID(dialog.getStatus());
if (status.has_value())
{
b->setDirtyStatus(status.value());
}
else
{
b->removeDirtyStatus();
}
}
else
{
b->removeDirtyStatus();
}
QListWidgetItem *item = ui->boardList->item(i);
item->setText(newName);
item->setToolTip(newDesc);
ui->label->setText(newName);
if (newDesc.length() > 0)
{
ui->boardDescription->setText(newDesc);
ui->boardDescription->setEnabled(true);
}
else
{
ui->boardDescription->setText("No description");
ui->boardDescription->setEnabled(false);
}
redrawBoardStatus();
save();
}
}
}
void MainWindow::onEditNameTaskMenu()
{
Task *t = getSelectedTask();
if (t != nullptr)
{
onEditTask(ui->taskList->currentItem());
}
}
void MainWindow::onEditFilterMenu()
{
if (menuSelectedFilterItem != nullptr)
{
int i = ui->filterListWidget->indexFromItem(menuSelectedFilterItem).row();
Filter f = filters[i];
FilterDialog dialog("Edit the filter", f, boards, this);
if (dialog.exec() == QDialog::DialogCode::Accepted)
{
Filter f = dialog.getFilter();
filters[selectedFilterIndex] = f;
QListWidgetItem *item = ui->filterListWidget->item(selectedFilterIndex);
item->setText(f.getName());
item->setToolTip(f.getDescription());
ui->label->setText(f.getName());
if (f.getDescription().length() > 0)
{
ui->boardDescription->setText(f.getDescription());
ui->boardDescription->setEnabled(true);
}
else
{
ui->boardDescription->setText("No description");
ui->boardDescription->setEnabled(false);
}
redrawTaskTree();
save();
}
}
}
void MainWindow::prepareFilterMenu(const QPoint &pos)
{
QMenu menu(this);
this->menuSelectedFilterItem = ui->filterListWidget->itemAt(pos);
if (this->menuSelectedFilterItem != nullptr) {
QAction *renameAction = new QAction(tr("Edit the filter"), this);
connect(renameAction, &QAction::triggered, this, &MainWindow::onEditFilterMenu);
menu.addAction(renameAction);
QAction *deleteAction = new QAction(tr("Delete"), this);
connect(deleteAction, &QAction::triggered, this, &MainWindow::onRemoveFilterMenu);
menu.addAction(deleteAction);
menu.addSeparator();
}
QAction *addAction = new QAction(tr("New filter"), this);
connect(addAction, &QAction::triggered, this, &MainWindow::onNewFilterClick);
menu.addAction(addAction);
menu.exec(ui->filterListWidget->mapToGlobal(pos));
}
void MainWindow::init()
{
if (Tools::isSaveFileExist())
{
QJsonDocument doc;
if (Tools::readSaveFile(doc))
{
QJsonObject save = doc.object();
QJsonArray jsonPriorities = save[PRIORITIES_KEY].toArray();
QJsonArray jsonStatus = save[STATUS_KEY].toArray();
QJsonArray jsonBoards = save[BOARDS_KEY].toArray();
QJsonArray jsonFilters = save[FILTERS_KEY].toArray();
QVector<Priority> priorities;
QVector<Status> statuses;
for (QJsonValueRef value : jsonPriorities)
{
priorities.append(Priority(value.toObject()));
}
for (QJsonValueRef value : jsonStatus)
{
statuses.append(Status(value.toObject()));
}
for (QJsonValueRef value : jsonBoards)
{
boards.append(new Board(value.toObject()));
}
for (QJsonValueRef value : jsonFilters)
{
filters.append(Filter(value.toObject()));
}
TaskStateService::getInstance()->updatePriorities(priorities);
TaskStateService::getInstance()->updateStatuses(statuses);
redrawFilterList();
redrawBoardList();
return;
}
}
save();
}
QVector<Filter> MainWindow::defaultFilters()
{
QVector<Filter> res;
return res;
}
const QString MainWindow::getPriorityLabel(QString uuid)
{
std::optional<Priority> p = TaskStateService::getInstance()->getPriorityByUUID(uuid);
if (p.has_value())
{
return p.value().getName();
}
return "";
}
const QString MainWindow::getStatusLabel(QString uuid)
{
std::optional<Status> s = TaskStateService::getInstance()->getStatusByUUID(uuid);
if (s.has_value())
{
return s.value().getName();
}
return "";
}
const QColor MainWindow::getPriorityColor(QString uuid, QColor defaultColor)
{
std::optional<Priority> p = TaskStateService::getInstance()->getPriorityByUUID(uuid);
if (p.has_value())
{
return p.value().getColor();
}
return defaultColor;
}
const QColor MainWindow::getStatusColor(QString uuid, QColor defaultColor)
{
std::optional<Status> s = TaskStateService::getInstance()->getStatusByUUID(uuid);
if (s.has_value())
{
return s.value().getColor();
}
return defaultColor;
}
const QJsonDocument MainWindow::getJsonSave()
{
QJsonDocument doc;
QJsonObject obj;
QJsonArray jsonPriorities;
QJsonArray jsonStatus;
QJsonArray jsonBoards;
QJsonArray jsonFilters;
QVector<Priority> priorities = TaskStateService::getInstance()->getPriorities();
foreach (Priority p, priorities) {
jsonPriorities.append(p.toJson());
}
QVector<Status> statuses = TaskStateService::getInstance()->getStatuses();
foreach (Status s, statuses) {
jsonStatus.append(s.toJson());
}
foreach (Board *b, this->boards) {
jsonBoards.append(b->toJson());
}
foreach (Filter f, this->filters)
{
jsonFilters.append(f.toJson());
}
obj[PRIORITIES_KEY] = jsonPriorities;
obj[STATUS_KEY] = jsonStatus;
obj[BOARDS_KEY] = jsonBoards;
obj[FILTERS_KEY] = jsonFilters;
doc.setObject(obj);
return doc;
}
Task *MainWindow::getSelectedTask()
{
QList<QTreeWidgetItem*> items = ui->taskList->selectedItems();
if (items.count() != 1)
{
return nullptr;
}
if (selectedBoardIndex > -1)
{
Board *b = boards[selectedBoardIndex];
int16_t i = ui->taskList->indexOfTopLevelItem(items[0]);
return b->taskAt(i);
}
else if (selectedFilterIndex > -1)
{
if (!filterResult.empty())
{
int16_t i = ui->taskList->indexOfTopLevelItem(items[0]);
return filterResult[i];
}
}
return nullptr;
}
void MainWindow::updateTaskRow(QTreeWidgetItem *item, Task t)
{
item->setText(0, t.getTitle());
item->setToolTip(0, t.getDescription());
item->setText(3, t.getExpectedFor().toString());
if (t.getExpectedFor() < QDate::currentDate())
{
QBrush fgColor = item->foreground(3);
fgColor.setColor(QColor(176, 67, 48));
fgColor.setStyle(Qt::BrushStyle::SolidPattern);
item->setForeground(3, fgColor);
}
else if (t.getExpectedFor() == QDate::currentDate())
{
QBrush fgColor = item->foreground(3);
fgColor.setColor(QColor(176, 142, 48));
fgColor.setStyle(Qt::BrushStyle::SolidPattern);
item->setForeground(3, fgColor);
}
else
{
QBrush fgColor = item->foreground(3);
fgColor.setStyle(Qt::BrushStyle::NoBrush);
item->setForeground(3, fgColor);
}
if (!t.getStatusUUID().isEmpty())
{
item->setText(1, getStatusLabel(t.getStatusUUID()));
QBrush bgColor = item->background(1);
QBrush fgColor = item->foreground(1);
bgColor.setColor(getStatusColor(t.getStatusUUID(), bgColor.color()));
bgColor.setStyle(Qt::BrushStyle::SolidPattern);
fgColor.setColor(Tools::getForegroundColor(bgColor.color()));
fgColor.setStyle(Qt::BrushStyle::SolidPattern);
item->setBackground(1, bgColor);
item->setForeground(1, fgColor);
}
if (!t.getPriorityUUID().isEmpty())
{
item->setText(2, getPriorityLabel(t.getPriorityUUID()));
QBrush bgColor = item->background(2);
QBrush fgColor = item->foreground(2);
bgColor.setColor(getPriorityColor(t.getPriorityUUID(), bgColor.color()));
bgColor.setStyle(Qt::BrushStyle::SolidPattern);
fgColor.setColor(Tools::getForegroundColor(bgColor.color()));
fgColor.setStyle(Qt::BrushStyle::SolidPattern);
item->setBackground(2, bgColor);
item->setForeground(2, fgColor);
}
}
void MainWindow::redrawBoardList()
{
QListWidget *l = ui->boardList;
uint16_t itemCount = l->count();
for (int16_t i = itemCount; i >= 0; i--)
{
delete l->takeItem(i);
}
foreach (Board *b, boards)
{
QListWidgetItem *item = new QListWidgetItem(b->getName());
item->setToolTip(b->getDescription());
l->addItem(item);
}
}
void MainWindow::redrawFilterList()
{
QListWidget *l = ui->filterListWidget;
uint16_t itemCount = l->count();
for (int16_t i = itemCount; i >= 0; i--)
{
delete l->takeItem(i);
}
foreach (Filter f, filters)
{
QListWidgetItem *item = new QListWidgetItem(f.getName());
item->setToolTip(f.getDescription());
l->addItem(item);
}
}
void MainWindow::redrawTaskTree()
{
QTreeWidget *l = ui->taskList;
uint16_t itemCount = l->topLevelItemCount();
for (int16_t i = itemCount; i >= 0; i--)
{
delete l->takeTopLevelItem(i);
}
if (selectedBoardIndex > -1)
{
Board *b = boards[selectedBoardIndex];
std::optional<Status> boardStatus = TaskStateService::getInstance()->getStatusByUUID(b->getStatus());
if (boardStatus.has_value())
{
ui->boardStatus->setStyleSheet(Tools::getStatusLabelStylesheet(boardStatus.value()));
ui->boardStatus->setText(boardStatus.value().getName());
ui->boardStatus->setVisible(true);
}
foreach (Task *t, b->getTasks())
{
QTreeWidgetItem *item = new QTreeWidgetItem();
updateTaskRow(item, *t);
ui->taskList->addTopLevelItem(item);
}
}
else if (selectedFilterIndex > -1)
{
Filter f = filters[selectedFilterIndex];
filterResult = f.filter(boards);
foreach (Task *t, filterResult)
{
QTreeWidgetItem *item = new QTreeWidgetItem();
updateTaskRow(item, *t);
ui->taskList->addTopLevelItem(item);
}
}
redrawBoardStatus();
}
void MainWindow::redrawBoardStatus()
{
ui->boardStatus->setVisible(false);
if (selectedBoardIndex > -1)
{
Board *b = boards[selectedBoardIndex];
if (b->isShowingStatus())
{
std::optional<Status> boardStatus = TaskStateService::getInstance()->getStatusByUUID(b->getStatus());
if (boardStatus.has_value())
{
ui->boardStatus->setStyleSheet(Tools::getStatusLabelStylesheet(boardStatus.value()));
ui->boardStatus->setText(boardStatus.value().getName());
ui->boardStatus->setVisible(true);
}
}
}
}
void MainWindow::save()
{
QJsonDocument doc = getJsonSave();
if (!Tools::writeSaveToFile(doc))
{
QMessageBox::critical(this, "Failed to save", "Failed to write the save to the file", QMessageBox::StandardButton::Ok);
}
}