Revert "cleanup manual sort partial implementation"
This reverts commit 741c6ef68281460035b03f7b9b695d90045b28d6.
This commit is contained in:
parent
d6c9bed42e
commit
67e9a249af
@ -5,6 +5,7 @@ CREATE TABLE Information (
|
|||||||
-- !
|
-- !
|
||||||
CREATE TABLE Accounts (
|
CREATE TABLE Accounts (
|
||||||
id $$,
|
id $$,
|
||||||
|
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||||
type TEXT NOT NULL CHECK (type != ''), /* ID of the account type. Each account defines its own, for example 'ttrss'. */
|
type TEXT NOT NULL CHECK (type != ''), /* ID of the account type. Each account defines its own, for example 'ttrss'. */
|
||||||
proxy_type INTEGER NOT NULL DEFAULT 0 CHECK (proxy_type >= 0),
|
proxy_type INTEGER NOT NULL DEFAULT 0 CHECK (proxy_type >= 0),
|
||||||
proxy_host TEXT,
|
proxy_host TEXT,
|
||||||
@ -17,6 +18,7 @@ CREATE TABLE Accounts (
|
|||||||
-- !
|
-- !
|
||||||
CREATE TABLE Categories (
|
CREATE TABLE Categories (
|
||||||
id $$,
|
id $$,
|
||||||
|
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||||
parent_id INTEGER NOT NULL CHECK (parent_id >= -1), /* Root categories contain -1 here. */
|
parent_id INTEGER NOT NULL CHECK (parent_id >= -1), /* Root categories contain -1 here. */
|
||||||
title TEXT NOT NULL CHECK (title != ''),
|
title TEXT NOT NULL CHECK (title != ''),
|
||||||
description TEXT,
|
description TEXT,
|
||||||
@ -30,6 +32,7 @@ CREATE TABLE Categories (
|
|||||||
-- !
|
-- !
|
||||||
CREATE TABLE Feeds (
|
CREATE TABLE Feeds (
|
||||||
id $$,
|
id $$,
|
||||||
|
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||||
title TEXT NOT NULL CHECK (title != ''),
|
title TEXT NOT NULL CHECK (title != ''),
|
||||||
description TEXT,
|
description TEXT,
|
||||||
date_created BIGINT,
|
date_created BIGINT,
|
||||||
|
@ -2,6 +2,7 @@ ALTER TABLE Feeds RENAME TO backup_Feeds;
|
|||||||
-- !
|
-- !
|
||||||
CREATE TABLE Feeds (
|
CREATE TABLE Feeds (
|
||||||
id $$,
|
id $$,
|
||||||
|
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||||
title TEXT NOT NULL CHECK (title != ''),
|
title TEXT NOT NULL CHECK (title != ''),
|
||||||
description TEXT,
|
description TEXT,
|
||||||
date_created BIGINT,
|
date_created BIGINT,
|
||||||
@ -25,3 +26,60 @@ SELECT id, title, description, date_created, icon, category, source, update_type
|
|||||||
FROM backup_Feeds;
|
FROM backup_Feeds;
|
||||||
-- !
|
-- !
|
||||||
DROP TABLE backup_Feeds;
|
DROP TABLE backup_Feeds;
|
||||||
|
-- !
|
||||||
|
UPDATE Feeds
|
||||||
|
SET ordr = (
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM Feeds ct
|
||||||
|
WHERE Feeds.account_id = ct.account_id AND Feeds.category = ct.category AND ct.id < Feeds.id
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
ALTER TABLE Categories RENAME TO backup_Categories;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE Categories (
|
||||||
|
id $$,
|
||||||
|
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||||
|
parent_id INTEGER NOT NULL CHECK (parent_id >= -1), /* Root categories contain -1 here. */
|
||||||
|
title TEXT NOT NULL CHECK (title != ''),
|
||||||
|
description TEXT,
|
||||||
|
date_created BIGINT,
|
||||||
|
icon ^^,
|
||||||
|
account_id INTEGER NOT NULL,
|
||||||
|
custom_id TEXT,
|
||||||
|
|
||||||
|
FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
INSERT INTO Categories (id, ordr, parent_id, title, description, date_created, icon, account_id, custom_id)
|
||||||
|
SELECT id, id, parent_id, title, description, date_created, icon, account_id, custom_id
|
||||||
|
FROM backup_Categories;
|
||||||
|
-- !
|
||||||
|
DROP TABLE backup_Categories;
|
||||||
|
-- !
|
||||||
|
UPDATE Categories
|
||||||
|
SET ordr = (
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM Categories ct
|
||||||
|
WHERE Categories.account_id = ct.account_id AND Categories.parent_id = ct.parent_id AND ct.id < Categories.id
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
ALTER TABLE Accounts RENAME TO backup_Accounts;
|
||||||
|
-- !
|
||||||
|
CREATE TABLE Accounts (
|
||||||
|
id $$,
|
||||||
|
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||||
|
type TEXT NOT NULL CHECK (type != ''), /* ID of the account type. Each account defines its own, for example 'ttrss'. */
|
||||||
|
proxy_type INTEGER NOT NULL DEFAULT 0 CHECK (proxy_type >= 0),
|
||||||
|
proxy_host TEXT,
|
||||||
|
proxy_port INTEGER,
|
||||||
|
proxy_username TEXT,
|
||||||
|
proxy_password TEXT,
|
||||||
|
/* Custom column for (serialized) custom account-specific data. */
|
||||||
|
custom_data TEXT
|
||||||
|
);
|
||||||
|
-- !
|
||||||
|
INSERT INTO Accounts (id, ordr, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data)
|
||||||
|
SELECT id, id - 1, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data
|
||||||
|
FROM backup_Accounts;
|
||||||
|
-- !
|
||||||
|
DROP TABLE backup_Accounts;
|
@ -524,6 +524,12 @@ bool FeedsModel::emptyAllBins() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedsModel::changeSortOrder(RootItem* item, bool move_top, bool move_bottom, int new_sort_order) {
|
||||||
|
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
|
||||||
|
|
||||||
|
DatabaseQueries::moveItem(item, move_top, move_bottom, new_sort_order, db);
|
||||||
|
}
|
||||||
|
|
||||||
void FeedsModel::loadActivatedServiceAccounts() {
|
void FeedsModel::loadActivatedServiceAccounts() {
|
||||||
auto serv = qApp->feedReader()->feedServices();
|
auto serv = qApp->feedReader()->feedServices();
|
||||||
|
|
||||||
|
@ -107,6 +107,8 @@ class RSSGUARD_DLLSPEC FeedsModel : public QAbstractItemModel {
|
|||||||
bool restoreAllBins();
|
bool restoreAllBins();
|
||||||
bool emptyAllBins();
|
bool emptyAllBins();
|
||||||
|
|
||||||
|
void changeSortOrder(RootItem* item, bool move_top, bool move_bottom, int new_sort_order);
|
||||||
|
|
||||||
// Feeds operations.
|
// Feeds operations.
|
||||||
bool markItemRead(RootItem* item, RootItem::ReadStatus read);
|
bool markItemRead(RootItem* item, RootItem::ReadStatus read);
|
||||||
bool markItemCleared(RootItem* item, bool clean_read_only);
|
bool markItemCleared(RootItem* item, bool clean_read_only);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent)
|
FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent)
|
||||||
: QSortFilterProxyModel(parent), m_sourceModel(source_model), m_view(nullptr),
|
: QSortFilterProxyModel(parent), m_sourceModel(source_model), m_view(nullptr),
|
||||||
m_selectedItem(nullptr), m_showUnreadOnly(false) {
|
m_selectedItem(nullptr), m_showUnreadOnly(false), m_sortAlphabetically(true) {
|
||||||
setObjectName(QSL("FeedsProxyModel"));
|
setObjectName(QSL("FeedsProxyModel"));
|
||||||
|
|
||||||
setSortRole(Qt::ItemDataRole::EditRole);
|
setSortRole(Qt::ItemDataRole::EditRole);
|
||||||
@ -171,6 +171,7 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right
|
|||||||
return sortOrder() == Qt::SortOrder::DescendingOrder;
|
return sortOrder() == Qt::SortOrder::DescendingOrder;
|
||||||
}
|
}
|
||||||
else if (left_item->kind() == right_item->kind()) {
|
else if (left_item->kind() == right_item->kind()) {
|
||||||
|
if (m_sortAlphabetically) {
|
||||||
// Both items are of the same type.
|
// Both items are of the same type.
|
||||||
if (left.column() == FDS_MODEL_COUNTS_INDEX) {
|
if (left.column() == FDS_MODEL_COUNTS_INDEX) {
|
||||||
// User wants to sort according to counts.
|
// User wants to sort according to counts.
|
||||||
@ -181,6 +182,23 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right
|
|||||||
return QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0;
|
return QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// We sort some types with sort order, other alphabetically.
|
||||||
|
switch (left_item->kind()) {
|
||||||
|
case RootItem::Kind::Feed:
|
||||||
|
case RootItem::Kind::Category:
|
||||||
|
case RootItem::Kind::ServiceRoot:
|
||||||
|
return sortOrder() == Qt::SortOrder::AscendingOrder
|
||||||
|
? left_item->sortOrder() < right_item->sortOrder()
|
||||||
|
: left_item->sortOrder() > right_item->sortOrder();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return sortOrder() == Qt::SortOrder::AscendingOrder
|
||||||
|
? QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0
|
||||||
|
: QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// We sort using priorities.
|
// We sort using priorities.
|
||||||
auto left_priority = m_priorities.indexOf(left_item->kind());
|
auto left_priority = m_priorities.indexOf(left_item->kind());
|
||||||
@ -297,6 +315,14 @@ void FeedsProxyModel::setShowUnreadOnly(bool show_unread_only) {
|
|||||||
qApp->settings()->setValue(GROUP(Feeds), Feeds::ShowOnlyUnreadFeeds, show_unread_only);
|
qApp->settings()->setValue(GROUP(Feeds), Feeds::ShowOnlyUnreadFeeds, show_unread_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedsProxyModel::setSortAlphabetically(bool sort_alphabetically) {
|
||||||
|
if (sort_alphabetically != m_sortAlphabetically) {
|
||||||
|
m_sortAlphabetically = sort_alphabetically;
|
||||||
|
qApp->settings()->setValue(GROUP(Feeds), Feeds::SortAlphabetically, sort_alphabetically);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList& indexes) const {
|
QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList& indexes) const {
|
||||||
QModelIndexList source_indexes;
|
QModelIndexList source_indexes;
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
|||||||
void setSelectedItem(const RootItem* selected_item);
|
void setSelectedItem(const RootItem* selected_item);
|
||||||
void setView(FeedsView* newView);
|
void setView(FeedsView* newView);
|
||||||
|
|
||||||
|
void setSortAlphabetically(bool sort_alphabetically);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
|
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
|
||||||
|
|
||||||
@ -52,6 +54,7 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
|||||||
FeedsView* m_view;
|
FeedsView* m_view;
|
||||||
const RootItem* m_selectedItem;
|
const RootItem* m_selectedItem;
|
||||||
bool m_showUnreadOnly;
|
bool m_showUnreadOnly;
|
||||||
|
bool m_sortAlphabetically;
|
||||||
QList<QPair<int, QModelIndex>> m_hiddenIndices;
|
QList<QPair<int, QModelIndex>> m_hiddenIndices;
|
||||||
QList<RootItem::Kind> m_priorities;
|
QList<RootItem::Kind> m_priorities;
|
||||||
};
|
};
|
||||||
|
@ -1939,9 +1939,26 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
|
|||||||
|
|
||||||
if (category->id() <= 0) {
|
if (category->id() <= 0) {
|
||||||
// We need to insert category first.
|
// We need to insert category first.
|
||||||
|
if (category->sortOrder() < 0) {
|
||||||
|
q.prepare(QSL("SELECT MAX(ordr) FROM Categories WHERE account_id = :account_id AND parent_id = :parent_id;"));
|
||||||
|
q.bindValue(QSL(":account_id"), account_id);
|
||||||
|
q.bindValue(QSL(":parent_id"), parent_id);
|
||||||
|
|
||||||
|
if (!q.exec()) {
|
||||||
|
throw ApplicationException(q.lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
|
q.next();
|
||||||
|
|
||||||
|
int next_order = (q.value(0).isNull() ? -1 : q.value(0).toInt()) + 1;
|
||||||
|
|
||||||
|
category->setSortOrder(next_order);
|
||||||
|
q.finish();
|
||||||
|
}
|
||||||
|
|
||||||
q.prepare(QSL("INSERT INTO "
|
q.prepare(QSL("INSERT INTO "
|
||||||
"Categories (parent_id, title, date_created, account_id) "
|
"Categories (parent_id, ordr, title, date_created, account_id) "
|
||||||
"VALUES (0, 'new', 0, %1);").arg(QString::number(account_id)));
|
"VALUES (0, 0, 'new', 0, %1);").arg(QString::number(account_id)));
|
||||||
|
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
throw ApplicationException(q.lastError().text());
|
throw ApplicationException(q.lastError().text());
|
||||||
@ -1952,7 +1969,7 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
|
|||||||
}
|
}
|
||||||
|
|
||||||
q.prepare("UPDATE Categories "
|
q.prepare("UPDATE Categories "
|
||||||
"SET parent_id = :parent_id, title = :title, description = :description, date_created = :date_created, "
|
"SET parent_id = :parent_id, ordr = :ordr, title = :title, description = :description, date_created = :date_created, "
|
||||||
" icon = :icon, account_id = :account_id, custom_id = :custom_id "
|
" icon = :icon, account_id = :account_id, custom_id = :custom_id "
|
||||||
"WHERE id = :id;");
|
"WHERE id = :id;");
|
||||||
q.bindValue(QSL(":parent_id"), parent_id);
|
q.bindValue(QSL(":parent_id"), parent_id);
|
||||||
@ -1963,6 +1980,7 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
|
|||||||
q.bindValue(QSL(":account_id"), account_id);
|
q.bindValue(QSL(":account_id"), account_id);
|
||||||
q.bindValue(QSL(":custom_id"), category->customId());
|
q.bindValue(QSL(":custom_id"), category->customId());
|
||||||
q.bindValue(QSL(":id"), category->id());
|
q.bindValue(QSL(":id"), category->id());
|
||||||
|
q.bindValue(QSL(":ordr"), category->sortOrder());
|
||||||
|
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
throw ApplicationException(q.lastError().text());
|
throw ApplicationException(q.lastError().text());
|
||||||
@ -1974,9 +1992,26 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
|
|||||||
|
|
||||||
if (feed->id() <= 0) {
|
if (feed->id() <= 0) {
|
||||||
// We need to insert feed first.
|
// We need to insert feed first.
|
||||||
|
if (feed->sortOrder() < 0) {
|
||||||
|
q.prepare(QSL("SELECT MAX(ordr) FROM Feeds WHERE account_id = :account_id AND category = :category;"));
|
||||||
|
q.bindValue(QSL(":account_id"), account_id);
|
||||||
|
q.bindValue(QSL(":category"), parent_id);
|
||||||
|
|
||||||
|
if (!q.exec()) {
|
||||||
|
throw ApplicationException(q.lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
|
q.next();
|
||||||
|
|
||||||
|
int next_order = (q.value(0).isNull() ? -1 : q.value(0).toInt()) + 1;
|
||||||
|
|
||||||
|
feed->setSortOrder(next_order);
|
||||||
|
q.finish();
|
||||||
|
}
|
||||||
|
|
||||||
q.prepare(QSL("INSERT INTO "
|
q.prepare(QSL("INSERT INTO "
|
||||||
"Feeds (title, date_created, category, update_type, update_interval, account_id, custom_id) "
|
"Feeds (title, ordr, date_created, category, update_type, update_interval, account_id, custom_id) "
|
||||||
"VALUES ('new', 0, 0, 0, 1, %1, 'new');").arg(QString::number(account_id)));
|
"VALUES ('new', 0, 0, 0, 0, 1, %1, 'new');").arg(QString::number(account_id)));
|
||||||
|
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
throw ApplicationException(q.lastError().text());
|
throw ApplicationException(q.lastError().text());
|
||||||
@ -1991,7 +2026,7 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
q.prepare("UPDATE Feeds "
|
q.prepare("UPDATE Feeds "
|
||||||
"SET title = :title, description = :description, date_created = :date_created, "
|
"SET title = :title, ordr = :ordr, description = :description, date_created = :date_created, "
|
||||||
" icon = :icon, category = :category, source = :source, update_type = :update_type, "
|
" icon = :icon, category = :category, source = :source, update_type = :update_type, "
|
||||||
" update_interval = :update_interval, is_off = :is_off, open_articles = :open_articles, "
|
" update_interval = :update_interval, is_off = :is_off, open_articles = :open_articles, "
|
||||||
" account_id = :account_id, custom_id = :custom_id, custom_data = :custom_data "
|
" account_id = :account_id, custom_id = :custom_id, custom_data = :custom_data "
|
||||||
@ -2007,6 +2042,7 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
|
|||||||
q.bindValue(QSL(":account_id"), account_id);
|
q.bindValue(QSL(":account_id"), account_id);
|
||||||
q.bindValue(QSL(":custom_id"), feed->customId());
|
q.bindValue(QSL(":custom_id"), feed->customId());
|
||||||
q.bindValue(QSL(":id"), feed->id());
|
q.bindValue(QSL(":id"), feed->id());
|
||||||
|
q.bindValue(QSL(":ordr"), feed->sortOrder());
|
||||||
q.bindValue(QSL(":is_off"), feed->isSwitchedOff());
|
q.bindValue(QSL(":is_off"), feed->isSwitchedOff());
|
||||||
q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly());
|
q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly());
|
||||||
|
|
||||||
@ -2024,9 +2060,22 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
|
|||||||
QSqlQuery q(db);
|
QSqlQuery q(db);
|
||||||
|
|
||||||
if (account->accountId() <= 0) {
|
if (account->accountId() <= 0) {
|
||||||
// We need to insert account first.
|
// We need to insert account and generate sort order first.
|
||||||
q.prepare(QSL("INSERT INTO Accounts (type) "
|
if (account->sortOrder() < 0) {
|
||||||
"VALUES (:type);"));
|
if (!q.exec(QSL("SELECT MAX(ordr) FROM Accounts;"))) {
|
||||||
|
throw ApplicationException(q.lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
|
q.next();
|
||||||
|
|
||||||
|
int next_order = (q.value(0).isNull() ? -1 : q.value(0).toInt()) + 1;
|
||||||
|
|
||||||
|
account->setSortOrder(next_order);
|
||||||
|
q.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
q.prepare(QSL("INSERT INTO Accounts (ordr, type) "
|
||||||
|
"VALUES (0, :type);"));
|
||||||
q.bindValue(QSL(":type"), account->code());
|
q.bindValue(QSL(":type"), account->code());
|
||||||
|
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
@ -2042,7 +2091,7 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
|
|||||||
|
|
||||||
q.prepare(QSL("UPDATE Accounts "
|
q.prepare(QSL("UPDATE Accounts "
|
||||||
"SET proxy_type = :proxy_type, proxy_host = :proxy_host, proxy_port = :proxy_port, "
|
"SET proxy_type = :proxy_type, proxy_host = :proxy_host, proxy_port = :proxy_port, "
|
||||||
" proxy_username = :proxy_username, proxy_password = :proxy_password, "
|
" proxy_username = :proxy_username, proxy_password = :proxy_password, ordr = :ordr, "
|
||||||
" custom_data = :custom_data "
|
" custom_data = :custom_data "
|
||||||
"WHERE id = :id"));
|
"WHERE id = :id"));
|
||||||
q.bindValue(QSL(":proxy_type"), proxy.type());
|
q.bindValue(QSL(":proxy_type"), proxy.type());
|
||||||
@ -2051,6 +2100,7 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
|
|||||||
q.bindValue(QSL(":proxy_username"), proxy.user());
|
q.bindValue(QSL(":proxy_username"), proxy.user());
|
||||||
q.bindValue(QSL(":proxy_password"), TextFactory::encrypt(proxy.password()));
|
q.bindValue(QSL(":proxy_password"), TextFactory::encrypt(proxy.password()));
|
||||||
q.bindValue(QSL(":id"), account->accountId());
|
q.bindValue(QSL(":id"), account->accountId());
|
||||||
|
q.bindValue(QSL(":ordr"), account->sortOrder());
|
||||||
|
|
||||||
auto custom_data = account->customDatabaseData();
|
auto custom_data = account->customDatabaseData();
|
||||||
QString serialized_custom_data = serializeCustomData(custom_data);
|
QString serialized_custom_data = serializeCustomData(custom_data);
|
||||||
@ -2093,6 +2143,81 @@ bool DatabaseQueries::deleteCategory(const QSqlDatabase& db, int id) {
|
|||||||
return q.exec();
|
return q.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DatabaseQueries::moveItem(RootItem* item, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db) {
|
||||||
|
switch (item->kind()) {
|
||||||
|
case RootItem::Kind::Feed:
|
||||||
|
moveFeed(item->toFeed(), move_top, move_bottom, move_index, db);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RootItem::Kind::Category:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RootItem::Kind::ServiceRoot:
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseQueries::moveFeed(Feed* feed, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db) {
|
||||||
|
if (feed->sortOrder() == move_index || /* Item is already sorted OK. */
|
||||||
|
(!move_top && !move_bottom && move_index < 0 ) || /* Order cannot be smaller than 0 if we do not move to begin/end. */
|
||||||
|
(move_top && feed->sortOrder() == 0)) { /* Item is already on top. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery q(db);
|
||||||
|
|
||||||
|
q.prepare(QSL("SELECT MAX(ordr) FROM Feeds WHERE account_id = :account_id AND category = :category;"));
|
||||||
|
q.bindValue(QSL(":account_id"), feed->getParentServiceRoot()->accountId());
|
||||||
|
q.bindValue(QSL(":category"), feed->parent()->id());
|
||||||
|
|
||||||
|
int max_sort_order;
|
||||||
|
|
||||||
|
if (q.exec() && q.next()) {
|
||||||
|
max_sort_order = q.value(0).toInt();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw ApplicationException(q.lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_sort_order == 0 || /* We only have 1 item, nothing to sort. */
|
||||||
|
max_sort_order == feed->sortOrder() || /* Item is already sorted OK. */
|
||||||
|
move_index > max_sort_order) { /* Cannot move past biggest sort order. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move_top) {
|
||||||
|
move_index = 0;
|
||||||
|
}
|
||||||
|
else if (move_bottom) {
|
||||||
|
move_index = max_sort_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
int move_low = qMin(move_index, feed->sortOrder());
|
||||||
|
int move_high = qMax(move_index, feed->sortOrder());
|
||||||
|
|
||||||
|
if (feed->sortOrder() > move_index) {
|
||||||
|
q.prepare(QSL("UPDATE Feeds SET ordr = ordr + 1 "
|
||||||
|
"WHERE account_id = :account_id AND category = :category AND ordr < :move_high AND ordr >= :move_low;"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
q.prepare(QSL("UPDATE Feeds SET ordr = ordr - 1 "
|
||||||
|
"WHERE account_id = :account_id AND category = :category AND ordr > :move_low AND ordr <= :move_high;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
q.bindValue(QSL(":account_id"), feed->getParentServiceRoot()->accountId());
|
||||||
|
q.bindValue(QSL(":category"), feed->parent()->id());
|
||||||
|
q.bindValue(QSL(":move_low"), move_low);
|
||||||
|
q.bindValue(QSL(":move_high"), move_high);
|
||||||
|
|
||||||
|
if (!q.exec()) {
|
||||||
|
throw ApplicationException(q.lastError().text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MessageFilter* DatabaseQueries::addMessageFilter(const QSqlDatabase& db, const QString& title,
|
MessageFilter* DatabaseQueries::addMessageFilter(const QSqlDatabase& db, const QString& title,
|
||||||
const QString& script) {
|
const QString& script) {
|
||||||
if (!db.driver()->hasFeature(QSqlDriver::DriverFeature::LastInsertId)) {
|
if (!db.driver()->hasFeature(QSqlDriver::DriverFeature::LastInsertId)) {
|
||||||
|
@ -134,6 +134,10 @@ class DatabaseQueries {
|
|||||||
static Assignment getFeeds(const QSqlDatabase& db, const QList<MessageFilter*>& global_filters,
|
static Assignment getFeeds(const QSqlDatabase& db, const QList<MessageFilter*>& global_filters,
|
||||||
int account_id, bool* ok = nullptr);
|
int account_id, bool* ok = nullptr);
|
||||||
|
|
||||||
|
// Item order methods.
|
||||||
|
static void moveItem(RootItem* item, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db);
|
||||||
|
static void moveFeed(Feed* feed, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db);
|
||||||
|
|
||||||
// Message filters operators.
|
// Message filters operators.
|
||||||
static bool purgeLeftoverMessageFilterAssignments(const QSqlDatabase& db, int account_id);
|
static bool purgeLeftoverMessageFilterAssignments(const QSqlDatabase& db, int account_id);
|
||||||
static MessageFilter* addMessageFilter(const QSqlDatabase& db, const QString& title, const QString& script);
|
static MessageFilter* addMessageFilter(const QSqlDatabase& db, const QString& title, const QString& script);
|
||||||
@ -167,6 +171,7 @@ QList<ServiceRoot*> DatabaseQueries::getAccounts(const QSqlDatabase& db, const Q
|
|||||||
|
|
||||||
// Load common data.
|
// Load common data.
|
||||||
root->setAccountId(query.value(QSL("id")).toInt());
|
root->setAccountId(query.value(QSL("id")).toInt());
|
||||||
|
root->setSortOrder(query.value(QSL("ordr")).toInt());
|
||||||
|
|
||||||
QNetworkProxy proxy(QNetworkProxy::ProxyType(query.value(QSL("proxy_type")).toInt()),
|
QNetworkProxy proxy(QNetworkProxy::ProxyType(query.value(QSL("proxy_type")).toInt()),
|
||||||
query.value(QSL("proxy_host")).toString(),
|
query.value(QSL("proxy_host")).toString(),
|
||||||
@ -232,6 +237,7 @@ Assignment DatabaseQueries::getCategories(const QSqlDatabase& db, int account_id
|
|||||||
auto* cat = static_cast<Category*>(pair.second);
|
auto* cat = static_cast<Category*>(pair.second);
|
||||||
|
|
||||||
cat->setId(query_categories.value(CAT_DB_ID_INDEX).toInt());
|
cat->setId(query_categories.value(CAT_DB_ID_INDEX).toInt());
|
||||||
|
cat->setSortOrder(query_categories.value(CAT_DB_ORDER_INDEX).toInt());
|
||||||
cat->setCustomId(query_categories.value(CAT_DB_CUSTOM_ID_INDEX).toString());
|
cat->setCustomId(query_categories.value(CAT_DB_CUSTOM_ID_INDEX).toString());
|
||||||
|
|
||||||
if (cat->customId().isEmpty()) {
|
if (cat->customId().isEmpty()) {
|
||||||
@ -287,6 +293,7 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
|
|||||||
// Load common data.
|
// Load common data.
|
||||||
feed->setTitle(query.value(FDS_DB_TITLE_INDEX).toString());
|
feed->setTitle(query.value(FDS_DB_TITLE_INDEX).toString());
|
||||||
feed->setId(query.value(FDS_DB_ID_INDEX).toInt());
|
feed->setId(query.value(FDS_DB_ID_INDEX).toInt());
|
||||||
|
feed->setSortOrder(query.value(FDS_DB_ORDER_INDEX).toInt());
|
||||||
feed->setSource(query.value(FDS_DB_SOURCE_INDEX).toString());
|
feed->setSource(query.value(FDS_DB_SOURCE_INDEX).toString());
|
||||||
feed->setCustomId(query.value(FDS_DB_CUSTOM_ID_INDEX).toString());
|
feed->setCustomId(query.value(FDS_DB_CUSTOM_ID_INDEX).toString());
|
||||||
|
|
||||||
|
@ -243,29 +243,31 @@
|
|||||||
|
|
||||||
// Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES.
|
// Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES.
|
||||||
#define CAT_DB_ID_INDEX 0
|
#define CAT_DB_ID_INDEX 0
|
||||||
#define CAT_DB_PARENT_ID_INDEX 1
|
#define CAT_DB_ORDER_INDEX 1
|
||||||
#define CAT_DB_TITLE_INDEX 2
|
#define CAT_DB_PARENT_ID_INDEX 2
|
||||||
#define CAT_DB_DESCRIPTION_INDEX 3
|
#define CAT_DB_TITLE_INDEX 3
|
||||||
#define CAT_DB_DCREATED_INDEX 4
|
#define CAT_DB_DESCRIPTION_INDEX 4
|
||||||
#define CAT_DB_ICON_INDEX 5
|
#define CAT_DB_DCREATED_INDEX 5
|
||||||
#define CAT_DB_ACCOUNT_ID_INDEX 6
|
#define CAT_DB_ICON_INDEX 6
|
||||||
#define CAT_DB_CUSTOM_ID_INDEX 7
|
#define CAT_DB_ACCOUNT_ID_INDEX 7
|
||||||
|
#define CAT_DB_CUSTOM_ID_INDEX 8
|
||||||
|
|
||||||
// Indexes of columns as they are DEFINED IN THE TABLE for FEEDS.
|
// Indexes of columns as they are DEFINED IN THE TABLE for FEEDS.
|
||||||
#define FDS_DB_ID_INDEX 0
|
#define FDS_DB_ID_INDEX 0
|
||||||
#define FDS_DB_TITLE_INDEX 1
|
#define FDS_DB_ORDER_INDEX 1
|
||||||
#define FDS_DB_DESCRIPTION_INDEX 2
|
#define FDS_DB_TITLE_INDEX 2
|
||||||
#define FDS_DB_DCREATED_INDEX 3
|
#define FDS_DB_DESCRIPTION_INDEX 3
|
||||||
#define FDS_DB_ICON_INDEX 4
|
#define FDS_DB_DCREATED_INDEX 4
|
||||||
#define FDS_DB_CATEGORY_INDEX 5
|
#define FDS_DB_ICON_INDEX 5
|
||||||
#define FDS_DB_SOURCE_INDEX 6
|
#define FDS_DB_CATEGORY_INDEX 6
|
||||||
#define FDS_DB_UPDATE_TYPE_INDEX 7
|
#define FDS_DB_SOURCE_INDEX 7
|
||||||
#define FDS_DB_UPDATE_INTERVAL_INDEX 8
|
#define FDS_DB_UPDATE_TYPE_INDEX 8
|
||||||
#define FDS_DB_IS_OFF_INDEX 9
|
#define FDS_DB_UPDATE_INTERVAL_INDEX 9
|
||||||
#define FDS_DB_OPEN_ARTICLES_INDEX 10
|
#define FDS_DB_IS_OFF_INDEX 10
|
||||||
#define FDS_DB_ACCOUNT_ID_INDEX 11
|
#define FDS_DB_OPEN_ARTICLES_INDEX 11
|
||||||
#define FDS_DB_CUSTOM_ID_INDEX 12
|
#define FDS_DB_ACCOUNT_ID_INDEX 12
|
||||||
#define FDS_DB_CUSTOM_DATA_INDEX 13
|
#define FDS_DB_CUSTOM_ID_INDEX 13
|
||||||
|
#define FDS_DB_CUSTOM_DATA_INDEX 14
|
||||||
|
|
||||||
// Indexes of columns for feed models.
|
// Indexes of columns for feed models.
|
||||||
#define FDS_MODEL_TITLE_INDEX 0
|
#define FDS_MODEL_TITLE_INDEX 0
|
||||||
|
@ -196,6 +196,7 @@ QList<QAction*> FormMain::allActions() const {
|
|||||||
actions << m_ui->m_actionClearSelectedItems;
|
actions << m_ui->m_actionClearSelectedItems;
|
||||||
actions << m_ui->m_actionClearAllItems;
|
actions << m_ui->m_actionClearAllItems;
|
||||||
actions << m_ui->m_actionShowOnlyUnreadItems;
|
actions << m_ui->m_actionShowOnlyUnreadItems;
|
||||||
|
actions << m_ui->m_actionSortFeedsAlphabetically;
|
||||||
actions << m_ui->m_actionShowTreeBranches;
|
actions << m_ui->m_actionShowTreeBranches;
|
||||||
actions << m_ui->m_actionAutoExpandItemsWhenSelected;
|
actions << m_ui->m_actionAutoExpandItemsWhenSelected;
|
||||||
actions << m_ui->m_actionShowOnlyUnreadMessages;
|
actions << m_ui->m_actionShowOnlyUnreadMessages;
|
||||||
@ -216,6 +217,10 @@ QList<QAction*> FormMain::allActions() const {
|
|||||||
actions << m_ui->m_actionServiceDelete;
|
actions << m_ui->m_actionServiceDelete;
|
||||||
actions << m_ui->m_actionCleanupDatabase;
|
actions << m_ui->m_actionCleanupDatabase;
|
||||||
actions << m_ui->m_actionAddFeedIntoSelectedItem;
|
actions << m_ui->m_actionAddFeedIntoSelectedItem;
|
||||||
|
actions << m_ui->m_actionFeedMoveUp;
|
||||||
|
actions << m_ui->m_actionFeedMoveDown;
|
||||||
|
actions << m_ui->m_actionFeedMoveTop;
|
||||||
|
actions << m_ui->m_actionFeedMoveBottom;
|
||||||
actions << m_ui->m_actionAddCategoryIntoSelectedItem;
|
actions << m_ui->m_actionAddCategoryIntoSelectedItem;
|
||||||
actions << m_ui->m_actionViewSelectedItemsNewspaperMode;
|
actions << m_ui->m_actionViewSelectedItemsNewspaperMode;
|
||||||
actions << m_ui->m_actionSelectNextItem;
|
actions << m_ui->m_actionSelectNextItem;
|
||||||
@ -475,6 +480,7 @@ void FormMain::updateFeedButtonsAvailability() {
|
|||||||
const bool feed_selected = anything_selected && selected_item->kind() == RootItem::Kind::Feed;
|
const bool feed_selected = anything_selected && selected_item->kind() == RootItem::Kind::Feed;
|
||||||
const bool category_selected = anything_selected && selected_item->kind() == RootItem::Kind::Category;
|
const bool category_selected = anything_selected && selected_item->kind() == RootItem::Kind::Category;
|
||||||
const bool service_selected = anything_selected && selected_item->kind() == RootItem::Kind::ServiceRoot;
|
const bool service_selected = anything_selected && selected_item->kind() == RootItem::Kind::ServiceRoot;
|
||||||
|
const bool manual_feed_sort = !m_ui->m_actionSortFeedsAlphabetically->isChecked();
|
||||||
|
|
||||||
m_ui->m_actionStopRunningItemsUpdate->setEnabled(is_update_running);
|
m_ui->m_actionStopRunningItemsUpdate->setEnabled(is_update_running);
|
||||||
m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running);
|
m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running);
|
||||||
@ -498,6 +504,11 @@ void FormMain::updateFeedButtonsAvailability() {
|
|||||||
m_ui->m_menuAddItem->setEnabled(!critical_action_running);
|
m_ui->m_menuAddItem->setEnabled(!critical_action_running);
|
||||||
m_ui->m_menuAccounts->setEnabled(!critical_action_running);
|
m_ui->m_menuAccounts->setEnabled(!critical_action_running);
|
||||||
m_ui->m_menuRecycleBin->setEnabled(!critical_action_running);
|
m_ui->m_menuRecycleBin->setEnabled(!critical_action_running);
|
||||||
|
|
||||||
|
m_ui->m_actionFeedMoveUp->setEnabled(manual_feed_sort &&(feed_selected || category_selected || service_selected));
|
||||||
|
m_ui->m_actionFeedMoveDown->setEnabled(manual_feed_sort &&(feed_selected || category_selected || service_selected));
|
||||||
|
m_ui->m_actionFeedMoveTop->setEnabled(manual_feed_sort &&(feed_selected || category_selected || service_selected));
|
||||||
|
m_ui->m_actionFeedMoveBottom->setEnabled(manual_feed_sort &&(feed_selected || category_selected || service_selected));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormMain::switchVisibility(bool force_hide) {
|
void FormMain::switchVisibility(bool force_hide) {
|
||||||
@ -595,6 +606,7 @@ void FormMain::setupIcons() {
|
|||||||
m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("arrow-down")));
|
m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("arrow-down")));
|
||||||
m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("arrow-up")));
|
m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("arrow-up")));
|
||||||
m_ui->m_actionSelectNextUnreadMessage->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
m_ui->m_actionSelectNextUnreadMessage->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
||||||
|
m_ui->m_actionSortFeedsAlphabetically->setIcon(icon_theme_factory->fromTheme(QSL("format-text-bold")));
|
||||||
m_ui->m_actionShowOnlyUnreadItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
m_ui->m_actionShowOnlyUnreadItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
||||||
m_ui->m_actionShowOnlyUnreadMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
m_ui->m_actionShowOnlyUnreadMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
||||||
m_ui->m_actionExpandCollapseItem->setIcon(icon_theme_factory->fromTheme(QSL("format-indent-more")));
|
m_ui->m_actionExpandCollapseItem->setIcon(icon_theme_factory->fromTheme(QSL("format-indent-more")));
|
||||||
@ -609,6 +621,11 @@ void FormMain::setupIcons() {
|
|||||||
m_ui->m_actionAddCategoryIntoSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("folder")));
|
m_ui->m_actionAddCategoryIntoSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("folder")));
|
||||||
m_ui->m_actionMessageFilters->setIcon(icon_theme_factory->fromTheme(QSL("view-list-details")));
|
m_ui->m_actionMessageFilters->setIcon(icon_theme_factory->fromTheme(QSL("view-list-details")));
|
||||||
|
|
||||||
|
m_ui->m_actionFeedMoveUp->setIcon(icon_theme_factory->fromTheme(QSL("arrow-up")));
|
||||||
|
m_ui->m_actionFeedMoveDown->setIcon(icon_theme_factory->fromTheme(QSL("arrow-down")));
|
||||||
|
m_ui->m_actionFeedMoveTop->setIcon(icon_theme_factory->fromTheme(QSL("arrow-up-double")));
|
||||||
|
m_ui->m_actionFeedMoveBottom->setIcon(icon_theme_factory->fromTheme(QSL("arrow-down-double")));
|
||||||
|
|
||||||
// Tabs & web browser.
|
// Tabs & web browser.
|
||||||
m_ui->m_actionTabNewWebBrowser->setIcon(icon_theme_factory->fromTheme(QSL("tab-new")));
|
m_ui->m_actionTabNewWebBrowser->setIcon(icon_theme_factory->fromTheme(QSL("tab-new")));
|
||||||
m_ui->m_actionTabsCloseAll->setIcon(icon_theme_factory->fromTheme(QSL("window-close")));
|
m_ui->m_actionTabsCloseAll->setIcon(icon_theme_factory->fromTheme(QSL("window-close")));
|
||||||
@ -667,6 +684,8 @@ void FormMain::loadSize() {
|
|||||||
m_ui->m_actionSwitchStatusBar->setChecked(settings->value(GROUP(GUI), SETTING(GUI::StatusBarVisible)).toBool());
|
m_ui->m_actionSwitchStatusBar->setChecked(settings->value(GROUP(GUI), SETTING(GUI::StatusBarVisible)).toBool());
|
||||||
|
|
||||||
// Other startup GUI-related settings.
|
// Other startup GUI-related settings.
|
||||||
|
m_ui->m_actionSortFeedsAlphabetically->setChecked(settings->value(GROUP(Feeds),
|
||||||
|
SETTING(Feeds::SortAlphabetically)).toBool());
|
||||||
m_ui->m_actionShowOnlyUnreadItems->setChecked(settings->value(GROUP(Feeds),
|
m_ui->m_actionShowOnlyUnreadItems->setChecked(settings->value(GROUP(Feeds),
|
||||||
SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool());
|
SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool());
|
||||||
m_ui->m_actionShowTreeBranches->setChecked(settings->value(GROUP(Feeds),
|
m_ui->m_actionShowTreeBranches->setChecked(settings->value(GROUP(Feeds),
|
||||||
@ -858,6 +877,8 @@ void FormMain::createConnections() {
|
|||||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchMessageSplitterOrientation);
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchMessageSplitterOrientation);
|
||||||
connect(m_ui->m_actionShowOnlyUnreadItems, &QAction::toggled,
|
connect(m_ui->m_actionShowOnlyUnreadItems, &QAction::toggled,
|
||||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds);
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds);
|
||||||
|
connect(m_ui->m_actionSortFeedsAlphabetically, &QAction::toggled,
|
||||||
|
tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::toggleFeedSortingMode);
|
||||||
connect(m_ui->m_actionShowTreeBranches, &QAction::toggled,
|
connect(m_ui->m_actionShowTreeBranches, &QAction::toggled,
|
||||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowFeedTreeBranches);
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowFeedTreeBranches);
|
||||||
connect(m_ui->m_actionAutoExpandItemsWhenSelected, &QAction::toggled,
|
connect(m_ui->m_actionAutoExpandItemsWhenSelected, &QAction::toggled,
|
||||||
@ -876,6 +897,8 @@ void FormMain::createConnections() {
|
|||||||
qApp->feedReader()->showMessageFiltersManager();
|
qApp->feedReader()->showMessageFiltersManager();
|
||||||
tabWidget()->feedMessageViewer()->messagesView()->reloadSelections();
|
tabWidget()->feedMessageViewer()->messagesView()->reloadSelections();
|
||||||
});
|
});
|
||||||
|
connect(m_ui->m_actionFeedMoveUp, &QAction::triggered,
|
||||||
|
tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::moveSelectedItemUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormMain::backupDatabaseSettings() {
|
void FormMain::backupDatabaseSettings() {
|
||||||
|
@ -107,6 +107,16 @@
|
|||||||
<string>&Add item</string>
|
<string>&Add item</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="m_menuFeedsMove">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Move</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="m_actionFeedMoveUp"/>
|
||||||
|
<addaction name="m_actionFeedMoveDown"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="m_actionFeedMoveTop"/>
|
||||||
|
<addaction name="m_actionFeedMoveBottom"/>
|
||||||
|
</widget>
|
||||||
<addaction name="m_actionUpdateAllItems"/>
|
<addaction name="m_actionUpdateAllItems"/>
|
||||||
<addaction name="m_actionUpdateSelectedItems"/>
|
<addaction name="m_actionUpdateSelectedItems"/>
|
||||||
<addaction name="m_actionUpdateSelectedItemsWithCustomTimers"/>
|
<addaction name="m_actionUpdateSelectedItemsWithCustomTimers"/>
|
||||||
@ -116,11 +126,13 @@
|
|||||||
<addaction name="m_actionEditSelectedItem"/>
|
<addaction name="m_actionEditSelectedItem"/>
|
||||||
<addaction name="m_actionDeleteSelectedItem"/>
|
<addaction name="m_actionDeleteSelectedItem"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="m_actionSortFeedsAlphabetically"/>
|
||||||
<addaction name="m_actionShowOnlyUnreadItems"/>
|
<addaction name="m_actionShowOnlyUnreadItems"/>
|
||||||
<addaction name="m_actionAutoExpandItemsWhenSelected"/>
|
<addaction name="m_actionAutoExpandItemsWhenSelected"/>
|
||||||
<addaction name="m_actionShowTreeBranches"/>
|
<addaction name="m_actionShowTreeBranches"/>
|
||||||
<addaction name="m_actionExpandCollapseItem"/>
|
<addaction name="m_actionExpandCollapseItem"/>
|
||||||
<addaction name="m_actionExpandCollapseItemRecursively"/>
|
<addaction name="m_actionExpandCollapseItemRecursively"/>
|
||||||
|
<addaction name="m_menuFeedsMove"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="m_actionSelectNextItem"/>
|
<addaction name="m_actionSelectNextItem"/>
|
||||||
<addaction name="m_actionSelectPreviousItem"/>
|
<addaction name="m_actionSelectPreviousItem"/>
|
||||||
@ -853,6 +865,37 @@
|
|||||||
<string>Open in internal browser (no new tab)</string>
|
<string>Open in internal browser (no new tab)</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="m_actionSortFeedsAlphabetically">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Sort alphabetically</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="m_actionFeedMoveUp">
|
||||||
|
<property name="text">
|
||||||
|
<string>Move &up</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="m_actionFeedMoveTop">
|
||||||
|
<property name="text">
|
||||||
|
<string>Move to &top</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="m_actionFeedMoveDown">
|
||||||
|
<property name="text">
|
||||||
|
<string>Move &down</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="m_actionFeedMoveBottom">
|
||||||
|
<property name="text">
|
||||||
|
<string>Move to &bottom</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -288,6 +288,10 @@ void FeedsView::deleteSelectedItem() {
|
|||||||
qApp->feedUpdateLock()->unlock();
|
qApp->feedUpdateLock()->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedsView::moveSelectedItemUp() {
|
||||||
|
m_sourceModel->changeSortOrder(selectedItem(), false, false, selectedItem()->sortOrder() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) {
|
void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) {
|
||||||
m_sourceModel->markItemRead(selectedItem(), read);
|
m_sourceModel->markItemRead(selectedItem(), read);
|
||||||
}
|
}
|
||||||
@ -512,6 +516,11 @@ void FeedsView::filterItems(const QString& pattern) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedsView::toggleFeedSortingMode(bool sort_alphabetically) {
|
||||||
|
setSortingEnabled(sort_alphabetically);
|
||||||
|
m_proxyModel->setSortAlphabetically(sort_alphabetically);
|
||||||
|
}
|
||||||
|
|
||||||
void FeedsView::onIndexExpanded(const QModelIndex& idx) {
|
void FeedsView::onIndexExpanded(const QModelIndex& idx) {
|
||||||
qDebugNN << LOGSEC_GUI << "Feed list item expanded - " << m_proxyModel->data(idx).toString();
|
qDebugNN << LOGSEC_GUI << "Feed list item expanded - " << m_proxyModel->data(idx).toString();
|
||||||
|
|
||||||
@ -698,6 +707,15 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) {
|
|||||||
m_contextMenuFeeds->addAction(qApp->mainForm()->m_ui->m_actionAddFeedIntoSelectedItem);
|
m_contextMenuFeeds->addAction(qApp->mainForm()->m_ui->m_actionAddFeedIntoSelectedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!qApp->settings()->value(GROUP(Feeds),
|
||||||
|
SETTING(Feeds::SortAlphabetically)).toBool()) {
|
||||||
|
m_contextMenuFeeds->addSeparator();
|
||||||
|
m_contextMenuFeeds->addAction(qApp->mainForm()->m_ui->m_actionFeedMoveUp);
|
||||||
|
m_contextMenuFeeds->addAction(qApp->mainForm()->m_ui->m_actionFeedMoveDown);
|
||||||
|
m_contextMenuFeeds->addAction(qApp->mainForm()->m_ui->m_actionFeedMoveTop);
|
||||||
|
m_contextMenuFeeds->addAction(qApp->mainForm()->m_ui->m_actionFeedMoveBottom);
|
||||||
|
}
|
||||||
|
|
||||||
if (!specific_actions.isEmpty()) {
|
if (!specific_actions.isEmpty()) {
|
||||||
m_contextMenuFeeds->addSeparator();
|
m_contextMenuFeeds->addSeparator();
|
||||||
m_contextMenuFeeds->addActions(specific_actions);
|
m_contextMenuFeeds->addActions(specific_actions);
|
||||||
|
@ -66,6 +66,9 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
|||||||
void editSelectedItem();
|
void editSelectedItem();
|
||||||
void deleteSelectedItem();
|
void deleteSelectedItem();
|
||||||
|
|
||||||
|
// Sort order manipulations.
|
||||||
|
void moveSelectedItemUp();
|
||||||
|
|
||||||
// Selects next/previous item (feed/category) in the list.
|
// Selects next/previous item (feed/category) in the list.
|
||||||
void selectNextItem();
|
void selectNextItem();
|
||||||
void selectPreviousItem();
|
void selectPreviousItem();
|
||||||
@ -76,6 +79,7 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
|||||||
void switchVisibility();
|
void switchVisibility();
|
||||||
|
|
||||||
void filterItems(const QString& pattern);
|
void filterItems(const QString& pattern);
|
||||||
|
void toggleFeedSortingMode(bool sort_alphabetically);
|
||||||
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
|
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -88,6 +88,9 @@ DVALUE(double) Feeds::FeedsUpdateStartupDelayDef = STARTUP_UPDATE_DELAY;
|
|||||||
DKEY Feeds::ShowOnlyUnreadFeeds = "show_only_unread_feeds";
|
DKEY Feeds::ShowOnlyUnreadFeeds = "show_only_unread_feeds";
|
||||||
DVALUE(bool) Feeds::ShowOnlyUnreadFeedsDef = false;
|
DVALUE(bool) Feeds::ShowOnlyUnreadFeedsDef = false;
|
||||||
|
|
||||||
|
DKEY Feeds::SortAlphabetically = "sort_alphabetically";
|
||||||
|
DVALUE(bool) Feeds::SortAlphabeticallyDef = true;
|
||||||
|
|
||||||
DKEY Feeds::ShowTreeBranches = "show_tree_branches";
|
DKEY Feeds::ShowTreeBranches = "show_tree_branches";
|
||||||
DVALUE(bool) Feeds::ShowTreeBranchesDef = true;
|
DVALUE(bool) Feeds::ShowTreeBranchesDef = true;
|
||||||
|
|
||||||
|
@ -93,6 +93,9 @@ namespace Feeds {
|
|||||||
KEY ShowOnlyUnreadFeeds;
|
KEY ShowOnlyUnreadFeeds;
|
||||||
VALUE(bool) ShowOnlyUnreadFeedsDef;
|
VALUE(bool) ShowOnlyUnreadFeedsDef;
|
||||||
|
|
||||||
|
KEY SortAlphabetically;
|
||||||
|
VALUE(bool) SortAlphabeticallyDef;
|
||||||
|
|
||||||
KEY ShowTreeBranches;
|
KEY ShowTreeBranches;
|
||||||
VALUE(bool) ShowTreeBranchesDef;
|
VALUE(bool) ShowTreeBranchesDef;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
RootItem::RootItem(RootItem* parent_item)
|
RootItem::RootItem(RootItem* parent_item)
|
||||||
: QObject(nullptr), m_kind(RootItem::Kind::Root), m_id(NO_PARENT_CATEGORY), m_customId(QL1S("")),
|
: QObject(nullptr), m_kind(RootItem::Kind::Root), m_id(NO_PARENT_CATEGORY), m_customId(QL1S("")),
|
||||||
m_title(QString()), m_description(QString()), m_creationDate(QDateTime::currentDateTimeUtc()),
|
m_title(QString()), m_description(QString()), m_creationDate(QDateTime::currentDateTimeUtc()),
|
||||||
m_keepOnTop(false), m_childItems(QList<RootItem*>()), m_parentItem(parent_item) {}
|
m_keepOnTop(false), m_sortOrder(NO_PARENT_CATEGORY), m_childItems(QList<RootItem*>()), m_parentItem(parent_item) {}
|
||||||
|
|
||||||
RootItem::RootItem(const RootItem& other) : RootItem(nullptr) {
|
RootItem::RootItem(const RootItem& other) : RootItem(nullptr) {
|
||||||
setTitle(other.title());
|
setTitle(other.title());
|
||||||
@ -24,6 +24,7 @@ RootItem::RootItem(const RootItem& other) : RootItem(nullptr) {
|
|||||||
setCustomId(other.customId());
|
setCustomId(other.customId());
|
||||||
setIcon(other.icon());
|
setIcon(other.icon());
|
||||||
setKeepOnTop(other.keepOnTop());
|
setKeepOnTop(other.keepOnTop());
|
||||||
|
setSortOrder(other.sortOrder());
|
||||||
|
|
||||||
// NOTE: We do not need to clone childs, because that would mean that
|
// NOTE: We do not need to clone childs, because that would mean that
|
||||||
// either source or target item tree would get corrupted.
|
// either source or target item tree would get corrupted.
|
||||||
@ -564,6 +565,14 @@ void RootItem::setKeepOnTop(bool keep_on_top) {
|
|||||||
m_keepOnTop = keep_on_top;
|
m_keepOnTop = keep_on_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RootItem::sortOrder() const {
|
||||||
|
return m_sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RootItem::setSortOrder(int sort_order) {
|
||||||
|
m_sortOrder = sort_order;
|
||||||
|
}
|
||||||
|
|
||||||
bool RootItem::removeChild(int index) {
|
bool RootItem::removeChild(int index) {
|
||||||
if (index >= 0 && index < m_childItems.size()) {
|
if (index >= 0 && index < m_childItems.size()) {
|
||||||
m_childItems.removeAt(index);
|
m_childItems.removeAt(index);
|
||||||
|
@ -199,6 +199,20 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
|||||||
bool keepOnTop() const;
|
bool keepOnTop() const;
|
||||||
void setKeepOnTop(bool keep_on_top);
|
void setKeepOnTop(bool keep_on_top);
|
||||||
|
|
||||||
|
// Sort order, when items in feeds list are sorted manually.
|
||||||
|
//
|
||||||
|
// NOTE: This is only used for "Account", "Category" and "Feed" classes
|
||||||
|
// which can be manually sorted. Other types like "Label" cannot be
|
||||||
|
// automatically sorted and are always sorted by title.
|
||||||
|
//
|
||||||
|
// Sort order number cannot be negative but order of list of items with same
|
||||||
|
// parent MUST form continuous series AND start with zero, for example:
|
||||||
|
// 0, 1, 2, 3, 4, ...
|
||||||
|
//
|
||||||
|
// NOTE: This is checked with DatabaseQueries::fixupOrders() method on app startup.
|
||||||
|
int sortOrder() const;
|
||||||
|
void setSortOrder(int sort_order);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RootItem::Kind m_kind;
|
RootItem::Kind m_kind;
|
||||||
int m_id;
|
int m_id;
|
||||||
@ -208,6 +222,7 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
|||||||
QIcon m_icon;
|
QIcon m_icon;
|
||||||
QDateTime m_creationDate;
|
QDateTime m_creationDate;
|
||||||
bool m_keepOnTop;
|
bool m_keepOnTop;
|
||||||
|
int m_sortOrder;
|
||||||
QList<RootItem*> m_childItems;
|
QList<RootItem*> m_childItems;
|
||||||
RootItem* m_parentItem;
|
RootItem* m_parentItem;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user