diff --git a/resources/icons.qrc b/resources/icons.qrc index d7807cf85..f1dac90e4 100644 --- a/resources/icons.qrc +++ b/resources/icons.qrc @@ -46,6 +46,7 @@ ./graphics/Faenza/actions/64/system-search.png ./graphics/Faenza/actions/64/system-upgrade.png ./graphics/Faenza/actions/64/tab-new.png + ./graphics/Faenza/actions/64/tag-new.png ./graphics/Faenza/actions/64/up.png ./graphics/Faenza/actions/64/view-fullscreen.png ./graphics/Faenza/actions/64/view-list-details.png @@ -114,6 +115,9 @@ ./graphics/Numix/22/actions/system-search.svg ./graphics/Numix/22/actions/system-upgrade.svg ./graphics/Numix/22/actions/tab-new.svg + ./graphics/Numix/22/actions/tag-folder.svg + ./graphics/Numix/22/actions/tag-new.svg + ./graphics/Numix/22/actions/tag-properties.svg ./graphics/Numix/22/actions/up.svg ./graphics/Numix/22/actions/view-fullscreen.svg ./graphics/Numix/22/actions/view-list-details.svg diff --git a/src/librssguard/gui/dialogs/formaddeditlabel.cpp b/src/librssguard/gui/dialogs/formaddeditlabel.cpp index 308754209..7ac576054 100644 --- a/src/librssguard/gui/dialogs/formaddeditlabel.cpp +++ b/src/librssguard/gui/dialogs/formaddeditlabel.cpp @@ -7,7 +7,7 @@ #include "miscellaneous/iconfactory.h" #include "services/abstract/label.h" -FormAddEditLabel::FormAddEditLabel(QWidget* parent) : QDialog(parent) { +FormAddEditLabel::FormAddEditLabel(QWidget* parent) : QDialog(parent), m_editableLabel(nullptr) { m_ui.setupUi(this); m_ui.m_txtName->lineEdit()->setPlaceholderText(tr("Name for your label")); @@ -26,11 +26,10 @@ FormAddEditLabel::FormAddEditLabel(QWidget* parent) : QDialog(parent) { } Label* FormAddEditLabel::execForAdd() { - GuiUtilities::applyDialogProperties(*this, qApp->icons()->fromTheme(QSL("tag-properties")), tr("Create new label")); + GuiUtilities::applyDialogProperties(*this, qApp->icons()->fromTheme(QSL("tag-new")), tr("Create new label")); m_ui.m_btnColor->setRandomColor(); auto exit_code = exec(); - auto xxx = m_ui.m_btnColor->color().name(); if (exit_code == QDialog::DialogCode::Accepted) { return new Label(m_ui.m_txtName->lineEdit()->text(), m_ui.m_btnColor->color()); @@ -39,3 +38,23 @@ Label* FormAddEditLabel::execForAdd() { return nullptr; } } + +bool FormAddEditLabel::execForEdit(Label* lbl) { + GuiUtilities::applyDialogProperties(*this, qApp->icons()->fromTheme(QSL("tag-properties")), tr("Edit label '%1'").arg(lbl->title())); + + m_editableLabel = lbl; + m_ui.m_btnColor->setColor(lbl->color()); + m_ui.m_txtName->lineEdit()->setText(lbl->title()); + + auto exit_code = exec(); + + if (exit_code == QDialog::DialogCode::Accepted) { + // TODO: Place server-side changes perhaps to here? + m_editableLabel->setColor(m_ui.m_btnColor->color()); + m_editableLabel->setTitle(m_ui.m_txtName->lineEdit()->text()); + return true; + } + else { + return false; + } +} diff --git a/src/librssguard/gui/dialogs/formaddeditlabel.h b/src/librssguard/gui/dialogs/formaddeditlabel.h index d4f86fea8..338b3230a 100644 --- a/src/librssguard/gui/dialogs/formaddeditlabel.h +++ b/src/librssguard/gui/dialogs/formaddeditlabel.h @@ -21,9 +21,11 @@ class FormAddEditLabel : public QDialog { public slots: Label* execForAdd(); + bool execForEdit(Label* lbl); private: Ui::FormAddEditLabel m_ui; + Label* m_editableLabel; }; #endif // FORMADDEDITLABEL_H diff --git a/src/librssguard/gui/feedsview.cpp b/src/librssguard/gui/feedsview.cpp index 89516bc2d..dbdecdc09 100755 --- a/src/librssguard/gui/feedsview.cpp +++ b/src/librssguard/gui/feedsview.cpp @@ -29,7 +29,8 @@ FeedsView::FeedsView(QWidget* parent) : QTreeView(parent), m_contextMenuService(nullptr), m_contextMenuBin(nullptr), m_contextMenuCategories(nullptr), - m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr), m_contextMenuOtherItems(nullptr) { + m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr), m_contextMenuOtherItems(nullptr), + m_contextMenuLabel(nullptr) { setObjectName(QSL("FeedsView")); // Allocate models. @@ -656,6 +657,28 @@ QMenu* FeedsView::initializeContextMenuOtherItem(RootItem* clicked_item) { return m_contextMenuOtherItems; } +QMenu* FeedsView::initializeContextMenuLabel(RootItem* clicked_item) { + if (m_contextMenuLabel == nullptr) { + m_contextMenuLabel = new QMenu(tr("Context menu for label"), this); + } + else { + m_contextMenuLabel->clear(); + } + + QList specific_actions = clicked_item->contextMenuFeedsList(); + + if (!specific_actions.isEmpty()) { + m_contextMenuLabel->addSeparator(); + m_contextMenuLabel->addActions(specific_actions); + } + else { + m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionEditSelectedItem); + m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionDeleteSelectedItem); + } + + return m_contextMenuLabel; +} + void FeedsView::setupAppearance() { // Setup column resize strategies. header()->setSectionResizeMode(FDS_MODEL_TITLE_INDEX, QHeaderView::Stretch); @@ -722,6 +745,9 @@ void FeedsView::contextMenuEvent(QContextMenuEvent* event) { else if (clicked_item->kind() == RootItem::Kind::ServiceRoot) { initializeContextMenuService(clicked_item)->exec(event->globalPos()); } + else if (clicked_item->kind() == RootItem::Kind::Label) { + initializeContextMenuLabel(clicked_item)->exec(event->globalPos()); + } else { initializeContextMenuOtherItem(clicked_item)->exec(event->globalPos()); } diff --git a/src/librssguard/gui/feedsview.h b/src/librssguard/gui/feedsview.h index 5545323a2..33cb6214a 100755 --- a/src/librssguard/gui/feedsview.h +++ b/src/librssguard/gui/feedsview.h @@ -110,6 +110,7 @@ class RSSGUARD_DLLSPEC FeedsView : public QTreeView { QMenu* initializeContextMenuImportant(RootItem* clicked_item); QMenu* initializeContextMenuEmptySpace(); QMenu* initializeContextMenuOtherItem(RootItem* clicked_item); + QMenu* initializeContextMenuLabel(RootItem* clicked_item); void setupAppearance(); void saveExpandStates(RootItem* item); @@ -121,6 +122,7 @@ class RSSGUARD_DLLSPEC FeedsView : public QTreeView { QMenu* m_contextMenuImportant; QMenu* m_contextMenuEmptySpace; QMenu* m_contextMenuOtherItems; + QMenu* m_contextMenuLabel; FeedsModel* m_sourceModel; FeedsProxyModel* m_proxyModel; }; diff --git a/src/librssguard/miscellaneous/databasequeries.cpp b/src/librssguard/miscellaneous/databasequeries.cpp index 31bfd6f87..b30f6d367 100755 --- a/src/librssguard/miscellaneous/databasequeries.cpp +++ b/src/librssguard/miscellaneous/databasequeries.cpp @@ -54,6 +54,42 @@ QList DatabaseQueries::getLabels(const QSqlDatabase& db, int account_id) return labels; } +bool DatabaseQueries::updateLabel(const QSqlDatabase& db, Label* label) { + QSqlQuery q(db); + + q.setForwardOnly(true); + q.prepare("UPDATE Labels SET name = :name, color = :color " + "WHERE id = :id AND account_id = :account_id;"); + q.bindValue(QSL(":name"), label->title()); + q.bindValue(QSL(":color"), label->color().name()); + q.bindValue(QSL(":id"), label->id()); + q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); + + return q.exec(); +} + +bool DatabaseQueries::deleteLabel(const QSqlDatabase& db, Label* label) { + // NOTE: All dependecies are done via SQL foreign cascaded keys, so no + // extra removals are needed. + QSqlQuery q(db); + + q.setForwardOnly(true); + q.prepare("DELETE FROM Labels WHERE id = :id AND account_id = :account_id;"); + q.bindValue(QSL(":id"), label->id()); + q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); + + if (q.exec()) { + q.prepare("DELETE FROM LabelsInMessages WHERE label = :custom_id AND account_id = :account_id;"); + q.bindValue(QSL(":custom_id"), label->customId()); + q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); + + return q.exec(); + } + else { + return false; + } +} + bool DatabaseQueries::createLabel(const QSqlDatabase& db, Label* label, int account_id) { QSqlQuery q(db); @@ -70,7 +106,10 @@ bool DatabaseQueries::createLabel(const QSqlDatabase& db, Label* label, int acco label->setId(q.lastInsertId().toInt()); } - return res; + // Fixup missing custom IDs. + q.prepare("UPDATE Labels SET custom_id = id WHERE custom_id IS NULL OR custom_id = '';"); + + return q.exec() && res; } bool DatabaseQueries::markImportantMessagesReadUnread(const QSqlDatabase& db, int account_id, RootItem::ReadStatus read) { diff --git a/src/librssguard/miscellaneous/databasequeries.h b/src/librssguard/miscellaneous/databasequeries.h index 3af72224b..f1ad058e8 100644 --- a/src/librssguard/miscellaneous/databasequeries.h +++ b/src/librssguard/miscellaneous/databasequeries.h @@ -20,6 +20,8 @@ class DatabaseQueries { // Label operators. static QList getLabels(const QSqlDatabase& db, int account_id); + static bool updateLabel(const QSqlDatabase& db, Label* label); + static bool deleteLabel(const QSqlDatabase& db, Label* label); static bool createLabel(const QSqlDatabase& db, Label* label, int account_id); // Message operators. diff --git a/src/librssguard/services/abstract/label.cpp b/src/librssguard/services/abstract/label.cpp index a8e2a955f..6b3746daf 100755 --- a/src/librssguard/services/abstract/label.cpp +++ b/src/librssguard/services/abstract/label.cpp @@ -2,6 +2,12 @@ #include "services/abstract/label.h" +#include "gui/dialogs/formaddeditlabel.h" +#include "miscellaneous/application.h" +#include "miscellaneous/databasefactory.h" +#include "miscellaneous/databasequeries.h" +#include "services/abstract/serviceroot.h" + #include #include @@ -23,6 +29,39 @@ void Label::setColor(const QColor& color) { m_color = color; } +bool Label::canBeEdited() const { + return true; +} + +bool Label::editViaGui() { + FormAddEditLabel form(qApp->mainFormWidget()); + + if (form.execForEdit(this)) { + QSqlDatabase db = qApp->database()->connection(metaObject()->className()); + + return DatabaseQueries::updateLabel(db, this); + } + else { + return false; + } +} + +bool Label::canBeDeleted() const { + return true; +} + +bool Label::deleteViaGui() { + QSqlDatabase db = qApp->database()->connection(metaObject()->className()); + + if (DatabaseQueries::deleteLabel(db, this)) { + getParentServiceRoot()->requestItemRemoval(this); + return true; + } + else { + return false; + } +} + QIcon Label::generateIcon(const QColor& color) { QPixmap pxm(64, 64); diff --git a/src/librssguard/services/abstract/label.h b/src/librssguard/services/abstract/label.h index 2daa15dbe..87ef4f144 100755 --- a/src/librssguard/services/abstract/label.h +++ b/src/librssguard/services/abstract/label.h @@ -17,6 +17,10 @@ class Label : public RootItem { QColor color() const; void setColor(const QColor& color); + virtual bool canBeEdited() const; + virtual bool editViaGui(); + virtual bool canBeDeleted() const; + virtual bool deleteViaGui(); static QIcon generateIcon(const QColor& color); private: diff --git a/src/librssguard/services/abstract/labelsnode.cpp b/src/librssguard/services/abstract/labelsnode.cpp index 24e26d73f..2ba5fc0ba 100755 --- a/src/librssguard/services/abstract/labelsnode.cpp +++ b/src/librssguard/services/abstract/labelsnode.cpp @@ -12,7 +12,7 @@ LabelsNode::LabelsNode(const QList& labels, RootItem* parent_item) : RootItem(parent_item), m_actLabelNew(nullptr) { setKind(RootItem::Kind::Labels); setId(ID_LABELS); - setIcon(qApp->icons()->fromTheme(QSL("mail-mark-important"))); + setIcon(qApp->icons()->fromTheme(QSL("tag-folder"))); setTitle(tr("Labels")); setDescription(tr("You can see all your labels (tags) here.")); setCreationDate(QDateTime::currentDateTime()); @@ -25,7 +25,7 @@ LabelsNode::LabelsNode(const QList& labels, RootItem* parent_item) : Roo QList LabelsNode::contextMenuFeedsList() { if (m_actLabelNew == nullptr) { // Initialize it all. - m_actLabelNew = new QAction(qApp->icons()->fromTheme("tag-new"), tr("New label"), this); + m_actLabelNew = new QAction(qApp->icons()->fromTheme(QSL("tag-new")), tr("New label"), this); connect(m_actLabelNew, &QAction::triggered, this, &LabelsNode::createLabel); } diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 4ba6701d7..e4ca969d9 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -506,7 +506,7 @@ bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) { // Show messages with particular label. model->setFilter(QString("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1 AND " "(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = Messages.custom_id AND label = %2) > 0") - .arg(QString::number(accountId()), QString::number(item->id()))); + .arg(QString::number(accountId()), item->customId())); } else if (item->kind() == RootItem::Kind::Labels) { // Show messages with any label.