working on persistent regexps + multiple of other fixes and small enhancements
This commit is contained in:
parent
2e2aba943c
commit
59b80f784b
@ -63,8 +63,6 @@ set(SOURCES
|
|||||||
gui/dialogs/formabout.h
|
gui/dialogs/formabout.h
|
||||||
gui/dialogs/formaddaccount.cpp
|
gui/dialogs/formaddaccount.cpp
|
||||||
gui/dialogs/formaddaccount.h
|
gui/dialogs/formaddaccount.h
|
||||||
gui/dialogs/formaddeditlabel.cpp
|
|
||||||
gui/dialogs/formaddeditlabel.h
|
|
||||||
gui/dialogs/formbackupdatabasesettings.cpp
|
gui/dialogs/formbackupdatabasesettings.cpp
|
||||||
gui/dialogs/formbackupdatabasesettings.h
|
gui/dialogs/formbackupdatabasesettings.h
|
||||||
gui/dialogs/formdatabasecleanup.cpp
|
gui/dialogs/formdatabasecleanup.cpp
|
||||||
@ -262,6 +260,10 @@ set(SOURCES
|
|||||||
services/abstract/gui/formcategorydetails.h
|
services/abstract/gui/formcategorydetails.h
|
||||||
services/abstract/gui/formfeeddetails.cpp
|
services/abstract/gui/formfeeddetails.cpp
|
||||||
services/abstract/gui/formfeeddetails.h
|
services/abstract/gui/formfeeddetails.h
|
||||||
|
services/abstract/gui/formaddeditlabel.cpp
|
||||||
|
services/abstract/gui/formaddeditlabel.h
|
||||||
|
services/abstract/gui/formaddeditprobe.cpp
|
||||||
|
services/abstract/gui/formaddeditprobe.h
|
||||||
services/abstract/gui/custommessagepreviewer.h
|
services/abstract/gui/custommessagepreviewer.h
|
||||||
services/abstract/gui/custommessagepreviewer.cpp
|
services/abstract/gui/custommessagepreviewer.cpp
|
||||||
services/abstract/importantnode.cpp
|
services/abstract/importantnode.cpp
|
||||||
@ -270,6 +272,10 @@ set(SOURCES
|
|||||||
services/abstract/label.h
|
services/abstract/label.h
|
||||||
services/abstract/labelsnode.cpp
|
services/abstract/labelsnode.cpp
|
||||||
services/abstract/labelsnode.h
|
services/abstract/labelsnode.h
|
||||||
|
services/abstract/search.cpp
|
||||||
|
services/abstract/search.h
|
||||||
|
services/abstract/searchsnode.cpp
|
||||||
|
services/abstract/searchsnode.h
|
||||||
services/abstract/recyclebin.cpp
|
services/abstract/recyclebin.cpp
|
||||||
services/abstract/recyclebin.h
|
services/abstract/recyclebin.h
|
||||||
services/abstract/rootitem.cpp
|
services/abstract/rootitem.cpp
|
||||||
@ -427,7 +433,6 @@ set(SOURCES
|
|||||||
set(UI_FILES
|
set(UI_FILES
|
||||||
gui/dialogs/formabout.ui
|
gui/dialogs/formabout.ui
|
||||||
gui/dialogs/formaddaccount.ui
|
gui/dialogs/formaddaccount.ui
|
||||||
gui/dialogs/formaddeditlabel.ui
|
|
||||||
gui/dialogs/formbackupdatabasesettings.ui
|
gui/dialogs/formbackupdatabasesettings.ui
|
||||||
gui/dialogs/formdatabasecleanup.ui
|
gui/dialogs/formdatabasecleanup.ui
|
||||||
gui/dialogs/formmain.ui
|
gui/dialogs/formmain.ui
|
||||||
@ -460,6 +465,8 @@ set(UI_FILES
|
|||||||
services/abstract/gui/formaccountdetails.ui
|
services/abstract/gui/formaccountdetails.ui
|
||||||
services/abstract/gui/formcategorydetails.ui
|
services/abstract/gui/formcategorydetails.ui
|
||||||
services/abstract/gui/formfeeddetails.ui
|
services/abstract/gui/formfeeddetails.ui
|
||||||
|
services/abstract/gui/formaddeditlabel.ui
|
||||||
|
services/abstract/gui/formaddeditprobe.ui
|
||||||
services/feedly/gui/feedlyaccountdetails.ui
|
services/feedly/gui/feedlyaccountdetails.ui
|
||||||
services/gmail/gui/formaddeditemail.ui
|
services/gmail/gui/formaddeditemail.ui
|
||||||
services/gmail/gui/gmailaccountdetails.ui
|
services/gmail/gui/gmailaccountdetails.ui
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "database/databasequeries.h"
|
#include "database/databasequeries.h"
|
||||||
#include "definitions/definitions.h"
|
#include "definitions/definitions.h"
|
||||||
#include "gui/dialogs/formmain.h"
|
#include "gui/dialogs/formmain.h"
|
||||||
|
#include "gui/messagebox.h"
|
||||||
#include "miscellaneous/feedreader.h"
|
#include "miscellaneous/feedreader.h"
|
||||||
#include "miscellaneous/iconfactory.h"
|
#include "miscellaneous/iconfactory.h"
|
||||||
#include "miscellaneous/textfactory.h"
|
#include "miscellaneous/textfactory.h"
|
||||||
@ -510,6 +511,17 @@ bool FeedsModel::markItemRead(RootItem* item, RootItem::ReadStatus read) {
|
|||||||
|
|
||||||
bool FeedsModel::markItemCleared(RootItem* item, bool clean_read_only) {
|
bool FeedsModel::markItemCleared(RootItem* item, bool clean_read_only) {
|
||||||
if (item != nullptr) {
|
if (item != nullptr) {
|
||||||
|
if (MsgBox::show(nullptr,
|
||||||
|
QMessageBox::Icon::Question,
|
||||||
|
tr("Are you sure?"),
|
||||||
|
tr("Do you really want to clean all articles from selected item?"),
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
|
||||||
|
QMessageBox::StandardButton::No) != QMessageBox::StandardButton::Yes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return item->cleanMessages(clean_read_only);
|
return item->cleanMessages(clean_read_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent)
|
|||||||
m_priorities = {RootItem::Kind::Category,
|
m_priorities = {RootItem::Kind::Category,
|
||||||
RootItem::Kind::Feed,
|
RootItem::Kind::Feed,
|
||||||
RootItem::Kind::Labels,
|
RootItem::Kind::Labels,
|
||||||
|
RootItem::Kind::Probes,
|
||||||
RootItem::Kind::Important,
|
RootItem::Kind::Important,
|
||||||
RootItem::Kind::Unread,
|
RootItem::Kind::Unread,
|
||||||
RootItem::Kind::Bin};
|
RootItem::Kind::Bin};
|
||||||
|
@ -115,14 +115,14 @@ QSqlDatabase SqliteDriver::connection(const QString& connection_name, DesiredSto
|
|||||||
database = QSqlDatabase::addDatabase(QSL(APP_DB_SQLITE_DRIVER), connection_name);
|
database = QSqlDatabase::addDatabase(QSL(APP_DB_SQLITE_DRIVER), connection_name);
|
||||||
|
|
||||||
if (want_in_memory) {
|
if (want_in_memory) {
|
||||||
database.setConnectOptions(QSL("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE"));
|
database.setConnectOptions(QSL("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE;QSQLITE_ENABLE_REGEXP"));
|
||||||
database.setDatabaseName(QSL("file::memory:"));
|
database.setDatabaseName(QSL("file::memory:"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const QDir db_path(m_databaseFilePath);
|
const QDir db_path(m_databaseFilePath);
|
||||||
QFile db_file(db_path.absoluteFilePath(QSL(APP_DB_SQLITE_FILE)));
|
QFile db_file(db_path.absoluteFilePath(QSL(APP_DB_SQLITE_FILE)));
|
||||||
|
|
||||||
database.setConnectOptions(QSL("QSQLITE_ENABLE_SHARED_CACHE"));
|
database.setConnectOptions(QSL("QSQLITE_ENABLE_SHARED_CACHE;QSQLITE_ENABLE_REGEXP"));
|
||||||
database.setDatabaseName(db_file.fileName());
|
database.setDatabaseName(db_file.fileName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,10 +203,10 @@ QSqlDatabase SqliteDriver::initializeDatabase(const QString& connection_name, bo
|
|||||||
database = QSqlDatabase::addDatabase(QSL(APP_DB_SQLITE_DRIVER), connection_name);
|
database = QSqlDatabase::addDatabase(QSL(APP_DB_SQLITE_DRIVER), connection_name);
|
||||||
|
|
||||||
if (in_memory) {
|
if (in_memory) {
|
||||||
database.setConnectOptions(QSL("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE"));
|
database.setConnectOptions(QSL("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE;QSQLITE_ENABLE_REGEXP"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
database.setConnectOptions(QSL("QSQLITE_ENABLE_SHARED_CACHE"));
|
database.setConnectOptions(QSL("QSQLITE_ENABLE_SHARED_CACHE;QSQLITE_ENABLE_REGEXP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
database.setDatabaseName(db_file_name);
|
database.setDatabaseName(db_file_name);
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#define ID_IMPORTANT -3
|
#define ID_IMPORTANT -3
|
||||||
#define ID_LABELS -4
|
#define ID_LABELS -4
|
||||||
#define ID_UNREAD -5
|
#define ID_UNREAD -5
|
||||||
|
#define ID_PROBES -6
|
||||||
|
|
||||||
#define MSG_SCORE_MAX 100.0
|
#define MSG_SCORE_MAX 100.0
|
||||||
#define MSG_SCORE_MIN 0.0
|
#define MSG_SCORE_MIN 0.0
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
FeedsView::FeedsView(QWidget* parent)
|
FeedsView::FeedsView(QWidget* parent)
|
||||||
: BaseTreeView(parent), m_contextMenuService(nullptr), m_contextMenuBin(nullptr), m_contextMenuCategories(nullptr),
|
: BaseTreeView(parent), m_contextMenuService(nullptr), m_contextMenuBin(nullptr), m_contextMenuCategories(nullptr),
|
||||||
m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr),
|
m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr),
|
||||||
m_contextMenuOtherItems(nullptr), m_contextMenuLabel(nullptr), m_dontSaveExpandState(false) {
|
m_contextMenuOtherItems(nullptr), m_contextMenuLabel(nullptr), m_contextMenuProbe(nullptr),
|
||||||
|
m_dontSaveExpandState(false) {
|
||||||
setObjectName(QSL("FeedsView"));
|
setObjectName(QSL("FeedsView"));
|
||||||
|
|
||||||
// Allocate models.
|
// Allocate models.
|
||||||
@ -852,20 +853,42 @@ QMenu* FeedsView::initializeContextMenuLabel(RootItem* clicked_item) {
|
|||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
|
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionEditSelectedItem);
|
||||||
|
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead);
|
||||||
|
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread);
|
||||||
|
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionDeleteSelectedItem);
|
||||||
|
|
||||||
if (!specific_actions.isEmpty()) {
|
if (!specific_actions.isEmpty()) {
|
||||||
m_contextMenuLabel->addSeparator();
|
m_contextMenuLabel->addSeparator();
|
||||||
m_contextMenuLabel->addActions(specific_actions);
|
m_contextMenuLabel->addActions(specific_actions);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionEditSelectedItem);
|
|
||||||
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead);
|
|
||||||
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread);
|
|
||||||
m_contextMenuLabel->addAction(qApp->mainForm()->m_ui->m_actionDeleteSelectedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_contextMenuLabel;
|
return m_contextMenuLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMenu* FeedsView::initializeContextMenuProbe(RootItem* clicked_item) {
|
||||||
|
if (m_contextMenuProbe == nullptr) {
|
||||||
|
m_contextMenuProbe = new QMenu(tr("Context menu for probe"), this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_contextMenuProbe->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
|
m_contextMenuProbe->addAction(qApp->mainForm()->m_ui->m_actionEditSelectedItem);
|
||||||
|
m_contextMenuProbe->addAction(qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead);
|
||||||
|
m_contextMenuProbe->addAction(qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread);
|
||||||
|
m_contextMenuProbe->addAction(qApp->mainForm()->m_ui->m_actionDeleteSelectedItem);
|
||||||
|
|
||||||
|
if (!specific_actions.isEmpty()) {
|
||||||
|
m_contextMenuProbe->addSeparator();
|
||||||
|
m_contextMenuProbe->addActions(specific_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_contextMenuProbe;
|
||||||
|
}
|
||||||
|
|
||||||
void FeedsView::setupAppearance() {
|
void FeedsView::setupAppearance() {
|
||||||
// Setup column resize strategies.
|
// Setup column resize strategies.
|
||||||
header()->setSectionResizeMode(FDS_MODEL_TITLE_INDEX, QHeaderView::ResizeMode::Stretch);
|
header()->setSectionResizeMode(FDS_MODEL_TITLE_INDEX, QHeaderView::ResizeMode::Stretch);
|
||||||
@ -950,6 +973,9 @@ void FeedsView::contextMenuEvent(QContextMenuEvent* event) {
|
|||||||
else if (clicked_item->kind() == RootItem::Kind::Label) {
|
else if (clicked_item->kind() == RootItem::Kind::Label) {
|
||||||
initializeContextMenuLabel(clicked_item)->exec(event->globalPos());
|
initializeContextMenuLabel(clicked_item)->exec(event->globalPos());
|
||||||
}
|
}
|
||||||
|
else if (clicked_item->kind() == RootItem::Kind::Probe) {
|
||||||
|
initializeContextMenuProbe(clicked_item)->exec(event->globalPos());
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
initializeContextMenuOtherItem(clicked_item)->exec(event->globalPos());
|
initializeContextMenuOtherItem(clicked_item)->exec(event->globalPos());
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,7 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
|||||||
QMenu* initializeContextMenuEmptySpace();
|
QMenu* initializeContextMenuEmptySpace();
|
||||||
QMenu* initializeContextMenuOtherItem(RootItem* clicked_item);
|
QMenu* initializeContextMenuOtherItem(RootItem* clicked_item);
|
||||||
QMenu* initializeContextMenuLabel(RootItem* clicked_item);
|
QMenu* initializeContextMenuLabel(RootItem* clicked_item);
|
||||||
|
QMenu* initializeContextMenuProbe(RootItem* clicked_item);
|
||||||
|
|
||||||
void setupAppearance();
|
void setupAppearance();
|
||||||
void saveExpandStates(RootItem* item);
|
void saveExpandStates(RootItem* item);
|
||||||
@ -141,13 +142,14 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
|||||||
QMenu* m_contextMenuEmptySpace;
|
QMenu* m_contextMenuEmptySpace;
|
||||||
QMenu* m_contextMenuOtherItems;
|
QMenu* m_contextMenuOtherItems;
|
||||||
QMenu* m_contextMenuLabel;
|
QMenu* m_contextMenuLabel;
|
||||||
|
QMenu* m_contextMenuProbe;
|
||||||
FeedsModel* m_sourceModel;
|
FeedsModel* m_sourceModel;
|
||||||
FeedsProxyModel* m_proxyModel;
|
FeedsProxyModel* m_proxyModel;
|
||||||
bool m_dontSaveExpandState;
|
bool m_dontSaveExpandState;
|
||||||
|
|
||||||
// QTreeView interface
|
// QTreeView interface
|
||||||
protected:
|
protected:
|
||||||
virtual void drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const;
|
virtual void drawRow(QPainter* painter, const QStyleOptionViewItem& options, const QModelIndex& index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline FeedsProxyModel* FeedsView::model() const {
|
inline FeedsProxyModel* FeedsView::model() const {
|
||||||
|
@ -35,6 +35,14 @@ SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent
|
|||||||
"performance of article list with big number of articles."),
|
"performance of article list with big number of articles."),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
|
QMetaEnum enumer = QMetaEnum::fromType<MessagesModel::MessageUnreadIcon>();
|
||||||
|
|
||||||
|
for (int i = 0; i < enumer.keyCount(); i++) {
|
||||||
|
auto en = MessagesModel::MessageUnreadIcon(enumer.value(i));
|
||||||
|
|
||||||
|
m_ui->m_cmbUnreadIconType->addItem(MessagesModel::descriptionOfUnreadIcon(en), int(en));
|
||||||
|
}
|
||||||
|
|
||||||
connect(m_ui->m_cbShowEnclosuresDirectly, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
|
connect(m_ui->m_cbShowEnclosuresDirectly, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
|
||||||
connect(m_ui->m_spinHeightImageAttachments,
|
connect(m_ui->m_spinHeightImageAttachments,
|
||||||
static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||||
@ -190,14 +198,6 @@ SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_ui->m_spinRelativeArticleTime->setValue(-1);
|
m_ui->m_spinRelativeArticleTime->setValue(-1);
|
||||||
|
|
||||||
QMetaEnum enumer = QMetaEnum::fromType<MessagesModel::MessageUnreadIcon>();
|
|
||||||
|
|
||||||
for (int i = 0; i < enumer.keyCount(); i++) {
|
|
||||||
auto en = MessagesModel::MessageUnreadIcon(enumer.value(i));
|
|
||||||
|
|
||||||
m_ui->m_cmbUnreadIconType->addItem(MessagesModel::descriptionOfUnreadIcon(en), int(en));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsFeedsMessages::~SettingsFeedsMessages() {
|
SettingsFeedsMessages::~SettingsFeedsMessages() {
|
||||||
|
@ -291,7 +291,12 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList<Message>& messages,
|
|||||||
QUrl url(NetworkFactory::sanitizeUrl(feed->source()));
|
QUrl url(NetworkFactory::sanitizeUrl(feed->source()));
|
||||||
|
|
||||||
if (url.isValid()) {
|
if (url.isValid()) {
|
||||||
base_url = url.scheme() + QSL("://") + url.host();
|
if (url.isLocalFile()) {
|
||||||
|
base_url = url.scheme() + QSL("://") + url.toLocalFile();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
base_url = url.scheme() + QSL("://") + url.host();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
#include "gui/dialogs/formaddeditlabel.h"
|
#include "services/abstract/gui/formaddeditlabel.h"
|
||||||
|
|
||||||
#include "gui/guiutilities.h"
|
#include "gui/guiutilities.h"
|
||||||
#include "miscellaneous/application.h"
|
#include "miscellaneous/application.h"
|
81
src/librssguard/services/abstract/gui/formaddeditprobe.cpp
Executable file
81
src/librssguard/services/abstract/gui/formaddeditprobe.cpp
Executable file
@ -0,0 +1,81 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#include "services/abstract/gui/formaddeditprobe.h"
|
||||||
|
|
||||||
|
#include "gui/guiutilities.h"
|
||||||
|
#include "miscellaneous/application.h"
|
||||||
|
#include "miscellaneous/iconfactory.h"
|
||||||
|
#include "services/abstract/search.h"
|
||||||
|
|
||||||
|
FormAddEditProbe::FormAddEditProbe(QWidget* parent) : QDialog(parent), m_editableProbe(nullptr) {
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
m_ui.m_txtName->lineEdit()->setPlaceholderText(tr("Name for your probe"));
|
||||||
|
m_ui.m_txtFilter->lineEdit()->setPlaceholderText(tr("Regular expression"));
|
||||||
|
|
||||||
|
connect(m_ui.m_txtName->lineEdit(), &QLineEdit::textChanged, this, [this](const QString& text) {
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
m_ui.m_txtName->setStatus(LineEditWithStatus::StatusType::Error, tr("Label's name cannot be empty."));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_ui.m_txtName->setStatus(LineEditWithStatus::StatusType::Ok, tr("Perfect!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(m_ui.m_txtFilter->lineEdit(), &QLineEdit::textChanged, this, [this](const QString& text) {
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
m_ui.m_txtFilter->setStatus(LineEditWithStatus::StatusType::Error, tr("Probe name cannot be empty."));
|
||||||
|
}
|
||||||
|
else if (!QRegularExpression(text).isValid()) {
|
||||||
|
m_ui.m_txtFilter->setStatus(LineEditWithStatus::StatusType::Error, tr("Regular expression is not well-formed."));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_ui.m_txtFilter->setStatus(LineEditWithStatus::StatusType::Ok, tr("Perfect!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
emit m_ui.m_txtName->lineEdit()->textChanged({});
|
||||||
|
emit m_ui.m_txtFilter->lineEdit()->textChanged({});
|
||||||
|
}
|
||||||
|
|
||||||
|
Search* FormAddEditProbe::execForAdd() {
|
||||||
|
GuiUtilities::applyDialogProperties(*this, qApp->icons()->fromTheme(QSL("tag-new")), tr("Create new probe"));
|
||||||
|
|
||||||
|
m_ui.m_btnColor->setRandomColor();
|
||||||
|
m_ui.m_txtName->lineEdit()->setText(tr("Hot stuff"));
|
||||||
|
m_ui.m_txtFilter->setFocus();
|
||||||
|
|
||||||
|
auto exit_code = exec();
|
||||||
|
|
||||||
|
if (exit_code == QDialog::DialogCode::Accepted) {
|
||||||
|
return new Search(m_ui.m_txtName->lineEdit()->text(),
|
||||||
|
m_ui.m_txtFilter->lineEdit()->text(),
|
||||||
|
m_ui.m_btnColor->color());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FormAddEditProbe::execForEdit(Search* prb) {
|
||||||
|
GuiUtilities::applyDialogProperties(*this,
|
||||||
|
qApp->icons()->fromTheme(QSL("tag-properties")),
|
||||||
|
tr("Edit probe '%1'").arg(prb->title()));
|
||||||
|
|
||||||
|
m_editableProbe = prb;
|
||||||
|
|
||||||
|
m_ui.m_btnColor->setColor(prb->color());
|
||||||
|
m_ui.m_txtName->lineEdit()->setText(prb->title());
|
||||||
|
m_ui.m_txtFilter->lineEdit()->setText(prb->filter());
|
||||||
|
m_ui.m_txtFilter->setFocus();
|
||||||
|
|
||||||
|
auto exit_code = exec();
|
||||||
|
|
||||||
|
if (exit_code == QDialog::DialogCode::Accepted) {
|
||||||
|
m_editableProbe->setColor(m_ui.m_btnColor->color());
|
||||||
|
m_editableProbe->setFilter(m_ui.m_txtFilter->lineEdit()->text());
|
||||||
|
m_editableProbe->setTitle(m_ui.m_txtName->lineEdit()->text());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
31
src/librssguard/services/abstract/gui/formaddeditprobe.h
Executable file
31
src/librssguard/services/abstract/gui/formaddeditprobe.h
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#ifndef FORMADDEDITPROBE_H
|
||||||
|
#define FORMADDEDITPROBE_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "ui_formaddeditprobe.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class FormAddEditProbe;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Search;
|
||||||
|
|
||||||
|
class FormAddEditProbe : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FormAddEditProbe(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
Search* execForAdd();
|
||||||
|
bool execForEdit(Search* prb);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::FormAddEditProbe m_ui;
|
||||||
|
Search* m_editableProbe;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FORMADDEDITPROBE_H
|
103
src/librssguard/services/abstract/gui/formaddeditprobe.ui
Executable file
103
src/librssguard/services/abstract/gui/formaddeditprobe.ui
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>FormAddEditProbe</class>
|
||||||
|
<widget class="QDialog" name="FormAddEditProbe">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>270</width>
|
||||||
|
<height>180</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="ColorToolButton" name="m_btnColor">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="LineEditWithStatus" name="m_txtName" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="LineEditWithStatus" name="m_txtFilter" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<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>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="m_buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>LineEditWithStatus</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>lineeditwithstatus.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>ColorToolButton</class>
|
||||||
|
<extends>QToolButton</extends>
|
||||||
|
<header>colortoolbutton.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>m_btnColor</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>m_buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>FormAddEditProbe</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>214</x>
|
||||||
|
<y>132</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>141</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>m_buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>FormAddEditProbe</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>214</x>
|
||||||
|
<y>132</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>223</x>
|
||||||
|
<y>141</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
#include "database/databasefactory.h"
|
#include "database/databasefactory.h"
|
||||||
#include "database/databasequeries.h"
|
#include "database/databasequeries.h"
|
||||||
#include "gui/dialogs/formaddeditlabel.h"
|
|
||||||
#include "miscellaneous/application.h"
|
#include "miscellaneous/application.h"
|
||||||
#include "services/abstract/cacheforserviceroot.h"
|
#include "services/abstract/cacheforserviceroot.h"
|
||||||
|
#include "services/abstract/gui/formaddeditlabel.h"
|
||||||
#include "services/abstract/labelsnode.h"
|
#include "services/abstract/labelsnode.h"
|
||||||
#include "services/abstract/serviceroot.h"
|
#include "services/abstract/serviceroot.h"
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
#include "database/databasefactory.h"
|
#include "database/databasefactory.h"
|
||||||
#include "database/databasequeries.h"
|
#include "database/databasequeries.h"
|
||||||
#include "exceptions/applicationexception.h"
|
#include "exceptions/applicationexception.h"
|
||||||
#include "gui/dialogs/formaddeditlabel.h"
|
|
||||||
#include "miscellaneous/application.h"
|
#include "miscellaneous/application.h"
|
||||||
#include "miscellaneous/iconfactory.h"
|
#include "miscellaneous/iconfactory.h"
|
||||||
|
#include "services/abstract/gui/formaddeditlabel.h"
|
||||||
#include "services/abstract/serviceroot.h"
|
#include "services/abstract/serviceroot.h"
|
||||||
|
|
||||||
#include "3rd-party/boolinq/boolinq.h"
|
#include "3rd-party/boolinq/boolinq.h"
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "services/abstract/recyclebin.h"
|
#include "services/abstract/recyclebin.h"
|
||||||
|
|
||||||
#include "database/databasequeries.h"
|
#include "database/databasequeries.h"
|
||||||
|
#include "gui/messagebox.h"
|
||||||
#include "miscellaneous/application.h"
|
#include "miscellaneous/application.h"
|
||||||
#include "miscellaneous/iconfactory.h"
|
#include "miscellaneous/iconfactory.h"
|
||||||
#include "miscellaneous/textfactory.h"
|
#include "miscellaneous/textfactory.h"
|
||||||
@ -92,7 +93,6 @@ bool RecycleBin::cleanMessages(bool clear_only_read) {
|
|||||||
parent_root->itemChanged(QList<RootItem*>() << this);
|
parent_root->itemChanged(QList<RootItem*>() << this);
|
||||||
parent_root->requestReloadMessageList(true);
|
parent_root->requestReloadMessageList(true);
|
||||||
return true;
|
return true;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
@ -100,6 +100,17 @@ bool RecycleBin::cleanMessages(bool clear_only_read) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RecycleBin::empty() {
|
bool RecycleBin::empty() {
|
||||||
|
if (MsgBox::show(nullptr,
|
||||||
|
QMessageBox::Icon::Question,
|
||||||
|
tr("Are you sure?"),
|
||||||
|
tr("Do you really want to empty your recycle bin?"),
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
|
||||||
|
QMessageBox::StandardButton::No) != QMessageBox::StandardButton::Yes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return cleanMessages(false);
|
return cleanMessages(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "services/abstract/feed.h"
|
#include "services/abstract/feed.h"
|
||||||
#include "services/abstract/label.h"
|
#include "services/abstract/label.h"
|
||||||
#include "services/abstract/recyclebin.h"
|
#include "services/abstract/recyclebin.h"
|
||||||
|
#include "services/abstract/search.h"
|
||||||
#include "services/abstract/serviceroot.h"
|
#include "services/abstract/serviceroot.h"
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
@ -570,6 +571,10 @@ Label* RootItem::toLabel() const {
|
|||||||
return qobject_cast<Label*>(const_cast<RootItem*>(this));
|
return qobject_cast<Label*>(const_cast<RootItem*>(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Search* RootItem::toProbe() const {
|
||||||
|
return qobject_cast<Search*>(const_cast<RootItem*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
ServiceRoot* RootItem::toServiceRoot() const {
|
ServiceRoot* RootItem::toServiceRoot() const {
|
||||||
return qobject_cast<ServiceRoot*>(const_cast<RootItem*>(this));
|
return qobject_cast<ServiceRoot*>(const_cast<RootItem*>(this));
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
class Category;
|
class Category;
|
||||||
class Feed;
|
class Feed;
|
||||||
class Label;
|
class Label;
|
||||||
|
class Search;
|
||||||
class ServiceRoot;
|
class ServiceRoot;
|
||||||
class QAction;
|
class QAction;
|
||||||
|
|
||||||
@ -43,7 +44,9 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
|||||||
Labels = 32,
|
Labels = 32,
|
||||||
Important = 64,
|
Important = 64,
|
||||||
Label = 128,
|
Label = 128,
|
||||||
Unread = 256
|
Unread = 256,
|
||||||
|
Probes = 512,
|
||||||
|
Probe = 1024
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructors and destructors.
|
// Constructors and destructors.
|
||||||
@ -196,6 +199,7 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
|||||||
Category* toCategory() const;
|
Category* toCategory() const;
|
||||||
Feed* toFeed() const;
|
Feed* toFeed() const;
|
||||||
Label* toLabel() const;
|
Label* toLabel() const;
|
||||||
|
Search* toProbe() const;
|
||||||
ServiceRoot* toServiceRoot() const;
|
ServiceRoot* toServiceRoot() const;
|
||||||
|
|
||||||
bool keepOnTop() const;
|
bool keepOnTop() const;
|
||||||
|
178
src/librssguard/services/abstract/search.cpp
Executable file
178
src/librssguard/services/abstract/search.cpp
Executable file
@ -0,0 +1,178 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#include "services/abstract/search.h"
|
||||||
|
|
||||||
|
#include "database/databasefactory.h"
|
||||||
|
#include "database/databasequeries.h"
|
||||||
|
#include "miscellaneous/application.h"
|
||||||
|
#include "services/abstract/cacheforserviceroot.h"
|
||||||
|
#include "services/abstract/gui/formaddeditprobe.h"
|
||||||
|
#include "services/abstract/labelsnode.h"
|
||||||
|
#include "services/abstract/serviceroot.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPainterPath>
|
||||||
|
|
||||||
|
Search::Search(const QString& name, const QString& filter, const QColor& color, RootItem* parent_item)
|
||||||
|
: Search(parent_item) {
|
||||||
|
setColor(color);
|
||||||
|
setTitle(name);
|
||||||
|
setFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Search::Search(RootItem* parent_item) : RootItem(parent_item) {
|
||||||
|
setKind(RootItem::Kind::Probe);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Search::color() const {
|
||||||
|
return m_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Search::setColor(const QColor& color) {
|
||||||
|
setIcon(generateIcon(color));
|
||||||
|
m_color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Search::countOfUnreadMessages() const {
|
||||||
|
return m_unreadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Search::countOfAllMessages() const {
|
||||||
|
return m_totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search::canBeEdited() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search::editViaGui() {
|
||||||
|
FormAddEditProbe form(qApp->mainFormWidget());
|
||||||
|
|
||||||
|
if (form.execForEdit(this)) {
|
||||||
|
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
// return DatabaseQueries::updateLabel(db, this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search::canBeDeleted() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search::deleteViaGui() {
|
||||||
|
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (DatabaseQueries::deleteLabel(db, this)) {
|
||||||
|
getParentServiceRoot()->requestItemRemoval(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Search::updateCounts(bool including_total_count) {
|
||||||
|
QSqlDatabase database = qApp->database()->driver()->threadSafeConnection(metaObject()->className());
|
||||||
|
int account_id = getParentServiceRoot()->accountId();
|
||||||
|
|
||||||
|
/*
|
||||||
|
auto ac = DatabaseQueries::getMessageCountsForLabel(database, this, account_id);
|
||||||
|
|
||||||
|
if (including_total_count) {
|
||||||
|
setCountOfAllMessages(ac.m_total);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCountOfUnreadMessages(ac.m_unread);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Message> Search::undeletedMessages() const {
|
||||||
|
return {};
|
||||||
|
/*
|
||||||
|
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
return DatabaseQueries::getUndeletedMessagesWithLabel(database, this);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Search::generateIcon(const QColor& color) {
|
||||||
|
QPixmap pxm(64, 64);
|
||||||
|
|
||||||
|
pxm.fill(Qt::GlobalColor::transparent);
|
||||||
|
|
||||||
|
QPainter paint(&pxm);
|
||||||
|
|
||||||
|
paint.setBrush(color);
|
||||||
|
paint.setPen(Qt::GlobalColor::transparent);
|
||||||
|
paint.drawEllipse(pxm.rect().marginsRemoved(QMargins(2, 2, 2, 2)));
|
||||||
|
|
||||||
|
return pxm;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Search::filter() const {
|
||||||
|
return m_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Search::setFilter(const QString& new_filter) {
|
||||||
|
m_filter = new_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Search::setCountOfAllMessages(int totalCount) {
|
||||||
|
m_totalCount = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Search::setCountOfUnreadMessages(int unreadCount) {
|
||||||
|
m_unreadCount = unreadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search::cleanMessages(bool clear_only_read) {
|
||||||
|
ServiceRoot* service = getParentServiceRoot();
|
||||||
|
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
if (DatabaseQueries::cleanLabelledMessages(database, clear_only_read, this)) {
|
||||||
|
service->updateCounts(true);
|
||||||
|
service->itemChanged(service->getSubTree());
|
||||||
|
service->requestReloadMessageList(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Search::markAsReadUnread(RootItem::ReadStatus status) {
|
||||||
|
ServiceRoot* service = getParentServiceRoot();
|
||||||
|
auto* cache = dynamic_cast<CacheForServiceRoot*>(service);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (cache != nullptr) {
|
||||||
|
cache->addMessageStatesToCache(service->customIDSOfMessagesForItem(this, status), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
if (DatabaseQueries::markLabelledMessagesReadUnread(database, this, status)) {
|
||||||
|
service->updateCounts(false);
|
||||||
|
service->itemChanged(service->getSubTree());
|
||||||
|
service->requestReloadMessageList(status == RootItem::ReadStatus::Read);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
50
src/librssguard/services/abstract/search.h
Executable file
50
src/librssguard/services/abstract/search.h
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#ifndef SEARCH_H
|
||||||
|
#define SEARCH_H
|
||||||
|
|
||||||
|
#include "services/abstract/rootitem.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
class RSSGUARD_DLLSPEC Search : public RootItem {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// Added for message filtering with labels.
|
||||||
|
Q_PROPERTY(QColor color READ color)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Search(const QString& name, const QString& filter, const QColor& color, RootItem* parent_item = nullptr);
|
||||||
|
explicit Search(RootItem* parent_item = nullptr);
|
||||||
|
|
||||||
|
QColor color() const;
|
||||||
|
void setColor(const QColor& color);
|
||||||
|
|
||||||
|
QString filter() const;
|
||||||
|
void setFilter(const QString& new_filter);
|
||||||
|
|
||||||
|
void setCountOfAllMessages(int totalCount);
|
||||||
|
void setCountOfUnreadMessages(int unreadCount);
|
||||||
|
|
||||||
|
virtual bool cleanMessages(bool clear_only_read);
|
||||||
|
virtual bool markAsReadUnread(ReadStatus status);
|
||||||
|
virtual int countOfAllMessages() const;
|
||||||
|
virtual int countOfUnreadMessages() const;
|
||||||
|
virtual bool canBeEdited() const;
|
||||||
|
virtual bool editViaGui();
|
||||||
|
virtual bool canBeDeleted() const;
|
||||||
|
virtual bool deleteViaGui();
|
||||||
|
virtual void updateCounts(bool including_total_count);
|
||||||
|
virtual QList<Message> undeletedMessages() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QIcon generateIcon(const QColor& color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_filter;
|
||||||
|
QColor m_color;
|
||||||
|
int m_totalCount{};
|
||||||
|
int m_unreadCount{};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SEARCH_H
|
139
src/librssguard/services/abstract/searchsnode.cpp
Executable file
139
src/librssguard/services/abstract/searchsnode.cpp
Executable file
@ -0,0 +1,139 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#include "services/abstract/searchsnode.h"
|
||||||
|
|
||||||
|
#include "database/databasefactory.h"
|
||||||
|
#include "database/databasequeries.h"
|
||||||
|
#include "exceptions/applicationexception.h"
|
||||||
|
#include "miscellaneous/application.h"
|
||||||
|
#include "miscellaneous/iconfactory.h"
|
||||||
|
#include "services/abstract/gui/formaddeditprobe.h"
|
||||||
|
#include "services/abstract/serviceroot.h"
|
||||||
|
|
||||||
|
#include "3rd-party/boolinq/boolinq.h"
|
||||||
|
|
||||||
|
SearchsNode::SearchsNode(RootItem* parent_item) : RootItem(parent_item), m_actProbeNew(nullptr) {
|
||||||
|
setKind(RootItem::Kind::Probes);
|
||||||
|
setId(ID_PROBES);
|
||||||
|
setIcon(qApp->icons()->fromTheme(QSL("system-search")));
|
||||||
|
setTitle(tr("Article probes"));
|
||||||
|
setDescription(tr("You can see all your permanent article probes here."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchsNode::loadProbes(const QList<Search*>& probes) {
|
||||||
|
for (auto* prb : probes) {
|
||||||
|
appendChild(prb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Message> SearchsNode::undeletedMessages() const {
|
||||||
|
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
return {};
|
||||||
|
// return DatabaseQueries::getUndeletedLabelledMessages(database, getParentServiceRoot()->accountId());
|
||||||
|
}
|
||||||
|
|
||||||
|
int SearchsNode::countOfUnreadMessages() const {
|
||||||
|
auto chi = childItems();
|
||||||
|
|
||||||
|
if (chi.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boolinq::from(chi)
|
||||||
|
.max([](RootItem* it) {
|
||||||
|
return it->countOfUnreadMessages();
|
||||||
|
})
|
||||||
|
->countOfUnreadMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SearchsNode::countOfAllMessages() const {
|
||||||
|
auto chi = childItems();
|
||||||
|
|
||||||
|
if (chi.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boolinq::from(chi)
|
||||||
|
.max([](RootItem* it) {
|
||||||
|
return it->countOfAllMessages();
|
||||||
|
})
|
||||||
|
->countOfAllMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchsNode::updateCounts(bool including_total_count) {
|
||||||
|
// TODO: This is still rather slow because this is automatically
|
||||||
|
// called when message is marked (un)read or starred.
|
||||||
|
// It would be enough if only labels which are assigned to article
|
||||||
|
// are recounted, not all.
|
||||||
|
|
||||||
|
QSqlDatabase database = qApp->database()->driver()->threadSafeConnection(metaObject()->className());
|
||||||
|
int account_id = getParentServiceRoot()->accountId();
|
||||||
|
auto acc = DatabaseQueries::getMessageCountsForAllLabels(database, account_id);
|
||||||
|
/*
|
||||||
|
for (Label* lbl : probes()) {
|
||||||
|
if (!acc.contains(lbl->customId())) {
|
||||||
|
if (including_total_count) {
|
||||||
|
lbl->setCountOfAllMessages(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbl->setCountOfUnreadMessages(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto ac = acc.value(lbl->customId());
|
||||||
|
|
||||||
|
if (including_total_count) {
|
||||||
|
lbl->setCountOfAllMessages(ac.m_total);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbl->setCountOfUnreadMessages(ac.m_unread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Search* SearchsNode::probeById(const QString& custom_id) {
|
||||||
|
auto chi = childItems();
|
||||||
|
|
||||||
|
return qobject_cast<Search*>(boolinq::from(chi).firstOrDefault([custom_id](RootItem* it) {
|
||||||
|
return it->customId() == custom_id;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Search*> SearchsNode::probes() const {
|
||||||
|
auto list = boolinq::from(childItems())
|
||||||
|
.select([](RootItem* it) {
|
||||||
|
return static_cast<Search*>(it);
|
||||||
|
})
|
||||||
|
.toStdList();
|
||||||
|
|
||||||
|
return FROM_STD_LIST(QList<Search*>, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QAction*> SearchsNode::contextMenuFeedsList() {
|
||||||
|
if (m_actProbeNew == nullptr) {
|
||||||
|
m_actProbeNew = new QAction(qApp->icons()->fromTheme(QSL("system-search")), tr("New article probe"), this);
|
||||||
|
|
||||||
|
connect(m_actProbeNew, &QAction::triggered, this, &SearchsNode::createProbe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QList<QAction*>{m_actProbeNew};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchsNode::createProbe() {
|
||||||
|
FormAddEditProbe frm(qApp->mainFormWidget());
|
||||||
|
Search* new_prb = frm.execForAdd();
|
||||||
|
|
||||||
|
if (new_prb != nullptr) {
|
||||||
|
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// DatabaseQueries::createLabel(db, new_prb, getParentServiceRoot()->accountId());
|
||||||
|
|
||||||
|
getParentServiceRoot()->requestItemReassignment(new_prb, this);
|
||||||
|
}
|
||||||
|
catch (const ApplicationException&) {
|
||||||
|
new_prb->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/librssguard/services/abstract/searchsnode.h
Executable file
34
src/librssguard/services/abstract/searchsnode.h
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#ifndef SEARCHSNODE_H
|
||||||
|
#define SEARCHSNODE_H
|
||||||
|
|
||||||
|
#include "services/abstract/rootitem.h"
|
||||||
|
|
||||||
|
#include "services/abstract/search.h"
|
||||||
|
|
||||||
|
class SearchsNode : public RootItem {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SearchsNode(RootItem* parent_item = nullptr);
|
||||||
|
|
||||||
|
QList<Search*> probes() const;
|
||||||
|
void loadProbes(const QList<Search*>& probes);
|
||||||
|
|
||||||
|
virtual QList<Message> undeletedMessages() const;
|
||||||
|
virtual QList<QAction*> contextMenuFeedsList();
|
||||||
|
virtual int countOfUnreadMessages() const;
|
||||||
|
virtual int countOfAllMessages() const;
|
||||||
|
virtual void updateCounts(bool including_total_count);
|
||||||
|
|
||||||
|
Search* probeById(const QString& custom_id);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void createProbe();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAction* m_actProbeNew;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SEARCHSNODE_H
|
@ -17,17 +17,19 @@
|
|||||||
#include "services/abstract/importantnode.h"
|
#include "services/abstract/importantnode.h"
|
||||||
#include "services/abstract/labelsnode.h"
|
#include "services/abstract/labelsnode.h"
|
||||||
#include "services/abstract/recyclebin.h"
|
#include "services/abstract/recyclebin.h"
|
||||||
|
#include "services/abstract/search.h"
|
||||||
|
#include "services/abstract/searchsnode.h"
|
||||||
#include "services/abstract/unreadnode.h"
|
#include "services/abstract/unreadnode.h"
|
||||||
|
|
||||||
ServiceRoot::ServiceRoot(RootItem* parent)
|
ServiceRoot::ServiceRoot(RootItem* parent)
|
||||||
: RootItem(parent), m_recycleBin(new RecycleBin(this)), m_importantNode(new ImportantNode(this)),
|
: RootItem(parent), m_recycleBin(new RecycleBin(this)), m_importantNode(new ImportantNode(this)),
|
||||||
m_labelsNode(new LabelsNode(this)), m_unreadNode(new UnreadNode(this)), m_accountId(NO_PARENT_CATEGORY),
|
m_labelsNode(new LabelsNode(this)), m_probesNode(new SearchsNode(this)), m_unreadNode(new UnreadNode(this)),
|
||||||
m_networkProxy(QNetworkProxy()) {
|
m_accountId(NO_PARENT_CATEGORY), m_networkProxy(QNetworkProxy()) {
|
||||||
setKind(RootItem::Kind::ServiceRoot);
|
setKind(RootItem::Kind::ServiceRoot);
|
||||||
appendCommonNodes();
|
appendCommonNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceRoot::~ServiceRoot() = default;
|
ServiceRoot::~ServiceRoot() {}
|
||||||
|
|
||||||
bool ServiceRoot::deleteViaGui() {
|
bool ServiceRoot::deleteViaGui() {
|
||||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
@ -242,6 +244,10 @@ void ServiceRoot::appendCommonNodes() {
|
|||||||
if (labelsNode() != nullptr && !childItems().contains(labelsNode())) {
|
if (labelsNode() != nullptr && !childItems().contains(labelsNode())) {
|
||||||
appendChild(labelsNode());
|
appendChild(labelsNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (probesNode() != nullptr && !childItems().contains(probesNode())) {
|
||||||
|
appendChild(probesNode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServiceRoot::cleanFeeds(const QList<Feed*>& items, bool clean_read_only) {
|
bool ServiceRoot::cleanFeeds(const QList<Feed*>& items, bool clean_read_only) {
|
||||||
@ -455,6 +461,10 @@ LabelsNode* ServiceRoot::labelsNode() const {
|
|||||||
return m_labelsNode;
|
return m_labelsNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SearchsNode* ServiceRoot::probesNode() const {
|
||||||
|
return m_probesNode;
|
||||||
|
}
|
||||||
|
|
||||||
UnreadNode* ServiceRoot::unreadNode() const {
|
UnreadNode* ServiceRoot::unreadNode() const {
|
||||||
return m_unreadNode;
|
return m_unreadNode;
|
||||||
}
|
}
|
||||||
@ -715,6 +725,11 @@ bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) {
|
|||||||
"Messages.account_id = %1")
|
"Messages.account_id = %1")
|
||||||
.arg(QString::number(accountId())));
|
.arg(QString::number(accountId())));
|
||||||
}
|
}
|
||||||
|
else if (item->kind() == RootItem::Kind::Probe) {
|
||||||
|
model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1 AND "
|
||||||
|
"Messages.contents REGEXP '%2'")
|
||||||
|
.arg(QString::number(accountId()), item->toProbe()->filter()));
|
||||||
|
}
|
||||||
else if (item->kind() == RootItem::Kind::Label) {
|
else if (item->kind() == RootItem::Kind::Label) {
|
||||||
// Show messages with particular label.
|
// Show messages with particular label.
|
||||||
model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND "
|
model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND "
|
||||||
|
@ -19,6 +19,7 @@ class FeedsModel;
|
|||||||
class RecycleBin;
|
class RecycleBin;
|
||||||
class ImportantNode;
|
class ImportantNode;
|
||||||
class UnreadNode;
|
class UnreadNode;
|
||||||
|
class SearchsNode;
|
||||||
class LabelsNode;
|
class LabelsNode;
|
||||||
class Label;
|
class Label;
|
||||||
class MessagesModel;
|
class MessagesModel;
|
||||||
@ -52,6 +53,7 @@ class ServiceRoot : public RootItem {
|
|||||||
RecycleBin* recycleBin() const;
|
RecycleBin* recycleBin() const;
|
||||||
ImportantNode* importantNode() const;
|
ImportantNode* importantNode() const;
|
||||||
LabelsNode* labelsNode() const;
|
LabelsNode* labelsNode() const;
|
||||||
|
SearchsNode* probesNode() const;
|
||||||
UnreadNode* unreadNode() const;
|
UnreadNode* unreadNode() const;
|
||||||
|
|
||||||
virtual void updateCounts(bool including_total_count);
|
virtual void updateCounts(bool including_total_count);
|
||||||
@ -297,6 +299,7 @@ class ServiceRoot : public RootItem {
|
|||||||
RecycleBin* m_recycleBin;
|
RecycleBin* m_recycleBin;
|
||||||
ImportantNode* m_importantNode;
|
ImportantNode* m_importantNode;
|
||||||
LabelsNode* m_labelsNode;
|
LabelsNode* m_labelsNode;
|
||||||
|
SearchsNode* m_probesNode;
|
||||||
UnreadNode* m_unreadNode;
|
UnreadNode* m_unreadNode;
|
||||||
int m_accountId;
|
int m_accountId;
|
||||||
QList<QAction*> m_serviceMenu;
|
QList<QAction*> m_serviceMenu;
|
||||||
|
@ -44,7 +44,7 @@ void TtRssServiceRoot::start(bool freshly_activated) {
|
|||||||
DatabaseQueries::loadRootFromDatabase<Category, TtRssFeed>(this);
|
DatabaseQueries::loadRootFromDatabase<Category, TtRssFeed>(this);
|
||||||
loadCacheFromFile();
|
loadCacheFromFile();
|
||||||
|
|
||||||
auto lbls = m_labelsNode->labels();
|
auto lbls = labelsNode()->labels();
|
||||||
|
|
||||||
boolinq::from(lbls).for_each([](Label* lbl) {
|
boolinq::from(lbls).for_each([](Label* lbl) {
|
||||||
if (lbl->customNumericId() == TTRSS_PUBLISHED_LABEL_ID) {
|
if (lbl->customNumericId() == TTRSS_PUBLISHED_LABEL_ID) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user