Shitload of changes, save it.
Also note that this introduces some SQL changes for metadata version 2 which is still unpublished but was introduced some commits ago, therefore individual "devbuild"s are NOT cross-compatible betweem each other.
This commit is contained in:
parent
83f2da43fa
commit
7f5d1473a3
@ -26,7 +26,7 @@
|
||||
<url type="donation">https://github.com/sponsors/martinrotter</url>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="4.1.2" date="2022-02-28"/>
|
||||
<release version="4.1.2" date="2022-03-01"/>
|
||||
</releases>
|
||||
<content_rating type="oars-1.0">
|
||||
<content_attribute id="violence-cartoon">none</content_attribute>
|
||||
|
@ -7,7 +7,9 @@
|
||||
<file>./graphics/Breeze/categories/32/applications-science.svg</file>
|
||||
<file>./graphics/Breeze/categories/32/applications-system.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/arrow-down.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/arrow-down-double.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/arrow-up.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/arrow-up-double.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/call-start.svg</file>
|
||||
<file>./graphics/Breeze/status/64/dialog-error.svg</file>
|
||||
<file>./graphics/Breeze/status/64/dialog-information.svg</file>
|
||||
@ -30,12 +32,11 @@
|
||||
<file>./graphics/Breeze/places/96/folder.svg</file>
|
||||
<file>./graphics/Breeze/actions/22/format-indent-more.svg</file>
|
||||
<file>./graphics/Breeze/actions/22/format-justify-fill.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/go-down.svg</file>
|
||||
<file>./graphics/Breeze/actions/22/format-text-bold.svg</file>
|
||||
<file>./graphics/Breeze/actions/64/go-home.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/go-jump.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/go-next.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/go-previous.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/go-up.svg</file>
|
||||
<file>./graphics/Breeze/actions/22/gtk-edit.svg</file>
|
||||
<file>./graphics/Breeze/actions/32/help-about.svg</file>
|
||||
<file>./graphics/Breeze/actions/22/help-contents.svg</file>
|
||||
@ -75,7 +76,9 @@
|
||||
<file>./graphics/Breeze Dark/categories/32/applications-science.svg</file>
|
||||
<file>./graphics/Breeze Dark/categories/32/applications-system.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/arrow-down.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/arrow-down-double.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/arrow-up.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/arrow-up-double.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/call-start.svg</file>
|
||||
<file>./graphics/Breeze Dark/status/64/dialog-error.svg</file>
|
||||
<file>./graphics/Breeze Dark/status/64/dialog-information.svg</file>
|
||||
@ -98,12 +101,11 @@
|
||||
<file>./graphics/Breeze Dark/places/96/folder.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/22/format-indent-more.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/22/format-justify-fill.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/go-down.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/22/format-text-bold.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/64/go-home.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/go-jump.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/go-next.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/go-previous.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/go-up.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/22/gtk-edit.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/32/help-about.svg</file>
|
||||
<file>./graphics/Breeze Dark/actions/22/help-contents.svg</file>
|
||||
@ -166,12 +168,11 @@
|
||||
<file>./graphics/Faenza/places/64/folder.png</file>
|
||||
<file>./graphics/Faenza/actions/64/format-indent-more.png</file>
|
||||
<file>./graphics/Faenza/actions/64/format-justify-fill.png</file>
|
||||
<file>./graphics/Faenza/actions/64/go-down.png</file>
|
||||
<file>./graphics/Faenza/actions/64/format-text-bold.png</file>
|
||||
<file>./graphics/Faenza/actions/64/go-home.png</file>
|
||||
<file>./graphics/Faenza/actions/64/go-jump.png</file>
|
||||
<file>./graphics/Faenza/actions/64/go-next.png</file>
|
||||
<file>./graphics/Faenza/actions/64/go-previous.png</file>
|
||||
<file>./graphics/Faenza/actions/64/go-up.png</file>
|
||||
<file>./graphics/Faenza/actions/64/gtk-edit.png</file>
|
||||
<file>./graphics/Faenza/actions/64/help-about.png</file>
|
||||
<file>./graphics/Faenza/actions/64/help-contents.png</file>
|
||||
@ -212,7 +213,9 @@
|
||||
<file>./graphics/Numix/22/categories/applications-science.svg</file>
|
||||
<file>./graphics/Numix/22/categories/applications-system.svg</file>
|
||||
<file>./graphics/Numix/22/actions/arrow-down.svg</file>
|
||||
<file>./graphics/Numix/22/actions/arrow-down-double.svg</file>
|
||||
<file>./graphics/Numix/22/actions/arrow-up.svg</file>
|
||||
<file>./graphics/Numix/22/actions/arrow-up-double.svg</file>
|
||||
<file>./graphics/Numix/22/actions/call-start.svg</file>
|
||||
<file>./graphics/Numix/22/status/dialog-error.svg</file>
|
||||
<file>./graphics/Numix/22/status/dialog-information.svg</file>
|
||||
@ -238,12 +241,11 @@
|
||||
<file>./graphics/Numix/22/places/folder.svg</file>
|
||||
<file>./graphics/Numix/22/actions/format-indent-more.svg</file>
|
||||
<file>./graphics/Numix/22/actions/format-justify-fill.svg</file>
|
||||
<file>./graphics/Numix/22/actions/go-down.svg</file>
|
||||
<file>./graphics/Numix/22/actions/format-text-bold.svg</file>
|
||||
<file>./graphics/Numix/22/actions/go-home.svg</file>
|
||||
<file>./graphics/Numix/22/actions/go-jump.svg</file>
|
||||
<file>./graphics/Numix/22/actions/go-next.svg</file>
|
||||
<file>./graphics/Numix/22/actions/go-previous.svg</file>
|
||||
<file>./graphics/Numix/22/actions/go-up.svg</file>
|
||||
<file>./graphics/Numix/22/actions/gtk-edit.svg</file>
|
||||
<file>./graphics/Numix/22/categories/help-about.svg</file>
|
||||
<file>./graphics/Numix/22/actions/help-contents.svg</file>
|
||||
|
@ -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,
|
||||
|
@ -1,3 +1,8 @@
|
||||
USE ##;
|
||||
-- !
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
-- !
|
||||
!! db_update_sqlite_1_2.sql
|
||||
-- !
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
-- !
|
@ -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;
|
||||
-- !
|
||||
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;
|
@ -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,6 +172,7 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right
|
||||
return sortOrder() == Qt::SortOrder::DescendingOrder;
|
||||
}
|
||||
else if (left_item->kind() == right_item->kind()) {
|
||||
if (m_sortAlphabetically) {
|
||||
// Both items are of the same type.
|
||||
if (left.column() == FDS_MODEL_COUNTS_INDEX) {
|
||||
// User wants to sort according to counts.
|
||||
@ -182,6 +183,23 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right
|
||||
return QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We sort some types with sort order, other alphabetically.
|
||||
switch (left_item->kind()) {
|
||||
case RootItem::Kind::Feed:
|
||||
case RootItem::Kind::Category:
|
||||
case RootItem::Kind::ServiceRoot:
|
||||
return sortOrder() == Qt::SortOrder::AscendingOrder
|
||||
? left_item->sortOrder() < right_item->sortOrder()
|
||||
: left_item->sortOrder() > right_item->sortOrder();
|
||||
|
||||
default:
|
||||
return sortOrder() == Qt::SortOrder::AscendingOrder
|
||||
? QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) < 0
|
||||
: QString::localeAwareCompare(left_item->title().toLower(), right_item->title().toLower()) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We sort using priorities.
|
||||
auto left_priority = m_priorities.indexOf(left_item->kind());
|
||||
@ -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;
|
||||
|
||||
|
@ -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<QPair<int, QModelIndex>> m_hiddenIndices;
|
||||
QList<RootItem::Kind> m_priorities;
|
||||
};
|
||||
|
@ -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)) {
|
||||
|
@ -108,7 +108,7 @@ class DatabaseQueries {
|
||||
static QList<ServiceRoot*> getAccounts(const QSqlDatabase& db, const QString& code, bool* ok = nullptr);
|
||||
|
||||
template<typename Categ, typename Fee>
|
||||
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<MessageFilter*>& 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<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());
|
||||
|
||||
@ -328,7 +335,7 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
|
||||
}
|
||||
|
||||
template<typename Categ, typename Fee>
|
||||
void DatabaseQueries::loadFromDatabase(ServiceRoot* root) {
|
||||
void DatabaseQueries::loadRootFromDatabase(ServiceRoot* root) {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(root->metaObject()->className());
|
||||
Assignment categories = DatabaseQueries::getCategories<Categ>(database, root->accountId());
|
||||
Assignment feeds = DatabaseQueries::getFeeds<Fee>(database, qApp->feedReader()->messageFilters(), root->accountId());
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>224</width>
|
||||
<width>270</width>
|
||||
<height>97</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -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<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) {
|
||||
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <QStyle>
|
||||
|
||||
TabBar::TabBar(QWidget* parent) : QTabBar(parent) {
|
||||
setDocumentMode(false);
|
||||
setDocumentMode(true);
|
||||
setUsesScrollButtons(true);
|
||||
setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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<NodeJs::PackageMetadata>& pkgs, const QString& error);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) &&
|
||||
|
@ -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;
|
||||
|
@ -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(0), 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.
|
||||
@ -53,7 +54,7 @@ QString RootItem::additionalTooltip() const {
|
||||
}
|
||||
|
||||
QList<QAction*> RootItem::contextMenuFeedsList() {
|
||||
return QList<QAction*>();
|
||||
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);
|
||||
|
@ -90,12 +90,16 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
||||
virtual QList<Message> 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<RootItem*> m_childItems;
|
||||
RootItem* m_parentItem;
|
||||
};
|
||||
|
@ -77,7 +77,18 @@ bool ServiceRoot::downloadAttachmentOnMyOwn(const QUrl& url) const {
|
||||
}
|
||||
|
||||
QList<QAction*> 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<QAction*> ServiceRoot::contextMenuMessagesList(const QList<Message>& messages) {
|
||||
|
@ -88,7 +88,7 @@ QList<Message> FeedlyServiceRoot::obtainNewMessages(Feed* feed,
|
||||
|
||||
void FeedlyServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, Feed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<Category, Feed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ bool GmailServiceRoot::supportsCategoryAdding() const {
|
||||
|
||||
void GmailServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, Feed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<Category, Feed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
|
@ -1012,7 +1012,7 @@ QList<Message> 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();
|
||||
|
||||
|
@ -155,7 +155,7 @@ bool GreaderServiceRoot::wantsBaggedIdsOfExistingMessages() const {
|
||||
|
||||
void GreaderServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, Feed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<Category, Feed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ QList<Message> NewsBlurServiceRoot::obtainNewMessages(Feed* feed,
|
||||
|
||||
void NewsBlurServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, Feed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<Category, Feed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ bool OwnCloudServiceRoot::supportsCategoryAdding() const {
|
||||
|
||||
void OwnCloudServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, OwnCloudFeed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<Category, OwnCloudFeed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ bool RedditServiceRoot::supportsCategoryAdding() const {
|
||||
|
||||
void RedditServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<RedditCategory, Feed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<RedditCategory, Feed>(this);
|
||||
loadCacheFromFile();
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ QString FeedParser::jsonMessageRawContents(const QJsonObject& msg_element) const
|
||||
QList<Message> FeedParser::messages() {
|
||||
QString feed_author = feedAuthor();
|
||||
QList<Message> messages;
|
||||
QDateTime current_time = QDateTime::currentDateTime();
|
||||
QDateTime current_time = QDateTime::currentDateTimeUtc();
|
||||
|
||||
// Pull out all messages.
|
||||
if (m_isXml) {
|
||||
|
@ -50,7 +50,7 @@ StandardServiceRoot::~StandardServiceRoot() {
|
||||
}
|
||||
|
||||
void StandardServiceRoot::start(bool freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<StandardCategory, StandardFeed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<StandardCategory, StandardFeed>(this);
|
||||
|
||||
if (freshly_activated && getSubTreeFeeds().isEmpty()) {
|
||||
// In other words, if there are no feeds or categories added.
|
||||
|
@ -41,7 +41,7 @@ ServiceRoot::LabelOperation TtRssServiceRoot::supportedLabelOperations() const {
|
||||
|
||||
void TtRssServiceRoot::start(bool freshly_activated) {
|
||||
if (!freshly_activated) {
|
||||
DatabaseQueries::loadFromDatabase<Category, TtRssFeed>(this);
|
||||
DatabaseQueries::loadRootFromDatabase<Category, TtRssFeed>(this);
|
||||
loadCacheFromFile();
|
||||
|
||||
auto lbls = m_labelsNode->labels();
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user