diff --git a/resources/desktop/com.github.rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml
index 281dbfc6b..432205d35 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/icons.qrc b/resources/icons.qrc
index 2c3d443cb..814381b3e 100644
--- a/resources/icons.qrc
+++ b/resources/icons.qrc
@@ -7,7 +7,9 @@
./graphics/Breeze/categories/32/applications-science.svg
./graphics/Breeze/categories/32/applications-system.svg
./graphics/Breeze/actions/32/arrow-down.svg
+ ./graphics/Breeze/actions/32/arrow-down-double.svg
./graphics/Breeze/actions/32/arrow-up.svg
+ ./graphics/Breeze/actions/32/arrow-up-double.svg
./graphics/Breeze/actions/32/call-start.svg
./graphics/Breeze/status/64/dialog-error.svg
./graphics/Breeze/status/64/dialog-information.svg
@@ -30,12 +32,11 @@
./graphics/Breeze/places/96/folder.svg
./graphics/Breeze/actions/22/format-indent-more.svg
./graphics/Breeze/actions/22/format-justify-fill.svg
- ./graphics/Breeze/actions/32/go-down.svg
+ ./graphics/Breeze/actions/22/format-text-bold.svg
./graphics/Breeze/actions/64/go-home.svg
./graphics/Breeze/actions/32/go-jump.svg
./graphics/Breeze/actions/32/go-next.svg
./graphics/Breeze/actions/32/go-previous.svg
- ./graphics/Breeze/actions/32/go-up.svg
./graphics/Breeze/actions/22/gtk-edit.svg
./graphics/Breeze/actions/32/help-about.svg
./graphics/Breeze/actions/22/help-contents.svg
@@ -75,7 +76,9 @@
./graphics/Breeze Dark/categories/32/applications-science.svg
./graphics/Breeze Dark/categories/32/applications-system.svg
./graphics/Breeze Dark/actions/32/arrow-down.svg
+ ./graphics/Breeze Dark/actions/32/arrow-down-double.svg
./graphics/Breeze Dark/actions/32/arrow-up.svg
+ ./graphics/Breeze Dark/actions/32/arrow-up-double.svg
./graphics/Breeze Dark/actions/32/call-start.svg
./graphics/Breeze Dark/status/64/dialog-error.svg
./graphics/Breeze Dark/status/64/dialog-information.svg
@@ -98,12 +101,11 @@
./graphics/Breeze Dark/places/96/folder.svg
./graphics/Breeze Dark/actions/22/format-indent-more.svg
./graphics/Breeze Dark/actions/22/format-justify-fill.svg
- ./graphics/Breeze Dark/actions/32/go-down.svg
+ ./graphics/Breeze Dark/actions/22/format-text-bold.svg
./graphics/Breeze Dark/actions/64/go-home.svg
./graphics/Breeze Dark/actions/32/go-jump.svg
./graphics/Breeze Dark/actions/32/go-next.svg
./graphics/Breeze Dark/actions/32/go-previous.svg
- ./graphics/Breeze Dark/actions/32/go-up.svg
./graphics/Breeze Dark/actions/22/gtk-edit.svg
./graphics/Breeze Dark/actions/32/help-about.svg
./graphics/Breeze Dark/actions/22/help-contents.svg
@@ -166,12 +168,11 @@
./graphics/Faenza/places/64/folder.png
./graphics/Faenza/actions/64/format-indent-more.png
./graphics/Faenza/actions/64/format-justify-fill.png
- ./graphics/Faenza/actions/64/go-down.png
+ ./graphics/Faenza/actions/64/format-text-bold.png
./graphics/Faenza/actions/64/go-home.png
./graphics/Faenza/actions/64/go-jump.png
./graphics/Faenza/actions/64/go-next.png
./graphics/Faenza/actions/64/go-previous.png
- ./graphics/Faenza/actions/64/go-up.png
./graphics/Faenza/actions/64/gtk-edit.png
./graphics/Faenza/actions/64/help-about.png
./graphics/Faenza/actions/64/help-contents.png
@@ -212,7 +213,9 @@
./graphics/Numix/22/categories/applications-science.svg
./graphics/Numix/22/categories/applications-system.svg
./graphics/Numix/22/actions/arrow-down.svg
+ ./graphics/Numix/22/actions/arrow-down-double.svg
./graphics/Numix/22/actions/arrow-up.svg
+ ./graphics/Numix/22/actions/arrow-up-double.svg
./graphics/Numix/22/actions/call-start.svg
./graphics/Numix/22/status/dialog-error.svg
./graphics/Numix/22/status/dialog-information.svg
@@ -238,12 +241,11 @@
./graphics/Numix/22/places/folder.svg
./graphics/Numix/22/actions/format-indent-more.svg
./graphics/Numix/22/actions/format-justify-fill.svg
- ./graphics/Numix/22/actions/go-down.svg
+ ./graphics/Numix/22/actions/format-text-bold.svg
./graphics/Numix/22/actions/go-home.svg
./graphics/Numix/22/actions/go-jump.svg
./graphics/Numix/22/actions/go-next.svg
./graphics/Numix/22/actions/go-previous.svg
- ./graphics/Numix/22/actions/go-up.svg
./graphics/Numix/22/actions/gtk-edit.svg
./graphics/Numix/22/categories/help-about.svg
./graphics/Numix/22/actions/help-contents.svg
diff --git a/resources/sql/db_init_sqlite.sql b/resources/sql/db_init_sqlite.sql
index 5fd1b067b..867eb8ab8 100644
--- a/resources/sql/db_init_sqlite.sql
+++ b/resources/sql/db_init_sqlite.sql
@@ -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,
diff --git a/resources/sql/db_update_mysql_1_2.sql b/resources/sql/db_update_mysql_1_2.sql
index 1b825cb30..8f00468ef 100755
--- a/resources/sql/db_update_mysql_1_2.sql
+++ b/resources/sql/db_update_mysql_1_2.sql
@@ -1,3 +1,8 @@
USE ##;
-- !
-!! db_update_sqlite_1_2.sql
\ No newline at end of file
+SET FOREIGN_KEY_CHECKS = 0;
+-- !
+!! db_update_sqlite_1_2.sql
+-- !
+SET FOREIGN_KEY_CHECKS = 1;
+-- !
\ No newline at end of file
diff --git a/resources/sql/db_update_sqlite_1_2.sql b/resources/sql/db_update_sqlite_1_2.sql
index 71cd4727a..bbded1e20 100755
--- a/resources/sql/db_update_sqlite_1_2.sql
+++ b/resources/sql/db_update_sqlite_1_2.sql
@@ -1,9 +1,8 @@
-CREATE TABLE backup_Feeds AS SELECT * FROM Feeds;
--- !
-DROP TABLE Feeds;
+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,
@@ -22,8 +21,51 @@ CREATE TABLE Feeds (
FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE
);
-- !
-INSERT INTO Feeds (id, title, description, date_created, icon, category, source, update_type, update_interval, account_id, custom_id, custom_data)
-SELECT id, title, description, date_created, icon, category, source, update_type, update_interval, account_id, custom_id, custom_data
+INSERT INTO Feeds (id, ordr, title, description, date_created, icon, category, source, update_type, update_interval, account_id, custom_id, custom_data)
+SELECT id, 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;
\ No newline at end of file
+DROP TABLE backup_Feeds;
+-- !
+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;
+-- !
+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, 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/feedsproxymodel.cpp b/src/librssguard/core/feedsproxymodel.cpp
index 5af28fff5..f93c7b27c 100644
--- a/src/librssguard/core/feedsproxymodel.cpp
+++ b/src/librssguard/core/feedsproxymodel.cpp
@@ -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);
@@ -172,14 +172,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 {
@@ -265,6 +283,10 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex
}
}
+void FeedsProxyModel::sort(int column, Qt::SortOrder order) {
+ QSortFilterProxyModel::sort(column, order);
+}
+
void FeedsProxyModel::setView(FeedsView* newView) {
m_view = newView;
}
@@ -294,6 +316,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;
diff --git a/src/librssguard/core/feedsproxymodel.h b/src/librssguard/core/feedsproxymodel.h
index bcea633df..d78a46053 100644
--- a/src/librssguard/core/feedsproxymodel.h
+++ b/src/librssguard/core/feedsproxymodel.h
@@ -17,9 +17,11 @@ class FeedsProxyModel : public QSortFilterProxyModel {
explicit FeedsProxyModel(FeedsModel* source_model, QObject* parent = nullptr);
virtual ~FeedsProxyModel();
+ virtual void sort(int column, Qt::SortOrder order = Qt::SortOrder::AscendingOrder);
+
// Returns index list of items which "match" given value.
// Used for finding items according to entered title text.
- QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const;
+ virtual QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const;
// Maps list of indexes.
QModelIndexList mapListToSource(const QModelIndexList& indexes) const;
@@ -32,24 +34,27 @@ 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);
signals:
void expandAfterFilterIn(QModelIndex source_idx) const;
- private:
+ protected:
+ virtual bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
- // Compares two rows of data.
- bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
- bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
- bool filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const;
+ private:
+ virtual bool filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const;
// Source model pointer.
FeedsModel* m_sourceModel;
FeedsView* m_view;
const RootItem* m_selectedItem;
bool m_showUnreadOnly;
+ bool m_sortAlphabetically;
QList> m_hiddenIndices;
QList m_priorities;
};
diff --git a/src/librssguard/database/databasequeries.cpp b/src/librssguard/database/databasequeries.cpp
index f348eb4d8..6770f41ca 100644
--- a/src/librssguard/database/databasequeries.cpp
+++ b/src/librssguard/database/databasequeries.cpp
@@ -1940,8 +1940,8 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
if (category->id() <= 0) {
// We need to insert category first.
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 +1952,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 +1963,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());
@@ -1975,8 +1976,8 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
if (feed->id() <= 0) {
// We need to insert feed first.
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 +1992,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 +2008,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());
@@ -2025,14 +2027,13 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
if (account->accountId() <= 0) {
// We need to insert account first.
- q.prepare(QSL("INSERT INTO Accounts (type) VALUES (:type);"));
+ q.prepare(QSL("INSERT INTO Accounts (ordr, type) VALUES (0, :type);"));
q.bindValue(QSL(":type"), account->code());
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
else {
- //account->setId(q.lastInsertId().toInt());
account->setAccountId(q.lastInsertId().toInt());
}
}
@@ -2042,7 +2043,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 +2052,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 +2095,10 @@ bool DatabaseQueries::deleteCategory(const QSqlDatabase& db, int id) {
return q.exec();
}
+void DatabaseQueries::moveItemUp(RootItem* item, const QSqlDatabase& db) {}
+
+void DatabaseQueries::moveItemDown(RootItem* item, const QSqlDatabase& db) {}
+
MessageFilter* DatabaseQueries::addMessageFilter(const QSqlDatabase& db, const QString& title,
const QString& script) {
if (!db.driver()->hasFeature(QSqlDriver::DriverFeature::LastInsertId)) {
diff --git a/src/librssguard/database/databasequeries.h b/src/librssguard/database/databasequeries.h
index de06513ae..38aeb3487 100644
--- a/src/librssguard/database/databasequeries.h
+++ b/src/librssguard/database/databasequeries.h
@@ -108,7 +108,7 @@ class DatabaseQueries {
static QList getAccounts(const QSqlDatabase& db, const QString& code, bool* ok = nullptr);
template
- static void loadFromDatabase(ServiceRoot* root);
+ static void loadRootFromDatabase(ServiceRoot* root);
static bool storeNewOauthTokens(const QSqlDatabase& db, const QString& refresh_token, int account_id);
static void createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account);
@@ -134,6 +134,10 @@ class DatabaseQueries {
static Assignment getFeeds(const QSqlDatabase& db, const QList& global_filters,
int account_id, bool* ok = nullptr);
+ // Item order methods.
+ static void moveItemUp(RootItem* item, const QSqlDatabase& db);
+ static void moveItemDown(RootItem* item, 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 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(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());
@@ -328,7 +335,7 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
}
template
-void DatabaseQueries::loadFromDatabase(ServiceRoot* root) {
+void DatabaseQueries::loadRootFromDatabase(ServiceRoot* root) {
QSqlDatabase database = qApp->database()->driver()->connection(root->metaObject()->className());
Assignment categories = DatabaseQueries::getCategories(database, root->accountId());
Assignment feeds = DatabaseQueries::getFeeds(database, qApp->feedReader()->messageFilters(), root->accountId());
diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h
index 0b4a22892..d8580e99a 100644
--- a/src/librssguard/definitions/definitions.h
+++ b/src/librssguard/definitions/definitions.h
@@ -119,6 +119,9 @@
#define CLI_NSTDOUTERR_SHORT "n"
#define CLI_NSTDOUTERR_LONG "no-standard-output"
+#define CLI_STYLE_SHORT "t"
+#define CLI_STYLE_LONG "style"
+
#define CLI_NDEBUG_SHORT "g"
#define CLI_NDEBUG_LONG "no-debug-output"
@@ -240,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
diff --git a/src/librssguard/gui/dialogs/formaddeditlabel.cpp b/src/librssguard/gui/dialogs/formaddeditlabel.cpp
index f52f7f855..59524a787 100644
--- a/src/librssguard/gui/dialogs/formaddeditlabel.cpp
+++ b/src/librssguard/gui/dialogs/formaddeditlabel.cpp
@@ -23,6 +23,7 @@ FormAddEditLabel::FormAddEditLabel(QWidget* parent) : QDialog(parent), m_editabl
});
m_ui.m_txtName->lineEdit()->setText(tr("Hot stuff"));
+ m_ui.m_txtName->lineEdit()->setFocus();
}
Label* FormAddEditLabel::execForAdd() {
diff --git a/src/librssguard/gui/dialogs/formaddeditlabel.ui b/src/librssguard/gui/dialogs/formaddeditlabel.ui
index be3f287e0..6a62a4c72 100644
--- a/src/librssguard/gui/dialogs/formaddeditlabel.ui
+++ b/src/librssguard/gui/dialogs/formaddeditlabel.ui
@@ -6,7 +6,7 @@
0
0
- 224
+ 270
97
diff --git a/src/librssguard/gui/dialogs/formmain.cpp b/src/librssguard/gui/dialogs/formmain.cpp
index 11becc9de..3da2bc3ed 100644
--- a/src/librssguard/gui/dialogs/formmain.cpp
+++ b/src/librssguard/gui/dialogs/formmain.cpp
@@ -56,7 +56,7 @@
FormMain::FormMain(QWidget* parent, Qt::WindowFlags f)
: QMainWindow(parent, f), m_ui(new Ui::FormMain), m_trayMenu(nullptr), m_statusBar(nullptr) {
- qDebugNN << LOGSEC_GUI << "Creating main application form in thread: '" << QThread::currentThreadId() << "'.";
+ qDebugNN << LOGSEC_GUI << "Creating main application form in thread:" << QUOTE_W_SPACE_DOT(QThread::currentThreadId());
m_ui->setupUi(this);
qApp->setMainForm(this);
@@ -196,6 +196,7 @@ QList 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 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) {
@@ -590,11 +601,12 @@ void FormMain::setupIcons() {
m_ui->m_actionOpenSelectedMessagesInternallyNoTab->setIcon(icon_theme_factory->fromTheme(QSL("document-open")));
m_ui->m_actionSendMessageViaEmail->setIcon(icon_theme_factory->fromTheme(QSL("mail-send")));
m_ui->m_actionViewSelectedItemsNewspaperMode->setIcon(icon_theme_factory->fromTheme(QSL("format-justify-fill")));
- m_ui->m_actionSelectNextItem->setIcon(icon_theme_factory->fromTheme(QSL("go-down")));
- m_ui->m_actionSelectPreviousItem->setIcon(icon_theme_factory->fromTheme(QSL("go-up")));
- m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-down")));
- m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-up")));
+ m_ui->m_actionSelectNextItem->setIcon(icon_theme_factory->fromTheme(QSL("arrow-down")));
+ m_ui->m_actionSelectPreviousItem->setIcon(icon_theme_factory->fromTheme(QSL("arrow-up")));
+ 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,
diff --git a/src/librssguard/gui/dialogs/formmain.ui b/src/librssguard/gui/dialogs/formmain.ui
index ecd059638..61f646c69 100644
--- a/src/librssguard/gui/dialogs/formmain.ui
+++ b/src/librssguard/gui/dialogs/formmain.ui
@@ -107,6 +107,16 @@
&Add item
+
@@ -116,11 +126,13 @@
+
+
@@ -853,6 +865,37 @@
Open in internal browser (no new tab)
+
+
+ true
+
+
+ true
+
+
+ &Sort alphabetically
+
+
+
+
+ Move &up
+
+
+
+
+ Move to &top
+
+
+
+
+ Move &down
+
+
+
+
+ Move to &bottom
+
+
diff --git a/src/librssguard/gui/feedsview.cpp b/src/librssguard/gui/feedsview.cpp
index f9dbdf52a..eb26b2a09 100644
--- a/src/librssguard/gui/feedsview.cpp
+++ b/src/librssguard/gui/feedsview.cpp
@@ -257,12 +257,12 @@ void FeedsView::deleteSelectedItem() {
if (selected_item->canBeDeleted()) {
// Ask user first.
if (MsgBox::show(qApp->mainFormWidget(),
- QMessageBox::Icon::Question,
- tr("Deleting \"%1\"").arg(selected_item->title()),
- tr("You are about to completely delete item \"%1\".").arg(selected_item->title()),
- tr("Are you sure?"),
- QString(), QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
- QMessageBox::StandardButton::Yes) == QMessageBox::StandardButton::No) {
+ QMessageBox::Icon::Question,
+ tr("Deleting \"%1\"").arg(selected_item->title()),
+ tr("You are about to completely delete item \"%1\".").arg(selected_item->title()),
+ tr("Are you sure?"),
+ QString(), QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
+ QMessageBox::StandardButton::Yes) == QMessageBox::StandardButton::No) {
// User refused.
qApp->feedUpdateLock()->unlock();
return;
@@ -512,6 +512,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 +703,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);
@@ -792,6 +806,7 @@ void FeedsView::setupAppearance() {
setUniformRowHeights(true);
setAnimated(true);
+
setSortingEnabled(true);
setItemsExpandable(true);
setAutoExpandDelay(0);
diff --git a/src/librssguard/gui/feedsview.h b/src/librssguard/gui/feedsview.h
index 6c2828226..c041b8e4d 100644
--- a/src/librssguard/gui/feedsview.h
+++ b/src/librssguard/gui/feedsview.h
@@ -76,7 +76,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:
diff --git a/src/librssguard/gui/guiutilities.cpp b/src/librssguard/gui/guiutilities.cpp
index 4faea8474..46b227945 100644
--- a/src/librssguard/gui/guiutilities.cpp
+++ b/src/librssguard/gui/guiutilities.cpp
@@ -28,11 +28,7 @@ void GuiUtilities::setLabelAsNotice(QLabel& label, bool is_warning, bool set_mar
void GuiUtilities::applyDialogProperties(QWidget& widget, const QIcon& icon, const QString& title) {
widget.setWindowFlags(
-#if defined(Q_OS_LINUX)
- Qt::WindowType::Window |
-#else
Qt::WindowType::Dialog |
-#endif
Qt::WindowType::WindowTitleHint |
Qt::WindowType::WindowMaximizeButtonHint |
Qt::WindowType::WindowCloseButtonHint);
diff --git a/src/librssguard/gui/tabbar.cpp b/src/librssguard/gui/tabbar.cpp
index 84d932c67..c33db4c90 100644
--- a/src/librssguard/gui/tabbar.cpp
+++ b/src/librssguard/gui/tabbar.cpp
@@ -11,7 +11,7 @@
#include
TabBar::TabBar(QWidget* parent) : QTabBar(parent) {
- setDocumentMode(false);
+ setDocumentMode(true);
setUsesScrollButtons(true);
setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
}
diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp
index bad0ddc2b..cb9ea20c0 100644
--- a/src/librssguard/miscellaneous/application.cpp
+++ b/src/librssguard/miscellaneous/application.cpp
@@ -60,9 +60,9 @@
#endif
#endif
-Application::Application(const QString& id, int& argc, char** argv)
+Application::Application(const QString& id, int& argc, char** argv, const QStringList& raw_cli_args)
: SingleApplication(id, argc, argv), m_updateFeedsLock(new Mutex()) {
- parseCmdArgumentsFromMyInstance();
+ parseCmdArgumentsFromMyInstance(raw_cli_args);
qInstallMessageHandler(performLogging);
m_feedReader = nullptr;
@@ -941,7 +941,7 @@ void Application::parseCmdArgumentsFromOtherInstance(const QString& message) {
}
}
-void Application::parseCmdArgumentsFromMyInstance() {
+void Application::parseCmdArgumentsFromMyInstance(const QStringList& raw_cli_args) {
QCommandLineOption help({ QSL(CLI_HELP_SHORT), QSL(CLI_HELP_LONG) },
QSL("Displays overview of CLI."));
QCommandLineOption version({ QSL(CLI_VER_SHORT), QSL(CLI_VER_LONG) },
@@ -958,15 +958,20 @@ void Application::parseCmdArgumentsFromMyInstance() {
QSL("Disable just \"debug\" output."));
QCommandLineOption disable_debug({ QSL(CLI_NSTDOUTERR_SHORT), QSL(CLI_NSTDOUTERR_LONG) },
QSL("Completely disable stdout/stderr outputs."));
+ QCommandLineOption forced_style({ QSL(CLI_STYLE_SHORT), QSL(CLI_STYLE_LONG) },
+ QSL("Force some application style."),
+ QSL("style-name"));
m_cmdParser.addOptions({ help, version, log_file, custom_data_folder,
- disable_singleinstance, disable_only_debug, disable_debug });
+ disable_singleinstance, disable_only_debug, disable_debug,
+ forced_style });
m_cmdParser.addPositionalArgument(QSL("urls"),
QSL("List of URL addresses pointing to individual online feeds which should be added."),
QSL("[url-1 ... url-n]"));
m_cmdParser.setApplicationDescription(QSL(APP_NAME));
+ m_cmdParser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode::ParseAsLongOptions);
- if (!m_cmdParser.parse(QCoreApplication::arguments())) {
+ if (!m_cmdParser.parse(raw_cli_args)) {
qCriticalNN << LOGSEC_CORE << m_cmdParser.errorText();
}
diff --git a/src/librssguard/miscellaneous/application.h b/src/librssguard/miscellaneous/application.h
index 761ccbb6e..4f83c4386 100644
--- a/src/librssguard/miscellaneous/application.h
+++ b/src/librssguard/miscellaneous/application.h
@@ -81,7 +81,7 @@ class RSSGUARD_DLLSPEC Application : public SingleApplication {
Q_OBJECT
public:
- explicit Application(const QString& id, int& argc, char** argv);
+ explicit Application(const QString& id, int& argc, char** argv, const QStringList &raw_cli_args);
virtual ~Application();
void reactOnForeignNotifications();
@@ -173,7 +173,7 @@ class RSSGUARD_DLLSPEC Application : public SingleApplication {
// Processes incoming message from another RSS Guard instance.
void parseCmdArgumentsFromOtherInstance(const QString& message);
- void parseCmdArgumentsFromMyInstance();
+ void parseCmdArgumentsFromMyInstance(const QStringList &raw_cli_args);
private slots:
void onNodeJsPackageUpdateError(const QList& pkgs, const QString& error);
diff --git a/src/librssguard/miscellaneous/settings.cpp b/src/librssguard/miscellaneous/settings.cpp
index 851e17ec9..337a58ecd 100644
--- a/src/librssguard/miscellaneous/settings.cpp
+++ b/src/librssguard/miscellaneous/settings.cpp
@@ -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;
diff --git a/src/librssguard/miscellaneous/settings.h b/src/librssguard/miscellaneous/settings.h
index 710654ebc..016a98210 100644
--- a/src/librssguard/miscellaneous/settings.h
+++ b/src/librssguard/miscellaneous/settings.h
@@ -93,6 +93,9 @@ namespace Feeds {
KEY ShowOnlyUnreadFeeds;
VALUE(bool) ShowOnlyUnreadFeedsDef;
+ KEY SortAlphabetically;
+ VALUE(bool) SortAlphabeticallyDef;
+
KEY ShowTreeBranches;
VALUE(bool) ShowTreeBranchesDef;
diff --git a/src/librssguard/miscellaneous/skinfactory.cpp b/src/librssguard/miscellaneous/skinfactory.cpp
index 3ae86cf81..e82cf86bf 100644
--- a/src/librssguard/miscellaneous/skinfactory.cpp
+++ b/src/librssguard/miscellaneous/skinfactory.cpp
@@ -51,9 +51,10 @@ bool SkinFactory::isStyleGoodForDarkVariant(const QString& style_name) const {
void SkinFactory::loadSkinFromData(const Skin& skin) {
QString style_name = qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString();
auto env = QProcessEnvironment::systemEnvironment();
- QString over_style = env.value(QSL("QT_STYLE_OVERRIDE"));
+ const QString env_forced_style = env.value(QSL("QT_STYLE_OVERRIDE"));
+ const QString cli_forced_style = qApp->cmdParser()->value(QSL(CLI_STYLE_SHORT));
- if (over_style.isEmpty()) {
+ if (env_forced_style.isEmpty() && cli_forced_style.isEmpty()) {
qApp->setStyle(style_name);
m_styleIsFrozen = false;
@@ -61,7 +62,9 @@ void SkinFactory::loadSkinFromData(const Skin& skin) {
}
else {
m_styleIsFrozen = true;
- qWarningNN << LOGSEC_GUI << "Respecting forced style:" << QUOTE_W_SPACE_DOT(over_style);
+ qWarningNN << LOGSEC_GUI << "Respecting forced style(s):\n"
+ << " QT_STYLE_OVERRIDE: " QUOTE_NO_SPACE(env_forced_style) << "\n"
+ << " CLI (-style): " QUOTE_NO_SPACE(cli_forced_style);
}
if (isStyleGoodForDarkVariant(style_name) &&
diff --git a/src/librssguard/miscellaneous/skinfactory.h b/src/librssguard/miscellaneous/skinfactory.h
index adeb82597..50a65bbf9 100644
--- a/src/librssguard/miscellaneous/skinfactory.h
+++ b/src/librssguard/miscellaneous/skinfactory.h
@@ -89,9 +89,9 @@ class RSSGUARD_DLLSPEC SkinFactory : public QObject {
bool styleIsFrozen() const;
-private:
+ private:
- // Loads the skin from give skin_data.
+ // Loads the skin from given skin_data.
void loadSkinFromData(const Skin& skin);
QString loadSkinFile(const QString& skin_folder, const QString& file_name, const QString& base_folder) const;
diff --git a/src/librssguard/services/abstract/rootitem.cpp b/src/librssguard/services/abstract/rootitem.cpp
index dcc80dfa0..62e8ed793 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_childItems(QList()), m_parentItem(parent_item) {}
+ m_keepOnTop(false), m_sortOrder(0), m_childItems(QList()), 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.
@@ -53,7 +54,7 @@ QString RootItem::additionalTooltip() const {
}
QList RootItem::contextMenuFeedsList() {
- return QList();
+ return {};
}
bool RootItem::canBeEdited() const {
@@ -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);
diff --git a/src/librssguard/services/abstract/rootitem.h b/src/librssguard/services/abstract/rootitem.h
index a0b7f27cf..e6a7d27b3 100644
--- a/src/librssguard/services/abstract/rootitem.h
+++ b/src/librssguard/services/abstract/rootitem.h
@@ -90,12 +90,16 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
virtual QList undeletedMessages() const;
// This method should "clean" all messages it contains.
- // What "clean" means? It means delete messages -> move them to recycle bin
+ //
+ // NOTE: What "clean" means? It means delete messages -> move them to recycle bin
// or eventually remove them completely if there is no recycle bin functionality.
//
// If this method is called on "recycle bin" instance of your
// service account, it should "empty" the recycle bin.
virtual bool cleanMessages(bool clear_only_read);
+
+ // Reloads current counts of articles in this item from DB and
+ // sets.
virtual void updateCounts(bool including_total_count);
virtual int row() const;
virtual QVariant data(int column, int role) const;
@@ -195,6 +199,16 @@ 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.
+ int sortOrder() const;
+ void setSortOrder(int sort_order);
+
private:
RootItem::Kind m_kind;
int m_id;
@@ -204,6 +218,7 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
QIcon m_icon;
QDateTime m_creationDate;
bool m_keepOnTop;
+ int m_sortOrder;
QList m_childItems;
RootItem* m_parentItem;
};
diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp
index dc6b4b3e2..5f08a449b 100644
--- a/src/librssguard/services/abstract/serviceroot.cpp
+++ b/src/librssguard/services/abstract/serviceroot.cpp
@@ -77,7 +77,18 @@ bool ServiceRoot::downloadAttachmentOnMyOwn(const QUrl& url) const {
}
QList ServiceRoot::contextMenuFeedsList() {
- return serviceMenu();
+ auto specific = serviceMenu();
+ auto base = RootItem::contextMenuFeedsList();
+
+ if (!specific.isEmpty()) {
+ auto* act_sep = new QAction(this);
+
+ act_sep->setSeparator(true);
+ base.append(act_sep);
+ base.append(specific);
+ }
+
+ return base;
}
QList ServiceRoot::contextMenuMessagesList(const QList& messages) {
diff --git a/src/librssguard/services/feedly/feedlyserviceroot.cpp b/src/librssguard/services/feedly/feedlyserviceroot.cpp
index 22fa40f45..e6923e380 100644
--- a/src/librssguard/services/feedly/feedlyserviceroot.cpp
+++ b/src/librssguard/services/feedly/feedlyserviceroot.cpp
@@ -88,7 +88,7 @@ QList FeedlyServiceRoot::obtainNewMessages(Feed* feed,
void FeedlyServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
}
diff --git a/src/librssguard/services/gmail/gmailserviceroot.cpp b/src/librssguard/services/gmail/gmailserviceroot.cpp
index 51825cc95..0aa104d68 100644
--- a/src/librssguard/services/gmail/gmailserviceroot.cpp
+++ b/src/librssguard/services/gmail/gmailserviceroot.cpp
@@ -163,7 +163,7 @@ bool GmailServiceRoot::supportsCategoryAdding() const {
void GmailServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
}
diff --git a/src/librssguard/services/greader/greadernetwork.cpp b/src/librssguard/services/greader/greadernetwork.cpp
index 0a59ec5e4..e6b70b653 100644
--- a/src/librssguard/services/greader/greadernetwork.cpp
+++ b/src/librssguard/services/greader/greadernetwork.cpp
@@ -1012,7 +1012,7 @@ QList GreaderNetwork::decodeStreamContents(ServiceRoot* root,
message.m_title = qApp->web()->unescapeHtml(message_obj[QSL("title")].toString());
message.m_author = qApp->web()->unescapeHtml(message_obj[QSL("author")].toString());
- message.m_created = QDateTime::fromSecsSinceEpoch(message_obj[QSL("published")].toInt(), Qt::UTC);
+ message.m_created = QDateTime::fromSecsSinceEpoch(message_obj[QSL("published")].toInt(), Qt::TimeSpec::UTC);
message.m_createdFromFeed = true;
message.m_customId = message_obj[QSL("id")].toString();
diff --git a/src/librssguard/services/greader/greaderserviceroot.cpp b/src/librssguard/services/greader/greaderserviceroot.cpp
index c3ff7d997..4bb309772 100644
--- a/src/librssguard/services/greader/greaderserviceroot.cpp
+++ b/src/librssguard/services/greader/greaderserviceroot.cpp
@@ -155,7 +155,7 @@ bool GreaderServiceRoot::wantsBaggedIdsOfExistingMessages() const {
void GreaderServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
}
diff --git a/src/librssguard/services/newsblur/newsblurserviceroot.cpp b/src/librssguard/services/newsblur/newsblurserviceroot.cpp
index 9576accfc..a57103999 100755
--- a/src/librssguard/services/newsblur/newsblurserviceroot.cpp
+++ b/src/librssguard/services/newsblur/newsblurserviceroot.cpp
@@ -72,7 +72,7 @@ QList NewsBlurServiceRoot::obtainNewMessages(Feed* feed,
void NewsBlurServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
}
diff --git a/src/librssguard/services/owncloud/owncloudserviceroot.cpp b/src/librssguard/services/owncloud/owncloudserviceroot.cpp
index 13d54e664..8992c1d23 100644
--- a/src/librssguard/services/owncloud/owncloudserviceroot.cpp
+++ b/src/librssguard/services/owncloud/owncloudserviceroot.cpp
@@ -50,7 +50,7 @@ bool OwnCloudServiceRoot::supportsCategoryAdding() const {
void OwnCloudServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
}
diff --git a/src/librssguard/services/reddit/redditserviceroot.cpp b/src/librssguard/services/reddit/redditserviceroot.cpp
index 3e6c921fc..fbf8d232f 100644
--- a/src/librssguard/services/reddit/redditserviceroot.cpp
+++ b/src/librssguard/services/reddit/redditserviceroot.cpp
@@ -94,7 +94,7 @@ bool RedditServiceRoot::supportsCategoryAdding() const {
void RedditServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
}
diff --git a/src/librssguard/services/standard/parsers/feedparser.cpp b/src/librssguard/services/standard/parsers/feedparser.cpp
index 7a4c17dfb..834d98787 100644
--- a/src/librssguard/services/standard/parsers/feedparser.cpp
+++ b/src/librssguard/services/standard/parsers/feedparser.cpp
@@ -83,7 +83,7 @@ QString FeedParser::jsonMessageRawContents(const QJsonObject& msg_element) const
QList FeedParser::messages() {
QString feed_author = feedAuthor();
QList messages;
- QDateTime current_time = QDateTime::currentDateTime();
+ QDateTime current_time = QDateTime::currentDateTimeUtc();
// Pull out all messages.
if (m_isXml) {
diff --git a/src/librssguard/services/standard/standardserviceroot.cpp b/src/librssguard/services/standard/standardserviceroot.cpp
index 37bf2956f..5754131bf 100644
--- a/src/librssguard/services/standard/standardserviceroot.cpp
+++ b/src/librssguard/services/standard/standardserviceroot.cpp
@@ -50,7 +50,7 @@ StandardServiceRoot::~StandardServiceRoot() {
}
void StandardServiceRoot::start(bool freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
if (freshly_activated && getSubTreeFeeds().isEmpty()) {
// In other words, if there are no feeds or categories added.
diff --git a/src/librssguard/services/tt-rss/ttrssserviceroot.cpp b/src/librssguard/services/tt-rss/ttrssserviceroot.cpp
index 5088524ad..c4c224023 100644
--- a/src/librssguard/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/librssguard/services/tt-rss/ttrssserviceroot.cpp
@@ -41,7 +41,7 @@ ServiceRoot::LabelOperation TtRssServiceRoot::supportedLabelOperations() const {
void TtRssServiceRoot::start(bool freshly_activated) {
if (!freshly_activated) {
- DatabaseQueries::loadFromDatabase(this);
+ DatabaseQueries::loadRootFromDatabase(this);
loadCacheFromFile();
auto lbls = m_labelsNode->labels();
diff --git a/src/rssguard/main.cpp b/src/rssguard/main.cpp
index 55cff7c7e..7b8d5506c 100644
--- a/src/rssguard/main.cpp
+++ b/src/rssguard/main.cpp
@@ -52,8 +52,17 @@ int main(int argc, char* argv[]) {
disableWindowTabbing();
#endif
+ // We create our own "arguments" list as Qt strips something
+ // sometimes out.
+ char** const av = argv;
+ QStringList raw_cli_args;
+
+ for (int a = 0; a < argc; a++) {
+ raw_cli_args << QString::fromLocal8Bit(av[a]);
+ }
+
// Instantiate base application object.
- Application application(QSL(APP_LOW_NAME), argc, argv);
+ Application application(QSL(APP_LOW_NAME), argc, argv, raw_cli_args);
qDebugNN << LOGSEC_CORE << "Starting" << NONQUOTE_W_SPACE_DOT(APP_LONG_NAME);
qDebugNN << LOGSEC_CORE << "Instantiated class " << QUOTE_W_SPACE_DOT(application.metaObject()->className());
@@ -97,8 +106,10 @@ int main(int argc, char* argv[]) {
qApp->showTrayIcon();
qApp->offerChanges();
qApp->showPolls();
- qApp->mainForm()->tabWidget()->feedMessageViewer()->respondToMainWindowResizes();
- qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates();
+
+ main_window.tabWidget()->feedMessageViewer()->respondToMainWindowResizes();
+ main_window.tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates();
+
qApp->parseCmdArgumentsFromOtherInstance(qApp->cmdParser()->positionalArguments().join(QSL(ARGUMENTS_LIST_SEPARATOR)));
return Application::exec();