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 (
|
||||
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,
|
||||
@ -17,6 +18,7 @@ CREATE TABLE Accounts (
|
||||
-- !
|
||||
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,
|
||||
@ -30,6 +32,7 @@ CREATE TABLE Categories (
|
||||
-- !
|
||||
CREATE TABLE Feeds (
|
||||
id $$,
|
||||
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||
title TEXT NOT NULL CHECK (title != ''),
|
||||
description TEXT,
|
||||
date_created BIGINT,
|
||||
|
@ -2,6 +2,7 @@ ALTER TABLE Feeds RENAME TO backup_Feeds;
|
||||
-- !
|
||||
CREATE TABLE Feeds (
|
||||
id $$,
|
||||
ordr INTEGER NOT NULL CHECK (ordr >= 0),
|
||||
title TEXT NOT NULL CHECK (title != ''),
|
||||
description TEXT,
|
||||
date_created BIGINT,
|
||||
@ -24,4 +25,61 @@ INSERT INTO Feeds (id, title, description, date_created, icon, category, source,
|
||||
SELECT id, title, description, date_created, icon, category, source, update_type, update_interval, account_id, custom_id, custom_data
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
auto serv = qApp->feedReader()->feedServices();
|
||||
|
||||
|
@ -107,6 +107,8 @@ class RSSGUARD_DLLSPEC FeedsModel : public QAbstractItemModel {
|
||||
bool restoreAllBins();
|
||||
bool emptyAllBins();
|
||||
|
||||
void changeSortOrder(RootItem* item, bool move_top, bool move_bottom, int new_sort_order);
|
||||
|
||||
// Feeds operations.
|
||||
bool markItemRead(RootItem* item, RootItem::ReadStatus read);
|
||||
bool markItemCleared(RootItem* item, bool clean_read_only);
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent)
|
||||
: 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"));
|
||||
|
||||
setSortRole(Qt::ItemDataRole::EditRole);
|
||||
@ -171,14 +171,32 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right
|
||||
return sortOrder() == Qt::SortOrder::DescendingOrder;
|
||||
}
|
||||
else if (left_item->kind() == right_item->kind()) {
|
||||
// Both items are of the same type.
|
||||
if (left.column() == FDS_MODEL_COUNTS_INDEX) {
|
||||
// User wants to sort according to counts.
|
||||
return left_item->countOfUnreadMessages() < right_item->countOfUnreadMessages();
|
||||
if (m_sortAlphabetically) {
|
||||
// Both items are of the same type.
|
||||
if (left.column() == FDS_MODEL_COUNTS_INDEX) {
|
||||
// User wants to sort according to counts.
|
||||
return left_item->countOfUnreadMessages() < right_item->countOfUnreadMessages();
|
||||
}
|
||||
else {
|
||||
// In other cases, sort by title.
|
||||
return QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// In other cases, sort by title.
|
||||
return QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0;
|
||||
// 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 {
|
||||
@ -297,6 +315,14 @@ void FeedsProxyModel::setShowUnreadOnly(bool 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 source_indexes;
|
||||
|
||||
|
@ -34,6 +34,8 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
||||
void setSelectedItem(const RootItem* selected_item);
|
||||
void setView(FeedsView* newView);
|
||||
|
||||
void setSortAlphabetically(bool sort_alphabetically);
|
||||
|
||||
public slots:
|
||||
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
|
||||
|
||||
@ -52,6 +54,7 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
||||
FeedsView* m_view;
|
||||
const RootItem* m_selectedItem;
|
||||
bool m_showUnreadOnly;
|
||||
bool m_sortAlphabetically;
|
||||
QList<QPair<int, QModelIndex>> m_hiddenIndices;
|
||||
QList<RootItem::Kind> m_priorities;
|
||||
};
|
||||
|
@ -1939,9 +1939,26 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
|
||||
|
||||
if (category->id() <= 0) {
|
||||
// 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 "
|
||||
"Categories (parent_id, title, date_created, account_id) "
|
||||
"VALUES (0, 'new', 0, %1);").arg(QString::number(account_id)));
|
||||
"Categories (parent_id, ordr, title, date_created, account_id) "
|
||||
"VALUES (0, 0, 'new', 0, %1);").arg(QString::number(account_id)));
|
||||
|
||||
if (!q.exec()) {
|
||||
throw ApplicationException(q.lastError().text());
|
||||
@ -1952,7 +1969,7 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
|
||||
}
|
||||
|
||||
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 "
|
||||
"WHERE id = :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(":custom_id"), category->customId());
|
||||
q.bindValue(QSL(":id"), category->id());
|
||||
q.bindValue(QSL(":ordr"), category->sortOrder());
|
||||
|
||||
if (!q.exec()) {
|
||||
throw ApplicationException(q.lastError().text());
|
||||
@ -1974,9 +1992,26 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
|
||||
|
||||
if (feed->id() <= 0) {
|
||||
// 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 "
|
||||
"Feeds (title, date_created, category, update_type, update_interval, account_id, custom_id) "
|
||||
"VALUES ('new', 0, 0, 0, 1, %1, 'new');").arg(QString::number(account_id)));
|
||||
"Feeds (title, ordr, date_created, category, update_type, update_interval, account_id, custom_id) "
|
||||
"VALUES ('new', 0, 0, 0, 0, 1, %1, 'new');").arg(QString::number(account_id)));
|
||||
|
||||
if (!q.exec()) {
|
||||
throw ApplicationException(q.lastError().text());
|
||||
@ -1991,7 +2026,7 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
|
||||
}
|
||||
|
||||
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, "
|
||||
" update_interval = :update_interval, is_off = :is_off, open_articles = :open_articles, "
|
||||
" 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(":custom_id"), feed->customId());
|
||||
q.bindValue(QSL(":id"), feed->id());
|
||||
q.bindValue(QSL(":ordr"), feed->sortOrder());
|
||||
q.bindValue(QSL(":is_off"), feed->isSwitchedOff());
|
||||
q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly());
|
||||
|
||||
@ -2024,9 +2060,22 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
|
||||
QSqlQuery q(db);
|
||||
|
||||
if (account->accountId() <= 0) {
|
||||
// We need to insert account first.
|
||||
q.prepare(QSL("INSERT INTO Accounts (type) "
|
||||
"VALUES (:type);"));
|
||||
// We need to insert account and generate sort order first.
|
||||
if (account->sortOrder() < 0) {
|
||||
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());
|
||||
|
||||
if (!q.exec()) {
|
||||
@ -2042,7 +2091,7 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
|
||||
|
||||
q.prepare(QSL("UPDATE Accounts "
|
||||
"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 "
|
||||
"WHERE id = :id"));
|
||||
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_password"), TextFactory::encrypt(proxy.password()));
|
||||
q.bindValue(QSL(":id"), account->accountId());
|
||||
q.bindValue(QSL(":ordr"), account->sortOrder());
|
||||
|
||||
auto custom_data = account->customDatabaseData();
|
||||
QString serialized_custom_data = serializeCustomData(custom_data);
|
||||
@ -2093,6 +2143,81 @@ bool DatabaseQueries::deleteCategory(const QSqlDatabase& db, int id) {
|
||||
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,
|
||||
const QString& script) {
|
||||
if (!db.driver()->hasFeature(QSqlDriver::DriverFeature::LastInsertId)) {
|
||||
|
@ -134,6 +134,10 @@ class DatabaseQueries {
|
||||
static Assignment getFeeds(const QSqlDatabase& db, const QList<MessageFilter*>& global_filters,
|
||||
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.
|
||||
static bool purgeLeftoverMessageFilterAssignments(const QSqlDatabase& db, int account_id);
|
||||
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.
|
||||
root->setAccountId(query.value(QSL("id")).toInt());
|
||||
root->setSortOrder(query.value(QSL("ordr")).toInt());
|
||||
|
||||
QNetworkProxy proxy(QNetworkProxy::ProxyType(query.value(QSL("proxy_type")).toInt()),
|
||||
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);
|
||||
|
||||
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());
|
||||
|
||||
if (cat->customId().isEmpty()) {
|
||||
@ -287,6 +293,7 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
|
||||
// Load common data.
|
||||
feed->setTitle(query.value(FDS_DB_TITLE_INDEX).toString());
|
||||
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->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.
|
||||
#define CAT_DB_ID_INDEX 0
|
||||
#define CAT_DB_PARENT_ID_INDEX 1
|
||||
#define CAT_DB_TITLE_INDEX 2
|
||||
#define CAT_DB_DESCRIPTION_INDEX 3
|
||||
#define CAT_DB_DCREATED_INDEX 4
|
||||
#define CAT_DB_ICON_INDEX 5
|
||||
#define CAT_DB_ACCOUNT_ID_INDEX 6
|
||||
#define CAT_DB_CUSTOM_ID_INDEX 7
|
||||
#define CAT_DB_ORDER_INDEX 1
|
||||
#define CAT_DB_PARENT_ID_INDEX 2
|
||||
#define CAT_DB_TITLE_INDEX 3
|
||||
#define CAT_DB_DESCRIPTION_INDEX 4
|
||||
#define CAT_DB_DCREATED_INDEX 5
|
||||
#define CAT_DB_ICON_INDEX 6
|
||||
#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.
|
||||
#define FDS_DB_ID_INDEX 0
|
||||
#define FDS_DB_TITLE_INDEX 1
|
||||
#define FDS_DB_DESCRIPTION_INDEX 2
|
||||
#define FDS_DB_DCREATED_INDEX 3
|
||||
#define FDS_DB_ICON_INDEX 4
|
||||
#define FDS_DB_CATEGORY_INDEX 5
|
||||
#define FDS_DB_SOURCE_INDEX 6
|
||||
#define FDS_DB_UPDATE_TYPE_INDEX 7
|
||||
#define FDS_DB_UPDATE_INTERVAL_INDEX 8
|
||||
#define FDS_DB_IS_OFF_INDEX 9
|
||||
#define FDS_DB_OPEN_ARTICLES_INDEX 10
|
||||
#define FDS_DB_ACCOUNT_ID_INDEX 11
|
||||
#define FDS_DB_CUSTOM_ID_INDEX 12
|
||||
#define FDS_DB_CUSTOM_DATA_INDEX 13
|
||||
#define FDS_DB_ORDER_INDEX 1
|
||||
#define FDS_DB_TITLE_INDEX 2
|
||||
#define FDS_DB_DESCRIPTION_INDEX 3
|
||||
#define FDS_DB_DCREATED_INDEX 4
|
||||
#define FDS_DB_ICON_INDEX 5
|
||||
#define FDS_DB_CATEGORY_INDEX 6
|
||||
#define FDS_DB_SOURCE_INDEX 7
|
||||
#define FDS_DB_UPDATE_TYPE_INDEX 8
|
||||
#define FDS_DB_UPDATE_INTERVAL_INDEX 9
|
||||
#define FDS_DB_IS_OFF_INDEX 10
|
||||
#define FDS_DB_OPEN_ARTICLES_INDEX 11
|
||||
#define FDS_DB_ACCOUNT_ID_INDEX 12
|
||||
#define FDS_DB_CUSTOM_ID_INDEX 13
|
||||
#define FDS_DB_CUSTOM_DATA_INDEX 14
|
||||
|
||||
// Indexes of columns for feed models.
|
||||
#define FDS_MODEL_TITLE_INDEX 0
|
||||
|
@ -196,6 +196,7 @@ QList<QAction*> FormMain::allActions() const {
|
||||
actions << m_ui->m_actionClearSelectedItems;
|
||||
actions << m_ui->m_actionClearAllItems;
|
||||
actions << m_ui->m_actionShowOnlyUnreadItems;
|
||||
actions << m_ui->m_actionSortFeedsAlphabetically;
|
||||
actions << m_ui->m_actionShowTreeBranches;
|
||||
actions << m_ui->m_actionAutoExpandItemsWhenSelected;
|
||||
actions << m_ui->m_actionShowOnlyUnreadMessages;
|
||||
@ -216,6 +217,10 @@ QList<QAction*> FormMain::allActions() const {
|
||||
actions << m_ui->m_actionServiceDelete;
|
||||
actions << m_ui->m_actionCleanupDatabase;
|
||||
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_actionViewSelectedItemsNewspaperMode;
|
||||
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 category_selected = anything_selected && selected_item->kind() == RootItem::Kind::Category;
|
||||
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_actionBackupDatabaseSettings->setEnabled(!critical_action_running);
|
||||
@ -498,6 +504,11 @@ void FormMain::updateFeedButtonsAvailability() {
|
||||
m_ui->m_menuAddItem->setEnabled(!critical_action_running);
|
||||
m_ui->m_menuAccounts->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) {
|
||||
@ -595,6 +606,7 @@ void FormMain::setupIcons() {
|
||||
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_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_actionShowOnlyUnreadMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
|
||||
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_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.
|
||||
m_ui->m_actionTabNewWebBrowser->setIcon(icon_theme_factory->fromTheme(QSL("tab-new")));
|
||||
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());
|
||||
|
||||
// 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),
|
||||
SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool());
|
||||
m_ui->m_actionShowTreeBranches->setChecked(settings->value(GROUP(Feeds),
|
||||
@ -858,6 +877,8 @@ void FormMain::createConnections() {
|
||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchMessageSplitterOrientation);
|
||||
connect(m_ui->m_actionShowOnlyUnreadItems, &QAction::toggled,
|
||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds);
|
||||
connect(m_ui->m_actionSortFeedsAlphabetically, &QAction::toggled,
|
||||
tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::toggleFeedSortingMode);
|
||||
connect(m_ui->m_actionShowTreeBranches, &QAction::toggled,
|
||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowFeedTreeBranches);
|
||||
connect(m_ui->m_actionAutoExpandItemsWhenSelected, &QAction::toggled,
|
||||
@ -876,6 +897,8 @@ void FormMain::createConnections() {
|
||||
qApp->feedReader()->showMessageFiltersManager();
|
||||
tabWidget()->feedMessageViewer()->messagesView()->reloadSelections();
|
||||
});
|
||||
connect(m_ui->m_actionFeedMoveUp, &QAction::triggered,
|
||||
tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::moveSelectedItemUp);
|
||||
}
|
||||
|
||||
void FormMain::backupDatabaseSettings() {
|
||||
|
@ -107,6 +107,16 @@
|
||||
<string>&Add item</string>
|
||||
</property>
|
||||
</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_actionUpdateSelectedItems"/>
|
||||
<addaction name="m_actionUpdateSelectedItemsWithCustomTimers"/>
|
||||
@ -116,11 +126,13 @@
|
||||
<addaction name="m_actionEditSelectedItem"/>
|
||||
<addaction name="m_actionDeleteSelectedItem"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="m_actionSortFeedsAlphabetically"/>
|
||||
<addaction name="m_actionShowOnlyUnreadItems"/>
|
||||
<addaction name="m_actionAutoExpandItemsWhenSelected"/>
|
||||
<addaction name="m_actionShowTreeBranches"/>
|
||||
<addaction name="m_actionExpandCollapseItem"/>
|
||||
<addaction name="m_actionExpandCollapseItemRecursively"/>
|
||||
<addaction name="m_menuFeedsMove"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="m_actionSelectNextItem"/>
|
||||
<addaction name="m_actionSelectPreviousItem"/>
|
||||
@ -853,6 +865,37 @@
|
||||
<string>Open in internal browser (no new tab)</string>
|
||||
</property>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -288,6 +288,10 @@ void FeedsView::deleteSelectedItem() {
|
||||
qApp->feedUpdateLock()->unlock();
|
||||
}
|
||||
|
||||
void FeedsView::moveSelectedItemUp() {
|
||||
m_sourceModel->changeSortOrder(selectedItem(), false, false, selectedItem()->sortOrder() - 1);
|
||||
}
|
||||
|
||||
void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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()) {
|
||||
m_contextMenuFeeds->addSeparator();
|
||||
m_contextMenuFeeds->addActions(specific_actions);
|
||||
|
@ -66,6 +66,9 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
||||
void editSelectedItem();
|
||||
void deleteSelectedItem();
|
||||
|
||||
// Sort order manipulations.
|
||||
void moveSelectedItemUp();
|
||||
|
||||
// Selects next/previous item (feed/category) in the list.
|
||||
void selectNextItem();
|
||||
void selectPreviousItem();
|
||||
@ -76,6 +79,7 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
||||
void switchVisibility();
|
||||
|
||||
void filterItems(const QString& pattern);
|
||||
void toggleFeedSortingMode(bool sort_alphabetically);
|
||||
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
|
||||
|
||||
signals:
|
||||
|
@ -88,6 +88,9 @@ DVALUE(double) Feeds::FeedsUpdateStartupDelayDef = STARTUP_UPDATE_DELAY;
|
||||
DKEY Feeds::ShowOnlyUnreadFeeds = "show_only_unread_feeds";
|
||||
DVALUE(bool) Feeds::ShowOnlyUnreadFeedsDef = false;
|
||||
|
||||
DKEY Feeds::SortAlphabetically = "sort_alphabetically";
|
||||
DVALUE(bool) Feeds::SortAlphabeticallyDef = true;
|
||||
|
||||
DKEY Feeds::ShowTreeBranches = "show_tree_branches";
|
||||
DVALUE(bool) Feeds::ShowTreeBranchesDef = true;
|
||||
|
||||
|
@ -93,6 +93,9 @@ namespace Feeds {
|
||||
KEY ShowOnlyUnreadFeeds;
|
||||
VALUE(bool) ShowOnlyUnreadFeedsDef;
|
||||
|
||||
KEY SortAlphabetically;
|
||||
VALUE(bool) SortAlphabeticallyDef;
|
||||
|
||||
KEY ShowTreeBranches;
|
||||
VALUE(bool) ShowTreeBranchesDef;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
RootItem::RootItem(RootItem* parent_item)
|
||||
: 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_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) {
|
||||
setTitle(other.title());
|
||||
@ -24,6 +24,7 @@ RootItem::RootItem(const RootItem& other) : RootItem(nullptr) {
|
||||
setCustomId(other.customId());
|
||||
setIcon(other.icon());
|
||||
setKeepOnTop(other.keepOnTop());
|
||||
setSortOrder(other.sortOrder());
|
||||
|
||||
// NOTE: We do not need to clone childs, because that would mean that
|
||||
// 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;
|
||||
}
|
||||
|
||||
int RootItem::sortOrder() const {
|
||||
return m_sortOrder;
|
||||
}
|
||||
|
||||
void RootItem::setSortOrder(int sort_order) {
|
||||
m_sortOrder = sort_order;
|
||||
}
|
||||
|
||||
bool RootItem::removeChild(int index) {
|
||||
if (index >= 0 && index < m_childItems.size()) {
|
||||
m_childItems.removeAt(index);
|
||||
|
@ -199,6 +199,20 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
||||
bool keepOnTop() const;
|
||||
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:
|
||||
RootItem::Kind m_kind;
|
||||
int m_id;
|
||||
@ -208,6 +222,7 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
||||
QIcon m_icon;
|
||||
QDateTime m_creationDate;
|
||||
bool m_keepOnTop;
|
||||
int m_sortOrder;
|
||||
QList<RootItem*> m_childItems;
|
||||
RootItem* m_parentItem;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user