Multi select feeds list - basic implementation (#1148)

* Starting to work on this.

* Working, saving of common data in feeds could work

* Working, saving of common data in feeds could work

* work on standard

* save work

* work on multi feed select, should work now for feeds

* kind of works, there are some corner cases and UX things that need to be sorted out, will merge so people can test
This commit is contained in:
martinrotter 2023-10-30 10:54:44 +01:00 committed by GitHub
parent 9ed369f34e
commit 623caa7631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1218 additions and 659 deletions

View File

@ -27,6 +27,7 @@
<file>./graphics/Breeze/actions/32/document-open.svg</file>
<file>./graphics/Breeze/actions/32/document-revert.svg</file>
<file>./graphics/Breeze/actions/22/download.svg</file>
<file>./graphics/Breeze/actions/22/draw-line.svg</file>
<file>./graphics/Breeze/actions/22/edit-clear.svg</file>
<file>./graphics/Breeze/actions/22/edit-copy.svg</file>
<file>./graphics/Breeze/actions/22/edit-cut.svg</file>
@ -120,6 +121,7 @@
<file>./graphics/Breeze Dark/actions/32/document-open.svg</file>
<file>./graphics/Breeze Dark/actions/32/document-revert.svg</file>
<file>./graphics/Breeze Dark/actions/22/download.svg</file>
<file>./graphics/Breeze Dark/actions/22/draw-line.svg</file>
<file>./graphics/Breeze Dark/actions/22/edit-clear.svg</file>
<file>./graphics/Breeze Dark/actions/22/edit-copy.svg</file>
<file>./graphics/Breeze Dark/actions/22/edit-cut.svg</file>

View File

@ -117,8 +117,6 @@ set(SOURCES
gui/reusable/colortoolbutton.h
gui/reusable/comboboxwithstatus.cpp
gui/reusable/comboboxwithstatus.h
gui/reusable/discoverfeedsbutton.cpp
gui/reusable/discoverfeedsbutton.h
gui/reusable/edittableview.cpp
gui/reusable/edittableview.h
gui/reusable/helpspoiler.cpp
@ -296,6 +294,8 @@ set(SOURCES
services/abstract/gui/formcategorydetails.h
services/abstract/gui/formfeeddetails.cpp
services/abstract/gui/formfeeddetails.h
services/abstract/gui/multifeededitcheckbox.cpp
services/abstract/gui/multifeededitcheckbox.h
services/abstract/importantnode.cpp
services/abstract/importantnode.h
services/abstract/label.cpp
@ -674,6 +674,7 @@ target_include_directories(rssguard
${CMAKE_CURRENT_SOURCE_DIR}/gui
${CMAKE_CURRENT_SOURCE_DIR}/gui/dialogs
${CMAKE_CURRENT_SOURCE_DIR}/gui/reusable
${CMAKE_CURRENT_SOURCE_DIR}/services/abstract/gui
${CMAKE_CURRENT_SOURCE_DIR}/dynamic-shortcuts
PRIVATE

View File

@ -231,6 +231,8 @@ QList<QAction*> FormMain::allActions() const {
actions << m_ui->m_actionExpandCollapseItem;
actions << m_ui->m_actionExpandCollapseItemRecursively;
actions << m_ui->m_actionMessageFilters;
actions << m_ui->m_actionEmptyAllRecycleBins;
actions << m_ui->m_actionRestoreAllRecycleBins;
actions << m_ui->m_actionTabNewWebBrowser;
actions << m_ui->m_actionTabsCloseCurrent;
actions << m_ui->m_actionTabsCloseAll;

View File

@ -2,6 +2,7 @@
#include "gui/feedsview.h"
#include "3rd-party/boolinq/boolinq.h"
#include "core/feedsmodel.h"
#include "core/feedsproxymodel.h"
#include "definitions/definitions.h"
@ -13,6 +14,7 @@
#include "miscellaneous/settings.h"
#include "miscellaneous/textfactory.h"
#include "services/abstract/feed.h"
#include "services/abstract/gui/formaccountdetails.h"
#include "services/abstract/rootitem.h"
#include "services/abstract/serviceroot.h"
@ -24,6 +26,8 @@
#include <QPointer>
#include <QTimer>
#include <algorithm>
FeedsView::FeedsView(QWidget* parent)
: BaseTreeView(parent), m_contextMenuService(nullptr), m_contextMenuBin(nullptr), m_contextMenuCategories(nullptr),
m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr),
@ -80,6 +84,7 @@ QList<Feed*> FeedsView::selectedFeeds() const {
RootItem* FeedsView::selectedItem() const {
const QModelIndexList selected_rows = selectionModel()->selectedRows();
const QModelIndex current_row = currentIndex();
if (selected_rows.isEmpty()) {
return nullptr;
@ -87,10 +92,39 @@ RootItem* FeedsView::selectedItem() const {
else {
RootItem* selected_item = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(selected_rows.at(0)));
return selected_item == m_sourceModel->rootItem() ? nullptr : selected_item;
if (selected_rows.size() == 1) {
return selected_item;
}
auto selected_items = boolinq::from(selected_rows)
.select([this](const QModelIndex& idx) {
return m_sourceModel->itemForIndex(m_proxyModel->mapToSource(idx));
})
.toStdList();
RootItem* current_item = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(current_row));
if (std::find(selected_items.begin(), selected_items.end(), current_item) != selected_items.end()) {
return current_item;
}
else {
return selected_items.front();
}
}
}
QList<RootItem*> FeedsView::selectedItems() const {
const QModelIndexList selected_rows = selectionModel()->selectedRows();
auto selected_items = boolinq::from(selected_rows)
.select([this](const QModelIndex& idx) {
return m_sourceModel->itemForIndex(m_proxyModel->mapToSource(idx));
})
.toStdList();
return FROM_STD_LIST(QList<RootItem*>, selected_items);
}
void FeedsView::copyUrlOfSelectedFeeds() const {
auto feeds = selectedFeeds();
QStringList urls;
@ -218,16 +252,84 @@ void FeedsView::editSelectedItem() {
return;
}
if (selectedItem()->canBeEdited()) {
selectedItem()->editViaGui();
auto selected_items = selectedItems();
if (selected_items.isEmpty()) {
qApp->feedUpdateLock()->unlock();
return;
}
else {
auto std_editable_items = boolinq::from(selected_items)
.where([](RootItem* it) {
return it->canBeEdited();
})
.toStdList();
if (std_editable_items.empty()) {
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot edit item"),
tr("Selected item cannot be edited, this is not (yet?) supported."),
{tr("Cannot edit items"),
tr("Selected items cannot be edited. This is not supported (yet)."),
QSystemTrayIcon::MessageIcon::Critical});
qApp->feedUpdateLock()->unlock();
return;
}
if (std_editable_items.front()->kind() == RootItem::Kind::ServiceRoot && std_editable_items.size() > 1) {
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot edit items"),
tr("%1 does not support batch editing of multiple accounts.").arg(QSL(APP_NAME)),
QSystemTrayIcon::MessageIcon::Critical});
qApp->feedUpdateLock()->unlock();
return;
}
// We also check if items are from single account, if not we end.
std::list<ServiceRoot*> distinct_accounts = boolinq::from(std_editable_items)
.select([](RootItem* it) {
return it->getParentServiceRoot();
})
.distinct()
.toStdList();
if (distinct_accounts.size() != 1) {
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot edit items"),
tr("%1 does not support batch editing of items from multiple accounts.").arg(QSL(APP_NAME)),
QSystemTrayIcon::MessageIcon::Critical});
qApp->feedUpdateLock()->unlock();
return;
}
std::list<RootItem::Kind> distinct_types = boolinq::from(std_editable_items)
.select([](RootItem* it) {
return it->kind();
})
.distinct()
.toStdList();
if (distinct_types.size() != 1) {
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot edit items"),
tr("%1 does not support batch editing of items of varying types.").arg(QSL(APP_NAME)),
QSystemTrayIcon::MessageIcon::Critical});
qApp->feedUpdateLock()->unlock();
return;
}
if (qsizetype(std_editable_items.size()) < selected_items.size()) {
// Some items are not editable.
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot edit some items"),
tr("Some of selected items cannot be edited. Proceeding to edit the rest."),
QSystemTrayIcon::MessageIcon::Warning});
}
distinct_accounts.front()->editItemsViaGui(FROM_STD_LIST(QList<RootItem*>, std_editable_items));
// Changes are done, unlock the update master lock.
qApp->feedUpdateLock()->unlock();

View File

@ -32,9 +32,11 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
// NOTE: This is recursive method which returns all descendants.
QList<Feed*> selectedFeeds() const;
// Returns pointers to selected feed/category if they are really
// selected.
// Returns selected item. If multiple items are selected, returns
// the one of them which is also "current". If none of them is
// "current", returns firs item of selected ones.
RootItem* selectedItem() const;
QList<RootItem*> selectedItems() const;
// Saves/loads expand states of all nodes (feeds/categories) of the list to/from settings.
void saveAllExpandStates();

View File

@ -1,81 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "gui/reusable/discoverfeedsbutton.h"
#include "core/feedsmodel.h"
#include "gui/dialogs/formmain.h"
#include "gui/feedmessageviewer.h"
#include "gui/feedsview.h"
#include "gui/tabwidget.h"
#include "miscellaneous/application.h"
#include "miscellaneous/feedreader.h"
#include "miscellaneous/iconfactory.h"
#include "services/abstract/serviceroot.h"
#include <QVariant>
DiscoverFeedsButton::DiscoverFeedsButton(QWidget* parent) : QToolButton(parent), m_addresses(QStringList()) {
setEnabled(false);
setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml")));
setPopupMode(QToolButton::ToolButtonPopupMode::InstantPopup);
}
DiscoverFeedsButton::~DiscoverFeedsButton() {}
void DiscoverFeedsButton::clearFeedAddresses() {
setFeedAddresses({});
}
void DiscoverFeedsButton::setFeedAddresses(const QStringList& addresses) {
setEnabled(!addresses.isEmpty());
setToolTip(addresses.isEmpty() ?
tr("This website does not contain any feeds") :
tr("Add one of %n feed(s)", 0, addresses.size()));
if (menu() == nullptr) {
// Initialize the menu.
setMenu(new QMenu(this));
connect(menu(), &QMenu::triggered, this, &DiscoverFeedsButton::linkTriggered);
connect(menu(), &QMenu::aboutToShow, this, &DiscoverFeedsButton::fillMenu);
}
menu()->hide();
m_addresses = addresses;
}
void DiscoverFeedsButton::linkTriggered(QAction* action) {
const QString url = action->property("url").toString();
ServiceRoot* root = static_cast<ServiceRoot*>(action->property("root").value<void*>());
if (root->supportsFeedAdding()) {
root->addNewFeed(qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->selectedItem(), url);
}
else {
qApp->showGuiMessage(Notification::Event::GeneralEvent, {
tr("Not supported by account"),
tr("Given account does not support adding feeds."),
QSystemTrayIcon::MessageIcon::Warning });
}
}
void DiscoverFeedsButton::fillMenu() {
menu()->clear();
auto srts = qApp->feedReader()->feedsModel()->serviceRoots();
for (const ServiceRoot* root : qAsConst(srts)) {
if (root->supportsFeedAdding()) {
QMenu* root_menu = menu()->addMenu(root->icon(), root->title());
for (const QString& url : qAsConst(m_addresses)) {
QAction* url_action = root_menu->addAction(root->icon(), url);
url_action->setProperty("url", url);
url_action->setProperty("root", QVariant::fromValue((void*) root));
}
}
}
if (menu()->isEmpty()) {
menu()->addAction(tr("Feeds were detected, but no suitable accounts are configured."))->setEnabled(false);
}
}

View File

@ -1,26 +0,0 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef DISCOVERFEEDSBUTTON_H
#define DISCOVERFEEDSBUTTON_H
#include <QToolButton>
class DiscoverFeedsButton : public QToolButton {
Q_OBJECT
public:
explicit DiscoverFeedsButton(QWidget* parent = nullptr);
virtual ~DiscoverFeedsButton();
void clearFeedAddresses();
void setFeedAddresses(const QStringList& addresses);
private slots:
void linkTriggered(QAction* action);
void fillMenu();
private:
QStringList m_addresses;
};
#endif // DISCOVERFEEDSBUTTON_H

View File

@ -80,7 +80,7 @@ void ToolBarEditor::loadEditor(const QList<QAction*>& activated_actions, const Q
if (action->isSeparator()) {
action_item->setData(Qt::ItemDataRole::UserRole, SEPARATOR_ACTION_NAME);
action_item->setIcon(qApp->icons()->fromTheme(QSL("insert-object")));
action_item->setIcon(qApp->icons()->fromTheme(QSL("draw-line"), QSL("insert-object")));
action_item->setText(tr("Separator"));
action_item->setToolTip(tr("Separator"));
}

View File

@ -5,7 +5,6 @@
#include "definitions/globals.h"
#include "gui/dialogs/formmain.h"
#include "gui/messagebox.h"
#include "gui/reusable/discoverfeedsbutton.h"
#include "gui/reusable/locationlineedit.h"
#include "gui/reusable/searchtextwidget.h"
#include "gui/tabwidget.h"
@ -27,7 +26,6 @@
WebBrowser::WebBrowser(WebViewer* viewer, QWidget* parent)
: TabContent(parent), m_layout(new QVBoxLayout(this)), m_toolBar(new QToolBar(tr("Navigation panel"), this)),
m_webView(viewer), m_searchWidget(new SearchTextWidget(this)), m_txtLocation(new LocationLineEdit(this)),
m_btnDiscoverFeeds(new DiscoverFeedsButton(this)),
m_actionOpenInSystemBrowser(new QAction(qApp->icons()->fromTheme(QSL("document-open")),
tr("Open this website in system web browser"),
this)),
@ -297,15 +295,9 @@ void WebBrowser::initializeLayout() {
m_actionReload->setIcon(qApp->icons()->fromTheme(QSL("reload"), QSL("view-refresh")));
m_actionStop->setIcon(qApp->icons()->fromTheme(QSL("process-stop")));
m_btnDiscoverFeedsAction = new QWidgetAction(this);
m_actionOpenInSystemBrowser->setEnabled(false);
m_actionReadabilePage->setEnabled(false);
// m_btnDiscoverFeedsAction->setDefaultWidget(new QWidget(this));
m_btnDiscoverFeedsAction->setDefaultWidget(m_btnDiscoverFeeds);
// Add needed actions into toolbar.
m_toolBar->addAction(m_actionBack);
m_toolBar->addAction(m_actionForward);
@ -314,7 +306,6 @@ void WebBrowser::initializeLayout() {
m_toolBar->addAction(m_actionOpenInSystemBrowser);
m_toolBar->addAction(m_actionReadabilePage);
m_toolBar->addAction(m_btnDiscoverFeedsAction);
m_txtLocationAction = m_toolBar->addWidget(m_txtLocation);
m_loadingProgress = new QProgressBar(this);
@ -336,7 +327,6 @@ void WebBrowser::initializeLayout() {
}
void WebBrowser::onLoadingStarted() {
m_btnDiscoverFeeds->clearFeedAddresses();
m_loadingProgress->show();
m_actionOpenInSystemBrowser->setEnabled(false);
m_actionReadabilePage->setEnabled(false);
@ -359,15 +349,6 @@ void WebBrowser::onLoadingFinished(bool success) {
m_actionOpenInSystemBrowser->setEnabled(false);
m_actionReadabilePage->setEnabled(false);
}
// TODO: nevolat toto u internich "rssguard" adres
// Let's check if there are any feeds defined on the web and eventually
// display "Add feeds" button.
m_btnDiscoverFeeds->setFeedAddresses(NetworkFactory::extractFeedLinksFromHtmlPage(m_webView->url(),
m_webView->html()));
}
else {
m_btnDiscoverFeeds->clearFeedAddresses();
}
m_loadingProgress->hide();

View File

@ -22,7 +22,6 @@ class QLabel;
class TabWidget;
class WebViewer;
class LocationLineEdit;
class DiscoverFeedsButton;
class SearchTextWidget;
class WebBrowser : public TabContent {
@ -91,8 +90,6 @@ class WebBrowser : public TabContent {
SearchTextWidget* m_searchWidget;
LocationLineEdit* m_txtLocation;
QAction* m_txtLocationAction;
DiscoverFeedsButton* m_btnDiscoverFeeds;
QWidgetAction* m_btnDiscoverFeedsAction;
QProgressBar* m_loadingProgress;
QAction* m_actionBack;
QAction* m_actionForward;

View File

@ -150,13 +150,6 @@ bool Feed::canBeEdited() const {
return true;
}
bool Feed::editViaGui() {
QScopedPointer<FormFeedDetails> form_pointer(new FormFeedDetails(getParentServiceRoot(), qApp->mainFormWidget()));
form_pointer->addEditFeed(this);
return false;
}
void Feed::setAutoUpdateInterval(int auto_update_interval) {
// If new initial auto-update interval is set, then
// we should reset time that remains to the next auto-update.

View File

@ -17,7 +17,11 @@ class Feed : public RootItem {
public:
// Specifies the auto-download strategy for the feed.
enum class AutoUpdateType { DontAutoUpdate = 0, DefaultAutoUpdate = 1, SpecificAutoUpdate = 2 };
enum class AutoUpdateType {
DontAutoUpdate = 0,
DefaultAutoUpdate = 1,
SpecificAutoUpdate = 2
};
// Specifies the actual "status" of the feed.
// For example if it has new messages, error
@ -47,7 +51,6 @@ class Feed : public RootItem {
virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data);
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual QVariant data(int column, int role) const;
void setCountOfAllMessages(int count_all_messages);

View File

@ -7,73 +7,92 @@
<x>0</x>
<y>0</y>
<width>350</width>
<height>196</height>
<height>153</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="m_gbAuthentication">
<property name="toolTip">
<string>Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported.</string>
</property>
<property name="title">
<string>Credentials</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="m_lblUsername">
<property name="text">
<string>Username</string>
</property>
<property name="buddy">
<cstring>m_txtUsername</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtUsername" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="m_lblPassword">
<property name="text">
<string>Password</string>
</property>
<property name="buddy">
<cstring>m_txtPassword</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtPassword" native="true"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Authentication type</string>
</property>
<property name="buddy">
<cstring>m_cbAuthType</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbAuthType"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Authentication type</string>
</property>
<property name="buddy">
<cstring>m_cbAuthType</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cbAuthType"/>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbAuthentication"/>
</item>
<item>
<widget class="QGroupBox" name="m_gbAuthentication">
<property name="toolTip">
<string>Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported.</string>
</property>
<property name="title">
<string>Credentials</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="m_lblUsername">
<property name="text">
<string>Username</string>
</property>
<property name="buddy">
<cstring>m_txtUsername</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtUsername" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="m_lblPassword">
<property name="text">
<string>Password</string>
</property>
<property name="buddy">
<cstring>m_txtPassword</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtPassword" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MultiFeedEditCheckBox</class>
<extends>QCheckBox</extends>
<header>multifeededitcheckbox.h</header>
</customwidget>
<customwidget>
<class>LineEditWithStatus</class>
<extends>QWidget</extends>

View File

@ -10,6 +10,7 @@
#include "gui/reusable/baselineedit.h"
#include "miscellaneous/iconfactory.h"
#include "services/abstract/category.h"
#include "services/abstract/gui/multifeededitcheckbox.h"
#include "services/abstract/rootitem.h"
#include <QAction>
@ -23,7 +24,7 @@
#include <QToolButton>
FormCategoryDetails::FormCategoryDetails(ServiceRoot* service_root, RootItem* parent_to_select, QWidget* parent)
: QDialog(parent), m_category(nullptr), m_serviceRoot(service_root), m_parentToSelect(parent_to_select) {
: QDialog(parent), m_serviceRoot(service_root), m_parentToSelect(parent_to_select) {
initialize();
createConnections();
@ -50,8 +51,28 @@ void FormCategoryDetails::createConnections() {
connect(m_actionUseDefaultIcon, &QAction::triggered, this, &FormCategoryDetails::onUseDefaultIcon);
}
bool FormCategoryDetails::isChangeAllowed(MultiFeedEditCheckBox* mcb) const {
return !m_isBatchEdit || mcb->isChecked();
}
void FormCategoryDetails::loadCategoryData() {
loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot, m_category);
Category* cat = category<Category>();
if (m_isBatchEdit) {
// We hook batch selectors.
m_ui->m_mcbParent->addActionWidget(m_ui->m_cmbParentCategory);
m_ui->m_mcbTitle->addActionWidget(m_ui->m_txtTitle);
m_ui->m_mcbDescription->addActionWidget(m_ui->m_txtDescription);
m_ui->m_mcbIcon->addActionWidget(m_ui->m_btnIcon);
}
else {
// We hide batch selectors.
for (auto* cb : findChildren<MultiFeedEditCheckBox*>()) {
cb->hide();
}
}
loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot, cat);
if (m_creatingNew) {
GuiUtilities::applyDialogProperties(*this, qApp->icons()->fromTheme(QSL("folder")), tr("Add new category"));
@ -76,42 +97,69 @@ void FormCategoryDetails::loadCategoryData() {
}
}
else {
GuiUtilities::applyDialogProperties(*this, m_category->fullIcon(), tr("Edit \"%1\"").arg(m_category->title()));
if (!m_isBatchEdit) {
GuiUtilities::applyDialogProperties(*this, cat->fullIcon(), tr("Edit \"%1\"").arg(cat->title()));
}
else {
GuiUtilities::applyDialogProperties(*this,
qApp->icons()->fromTheme(QSL("folder")),
tr("Edit %n categories", nullptr, m_categories.size()));
}
m_ui->m_cmbParentCategory
->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue(m_category->parent())));
m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue(cat->parent())));
}
m_ui->m_txtTitle->lineEdit()->setText(m_category->title());
m_ui->m_txtDescription->lineEdit()->setText(m_category->description());
m_ui->m_btnIcon->setIcon(m_category->icon());
m_ui->m_txtTitle->lineEdit()->setText(cat->title());
m_ui->m_txtDescription->lineEdit()->setText(cat->description());
m_ui->m_btnIcon->setIcon(cat->icon());
m_ui->m_txtTitle->lineEdit()->setFocus();
}
void FormCategoryDetails::apply() {
QList<Category*> cats = categories<Category>();
RootItem* parent = m_ui->m_cmbParentCategory->currentData().value<RootItem*>();
m_category->setTitle(m_ui->m_txtTitle->lineEdit()->text());
m_category->setDescription(m_ui->m_txtDescription->lineEdit()->text());
m_category->setIcon(m_ui->m_btnIcon->icon());
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
try {
DatabaseQueries::createOverwriteCategory(database, m_category, m_serviceRoot->accountId(), parent->id());
}
catch (const ApplicationException& ex) {
qFatal("Cannot save category: '%s'.", qPrintable(ex.message()));
}
m_serviceRoot->requestItemReassignment(m_category, parent);
m_serviceRoot->itemChanged({m_category});
if (m_creatingNew) {
m_serviceRoot->requestItemExpand({parent}, true);
for (Category* cat : cats) {
if (isChangeAllowed(m_ui->m_mcbTitle)) {
cat->setTitle(m_ui->m_txtTitle->lineEdit()->text());
}
if (isChangeAllowed(m_ui->m_mcbDescription)) {
cat->setDescription(m_ui->m_txtDescription->lineEdit()->text());
}
if (isChangeAllowed(m_ui->m_mcbIcon)) {
cat->setIcon(m_ui->m_btnIcon->icon());
}
int new_parent_id;
if (isChangeAllowed(m_ui->m_mcbParent)) {
new_parent_id = parent->id();
}
else {
new_parent_id = cat->parent()->id();
}
try {
DatabaseQueries::createOverwriteCategory(database, cat, m_serviceRoot->accountId(), new_parent_id);
}
catch (const ApplicationException& ex) {
qFatal("Cannot save category: '%s'.", qPrintable(ex.message()));
}
if (isChangeAllowed(m_ui->m_mcbParent)) {
m_serviceRoot->requestItemReassignment(cat, parent);
}
if (m_creatingNew) {
m_serviceRoot->requestItemExpand({parent}, true);
}
}
m_serviceRoot->itemChanged(categories<RootItem>());
accept();
}

View File

@ -7,6 +7,9 @@
#include <QDialog>
#include "3rd-party/boolinq/boolinq.h"
#include "definitions/definitions.h"
namespace Ui {
class FormCategoryDetails;
}
@ -17,21 +20,29 @@ class FeedsModel;
class RootItem;
class QMenu;
class QAction;
class MultiFeedEditCheckBox;
class FormCategoryDetails : public QDialog {
Q_OBJECT
Q_OBJECT
public:
explicit FormCategoryDetails(ServiceRoot* service_root, RootItem* parent_to_select = nullptr, QWidget* parent = nullptr);
explicit FormCategoryDetails(ServiceRoot* service_root,
RootItem* parent_to_select = nullptr,
QWidget* parent = nullptr);
virtual ~FormCategoryDetails();
template<class T>
T* addEditCategory(T* category_to_edit = nullptr);
template <class T>
QList<T*> addEditCategory(const QList<Category*>& cats_to_edit = {});
template<class T>
template <class T>
T* category() const;
// Returns all cats.
template <class T>
QList<T*> categories() const;
protected:
bool isChangeAllowed(MultiFeedEditCheckBox* mcb) const;
virtual void loadCategoryData();
protected slots:
@ -58,39 +69,52 @@ class FormCategoryDetails : public QDialog {
private:
QScopedPointer<Ui::FormCategoryDetails> m_ui;
Category* m_category;
QList<Category*> m_categories;
ServiceRoot* m_serviceRoot;
QMenu* m_iconMenu{};
QAction* m_actionLoadIconFromFile{};
QAction* m_actionUseDefaultIcon{};
RootItem* m_parentToSelect;
bool m_creatingNew;
bool m_isBatchEdit;
};
template<class T>
inline T* FormCategoryDetails::addEditCategory(T* category_to_edit) {
m_creatingNew = category_to_edit == nullptr;
template <class T>
inline QList<T*> FormCategoryDetails::addEditCategory(const QList<Category*>& cats_to_edit) {
m_creatingNew = cats_to_edit.isEmpty();
m_isBatchEdit = cats_to_edit.size() > 1;
if (m_creatingNew) {
m_category = new T();
m_categories.append(new T());
}
else {
m_category = category_to_edit;
m_categories.append(cats_to_edit);
}
loadCategoryData();
if (exec() == QDialog::DialogCode::Accepted) {
return category<T>();
return categories<T>();
}
else {
return nullptr;
return {};
}
}
template<class T>
template <class T>
inline T* FormCategoryDetails::category() const {
return qobject_cast<T*>(m_category);
return qobject_cast<T*>(m_categories.first());
}
template <class T>
inline QList<T*> FormCategoryDetails::categories() const {
std::list<T*> std_cats = boolinq::from(m_categories)
.select([](Category* fd) {
return qobject_cast<T*>(fd);
})
.toStdList();
return FROM_STD_LIST(QList<T*>, std_cats);
}
#endif // FORMCATEGORYDETAILS_H

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>397</width>
<height>209</height>
<width>455</width>
<height>262</height>
</rect>
</property>
<property name="minimumSize">
@ -19,13 +19,13 @@
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbParent"/>
</item>
<item>
<widget class="QLabel" name="m_lblParentCategory">
<property name="text">
<string>Parent folder</string>
@ -35,20 +35,27 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbParentCategory">
<property name="toolTip">
<string>Select parent item for your category.</string>
</property>
<property name="iconSize">
<size>
<width>13</width>
<height>12</height>
</size>
</property>
</widget>
</layout>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbParentCategory">
<property name="toolTip">
<string>Select parent item for your category.</string>
</property>
<property name="iconSize">
<size>
<width>13</width>
<height>12</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbTitle"/>
</item>
<item row="1" column="0">
<item>
<widget class="QLabel" name="m_lblTitle">
<property name="text">
<string>Title</string>
@ -58,7 +65,17 @@
</property>
</widget>
</item>
<item row="2" column="0">
</layout>
</item>
<item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtTitle" native="true"/>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbDescription"/>
</item>
<item>
<widget class="QLabel" name="m_lblDescription">
<property name="text">
<string>Description</string>
@ -68,7 +85,17 @@
</property>
</widget>
</item>
<item row="3" column="0">
</layout>
</item>
<item row="2" column="1">
<widget class="LineEditWithStatus" name="m_txtDescription" native="true"/>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbIcon"/>
</item>
<item>
<widget class="QLabel" name="m_lblIcon">
<property name="text">
<string>Icon</string>
@ -78,52 +105,46 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="m_btnIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Select icon for your category.</string>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtTitle" native="true"/>
</item>
<item row="2" column="1">
<widget class="LineEditWithStatus" name="m_txtDescription" native="true"/>
</item>
</layout>
</item>
<item>
<item row="3" column="1">
<widget class="QToolButton" name="m_btnIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Select icon for your category.</string>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QDialogButtonBox" name="m_buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -133,9 +154,27 @@
</property>
</widget>
</item>
<item row="4" 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>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MultiFeedEditCheckBox</class>
<extends>QCheckBox</extends>
<header>multifeededitcheckbox.h</header>
</customwidget>
<customwidget>
<class>LineEditWithStatus</class>
<extends>QWidget</extends>
@ -157,8 +196,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>170</y>
<x>340</x>
<y>353</y>
</hint>
<hint type="destinationlabel">
<x>286</x>

View File

@ -8,6 +8,7 @@
#include "gui/guiutilities.h"
#include "miscellaneous/iconfactory.h"
#include "miscellaneous/textfactory.h"
#include "services/abstract/gui/multifeededitcheckbox.h"
#include "services/abstract/rootitem.h"
#include <QMenu>
@ -17,92 +18,150 @@
#include <QTextCodec>
FormFeedDetails::FormFeedDetails(ServiceRoot* service_root, QWidget* parent)
: QDialog(parent), m_feed(nullptr), m_serviceRoot(service_root) {
: QDialog(parent), m_serviceRoot(service_root) {
initialize();
createConnections();
}
void FormFeedDetails::activateTab(int index) {
m_ui->m_tabWidget->setCurrentIndex(index);
m_ui.m_tabWidget->setCurrentIndex(index);
}
void FormFeedDetails::clearTabs() {
m_ui->m_tabWidget->clear();
m_ui.m_tabWidget->clear();
}
void FormFeedDetails::insertCustomTab(QWidget* custom_tab, const QString& title, int index) {
m_ui->m_tabWidget->insertTab(index, custom_tab, title);
m_ui.m_tabWidget->insertTab(index, custom_tab, title);
}
void FormFeedDetails::apply() {
// Setup common data for the feed.
m_feed->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType
->itemData(m_ui->m_cmbAutoUpdateType->currentIndex())
QList<Feed*> fds = feeds<Feed>();
for (Feed* fd : fds) {
// Setup common data for the feed.
if (isChangeAllowed(m_ui.m_mcbAutoDownloading)) {
fd->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(m_ui.m_cmbAutoUpdateType
->itemData(m_ui.m_cmbAutoUpdateType->currentIndex())
.toInt()));
m_feed->setAutoUpdateInterval(int(m_ui->m_spinAutoUpdateInterval->value()));
m_feed->setOpenArticlesDirectly(m_ui->m_cbOpenArticlesAutomatically->isChecked());
m_feed->setIsRtl(m_ui->m_cbFeedRTL->isChecked());
m_feed->setAddAnyDatetimeArticles(m_ui->m_cbAddAnyDateArticles->isChecked());
m_feed->setDatetimeToAvoid(m_ui->m_gbAvoidOldArticles->isChecked() ? m_ui->m_dtDateTimeToAvoid->dateTime()
: TextFactory::parseDateTime(0));
m_feed->setIsSwitchedOff(m_ui->m_cbDisableFeed->isChecked());
m_feed->setIsQuiet(m_ui->m_cbSuppressFeed->isChecked());
fd->setAutoUpdateInterval(int(m_ui.m_spinAutoUpdateInterval->value()));
}
if (isChangeAllowed(m_ui.m_mcbOpenArticlesAutomatically)) {
fd->setOpenArticlesDirectly(m_ui.m_cbOpenArticlesAutomatically->isChecked());
}
if (isChangeAllowed(m_ui.m_mcbFeedRtl)) {
fd->setIsRtl(m_ui.m_cbFeedRTL->isChecked());
}
if (isChangeAllowed(m_ui.m_mcbAddAnyDateArticles)) {
fd->setAddAnyDatetimeArticles(m_ui.m_cbAddAnyDateArticles->isChecked());
}
if (isChangeAllowed(m_ui.m_mcbAvoidOldArticles)) {
fd->setDatetimeToAvoid(m_ui.m_gbAvoidOldArticles->isChecked() ? m_ui.m_dtDateTimeToAvoid->dateTime()
: TextFactory::parseDateTime(0));
}
if (isChangeAllowed(m_ui.m_mcbDisableFeed)) {
fd->setIsSwitchedOff(m_ui.m_cbDisableFeed->isChecked());
}
if (isChangeAllowed(m_ui.m_mcbSuppressFeed)) {
fd->setIsQuiet(m_ui.m_cbSuppressFeed->isChecked());
}
if (!m_creatingNew) {
// We need to make sure that common data are saved.
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::createOverwriteFeed(database, fd, m_serviceRoot->accountId(), fd->parent()->id());
}
}
if (!m_creatingNew) {
// We need to make sure that common data are saved.
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::createOverwriteFeed(database, m_feed, m_serviceRoot->accountId(), m_feed->parent()->id());
m_serviceRoot->itemChanged(feeds<RootItem>());
}
}
bool FormFeedDetails::isChangeAllowed(MultiFeedEditCheckBox* mcb) const {
return !m_isBatchEdit || mcb->isChecked();
}
void FormFeedDetails::onAutoUpdateTypeChanged(int new_index) {
Feed::AutoUpdateType auto_update_type =
static_cast<Feed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt());
static_cast<Feed::AutoUpdateType>(m_ui.m_cmbAutoUpdateType->itemData(new_index).toInt());
switch (auto_update_type) {
case Feed::AutoUpdateType::DontAutoUpdate:
case Feed::AutoUpdateType::DefaultAutoUpdate:
m_ui->m_spinAutoUpdateInterval->setEnabled(false);
m_ui.m_spinAutoUpdateInterval->setEnabled(false);
break;
default:
m_ui->m_spinAutoUpdateInterval->setEnabled(true);
m_ui.m_spinAutoUpdateInterval->setEnabled(true);
}
}
void FormFeedDetails::createConnections() {
connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormFeedDetails::acceptIfPossible);
connect(m_ui->m_cmbAutoUpdateType,
connect(m_ui.m_buttonBox, &QDialogButtonBox::accepted, this, &FormFeedDetails::acceptIfPossible);
connect(m_ui.m_cmbAutoUpdateType,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this,
&FormFeedDetails::onAutoUpdateTypeChanged);
connect(m_ui->m_cbAddAnyDateArticles, &QCheckBox::toggled, this, [this](bool checked) {
m_ui->m_gbAvoidOldArticles->setEnabled(!checked);
connect(m_ui.m_cbAddAnyDateArticles, &QCheckBox::toggled, this, [this](bool checked) {
m_ui.m_gbAvoidOldArticles->setEnabled(!checked);
});
}
void FormFeedDetails::loadFeedData() {
Feed* fd = feed<Feed>();
if (m_isBatchEdit) {
// We hook batch selectors.
m_ui.m_mcbAutoDownloading->addActionWidget(m_ui.m_wdgAutoUpdate);
m_ui.m_mcbAddAnyDateArticles->addActionWidget(m_ui.m_cbAddAnyDateArticles);
m_ui.m_mcbOpenArticlesAutomatically->addActionWidget(m_ui.m_cbOpenArticlesAutomatically);
m_ui.m_mcbAvoidOldArticles->addActionWidget(m_ui.m_gbAvoidOldArticles);
m_ui.m_mcbDisableFeed->addActionWidget(m_ui.m_cbDisableFeed);
m_ui.m_mcbSuppressFeed->addActionWidget(m_ui.m_cbSuppressFeed);
m_ui.m_mcbFeedRtl->addActionWidget(m_ui.m_cbFeedRTL);
}
else {
// We hide batch selectors.
for (auto* cb : findChildren<MultiFeedEditCheckBox*>()) {
cb->hide();
}
}
if (m_creatingNew) {
GuiUtilities::applyDialogProperties(*this,
qApp->icons()->fromTheme(QSL("application-rss+xml")),
tr("Add new feed"));
}
else {
GuiUtilities::applyDialogProperties(*this, m_feed->fullIcon(), tr("Edit \"%1\"").arg(m_feed->title()));
if (!m_isBatchEdit) {
GuiUtilities::applyDialogProperties(*this, fd->fullIcon(), tr("Edit \"%1\"").arg(fd->title()));
}
else {
GuiUtilities::applyDialogProperties(*this,
qApp->icons()->fromTheme(QSL("application-rss+xml")),
tr("Edit %n feeds", nullptr, m_feeds.size()));
}
}
m_ui->m_cmbAutoUpdateType
->setCurrentIndex(m_ui->m_cmbAutoUpdateType->findData(QVariant::fromValue(int(m_feed->autoUpdateType()))));
m_ui->m_spinAutoUpdateInterval->setValue(m_feed->autoUpdateInterval());
m_ui->m_cbOpenArticlesAutomatically->setChecked(m_feed->openArticlesDirectly());
m_ui->m_cbFeedRTL->setChecked(m_feed->isRtl());
m_ui->m_cbAddAnyDateArticles->setChecked(m_feed->addAnyDatetimeArticles());
m_ui->m_gbAvoidOldArticles->setChecked(m_feed->datetimeToAvoid().toMSecsSinceEpoch() > 0);
m_ui->m_dtDateTimeToAvoid->setDateTime(m_feed->datetimeToAvoid());
m_ui->m_cbDisableFeed->setChecked(m_feed->isSwitchedOff());
m_ui->m_cbSuppressFeed->setChecked(m_feed->isQuiet());
m_ui.m_cmbAutoUpdateType
->setCurrentIndex(m_ui.m_cmbAutoUpdateType->findData(QVariant::fromValue(int(fd->autoUpdateType()))));
m_ui.m_spinAutoUpdateInterval->setValue(fd->autoUpdateInterval());
m_ui.m_cbOpenArticlesAutomatically->setChecked(fd->openArticlesDirectly());
m_ui.m_cbFeedRTL->setChecked(fd->isRtl());
m_ui.m_cbAddAnyDateArticles->setChecked(fd->addAnyDatetimeArticles());
m_ui.m_gbAvoidOldArticles->setChecked(fd->datetimeToAvoid().toMSecsSinceEpoch() > 0);
m_ui.m_dtDateTimeToAvoid->setDateTime(fd->datetimeToAvoid());
m_ui.m_cbDisableFeed->setChecked(fd->isSwitchedOff());
m_ui.m_cbSuppressFeed->setChecked(fd->isQuiet());
}
void FormFeedDetails::acceptIfPossible() {
@ -122,19 +181,18 @@ void FormFeedDetails::acceptIfPossible() {
}
void FormFeedDetails::initialize() {
m_ui.reset(new Ui::FormFeedDetails());
m_ui->setupUi(this);
m_ui.setupUi(this);
m_ui->m_dtDateTimeToAvoid
m_ui.m_dtDateTimeToAvoid
->setDisplayFormat(qApp->localization()->loadedLocale().dateTimeFormat(QLocale::FormatType::ShortFormat));
// Setup auto-update options.
m_ui->m_spinAutoUpdateInterval->setMode(TimeSpinBox::Mode::MinutesSeconds);
m_ui->m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL);
m_ui->m_cmbAutoUpdateType->addItem(tr("Fetch articles using global interval"),
QVariant::fromValue(int(Feed::AutoUpdateType::DefaultAutoUpdate)));
m_ui->m_cmbAutoUpdateType->addItem(tr("Fetch articles every"),
QVariant::fromValue(int(Feed::AutoUpdateType::SpecificAutoUpdate)));
m_ui->m_cmbAutoUpdateType->addItem(tr("Disable auto-fetching of articles"),
QVariant::fromValue(int(Feed::AutoUpdateType::DontAutoUpdate)));
m_ui.m_spinAutoUpdateInterval->setMode(TimeSpinBox::Mode::MinutesSeconds);
m_ui.m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL);
m_ui.m_cmbAutoUpdateType->addItem(tr("Fetch articles using global interval"),
QVariant::fromValue(int(Feed::AutoUpdateType::DefaultAutoUpdate)));
m_ui.m_cmbAutoUpdateType->addItem(tr("Fetch articles every"),
QVariant::fromValue(int(Feed::AutoUpdateType::SpecificAutoUpdate)));
m_ui.m_cmbAutoUpdateType->addItem(tr("Disable auto-fetching of articles"),
QVariant::fromValue(int(Feed::AutoUpdateType::DontAutoUpdate)));
}

View File

@ -7,6 +7,9 @@
#include "ui_formfeeddetails.h"
#include "3rd-party/boolinq/boolinq.h"
#include "definitions/definitions.h"
namespace Ui {
class FormFeedDetails;
}
@ -17,18 +20,23 @@ class Category;
class RootItem;
class FormFeedDetails : public QDialog {
Q_OBJECT
Q_OBJECT
public:
explicit FormFeedDetails(ServiceRoot* service_root, QWidget* parent = nullptr);
virtual ~FormFeedDetails() = default;
template<class T>
T* addEditFeed(T* feed_to_edit = nullptr);
template <class T>
QList<T*> addEditFeed(const QList<Feed*>& feeds_to_edit = {});
template<class T>
// Returns first feed.
template <class T>
T* feed() const;
// Returns all feeds.
template <class T>
QList<T*> feeds() const;
protected slots:
void activateTab(int index);
void clearTabs();
@ -39,6 +47,7 @@ class FormFeedDetails : public QDialog {
virtual void apply();
protected:
bool isChangeAllowed(MultiFeedEditCheckBox* mcb) const;
void insertCustomTab(QWidget* custom_tab, const QString& title, int index);
// Sets the feed which will be edited.
@ -55,37 +64,49 @@ class FormFeedDetails : public QDialog {
void initialize();
protected:
QScopedPointer<Ui::FormFeedDetails> m_ui;
Feed* m_feed;
Ui::FormFeedDetails m_ui;
QList<Feed*> m_feeds;
ServiceRoot* m_serviceRoot;
bool m_creatingNew;
bool m_isBatchEdit;
};
template<class T>
inline T* FormFeedDetails::addEditFeed(T* feed_to_edit) {
m_creatingNew = feed_to_edit == nullptr;
template <class T>
inline QList<T*> FormFeedDetails::addEditFeed(const QList<Feed*>& feeds_to_edit) {
m_creatingNew = feeds_to_edit.isEmpty();
m_isBatchEdit = feeds_to_edit.size() > 1;
if (m_creatingNew) {
m_feed = new T();
m_feeds.append(new T());
}
else {
m_feed = feed_to_edit;
m_feeds.append(feeds_to_edit);
}
// Load custom logic for feed data loading.
loadFeedData();
if (exec() == QDialog::DialogCode::Accepted) {
return feed<T>();
return feeds<T>();
}
else {
return nullptr;
return {};
}
}
template<class T>
template <class T>
inline T* FormFeedDetails::feed() const {
return qobject_cast<T*>(m_feed);
return qobject_cast<T*>(m_feeds.first());
}
template <class T>
inline QList<T*> FormFeedDetails::feeds() const {
std::list<T*> std_fds = boolinq::from(m_feeds)
.select([](Feed* fd) {
return qobject_cast<T*>(fd);
})
.toStdList();
return FROM_STD_LIST(QList<T*>, std_fds);
}
#endif // FORMFEEDDETAILS_H

View File

@ -24,92 +24,140 @@
<string>Articles</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Auto-downloading of articles</string>
</property>
<property name="buddy">
<cstring>m_cmbAutoUpdateType</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="m_cmbAutoUpdateType">
<property name="toolTip">
<string>Select the auto-download strategy for messages of this feed. Default auto-download strategy means that new messges of this feed will be downloaded in time intervals set in application settings.</string>
</property>
</widget>
<widget class="MultiFeedEditCheckBox" name="m_mcbAutoDownloading"/>
</item>
<item>
<widget class="TimeSpinBox" name="m_spinAutoUpdateInterval">
<property name="enabled">
<bool>false</bool>
</property>
<widget class="QWidget" name="m_wdgAutoUpdate" native="true">
<layout class="QFormLayout" name="formLayout_3">
<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 row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Auto-downloading of articles</string>
</property>
<property name="buddy">
<cstring>m_cmbAutoUpdateType</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbAutoUpdateType">
<property name="toolTip">
<string>Select the auto-download strategy for messages of this feed. Default auto-download strategy means that new messges of this feed will be downloaded in time intervals set in application settings.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="TimeSpinBox" name="m_spinAutoUpdateInterval">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbOpenArticlesAutomatically">
<property name="text">
<string>Open articles via their URL automatically</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbOpenArticlesAutomatically"/>
</item>
<item>
<widget class="QCheckBox" name="m_cbOpenArticlesAutomatically">
<property name="text">
<string>Open articles via their URL automatically</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbAddAnyDateArticles">
<property name="text">
<string>Add articles with any date into the database</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbAddAnyDateArticles"/>
</item>
<item>
<widget class="QCheckBox" name="m_cbAddAnyDateArticles">
<property name="text">
<string>Add articles with any date into the database</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="m_gbAvoidOldArticles">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Avoid adding articles before this date into the database</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QDateTimeEdit" name="m_dtDateTimeToAvoid">
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<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>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbAvoidOldArticles"/>
</item>
<item>
<widget class="QGroupBox" name="m_gbAvoidOldArticles">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Avoid adding articles before this date into the database</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QDateTimeEdit" name="m_dtDateTimeToAvoid">
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<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>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -118,26 +166,47 @@
<string>Miscellaneous</string>
</attribute>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="m_cbSuppressFeed">
<property name="text">
<string>Ignore notifications for this feed</string>
</property>
</widget>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbSuppressFeed"/>
</item>
<item>
<widget class="QCheckBox" name="m_cbSuppressFeed">
<property name="text">
<string>Ignore notifications for this feed</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="m_cbDisableFeed">
<property name="text">
<string>Disable this feed</string>
</property>
</widget>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbDisableFeed"/>
</item>
<item>
<widget class="QCheckBox" name="m_cbDisableFeed">
<property name="text">
<string>Disable this feed</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="m_cbFeedRTL">
<property name="text">
<string>Right-to-left layout</string>
</property>
</widget>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbFeedRtl"/>
</item>
<item>
<widget class="QCheckBox" name="m_cbFeedRTL">
<property name="text">
<string>Right-to-left layout</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -164,11 +233,13 @@
<extends>QDoubleSpinBox</extends>
<header>timespinbox.h</header>
</customwidget>
<customwidget>
<class>MultiFeedEditCheckBox</class>
<extends>QCheckBox</extends>
<header>multifeededitcheckbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>m_tabWidget</tabstop>
<tabstop>m_cmbAutoUpdateType</tabstop>
<tabstop>m_spinAutoUpdateInterval</tabstop>
<tabstop>m_cbOpenArticlesAutomatically</tabstop>
<tabstop>m_cbAddAnyDateArticles</tabstop>
<tabstop>m_gbAvoidOldArticles</tabstop>

View File

@ -0,0 +1,22 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/abstract/gui/multifeededitcheckbox.h"
MultiFeedEditCheckBox::MultiFeedEditCheckBox(QWidget* parent) : QCheckBox(parent) {
setToolTip(tr("Apply this to all edited feeds."));
setText(QString(4, ' '));
setSizePolicy(QSizePolicy::Policy::Maximum, QSizePolicy::Policy::Maximum);
}
QList<QWidget*> MultiFeedEditCheckBox::actionWidgets() const {
return m_actionWidgets;
}
void MultiFeedEditCheckBox::addActionWidget(QWidget* widget) {
if (widget != nullptr) {
m_actionWidgets.append(widget);
connect(this, &MultiFeedEditCheckBox::toggled, widget, &QWidget::setEnabled);
emit toggled(isChecked());
}
}

View File

@ -0,0 +1,21 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef MULTIFEEDEDITCHECKBOX_H
#define MULTIFEEDEDITCHECKBOX_H
#include <QCheckBox>
class MultiFeedEditCheckBox : public QCheckBox {
Q_OBJECT
public:
explicit MultiFeedEditCheckBox(QWidget* parent = nullptr);
QList<QWidget*> actionWidgets() const;
void addActionWidget(QWidget* widget);
private:
QList<QWidget*> m_actionWidgets;
};
#endif // MULTIFEEDEDITCHECKBOX_H

View File

@ -43,19 +43,6 @@ bool Label::canBeEdited() const {
return Globals::hasFlag(getParentServiceRoot()->supportedLabelOperations(), ServiceRoot::LabelOperation::Editing);
}
bool Label::editViaGui() {
FormAddEditLabel form(qApp->mainFormWidget());
if (form.execForEdit(this)) {
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
return DatabaseQueries::updateLabel(db, this);
}
else {
return false;
}
}
bool Label::canBeDeleted() const {
return Globals::hasFlag(getParentServiceRoot()->supportedLabelOperations(), ServiceRoot::LabelOperation::Deleting);
}

View File

@ -28,7 +28,6 @@ class RSSGUARD_DLLSPEC Label : public RootItem {
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);

View File

@ -59,10 +59,6 @@ bool RootItem::canBeEdited() const {
return false;
}
bool RootItem::editViaGui() {
return false;
}
bool RootItem::canBeDeleted() const {
return false;
}

View File

@ -28,11 +28,19 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
Q_PROPERTY(QString customId READ customId)
public:
enum class ReadStatus { Unread = 0, Read = 1, Unknown = 256 };
enum class ReadStatus {
Unread = 0,
Read = 1,
Unknown = 256
};
// Holds statuses for messages
// to be switched importance (starred).
enum class Importance { NotImportant = 0, Important = 1, Unknown = 256 };
enum class Importance {
NotImportant = 0,
Important = 1,
Unknown = 256
};
// Describes the kind of the item.
enum class Kind {
@ -66,10 +74,6 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
// Can properties of this item be edited?
virtual bool canBeEdited() const;
// Performs editing of properties of this item (probably via dialog)
// and returns result status.
virtual bool editViaGui();
// Can the item be deleted?
virtual bool canBeDeleted() const;

View File

@ -44,28 +44,6 @@ 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());
try {
DatabaseQueries::updateProbe(db, this);
updateCounts(true);
getParentServiceRoot()->itemChanged({this});
return true;
}
catch (const ApplicationException& ex) {
qCriticalNN << LOGSEC_CORE << "Failed to edit probe:" << QUOTE_W_SPACE_DOT(ex.message());
return false;
}
}
else {
return true;
}
}
bool Search::canBeDeleted() const {
return true;
}

View File

@ -32,7 +32,6 @@ class RSSGUARD_DLLSPEC Search : public RootItem {
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);

View File

@ -14,6 +14,11 @@
#include "services/abstract/category.h"
#include "services/abstract/feed.h"
#include "services/abstract/gui/custommessagepreviewer.h"
#include "services/abstract/gui/formaccountdetails.h"
#include "services/abstract/gui/formaddeditlabel.h"
#include "services/abstract/gui/formaddeditprobe.h"
#include "services/abstract/gui/formcategorydetails.h"
#include "services/abstract/gui/formfeeddetails.h"
#include "services/abstract/importantnode.h"
#include "services/abstract/labelsnode.h"
#include "services/abstract/recyclebin.h"
@ -44,6 +49,95 @@ bool ServiceRoot::deleteViaGui() {
}
}
void ServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
// Feed editing.
auto std_feeds = boolinq::from(items)
.select([](RootItem* it) {
return qobject_cast<Feed*>(it);
})
.where([](Feed* fd) {
return fd != nullptr;
})
.toStdList();
if (!std_feeds.empty()) {
QScopedPointer<FormFeedDetails> form_pointer(new FormFeedDetails(this, qApp->mainFormWidget()));
form_pointer->addEditFeed<Feed>(FROM_STD_LIST(QList<Feed*>, std_feeds));
return;
}
// Category editing.
auto std_categories = boolinq::from(items)
.select([](RootItem* it) {
return qobject_cast<Category*>(it);
})
.where([](Category* fd) {
return fd != nullptr;
})
.toStdList();
if (!std_categories.empty()) {
QScopedPointer<FormCategoryDetails> form_pointer(new FormCategoryDetails(this, nullptr, qApp->mainFormWidget()));
form_pointer->addEditCategory<Category>(FROM_STD_LIST(QList<Category*>, std_categories));
return;
}
// Label editing.
auto std_labels = boolinq::from(items)
.select([](RootItem* it) {
return qobject_cast<Label*>(it);
})
.where([](Label* fd) {
return fd != nullptr;
})
.toStdList();
if (std_labels.size() == 1) {
// Support editing labels one by one.
FormAddEditLabel form(qApp->mainFormWidget());
Label* lbl = std_labels.front();
if (form.execForEdit(lbl)) {
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::updateLabel(db, lbl);
}
return;
}
// Probe editing.
auto std_probes = boolinq::from(items)
.select([](RootItem* it) {
return qobject_cast<Search*>(it);
})
.where([](Search* fd) {
return fd != nullptr;
})
.toStdList();
if (std_probes.size() == 1) {
// Support editing probes one by one.
FormAddEditProbe form(qApp->mainFormWidget());
Search* probe = std_probes.front();
if (form.execForEdit(probe)) {
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::updateProbe(db, probe);
updateCounts(probe);
itemChanged({probe});
}
return;
}
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Unsupported"), tr("This is not suppported (yet)."), QSystemTrayIcon::MessageIcon::Warning});
}
bool ServiceRoot::markAsReadUnread(RootItem::ReadStatus status) {
auto* cache = dynamic_cast<CacheForServiceRoot*>(this);
@ -356,6 +450,12 @@ void ServiceRoot::requestItemReassignment(RootItem* item, RootItem* new_parent)
emit itemReassignmentRequested(item, new_parent);
}
void ServiceRoot::requestItemsReassignment(const QList<RootItem*>& items, RootItem* new_parent) {
for (RootItem* it : items) {
requestItemReassignment(it, new_parent);
}
}
void ServiceRoot::requestItemRemoval(RootItem* item) {
emit itemRemovalRequested(item);
}
@ -479,6 +579,10 @@ UnreadNode* ServiceRoot::unreadNode() const {
return m_unreadNode;
}
FormAccountDetails* ServiceRoot::accountSetupDialog() const {
return nullptr;
}
void ServiceRoot::onDatabaseCleanup() {}
void ServiceRoot::syncIn() {

View File

@ -24,6 +24,7 @@ class Label;
class MessagesModel;
class CustomMessagePreviewer;
class CacheForServiceRoot;
class FormAccountDetails;
// THIS IS the root node of the service.
// NOTE: The root usually contains some core functionality of the
@ -59,10 +60,12 @@ class ServiceRoot : public RootItem {
SearchsNode* probesNode() const;
UnreadNode* unreadNode() const;
virtual FormAccountDetails* accountSetupDialog() const;
virtual void onDatabaseCleanup();
virtual void updateCounts(bool including_total_count);
virtual bool canBeDeleted() const;
virtual bool deleteViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual bool markAsReadUnread(ReadStatus status);
virtual QList<Message> undeletedMessages() const;
virtual bool supportsFeedAdding() const;
@ -222,6 +225,7 @@ class ServiceRoot : public RootItem {
void requestItemExpand(const QList<RootItem*>& items, bool expand);
void requestItemExpandStateSave(RootItem* subtree_root);
void requestItemReassignment(RootItem* item, RootItem* new_parent);
void requestItemsReassignment(const QList<RootItem*>& items, RootItem* new_parent);
void requestItemRemoval(RootItem* item);
// Some message/feed attribute selectors.

View File

@ -32,11 +32,19 @@ bool FeedlyServiceRoot::canBeEdited() const {
return true;
}
bool FeedlyServiceRoot::editViaGui() {
FormEditFeedlyAccount form_pointer(qApp->mainFormWidget());
FormAccountDetails* FeedlyServiceRoot::accountSetupDialog() const {
return new FormEditFeedlyAccount(qApp->mainFormWidget());
}
form_pointer.addEditAccount(this);
return true;
void FeedlyServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditFeedlyAccount> p(qobject_cast<FormEditFeedlyAccount*>(accountSetupDialog()));
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
QVariantHash FeedlyServiceRoot::customDatabaseData() const {

View File

@ -9,14 +9,15 @@
class FeedlyNetwork;
class FeedlyServiceRoot : public ServiceRoot, public CacheForServiceRoot {
Q_OBJECT
Q_OBJECT
public:
explicit FeedlyServiceRoot(RootItem* parent = nullptr);
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual FormAccountDetails* accountSetupDialog() const;
virtual void start(bool freshly_activated);
virtual QString code() const;
virtual void saveAllCachedData(bool ignore_errors);

View File

@ -162,11 +162,19 @@ bool GmailServiceRoot::canBeEdited() const {
return true;
}
bool GmailServiceRoot::editViaGui() {
FormEditGmailAccount form_pointer(qApp->mainFormWidget());
FormAccountDetails* GmailServiceRoot::accountSetupDialog() const {
return new FormEditGmailAccount(qApp->mainFormWidget());
}
form_pointer.addEditAccount(this);
return true;
void GmailServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditGmailAccount> p(qobject_cast<FormEditGmailAccount*>(accountSetupDialog()));
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
bool GmailServiceRoot::supportsFeedAdding() const {

View File

@ -24,7 +24,8 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual QList<QAction*> serviceMenu();
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual FormAccountDetails* accountSetupDialog() const;
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual void start(bool freshly_activated);

View File

@ -26,11 +26,19 @@ bool GreaderServiceRoot::canBeEdited() const {
return true;
}
bool GreaderServiceRoot::editViaGui() {
FormEditGreaderAccount form_pointer(qApp->mainFormWidget());
FormAccountDetails* GreaderServiceRoot::accountSetupDialog() const {
return new FormEditGreaderAccount(qApp->mainFormWidget());
}
form_pointer.addEditAccount(this);
return true;
void GreaderServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditGreaderAccount> p(qobject_cast<FormEditGreaderAccount*>(accountSetupDialog()));
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
QVariantHash GreaderServiceRoot::customDatabaseData() const {

View File

@ -28,7 +28,8 @@ class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual FormAccountDetails* accountSetupDialog() const;
virtual void start(bool freshly_activated);
virtual QString code() const;
virtual void saveAllCachedData(bool ignore_errors);

View File

@ -30,11 +30,19 @@ bool OwnCloudServiceRoot::canBeEdited() const {
return true;
}
bool OwnCloudServiceRoot::editViaGui() {
QScopedPointer<FormEditOwnCloudAccount> form_pointer(new FormEditOwnCloudAccount(qApp->mainFormWidget()));
FormAccountDetails* OwnCloudServiceRoot::accountSetupDialog() const {
return new FormEditOwnCloudAccount(qApp->mainFormWidget());
}
form_pointer->addEditAccount(this);
return true;
void OwnCloudServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditOwnCloudAccount> p(qobject_cast<FormEditOwnCloudAccount*>(accountSetupDialog()));
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
bool OwnCloudServiceRoot::supportsFeedAdding() const {

View File

@ -12,7 +12,7 @@ class OwnCloudNetworkFactory;
class Mutex;
class OwnCloudServiceRoot : public ServiceRoot, public CacheForServiceRoot {
Q_OBJECT
Q_OBJECT
public:
explicit OwnCloudServiceRoot(RootItem* parent = nullptr);
@ -20,7 +20,8 @@ class OwnCloudServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual FormAccountDetails* accountSetupDialog() const;
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual void start(bool freshly_activated);

View File

@ -80,11 +80,19 @@ bool RedditServiceRoot::canBeEdited() const {
return true;
}
bool RedditServiceRoot::editViaGui() {
FormEditRedditAccount form_pointer(qApp->mainFormWidget());
void RedditServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditRedditAccount> p(qobject_cast<FormEditRedditAccount*>(accountSetupDialog()));
form_pointer.addEditAccount(this);
return true;
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
FormAccountDetails* RedditServiceRoot::accountSetupDialog() const {
return new FormEditRedditAccount(qApp->mainFormWidget());
}
bool RedditServiceRoot::supportsFeedAdding() const {

View File

@ -9,7 +9,7 @@
class RedditNetworkFactory;
class RedditServiceRoot : public ServiceRoot, public CacheForServiceRoot {
Q_OBJECT
Q_OBJECT
public:
explicit RedditServiceRoot(RootItem* parent = nullptr);
@ -19,7 +19,8 @@ class RedditServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual FormAccountDetails* accountSetupDialog() const;
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual void start(bool freshly_activated);

View File

@ -116,7 +116,10 @@ void FormDiscoverFeeds::onDiscoveryFinished() {
loadDiscoveredFeeds(res);
}
catch (const ApplicationException& ex) {
// TODO: display error
qApp->showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot discover feeds"),
tr("Error: %1").arg(ex.message()),
QSystemTrayIcon::MessageIcon::Critical});
}
setEnabled(true);
@ -212,7 +215,7 @@ void FormDiscoverFeeds::addSingleFeed() {
fd->source(),
qApp->mainFormWidget()));
if (form_pointer->addEditFeed<StandardFeed>() != nullptr) {
if (!form_pointer->addEditFeed<StandardFeed>().isEmpty()) {
// Feed was added, remove from list.
if (m_discoveredModel->removeItem(idx) != nullptr) {
// Feed was guessed by the dialog, we do not need this object.

View File

@ -6,6 +6,8 @@
#include "services/abstract/gui/formaccountdetails.h"
class FormEditStandardAccount : public FormAccountDetails {
Q_OBJECT
public:
explicit FormEditStandardAccount(QWidget* parent = nullptr);

View File

@ -60,54 +60,119 @@ void FormStandardFeedDetails::guessIconOnly() {
}
void FormStandardFeedDetails::onTitleChanged(const QString& title) {
m_ui->m_buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setEnabled(!title.simplified().isEmpty());
m_ui.m_buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setEnabled(!title.simplified().isEmpty());
}
void FormStandardFeedDetails::apply() {
FormFeedDetails::apply();
auto* std_feed = feed<StandardFeed>();
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
RootItem* parent = m_standardFeedDetails->m_ui.m_cmbParentCategory->currentData().value<RootItem*>();
StandardFeed::Type type =
static_cast<StandardFeed::Type>(m_standardFeedDetails->m_ui.m_cmbType
->itemData(m_standardFeedDetails->m_ui.m_cmbType->currentIndex())
.toInt());
// Setup data for new_feed.
std_feed->setTitle(m_standardFeedDetails->m_ui.m_txtTitle->lineEdit()->text().simplified());
std_feed->setCreationDate(QDateTime::currentDateTime());
std_feed->setDescription(m_standardFeedDetails->m_ui.m_txtDescription->lineEdit()->text());
std_feed->setIcon(m_standardFeedDetails->m_ui.m_btnIcon->icon());
QList<StandardFeed*> fds = feeds<StandardFeed>();
std_feed->setSource(m_standardFeedDetails->m_ui.m_txtSource->textEdit()->toPlainText());
std_feed->setLastEtag({});
for (StandardFeed* std_feed : fds) {
// Setup data for the feed.
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbTitle)) {
std_feed->setTitle(m_standardFeedDetails->m_ui.m_txtTitle->lineEdit()->text().simplified());
}
std_feed->setEncoding(m_standardFeedDetails->m_ui.m_cmbEncoding->currentText());
std_feed->setType(type);
std_feed->setSourceType(m_standardFeedDetails->sourceType());
std_feed->setPostProcessScript(m_standardFeedDetails->m_ui.m_txtPostProcessScript->textEdit()->toPlainText());
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbDescription)) {
std_feed->setDescription(m_standardFeedDetails->m_ui.m_txtDescription->lineEdit()->text());
}
std_feed->setProtection(m_authDetails->authenticationType());
std_feed->setUsername(m_authDetails->m_txtUsername->lineEdit()->text());
std_feed->setPassword(m_authDetails->m_txtPassword->lineEdit()->text());
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbIcon)) {
std_feed->setIcon(m_standardFeedDetails->m_ui.m_btnIcon->icon());
}
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbSource)) {
std_feed->setSource(m_standardFeedDetails->m_ui.m_txtSource->textEdit()->toPlainText());
}
try {
DatabaseQueries::createOverwriteFeed(database, std_feed, m_serviceRoot->accountId(), parent->id());
}
catch (const ApplicationException& ex) {
qFatal("Cannot save feed: '%s'.", qPrintable(ex.message()));
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbSourceType)) {
std_feed->setSourceType(m_standardFeedDetails->sourceType());
}
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbType)) {
std_feed->setType(type);
}
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbEncoding)) {
std_feed->setEncoding(m_standardFeedDetails->m_ui.m_cmbEncoding->currentText());
}
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbPostProcessScript)) {
std_feed->setPostProcessScript(m_standardFeedDetails->m_ui.m_txtPostProcessScript->textEdit()->toPlainText());
}
if (isChangeAllowed(m_authDetails->m_mcbAuthType)) {
std_feed->setProtection(m_authDetails->authenticationType());
}
if (isChangeAllowed(m_authDetails->m_mcbAuthentication)) {
std_feed->setUsername(m_authDetails->m_txtUsername->lineEdit()->text());
std_feed->setPassword(m_authDetails->m_txtPassword->lineEdit()->text());
}
std_feed->setCreationDate(QDateTime::currentDateTime());
std_feed->setLastEtag({});
int new_parent_id;
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbParentCategory)) {
new_parent_id = parent->id();
}
else {
new_parent_id = std_feed->parent()->id();
}
try {
DatabaseQueries::createOverwriteFeed(database, std_feed, m_serviceRoot->accountId(), new_parent_id);
}
catch (const ApplicationException& ex) {
qFatal("Cannot save feed: '%s'.", qPrintable(ex.message()));
}
if (isChangeAllowed(m_standardFeedDetails->m_ui.m_mcbParentCategory)) {
m_serviceRoot->requestItemReassignment(std_feed, parent);
}
}
m_serviceRoot->requestItemReassignment(m_feed, parent);
m_serviceRoot->itemChanged({m_feed});
m_serviceRoot->itemChanged(feeds<RootItem>());
}
void FormStandardFeedDetails::loadFeedData() {
FormFeedDetails::loadFeedData();
if (m_isBatchEdit) {
// We hook batch selectors.
m_standardFeedDetails->m_ui.m_mcbDescription->addActionWidget(m_standardFeedDetails->m_ui.m_txtDescription);
m_standardFeedDetails->m_ui.m_mcbIcon->addActionWidget(m_standardFeedDetails->m_ui.m_btnIcon);
m_standardFeedDetails->m_ui.m_mcbParentCategory->addActionWidget(m_standardFeedDetails->m_ui.m_cmbParentCategory);
m_standardFeedDetails->m_ui.m_mcbPostProcessScript
->addActionWidget(m_standardFeedDetails->m_ui.m_txtPostProcessScript);
m_standardFeedDetails->m_ui.m_mcbSourceType->addActionWidget(m_standardFeedDetails->m_ui.m_cmbSourceType);
m_standardFeedDetails->m_ui.m_mcbSource->addActionWidget(m_standardFeedDetails->m_ui.m_txtSource);
m_standardFeedDetails->m_ui.m_mcbTitle->addActionWidget(m_standardFeedDetails->m_ui.m_txtTitle);
m_standardFeedDetails->m_ui.m_mcbType->addActionWidget(m_standardFeedDetails->m_ui.m_cmbType);
m_standardFeedDetails->m_ui.m_mcbEncoding->addActionWidget(m_standardFeedDetails->m_ui.m_cmbEncoding);
m_authDetails->m_mcbAuthType->addActionWidget(m_authDetails->m_cbAuthType);
m_authDetails->m_mcbAuthentication->addActionWidget(m_authDetails->m_gbAuthentication);
m_standardFeedDetails->m_ui.m_btnFetchMetadata->setEnabled(false);
}
else {
// We hide batch selectors.
for (auto* cb : findChildren<MultiFeedEditCheckBox*>()) {
cb->hide();
}
}
auto* std_feed = feed<StandardFeed>();
// Load categories.

View File

@ -348,6 +348,7 @@ void StandardFeedDetails::prepareForNewFeed(RootItem* parent_to_select, const QS
// Make sure that "default" icon is used as the default option for new
// feed.
m_actionUseDefaultIcon->trigger();
int default_encoding_index = m_ui.m_cmbEncoding->findText(QSL(DEFAULT_FEED_ENCODING));
if (default_encoding_index >= 0) {

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<width>505</width>
<height>520</height>
</rect>
</property>
@ -15,14 +15,21 @@
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="m_lblParentCategory">
<property name="text">
<string>Parent folder</string>
</property>
<property name="buddy">
<cstring>m_cmbParentCategory</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbParentCategory"/>
</item>
<item>
<widget class="QLabel" name="m_lblParentCategory">
<property name="text">
<string>Parent folder</string>
</property>
<property name="buddy">
<cstring>m_cmbParentCategory</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbParentCategory">
@ -41,14 +48,21 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Type</string>
</property>
<property name="buddy">
<cstring>m_cmbType</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbType"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Type</string>
</property>
<property name="buddy">
<cstring>m_cmbType</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
@ -59,6 +73,9 @@
</property>
</widget>
</item>
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbEncoding"/>
</item>
<item>
<widget class="QComboBox" name="m_cmbEncoding">
<property name="toolTip">
@ -69,45 +86,66 @@
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="m_lblTitle">
<property name="text">
<string>Title</string>
</property>
<property name="buddy">
<cstring>m_txtTitle</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbTitle"/>
</item>
<item>
<widget class="QLabel" name="m_lblTitle">
<property name="text">
<string>Title</string>
</property>
<property name="buddy">
<cstring>m_txtTitle</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="LineEditWithStatus" name="m_txtTitle" native="true"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="m_lblDescription">
<property name="text">
<string>Description</string>
</property>
<property name="buddy">
<cstring>m_txtDescription</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbDescription"/>
</item>
<item>
<widget class="QLabel" name="m_lblDescription">
<property name="text">
<string>Description</string>
</property>
<property name="buddy">
<cstring>m_txtDescription</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<widget class="LineEditWithStatus" name="m_txtDescription" native="true"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Source</string>
</property>
<property name="buddy">
<cstring>m_txtSource</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbSourceType"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Source</string>
</property>
<property name="buddy">
<cstring>m_txtSource</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="m_cmbSourceType"/>
</item>
<item row="5" column="0" colspan="2">
<item row="5" column="1">
<widget class="TextEditWithStatus" name="m_txtSource" native="true">
<property name="minimumSize">
<size>
@ -124,14 +162,21 @@
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Post-processing script</string>
</property>
<property name="buddy">
<cstring>m_txtPostProcessScript</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbPostProcessScript"/>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Post-processing script</string>
</property>
<property name="buddy">
<cstring>m_txtPostProcessScript</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="0" colspan="2">
<widget class="TextEditWithStatus" name="m_txtPostProcessScript" native="true">
@ -187,14 +232,21 @@
</layout>
</item>
<item row="10" column="0">
<widget class="QLabel" name="m_lblIcon">
<property name="text">
<string>Icon</string>
</property>
<property name="buddy">
<cstring>m_btnIcon</cstring>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="MultiFeedEditCheckBox" name="m_mcbIcon"/>
</item>
<item>
<widget class="QLabel" name="m_lblIcon">
<property name="text">
<string>Icon</string>
</property>
<property name="buddy">
<cstring>m_btnIcon</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="1">
<widget class="QToolButton" name="m_btnIcon">
@ -246,9 +298,17 @@
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="MultiFeedEditCheckBox" name="m_mcbSource"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MultiFeedEditCheckBox</class>
<extends>QCheckBox</extends>
<header>multifeededitcheckbox.h</header>
</customwidget>
<customwidget>
<class>HelpSpoiler</class>
<extends>QWidget</extends>

View File

@ -47,15 +47,6 @@ bool StandardCategory::canBeDeleted() const {
return true;
}
bool StandardCategory::editViaGui() {
QScopedPointer<FormCategoryDetails> form_pointer(new FormCategoryDetails(serviceRoot(),
nullptr,
qApp->mainFormWidget()));
form_pointer->addEditCategory(this);
return false;
}
bool StandardCategory::deleteViaGui() {
if (removeItself()) {
serviceRoot()->requestItemRemoval(this);

View File

@ -10,7 +10,7 @@
class StandardServiceRoot;
class StandardCategory : public Category {
Q_OBJECT
Q_OBJECT
public:
explicit StandardCategory(RootItem* parent_item = nullptr);
@ -21,7 +21,6 @@ class StandardCategory : public Category {
virtual Qt::ItemFlags additionalFlags() const;
virtual bool performDragDropChange(RootItem* target_item);
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual bool canBeDeleted() const;
virtual bool deleteViaGui();

View File

@ -78,19 +78,6 @@ StandardServiceRoot* StandardFeed::serviceRoot() const {
return qobject_cast<StandardServiceRoot*>(getParentServiceRoot());
}
bool StandardFeed::editViaGui() {
QScopedPointer<FormStandardFeedDetails> form_pointer(new FormStandardFeedDetails(serviceRoot(),
nullptr,
{},
qApp->mainFormWidget()));
if (form_pointer->addEditFeed(this) != nullptr) {
setLastEtag({});
}
return false;
}
bool StandardFeed::deleteViaGui() {
if (removeItself()) {
serviceRoot()->requestItemRemoval(this);

View File

@ -46,7 +46,6 @@ class StandardFeed : public Feed {
virtual QString additionalTooltip() const;
virtual bool canBeDeleted() const;
virtual bool deleteViaGui();
virtual bool editViaGui();
virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data);
virtual Qt::ItemFlags additionalFlags() const;

View File

@ -113,11 +113,38 @@ bool StandardServiceRoot::canBeEdited() const {
return true;
}
bool StandardServiceRoot::editViaGui() {
FormEditStandardAccount form_pointer(qApp->mainFormWidget());
FormAccountDetails* StandardServiceRoot::accountSetupDialog() const {
return new FormEditStandardAccount(qApp->mainFormWidget());
}
form_pointer.addEditAccount(this);
return true;
void StandardServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
auto std_feeds = boolinq::from(items)
.select([](RootItem* it) {
return qobject_cast<Feed*>(it);
})
.where([](Feed* fd) {
return fd != nullptr;
})
.toStdList();
if (!std_feeds.empty()) {
QScopedPointer<FormStandardFeedDetails> form_pointer(new FormStandardFeedDetails(this,
nullptr,
{},
qApp->mainFormWidget()));
form_pointer->addEditFeed<StandardFeed>(FROM_STD_LIST(QList<Feed*>, std_feeds));
return;
}
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditStandardAccount> p(qobject_cast<FormEditStandardAccount*>(accountSetupDialog()));
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
bool StandardServiceRoot::supportsFeedAdding() const {

View File

@ -24,12 +24,13 @@ class StandardServiceRoot : public ServiceRoot {
explicit StandardServiceRoot(RootItem* parent = nullptr);
virtual ~StandardServiceRoot();
virtual FormAccountDetails* accountSetupDialog() const;
virtual void onDatabaseCleanup();
virtual void start(bool freshly_activated);
virtual void stop();
virtual QString code() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual Qt::ItemFlags additionalFlags() const;

View File

@ -75,11 +75,19 @@ bool TtRssServiceRoot::isSyncable() const {
return true;
}
bool TtRssServiceRoot::editViaGui() {
QScopedPointer<FormEditTtRssAccount> form_pointer(new FormEditTtRssAccount(qApp->mainFormWidget()));
FormAccountDetails* TtRssServiceRoot::accountSetupDialog() const {
return new FormEditTtRssAccount(qApp->mainFormWidget());
}
form_pointer->addEditAccount(this);
return true;
void TtRssServiceRoot::editItemsViaGui(const QList<RootItem*>& items) {
if (items.first()->kind() == RootItem::Kind::ServiceRoot) {
QScopedPointer<FormEditTtRssAccount> p(qobject_cast<FormEditTtRssAccount*>(accountSetupDialog()));
p->addEditAccount(this);
return;
}
ServiceRoot::editItemsViaGui(items);
}
bool TtRssServiceRoot::supportsFeedAdding() const {

View File

@ -26,7 +26,8 @@ class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual QString code() const;
virtual bool isSyncable() const;
virtual bool canBeEdited() const;
virtual bool editViaGui();
virtual void editItemsViaGui(const QList<RootItem*>& items);
virtual FormAccountDetails* accountSetupDialog() const;
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual void addNewFeed(RootItem* selected_item, const QString& url = QString());