diff --git a/resources/desktop/com.github.rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml
index d12ea4713..44aaa380f 100644
--- a/resources/desktop/com.github.rssguard.appdata.xml
+++ b/resources/desktop/com.github.rssguard.appdata.xml
@@ -26,7 +26,7 @@
https://github.com/sponsors/martinrotter
-
+
none
diff --git a/resources/sql/db_update_sqlite_1_2.sql b/resources/sql/db_update_sqlite_1_2.sql
index bbded1e20..32ededa85 100644
--- a/resources/sql/db_update_sqlite_1_2.sql
+++ b/resources/sql/db_update_sqlite_1_2.sql
@@ -27,6 +27,13 @@ FROM 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 (
@@ -49,6 +56,13 @@ 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 (
@@ -65,7 +79,7 @@ CREATE TABLE Accounts (
);
-- !
INSERT INTO Accounts (id, ordr, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data)
-SELECT id, id, 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;
\ No newline at end of file
diff --git a/src/librssguard/core/feedsmodel.cpp b/src/librssguard/core/feedsmodel.cpp
index f1b48ea94..ea1f05632 100644
--- a/src/librssguard/core/feedsmodel.cpp
+++ b/src/librssguard/core/feedsmodel.cpp
@@ -4,6 +4,7 @@
#include "3rd-party/boolinq/boolinq.h"
#include "database/databasefactory.h"
+#include "database/databasequeries.h"
#include "definitions/definitions.h"
#include "gui/dialogs/formmain.h"
#include "miscellaneous/feedreader.h"
@@ -523,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();
diff --git a/src/librssguard/core/feedsmodel.h b/src/librssguard/core/feedsmodel.h
index 7a29d6839..0b55473c9 100644
--- a/src/librssguard/core/feedsmodel.h
+++ b/src/librssguard/core/feedsmodel.h
@@ -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);
diff --git a/src/librssguard/database/databasequeries.cpp b/src/librssguard/database/databasequeries.cpp
index d159aaaf2..ebf52f87d 100644
--- a/src/librssguard/database/databasequeries.cpp
+++ b/src/librssguard/database/databasequeries.cpp
@@ -1939,6 +1939,23 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
if (category->id() <= 0) {
// We need to insert category first.
+ if (category->sortOrder() < 0) {
+ q.exec(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() ? 0 : q.value(0).toInt()) + 1;
+
+ category->setSortOrder(next_order);
+ q.finish();
+ }
+
q.prepare(QSL("INSERT INTO "
"Categories (parent_id, ordr, title, date_created, account_id) "
"VALUES (0, 0, 'new', 0, %1);").arg(QString::number(account_id)));
@@ -1975,6 +1992,23 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
if (feed->id() <= 0) {
// We need to insert feed first.
+ if (feed->sortOrder() < 0) {
+ q.exec(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() ? 0 : q.value(0).toInt()) + 1;
+
+ feed->setSortOrder(next_order);
+ q.finish();
+ }
+
q.prepare(QSL("INSERT INTO "
"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)));
@@ -2008,6 +2042,9 @@ 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());
+
+ // TODO: upravit set na ordr = (SELECT MAX(ordr) FROM Feeds WHERE account_id = :account_id AND category = :category) + 1;
+ // to ale pokud je sortOrder < 0
q.bindValue(QSL(":ordr"), feed->sortOrder());
q.bindValue(QSL(":is_off"), feed->isSwitchedOff());
q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly());
@@ -2026,8 +2063,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 (ordr, type) VALUES (0, :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() ? 0 : 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()) {
@@ -2095,36 +2146,80 @@ bool DatabaseQueries::deleteCategory(const QSqlDatabase& db, int id) {
return q.exec();
}
-void DatabaseQueries::fixupOrders(const QSqlDatabase& db) {
- // We first determine if there are same orders assigned to some items
- // which have same parent category/acc.
- QSqlQuery res = db.exec(QSL("SELECT COUNT(*) FROM Accounts GROUP BY ordr HAVING COUNT(*) > 1 "
- "UNION ALL "
- "SELECT COUNT(*) FROM Categories GROUP BY account_id, parent_id, ordr HAVING COUNT(*) > 1 "
- "UNION ALL "
- "SELECT COUNT(*) FROM Feeds GROUP BY account_id, category, ordr HAVING COUNT(*) > 1;"));
- bool should_fixup = res.lastError().isValid() || res.size() > 0;
+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;
- if (should_fixup) {
- // Some orders are messed up, fix.
- qCriticalNN << LOGSEC_DB << "Order of items is messed up, fixing.";
+ case RootItem::Kind::Category:
+ break;
- for (const QString& table : { QSL("Accounts"), QSL("Categories"), QSL("Feeds") }) {
- QSqlQuery q = db.exec(QSL("UPDATE %1 SET ordr = id;").arg(table));
+ case RootItem::Kind::ServiceRoot:
- if (q.lastError().isValid()) {
- qFatal("Fixup of messed up order failed: '%s'.", qPrintable(q.lastError().text()));
- }
- }
- }
- else {
- qDebugNN << LOGSEC_DB << "No fixing of item order is needed.";
+ break;
+
+ default:
+ return;
}
}
-void DatabaseQueries::moveItemUp(RootItem* item, const QSqlDatabase& db) {}
+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;
+ }
-void DatabaseQueries::moveItemDown(RootItem* item, const QSqlDatabase& db) {}
+ 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) {
diff --git a/src/librssguard/database/databasequeries.h b/src/librssguard/database/databasequeries.h
index a4f9e0841..ca99323a7 100644
--- a/src/librssguard/database/databasequeries.h
+++ b/src/librssguard/database/databasequeries.h
@@ -135,9 +135,8 @@ class DatabaseQueries {
int account_id, bool* ok = nullptr);
// Item order methods.
- static void fixupOrders(const QSqlDatabase& db);
- static void moveItemUp(RootItem* item, const QSqlDatabase& db);
- static void moveItemDown(RootItem* item, const QSqlDatabase& db);
+ 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);
@@ -338,9 +337,6 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
template
void DatabaseQueries::loadRootFromDatabase(ServiceRoot* root) {
QSqlDatabase database = qApp->database()->driver()->connection(root->metaObject()->className());
-
- fixupOrders(database);
-
Assignment categories = DatabaseQueries::getCategories(database, root->accountId());
Assignment feeds = DatabaseQueries::getFeeds(database, qApp->feedReader()->messageFilters(), root->accountId());
auto labels = DatabaseQueries::getLabelsForAccount(database, root->accountId());
diff --git a/src/librssguard/gui/dialogs/formmain.cpp b/src/librssguard/gui/dialogs/formmain.cpp
index 3da2bc3ed..0965c4ed1 100644
--- a/src/librssguard/gui/dialogs/formmain.cpp
+++ b/src/librssguard/gui/dialogs/formmain.cpp
@@ -897,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() {
diff --git a/src/librssguard/gui/feedsview.cpp b/src/librssguard/gui/feedsview.cpp
index eb26b2a09..76373b37d 100644
--- a/src/librssguard/gui/feedsview.cpp
+++ b/src/librssguard/gui/feedsview.cpp
@@ -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);
}
diff --git a/src/librssguard/gui/feedsview.h b/src/librssguard/gui/feedsview.h
index c041b8e4d..e715ad94e 100644
--- a/src/librssguard/gui/feedsview.h
+++ b/src/librssguard/gui/feedsview.h
@@ -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();
diff --git a/src/librssguard/services/abstract/rootitem.cpp b/src/librssguard/services/abstract/rootitem.cpp
index 62e8ed793..198b3f05e 100644
--- a/src/librssguard/services/abstract/rootitem.cpp
+++ b/src/librssguard/services/abstract/rootitem.cpp
@@ -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_sortOrder(0), m_childItems(QList()), m_parentItem(parent_item) {}
+ m_keepOnTop(false), m_sortOrder(NO_PARENT_CATEGORY), m_childItems(QList()), m_parentItem(parent_item) {}
RootItem::RootItem(const RootItem& other) : RootItem(nullptr) {
setTitle(other.title());