diff --git a/resources/skins/nudus-base/html_single_message.html b/resources/skins/nudus-base/html_single_message.html index f675f11fd..7aa4c3e5d 100644 --- a/resources/skins/nudus-base/html_single_message.html +++ b/resources/skins/nudus-base/html_single_message.html @@ -1,4 +1,4 @@ -
+
%7
diff --git a/resources/sql.qrc b/resources/sql.qrc index 88d7f8529..d15d562c4 100644 --- a/resources/sql.qrc +++ b/resources/sql.qrc @@ -5,11 +5,13 @@ sql/db_update_mysql_2_3.sql sql/db_update_mysql_3_4.sql sql/db_update_mysql_4_5.sql + sql/db_update_mysql_5_6.sql sql/db_init_sqlite.sql sql/db_update_sqlite_1_2.sql sql/db_update_sqlite_2_3.sql sql/db_update_sqlite_3_4.sql sql/db_update_sqlite_4_5.sql + sql/db_update_sqlite_5_6.sql \ No newline at end of file diff --git a/resources/sql/db_init_sqlite.sql b/resources/sql/db_init_sqlite.sql index 9810a1034..5dc63086a 100644 --- a/resources/sql/db_init_sqlite.sql +++ b/resources/sql/db_init_sqlite.sql @@ -31,21 +31,24 @@ 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, - icon ^^, - category INTEGER NOT NULL CHECK (category >= -1), /* Physical category ID, also root feeds contain -1 here. */ - source TEXT, - update_type INTEGER NOT NULL CHECK (update_type >= 0), - update_interval INTEGER NOT NULL DEFAULT 900 CHECK (update_interval >= 1), - is_off INTEGER NOT NULL DEFAULT 0 CHECK (is_off >= 0 AND is_off <= 1), - is_quiet INTEGER NOT NULL DEFAULT 0 CHECK (is_quiet >= 0 AND is_quiet <= 1), - open_articles INTEGER NOT NULL DEFAULT 0 CHECK (open_articles >= 0 AND open_articles <= 1), - account_id INTEGER NOT NULL, - custom_id TEXT NOT NULL CHECK (custom_id != ''), /* Custom ID cannot be empty, it must contain either service-specific ID, or Feeds/id. */ + id $$, + ordr INTEGER NOT NULL CHECK (ordr >= 0), + title TEXT NOT NULL CHECK (title != ''), + description TEXT, + date_created BIGINT, + icon ^^, + category INTEGER NOT NULL CHECK (category >= -1), /* Physical category ID, also root feeds contain -1 here. */ + source TEXT, + update_type INTEGER NOT NULL CHECK (update_type >= 0), + update_interval INTEGER NOT NULL DEFAULT 900 CHECK (update_interval >= 1), + is_off INTEGER NOT NULL DEFAULT 0 CHECK (is_off >= 0 AND is_off <= 1), + is_quiet INTEGER NOT NULL DEFAULT 0 CHECK (is_quiet >= 0 AND is_quiet <= 1), + is_rtl INTEGER NOT NULL DEFAULT 0 CHECK (is_rtl >= 0 AND is_rtl <= 1), + add_any_datetime_articles INTEGER NOT NULL DEFAULT 1 CHECK (add_any_datetime_articles >= 0 AND add_any_datetime_articles <= 1), + datetime_to_avoid BIGINT NOT NULL DEFAULT 0 CHECK (datetime_to_avoid >= 0), + open_articles INTEGER NOT NULL DEFAULT 0 CHECK (open_articles >= 0 AND open_articles <= 1), + account_id INTEGER NOT NULL, + custom_id TEXT NOT NULL CHECK (custom_id != ''), /* Custom ID cannot be empty, it must contain either service-specific ID, or Feeds/id. */ /* Custom column for (serialized) custom account-specific data. */ custom_data TEXT, @@ -97,4 +100,4 @@ CREATE TABLE Labels ( account_id INTEGER NOT NULL, FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE -); \ No newline at end of file +); diff --git a/resources/sql/db_update_mysql_5_6.sql b/resources/sql/db_update_mysql_5_6.sql new file mode 100755 index 000000000..6dbe7a0ee --- /dev/null +++ b/resources/sql/db_update_mysql_5_6.sql @@ -0,0 +1,8 @@ +USE ##; +-- ! +SET FOREIGN_KEY_CHECKS = 0; +-- ! +!! db_update_sqlite_5_6.sql +-- ! +SET FOREIGN_KEY_CHECKS = 1; +-- ! \ No newline at end of file diff --git a/resources/sql/db_update_sqlite_5_6.sql b/resources/sql/db_update_sqlite_5_6.sql new file mode 100755 index 000000000..e1703ae1d --- /dev/null +++ b/resources/sql/db_update_sqlite_5_6.sql @@ -0,0 +1,32 @@ +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, + icon ^^, + category INTEGER NOT NULL CHECK (category >= -1), /* Physical category ID, also root feeds contain -1 here. */ + source TEXT, + update_type INTEGER NOT NULL CHECK (update_type >= 0), + update_interval INTEGER NOT NULL DEFAULT 900 CHECK (update_interval >= 1), + is_off INTEGER NOT NULL DEFAULT 0 CHECK (is_off >= 0 AND is_off <= 1), + is_quiet INTEGER NOT NULL DEFAULT 0 CHECK (is_quiet >= 0 AND is_quiet <= 1), + is_rtl INTEGER NOT NULL DEFAULT 0 CHECK (is_rtl >= 0 AND is_rtl <= 1), + add_any_datetime_articles INTEGER NOT NULL DEFAULT 1 CHECK (add_any_datetime_articles >= 0 AND add_any_datetime_articles <= 1), + datetime_to_avoid BIGINT NOT NULL DEFAULT 0 CHECK (datetime_to_avoid >= 0), + open_articles INTEGER NOT NULL DEFAULT 0 CHECK (open_articles >= 0 AND open_articles <= 1), + account_id INTEGER NOT NULL, + custom_id TEXT NOT NULL CHECK (custom_id != ''), /* Custom ID cannot be empty, it must contain either service-specific ID, or Feeds/id. */ + /* Custom column for (serialized) custom account-specific data. */ + custom_data TEXT, + + FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE +); +-- ! +INSERT INTO Feeds (id, ordr, title, description, date_created, icon, category, source, update_type, update_interval, is_off, is_quiet, open_articles, account_id, custom_id, custom_data) +SELECT id, ordr, title, description, date_created, icon, category, source, update_type, update_interval, is_off, is_quiet, open_articles, account_id, custom_id, custom_data +FROM backup_Feeds; +-- ! +DROP TABLE backup_Feeds; \ No newline at end of file diff --git a/src/librssguard/core/feeddownloader.cpp b/src/librssguard/core/feeddownloader.cpp index 181d49fcc..caefdef2e 100644 --- a/src/librssguard/core/feeddownloader.cpp +++ b/src/librssguard/core/feeddownloader.cpp @@ -238,6 +238,28 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc, msg.sanitize(feed, fix_future_datetimes); } + if (!feed->addAnyDatetimeArticles()) { + QDateTime dt_to_avoid; + + if (feed->datetimeToAvoid().isValid()) { + dt_to_avoid = feed->datetimeToAvoid(); + } + else if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::AvoidOldArticles)).toBool()) { + dt_to_avoid = qApp->settings()->value(GROUP(Messages), SETTING(Messages::DateTimeToAvoidArticle)).toDateTime(); + } + + if (dt_to_avoid.isValid()) { + for (int i = 0; i < msgs.size(); i++) { + const auto& mss = msgs.at(i); + + if (mss.m_createdFromFeed && mss.m_created < dt_to_avoid) { + qDebugNN << LOGSEC_CORE << "Removing message" << QUOTE_W_SPACE(mss.m_title) << "for being too old."; + msgs.removeAt(i--); + } + } + } + } + if (!feed->messageFilters().isEmpty()) { tmr.restart(); diff --git a/src/librssguard/core/feedsproxymodel.cpp b/src/librssguard/core/feedsproxymodel.cpp index 69db2a21d..b82fd016f 100644 --- a/src/librssguard/core/feedsproxymodel.cpp +++ b/src/librssguard/core/feedsproxymodel.cpp @@ -322,9 +322,11 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { bool should_show = filterAcceptsRowInternal(source_row, source_parent); + /* qDebugNN << LOGSEC_CORE << "Filter accepts row" << QUOTE_W_SPACE(m_sourceModel->itemForIndex(m_sourceModel->index(source_row, 0, source_parent))->title()) << "and filter result is:" << QUOTE_W_SPACE_DOT(should_show); + */ /* if (should_show && (!filterRegularExpression().pattern().isEmpty() || diff --git a/src/librssguard/core/message.cpp b/src/librssguard/core/message.cpp index 74de1be48..8ec33f85a 100644 --- a/src/librssguard/core/message.cpp +++ b/src/librssguard/core/message.cpp @@ -68,7 +68,7 @@ Message::Message() { m_categories = QList(); m_accountId = m_id = 0; m_score = 0.0; - m_isRead = m_isImportant = m_isDeleted = false; + m_isRead = m_isImportant = m_isDeleted = m_isRtl = false; m_assignedLabels = QList(); m_assignedLabelsByFilter = QList(); m_deassignedLabelsByFilter = QList(); @@ -140,6 +140,7 @@ Message Message::fromSqlRecord(const QSqlRecord& record, bool* result) { message.m_contents = record.value(MSG_DB_CONTENTS_INDEX).toString(); message.m_enclosures = Enclosures::decodeEnclosuresFromString(record.value(MSG_DB_ENCLOSURES_INDEX).toString()); message.m_score = record.value(MSG_DB_SCORE_INDEX).toDouble(); + message.m_isRtl = record.value(MSG_DB_FEED_IS_RTL_INDEX).toBool(); message.m_accountId = record.value(MSG_DB_ACCOUNT_ID_INDEX).toInt(); message.m_customId = record.value(MSG_DB_CUSTOM_ID_INDEX).toString(); message.m_customHash = record.value(MSG_DB_CUSTOM_HASH_INDEX).toString(); @@ -179,7 +180,7 @@ QString Message::generateRawAtomContents(const Message& msg) { QDataStream& operator<<(QDataStream& out, const Message& my_obj) { out << my_obj.m_accountId << my_obj.m_customHash << my_obj.m_customId << my_obj.m_feedId << my_obj.m_id - << my_obj.m_isImportant << my_obj.m_isRead << my_obj.m_isDeleted << my_obj.m_score; + << my_obj.m_isImportant << my_obj.m_isRead << my_obj.m_isDeleted << my_obj.m_score << my_obj.m_isRtl; return out; } @@ -193,9 +194,11 @@ QDataStream& operator>>(QDataStream& in, Message& my_obj) { bool is_important; bool is_read; bool is_deleted; + bool is_rtl; double score; - in >> account_id >> custom_hash >> custom_id >> feed_id >> id >> is_important >> is_read >> is_deleted >> score; + in >> account_id >> custom_hash >> custom_id >> feed_id >> id >> is_important >> is_read >> is_deleted >> score >> + is_rtl; my_obj.m_accountId = account_id; my_obj.m_customHash = custom_hash; @@ -206,6 +209,7 @@ QDataStream& operator>>(QDataStream& in, Message& my_obj) { my_obj.m_isRead = is_read; my_obj.m_isDeleted = is_deleted; my_obj.m_score = score; + my_obj.m_isRtl = is_rtl; return in; } diff --git a/src/librssguard/core/message.h b/src/librssguard/core/message.h index a8824e125..d04640c66 100644 --- a/src/librssguard/core/message.h +++ b/src/librssguard/core/message.h @@ -80,6 +80,7 @@ class RSSGUARD_DLLSPEC Message { bool m_isImportant; bool m_isDeleted; double m_score; + bool m_isRtl; QList m_enclosures; // List of assigned labels. diff --git a/src/librssguard/core/messagesmodel.cpp b/src/librssguard/core/messagesmodel.cpp index 0e202ac5f..c23c09dfc 100644 --- a/src/librssguard/core/messagesmodel.cpp +++ b/src/librssguard/core/messagesmodel.cpp @@ -294,6 +294,7 @@ void MessagesModel::setupHeaderData() { /*: Tooltip for custom ID of message.*/ tr("Custom ID") << /*: Tooltip for custom hash string of message.*/ tr("Custom hash") << /*: Tooltip for name of feed for message.*/ tr("Feed") << + /*: Tooltip for indication whether article is RTL or not.*/ tr("RTL") << /*: Tooltip for indication of presence of enclosures.*/ tr("Has enclosures") << /*: Tooltip for indication of labels of message.*/ tr("Assigned labels") << /*: Tooltip for indication of label IDs of message.*/ tr("Assigned label IDs"); @@ -305,8 +306,8 @@ void MessagesModel::setupHeaderData() { << tr("Contents of the article.") << tr("List of attachments.") << tr("Score of the article.") << tr("Account ID of the article.") << tr("Custom ID of the article.") << tr("Custom hash of the article.") << tr("Name of feed of the article.") - << tr("Indication of enclosures presence within the article.") << tr("Labels assigned to the article.") - << tr("Label IDs assigned to the article."); + << tr("Layout direction of the article") << tr("Indication of enclosures presence within the article.") + << tr("Labels assigned to the article.") << tr("Label IDs assigned to the article."); } Qt::ItemFlags MessagesModel::flags(const QModelIndex& index) const { @@ -409,6 +410,23 @@ QVariant MessagesModel::data(const QModelIndex& idx, int role) const { } } + case TEXT_DIRECTION_ROLE: { + int index_column = idx.column(); + + if (index_column != MSG_DB_TITLE_INDEX && index_column != MSG_DB_FEED_TITLE_INDEX && + index_column != MSG_DB_AUTHOR_INDEX) { + return Qt::LayoutDirection::LayoutDirectionAuto; + } + else { + return (m_cache->containsData(idx.row()) + ? m_cache->data(index(idx.row(), MSG_DB_FEED_IS_RTL_INDEX)) + : QSqlQueryModel::data(index(idx.row(), MSG_DB_FEED_IS_RTL_INDEX), Qt::ItemDataRole::EditRole)) + .toInt() == 0 + ? Qt::LayoutDirection::LayoutDirectionAuto + : Qt::LayoutDirection::RightToLeft; + } + } + case LOWER_TITLE_ROLE: return m_cache->containsData(idx.row()) ? m_cache->data(idx).toString().toLower() diff --git a/src/librssguard/core/messagesmodelsqllayer.cpp b/src/librssguard/core/messagesmodelsqllayer.cpp index d177b32bc..802de6d52 100644 --- a/src/librssguard/core/messagesmodelsqllayer.cpp +++ b/src/librssguard/core/messagesmodelsqllayer.cpp @@ -32,12 +32,14 @@ MessagesModelSqlLayer::MessagesModelSqlLayer() m_orderByNames[MSG_DB_CUSTOM_ID_INDEX] = QSL("Messages.custom_id"); m_orderByNames[MSG_DB_CUSTOM_HASH_INDEX] = QSL("Messages.custom_hash"); m_orderByNames[MSG_DB_FEED_TITLE_INDEX] = QSL("Feeds.title"); + m_orderByNames[MSG_DB_FEED_IS_RTL_INDEX] = QSL("Feeds.is_rtl"); m_orderByNames[MSG_DB_HAS_ENCLOSURES] = QSL("has_enclosures"); m_orderByNames[MSG_DB_LABELS] = QSL("msg_labels"); m_orderByNames[MSG_DB_LABELS_IDS] = QSL("Messages.labels"); m_numericColumns << MSG_DB_ID_INDEX << MSG_DB_READ_INDEX << MSG_DB_DELETED_INDEX << MSG_DB_PDELETED_INDEX - << MSG_DB_IMPORTANT_INDEX << MSG_DB_ACCOUNT_ID_INDEX << MSG_DB_DCREATED_INDEX << MSG_DB_SCORE_INDEX; + << MSG_DB_IMPORTANT_INDEX << MSG_DB_ACCOUNT_ID_INDEX << MSG_DB_DCREATED_INDEX << MSG_DB_SCORE_INDEX + << MSG_DB_FEED_IS_RTL_INDEX; } void MessagesModelSqlLayer::addSortState(int column, Qt::SortOrder order, bool ignore_multicolumn_sorting) { diff --git a/src/librssguard/database/databasequeries.cpp b/src/librssguard/database/databasequeries.cpp index 933f07087..6cec2b2cb 100644 --- a/src/librssguard/database/databasequeries.cpp +++ b/src/librssguard/database/databasequeries.cpp @@ -33,7 +33,8 @@ QMap DatabaseQueries::messageTableAttributes(bool only_msg_table, field_names[MSG_DB_CUSTOM_ID_INDEX] = QSL("Messages.custom_id"); field_names[MSG_DB_CUSTOM_HASH_INDEX] = QSL("Messages.custom_hash"); field_names[MSG_DB_FEED_TITLE_INDEX] = only_msg_table ? QSL("Messages.feed") : QSL("Feeds.title"); - field_names[MSG_DB_HAS_ENCLOSURES] = QSL("CASE WHEN length(Messages.enclosures) > 10 " + field_names[MSG_DB_FEED_IS_RTL_INDEX] = QSL("Feeds.is_rtl"); + field_names[MSG_DB_HAS_ENCLOSURES] = QSL("CASE WHEN LENGTH(Messages.enclosures) > 10 " "THEN 'true' " "ELSE 'false' " "END AS has_enclosures"); @@ -2178,11 +2179,11 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in q.prepare("UPDATE Feeds " "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, is_quiet = :is_quiet, open_articles = " - ":open_articles, " - " account_id = :account_id, custom_id = :custom_id, custom_data = :custom_data " - "WHERE id = :id;"); + " icon = :icon, category = :category, source = :source, update_type = :update_type," + " update_interval = :update_interval, is_off = :is_off, is_quiet = :is_quiet, open_articles =" + " :open_articles, is_rtl = :is_rtl, add_any_datetime_articles = :add_any_datetime_articles," + " datetime_to_avoid = :datetime_to_avoid, account_id" + " = :account_id, custom_id = :custom_id, custom_data = :custom_data WHERE id = :id;"); q.bindValue(QSL(":title"), feed->title()); q.bindValue(QSL(":description"), feed->description()); q.bindValue(QSL(":date_created"), feed->creationDate().toMSecsSinceEpoch()); @@ -2198,6 +2199,10 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in q.bindValue(QSL(":is_off"), feed->isSwitchedOff()); q.bindValue(QSL(":is_quiet"), feed->isQuiet()); q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly()); + q.bindValue(QSL(":is_rtl"), feed->isRtl()); + q.bindValue(QSL(":add_any_datetime_articles"), feed->addAnyDatetimeArticles()); + q.bindValue(QSL(":datetime_to_avoid"), + feed->datetimeToAvoid().isValid() ? feed->datetimeToAvoid().toMSecsSinceEpoch() : 0); auto custom_data = feed->customDatabaseData(); QString serialized_custom_data = serializeCustomData(custom_data); diff --git a/src/librssguard/database/databasequeries.h b/src/librssguard/database/databasequeries.h index 1a65e7df1..78dc8f12b 100644 --- a/src/librssguard/database/databasequeries.h +++ b/src/librssguard/database/databasequeries.h @@ -353,6 +353,13 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db, feed->setAutoUpdateInterval(query.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); feed->setIsSwitchedOff(query.value(FDS_DB_IS_OFF_INDEX).toBool()); feed->setIsQuiet(query.value(FDS_DB_IS_QUIET_INDEX).toBool()); + feed->setIsRtl(query.value(FDS_DB_IS_RTL_INDEX).toBool()); + feed->setAddAnyDatetimeArticles(query.value(FDS_DB_ADD_ANY_DATETIME_ARTICLES_INDEX).toBool()); + + auto dt_avoid = query.value(FDS_DB_DATETIME_TO_AVOID_INDEX).value(); + + feed->setDatetimeToAvoid(dt_avoid > 0 ? TextFactory::parseDateTime(dt_avoid) : QDateTime()); + feed->setOpenArticlesDirectly(query.value(FDS_DB_OPEN_ARTICLES_INDEX).toBool()); qDebugNN << LOGSEC_CORE << "Custom ID of feed when loading from DB is" << QUOTE_W_SPACE_DOT(feed->customId()); diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index ec6af0b23..0aa4b3c5c 100644 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -50,7 +50,8 @@ #define MSG_SCORE_MIN 0.0 #define LOWER_TITLE_ROLE 64 -#define HIGHLIGHTED_FOREGROUND_TITLE_ROLE 128 +#define HIGHLIGHTED_FOREGROUND_TITLE_ROLE 65 +#define TEXT_DIRECTION_ROLE 66 #define SOUNDS_BUILTIN_DIRECTORY ":/sounds" #define ARGUMENTS_LIST_SEPARATOR "\n" @@ -214,7 +215,7 @@ #define APP_DB_SQLITE_FILE "database.db" // Keep this in sync with schema versions declared in SQL initialization code. -#define APP_DB_SCHEMA_VERSION "5" +#define APP_DB_SCHEMA_VERSION "6" #define APP_DB_UPDATE_FILE_PATTERN "db_update_%1_%2_%3.sql" #define APP_DB_COMMENT_SPLIT "-- !\n" #define APP_DB_INCLUDE_PLACEHOLDER "!!" @@ -267,9 +268,10 @@ #define MSG_DB_CUSTOM_ID_INDEX 14 #define MSG_DB_CUSTOM_HASH_INDEX 15 #define MSG_DB_FEED_TITLE_INDEX 16 -#define MSG_DB_HAS_ENCLOSURES 17 -#define MSG_DB_LABELS 18 -#define MSG_DB_LABELS_IDS 19 +#define MSG_DB_FEED_IS_RTL_INDEX 17 +#define MSG_DB_HAS_ENCLOSURES 18 +#define MSG_DB_LABELS 19 +#define MSG_DB_LABELS_IDS 20 // Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES. #define CAT_DB_ID_INDEX 0 @@ -295,10 +297,13 @@ #define FDS_DB_UPDATE_INTERVAL_INDEX 9 #define FDS_DB_IS_OFF_INDEX 10 #define FDS_DB_IS_QUIET_INDEX 11 -#define FDS_DB_OPEN_ARTICLES_INDEX 12 -#define FDS_DB_ACCOUNT_ID_INDEX 13 -#define FDS_DB_CUSTOM_ID_INDEX 14 -#define FDS_DB_CUSTOM_DATA_INDEX 15 +#define FDS_DB_IS_RTL_INDEX 12 +#define FDS_DB_ADD_ANY_DATETIME_ARTICLES_INDEX 13 +#define FDS_DB_DATETIME_TO_AVOID_INDEX 14 +#define FDS_DB_OPEN_ARTICLES_INDEX 15 +#define FDS_DB_ACCOUNT_ID_INDEX 16 +#define FDS_DB_CUSTOM_ID_INDEX 17 +#define FDS_DB_CUSTOM_DATA_INDEX 18 // Indexes of columns for feed models. #define FDS_MODEL_TITLE_INDEX 0 diff --git a/src/librssguard/gui/feedsview.cpp b/src/librssguard/gui/feedsview.cpp index dabe3a187..a54b1f578 100644 --- a/src/librssguard/gui/feedsview.cpp +++ b/src/librssguard/gui/feedsview.cpp @@ -233,6 +233,10 @@ void FeedsView::editSelectedItem() { // Changes are done, unlock the update master lock. qApp->feedUpdateLock()->unlock(); + + // TODO: NOTE: Make sure to refresh article list if RTL is changed? + // RootItem* selected_item = selectedItem(); + // emit itemSelected(selected_item); } void FeedsView::deleteSelectedItem() { diff --git a/src/librssguard/gui/messagepreviewer.cpp b/src/librssguard/gui/messagepreviewer.cpp index 1384fe6aa..198629210 100644 --- a/src/librssguard/gui/messagepreviewer.cpp +++ b/src/librssguard/gui/messagepreviewer.cpp @@ -175,7 +175,6 @@ void MessagePreviewer::loadMessage(const Message& message, RootItem* root) { } else { ensureDefaultBrowserVisible(); - m_msgBrowser->loadMessages({message}, m_root); } } diff --git a/src/librssguard/gui/messagesview.cpp b/src/librssguard/gui/messagesview.cpp index e36d6bd39..a3a377d08 100644 --- a/src/librssguard/gui/messagesview.cpp +++ b/src/librssguard/gui/messagesview.cpp @@ -516,6 +516,20 @@ void MessagesView::loadItem(RootItem* item) { sort(col, ord, false, true, false, true); m_sourceModel->loadMessages(item); + /* + if (item->kind() == RootItem::Kind::Feed) { + if (item->toFeed()->isRtl()) { + setLayoutDirection(Qt::LayoutDirection::RightToLeft); + } + else { + setLayoutDirection(Qt::LayoutDirection::LeftToRight); + } + } + else { + setLayoutDirection(Qt::LayoutDirection::LeftToRight); + } + */ + // Messages are loaded, make sure that previously // active message is not shown in browser. emit currentMessageRemoved(m_sourceModel->loadedItem()); @@ -826,6 +840,7 @@ void MessagesView::adjustColumns() { hideColumn(MSG_DB_CUSTOM_HASH_INDEX); hideColumn(MSG_DB_FEED_CUSTOM_ID_INDEX); hideColumn(MSG_DB_FEED_TITLE_INDEX); + hideColumn(MSG_DB_FEED_IS_RTL_INDEX); hideColumn(MSG_DB_HAS_ENCLOSURES); hideColumn(MSG_DB_LABELS); diff --git a/src/librssguard/gui/reusable/styleditemdelegatewithoutfocus.cpp b/src/librssguard/gui/reusable/styleditemdelegatewithoutfocus.cpp index 1b1c09d7c..bf26fbffe 100644 --- a/src/librssguard/gui/reusable/styleditemdelegatewithoutfocus.cpp +++ b/src/librssguard/gui/reusable/styleditemdelegatewithoutfocus.cpp @@ -16,6 +16,12 @@ void StyledItemDelegateWithoutFocus::paint(QPainter* painter, item_option.state = item_option.state ^ QStyle::StateFlag::State_HasFocus; } + bool rtl = index.data(TEXT_DIRECTION_ROLE).value() == Qt::LayoutDirection::RightToLeft; + + if (rtl) { + item_option.direction = Qt::LayoutDirection::RightToLeft; + } + if ((item_option.state & QStyle::StateFlag::State_Selected) == QStyle::StateFlag::State_Selected && index.data(Qt::ItemDataRole::ForegroundRole).isValid()) { item_option.palette.setColor(QPalette::ColorRole::HighlightedText, diff --git a/src/librssguard/gui/settings/settingsfeedsmessages.cpp b/src/librssguard/gui/settings/settingsfeedsmessages.cpp index 48c4cd5ec..cee73900a 100644 --- a/src/librssguard/gui/settings/settingsfeedsmessages.cpp +++ b/src/librssguard/gui/settings/settingsfeedsmessages.cpp @@ -197,6 +197,11 @@ SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent m_ui->m_spinFeedUpdateTimeout->setSuffix(QSL(" ") + m_ui->m_spinFeedUpdateTimeout->suffix()); } + m_ui->m_dtDateTimeToAvoid->setDisplayFormat(qApp->localization()->loadedLocale().dateFormat()); + + connect(m_ui->m_gbAvoidOldArticles, &QGroupBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_dtDateTimeToAvoid, &QDateTimeEdit::dateTimeChanged, this, &SettingsFeedsMessages::dirtifySettings); + m_ui->m_spinRelativeArticleTime->setValue(-1); } @@ -260,6 +265,10 @@ void SettingsFeedsMessages::loadSettings() { ->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateOnlyUnfocused)).toBool()); m_ui->m_spinAutoUpdateInterval->setValue(settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt()); m_ui->m_spinFeedUpdateTimeout->setValue(settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt()); + m_ui->m_gbAvoidOldArticles + ->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::AvoidOldArticles)).toBool()); + m_ui->m_dtDateTimeToAvoid + ->setDateTime(settings()->value(GROUP(Messages), SETTING(Messages::DateTimeToAvoidArticle)).toDateTime()); m_ui->m_cmbFastAutoUpdate->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::FastAutoUpdate)).toBool()); m_ui->m_checkUpdateAllFeedsOnStartup ->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()); @@ -346,6 +355,8 @@ void SettingsFeedsMessages::saveSettings() { settings()->setValue(GROUP(Feeds), Feeds::AutoUpdateOnlyUnfocused, m_ui->m_checkAutoUpdateOnlyUnfocused->isChecked()); settings()->setValue(GROUP(Feeds), Feeds::AutoUpdateInterval, m_ui->m_spinAutoUpdateInterval->value()); settings()->setValue(GROUP(Feeds), Feeds::UpdateTimeout, m_ui->m_spinFeedUpdateTimeout->value()); + settings()->setValue(GROUP(Messages), Messages::AvoidOldArticles, m_ui->m_gbAvoidOldArticles->isChecked()); + settings()->setValue(GROUP(Messages), Messages::DateTimeToAvoidArticle, m_ui->m_dtDateTimeToAvoid->dateTime()); settings()->setValue(GROUP(Feeds), Feeds::FastAutoUpdate, m_ui->m_cmbFastAutoUpdate->isChecked()); settings()->setValue(GROUP(Feeds), Feeds::FeedsUpdateOnStartup, m_ui->m_checkUpdateAllFeedsOnStartup->isChecked()); settings()->setValue(GROUP(Feeds), Feeds::FeedsUpdateStartupDelay, m_ui->m_spinStartupUpdateDelay->value()); diff --git a/src/librssguard/gui/settings/settingsfeedsmessages.ui b/src/librssguard/gui/settings/settingsfeedsmessages.ui index b5c85383e..f0229890a 100644 --- a/src/librssguard/gui/settings/settingsfeedsmessages.ui +++ b/src/librssguard/gui/settings/settingsfeedsmessages.ui @@ -6,7 +6,7 @@ 0 0 - 558 + 570 531 @@ -80,6 +80,13 @@ + + + + Support very fast auto-fetching intervals (under 10 seconds) + + + @@ -115,11 +122,42 @@ - - - - Support very fast auto-fetching intervals (under 10 seconds) + + + + Avoid adding articles before this date into the database + + true + + + false + + + + + + dd/MM/yyyy HH:mm + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp index b82ab6e06..2fa266eb2 100644 --- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp +++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp @@ -302,6 +302,22 @@ void TextBrowserViewer::loadMessages(const QList& messages, RootItem* r // auto html_messages = qApp->skins()->generateHtmlOfArticles(messages, root); setHtml(html_messages.m_html, html_messages.m_baseUrl); + + /* + auto* feed = root != nullptr + ? root->getParentServiceRoot() + ->getItemFromSubTree([messages](const RootItem* it) { + return it->kind() == RootItem::Kind::Feed && it->customId() == messages.at(0).m_feedId; + }) + ->toFeed() + : nullptr; + bool is_rtl_feed = feed != nullptr && feed->isRtl(); + */ + + QTextOption op; + op.setTextDirection(messages.at(0).m_isRtl ? Qt::LayoutDirection::RightToLeft : Qt::LayoutDirection::LeftToRight); + document()->setDefaultTextOption(op); + emit loadingFinished(true); } @@ -544,16 +560,6 @@ void TextBrowserViewer::setHtml(const QString& html, const QUrl& base_url) { } setVerticalScrollBarPosition(0.0); - - // TODO: implement RTL for viewers somehow? - /* - auto to = document()->defaultTextOption(); - - to.setTextDirection(Qt::LayoutDirection::RightToLeft); - to.setAlignment(Qt::AlignmentFlag::AlignRight); - - document()->setDefaultTextOption(to); - */ } void TextBrowserViewer::setReadabledHtml(const QString& html, const QUrl& base_url) { diff --git a/src/librssguard/miscellaneous/settings.cpp b/src/librssguard/miscellaneous/settings.cpp index 12ff844c8..b382c93c1 100644 --- a/src/librssguard/miscellaneous/settings.cpp +++ b/src/librssguard/miscellaneous/settings.cpp @@ -130,6 +130,12 @@ DVALUE(int) Messages::MessageHeadImageHeightDef = 36; DKEY Messages::DisplayEnclosuresInMessage = "show_enclosures_in_message"; DVALUE(bool) Messages::DisplayEnclosuresInMessageDef = false; +DKEY Messages::AvoidOldArticles = "avoid_old_articles"; +DVALUE(bool) Messages::AvoidOldArticlesDef = false; + +DKEY Messages::DateTimeToAvoidArticle = "datetime_to_avoid_article"; +DVALUE(QDateTime) Messages::DateTimeToAvoidArticleDef = QDateTime::currentDateTime(); + DKEY Messages::AlwaysDisplayItemPreview = "always_display_preview"; DVALUE(bool) Messages::AlwaysDisplayItemPreviewDef = true; diff --git a/src/librssguard/miscellaneous/settings.h b/src/librssguard/miscellaneous/settings.h index 1fb5f1299..81fd66941 100644 --- a/src/librssguard/miscellaneous/settings.h +++ b/src/librssguard/miscellaneous/settings.h @@ -129,6 +129,12 @@ namespace Messages { KEY DisplayEnclosuresInMessage; VALUE(bool) DisplayEnclosuresInMessageDef; + KEY AvoidOldArticles; + VALUE(bool) AvoidOldArticlesDef; + + KEY DateTimeToAvoidArticle; + VALUE(QDateTime) DateTimeToAvoidArticleDef; + KEY AlwaysDisplayItemPreview; VALUE(bool) AlwaysDisplayItemPreviewDef; diff --git a/src/librssguard/miscellaneous/skinfactory.cpp b/src/librssguard/miscellaneous/skinfactory.cpp index 7673bea1b..b546824ae 100644 --- a/src/librssguard/miscellaneous/skinfactory.cpp +++ b/src/librssguard/miscellaneous/skinfactory.cpp @@ -235,6 +235,17 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList& messages, const auto forced_img_size = qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toInt(); + auto* feed = root != nullptr + ? root->getParentServiceRoot() + ->getItemFromSubTree([messages](const RootItem* it) { + return it->kind() == RootItem::Kind::Feed && it->customId() == messages.at(0).m_feedId; + }) + ->toFeed() + : nullptr; + /* + bool is_rtl_feed = feed != nullptr && feed->isRtl(); + */ + for (const Message& message : messages) { QString enclosures; QString enclosure_images; @@ -278,17 +289,13 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList& messages, msg_date, enclosures, enclosure_images, - QString::number(message.m_id))); + QString::number(message.m_id), + message.m_isRtl ? QSL("rtl") : QSL("ltr"))); } QString msg_contents = skin.m_layoutMarkupWrapper.arg(messages.size() == 1 ? messages.at(0).m_title : tr("Newspaper view"), messages_layout); - auto* feed = root->getParentServiceRoot() - ->getItemFromSubTree([messages](const RootItem* it) { - return it->kind() == RootItem::Kind::Feed && it->customId() == messages.at(0).m_feedId; - }) - ->toFeed(); QString base_url; if (feed != nullptr) { diff --git a/src/librssguard/services/abstract/feed.cpp b/src/librssguard/services/abstract/feed.cpp index 5a98c9ff6..0aea35c6a 100644 --- a/src/librssguard/services/abstract/feed.cpp +++ b/src/librssguard/services/abstract/feed.cpp @@ -20,8 +20,9 @@ Feed::Feed(RootItem* parent) : RootItem(parent), m_source(QString()), m_status(Status::Normal), m_statusString(QString()), m_autoUpdateType(AutoUpdateType::DefaultAutoUpdate), m_autoUpdateInterval(DEFAULT_AUTO_UPDATE_INTERVAL), - m_lastUpdated(QDateTime::currentDateTimeUtc()), m_isSwitchedOff(false), m_isQuiet(false), - m_openArticlesDirectly(false), m_messageFilters(QList>()) { + m_lastUpdated(QDateTime::currentDateTimeUtc()), m_isSwitchedOff(false), m_isQuiet(false), m_isRtl(false), + m_addAnyDatetimeArticles(false), m_avoidOldArticles(false), m_openArticlesDirectly(false), + m_datetimeToAvoid(QDateTime::currentDateTime()), m_messageFilters(QList>()) { setKind(RootItem::Kind::Feed); } @@ -43,6 +44,9 @@ Feed::Feed(const Feed& other) : RootItem(other) { setLastUpdated(other.lastUpdated()); setMessageFilters(other.messageFilters()); setOpenArticlesDirectly(other.openArticlesDirectly()); + setAddAnyDatetimeArticles(other.addAnyDatetimeArticles()); + setDatetimeToAvoid(other.datetimeToAvoid()); + setIsRtl(other.isRtl()); setIsSwitchedOff(other.isSwitchedOff()); setIsQuiet(other.isQuiet()); } @@ -78,6 +82,9 @@ QVariant Feed::data(int column, int role) const { return QVariant(); } + case TEXT_DIRECTION_ROLE: + return isRtl() ? Qt::LayoutDirection::RightToLeft : Qt::LayoutDirection::LayoutDirectionAuto; + case Qt::ItemDataRole::ForegroundRole: switch (status()) { case Status::NewMessages: @@ -189,6 +196,30 @@ void Feed::setOpenArticlesDirectly(bool opn) { m_openArticlesDirectly = opn; } +bool Feed::isRtl() const { + return m_isRtl; +} + +void Feed::setIsRtl(bool rtl) { + m_isRtl = rtl; +} + +bool Feed::addAnyDatetimeArticles() const { + return m_addAnyDatetimeArticles; +} + +void Feed::setAddAnyDatetimeArticles(bool add_any_articles) { + m_addAnyDatetimeArticles = add_any_articles; +} + +QDateTime Feed::datetimeToAvoid() const { + return m_datetimeToAvoid; +} + +void Feed::setDatetimeToAvoid(const QDateTime& dt) { + m_datetimeToAvoid = dt; +} + void Feed::appendMessageFilter(MessageFilter* filter) { m_messageFilters.append(QPointer(filter)); } diff --git a/src/librssguard/services/abstract/feed.h b/src/librssguard/services/abstract/feed.h index 28ce3d983..5cb16ddea 100644 --- a/src/librssguard/services/abstract/feed.h +++ b/src/librssguard/services/abstract/feed.h @@ -82,6 +82,16 @@ class Feed : public RootItem { QDateTime lastUpdated() const; void setLastUpdated(const QDateTime& last_updated); + + bool isRtl() const; + void setIsRtl(bool rtl); + + bool addAnyDatetimeArticles() const; + void setAddAnyDatetimeArticles(bool add_any_articles); + + QDateTime datetimeToAvoid() const; + void setDatetimeToAvoid(const QDateTime& dt); + public slots: virtual void updateCounts(bool including_total_count); @@ -99,6 +109,11 @@ class Feed : public RootItem { bool m_isSwitchedOff; bool m_isQuiet; bool m_openArticlesDirectly; + bool m_isRtl; + bool m_addAnyDatetimeArticles; + bool m_avoidOldArticles; + bool m_avoidOldArticlesEnabled; + QDateTime m_datetimeToAvoid; int m_totalCount{}; int m_unreadCount{}; QList> m_messageFilters; diff --git a/src/librssguard/services/abstract/gui/formfeeddetails.cpp b/src/librssguard/services/abstract/gui/formfeeddetails.cpp index 4adb9b1f5..0bf8a27aa 100644 --- a/src/librssguard/services/abstract/gui/formfeeddetails.cpp +++ b/src/librssguard/services/abstract/gui/formfeeddetails.cpp @@ -49,6 +49,10 @@ void FormFeedDetails::apply() { .toInt())); m_feed->setAutoUpdateInterval(int(m_ui->m_spinAutoUpdateInterval->value())); m_feed->setOpenArticlesDirectly(m_ui->m_cbOpenArticlesAutomatically->isChecked()); + m_feed->setIsRtl(m_ui->m_cbFeedRTL->isChecked()); + m_feed->setAddAnyDatetimeArticles(m_ui->m_cbAddAnyDateArticles->isChecked()); + m_feed->setDatetimeToAvoid(m_ui->m_gbAvoidOldArticles->isChecked() ? m_ui->m_dtDateTimeToAvoid->dateTime() + : QDateTime()); m_feed->setIsSwitchedOff(m_ui->m_cbDisableFeed->isChecked()); m_feed->setIsQuiet(m_ui->m_cbSuppressFeed->isChecked()); @@ -81,6 +85,10 @@ void FormFeedDetails::createConnections() { static_cast(&QComboBox::currentIndexChanged), this, &FormFeedDetails::onAutoUpdateTypeChanged); + + connect(m_ui->m_cbAddAnyDateArticles, &QCheckBox::toggled, this, [this](bool checked) { + m_ui->m_gbAvoidOldArticles->setEnabled(!checked); + }); } void FormFeedDetails::loadFeedData() { @@ -97,6 +105,10 @@ void FormFeedDetails::loadFeedData() { ->setCurrentIndex(m_ui->m_cmbAutoUpdateType->findData(QVariant::fromValue(int(m_feed->autoUpdateType())))); m_ui->m_spinAutoUpdateInterval->setValue(m_feed->autoUpdateInterval()); m_ui->m_cbOpenArticlesAutomatically->setChecked(m_feed->openArticlesDirectly()); + m_ui->m_cbFeedRTL->setChecked(m_feed->isRtl()); + m_ui->m_cbAddAnyDateArticles->setChecked(m_feed->addAnyDatetimeArticles()); + m_ui->m_gbAvoidOldArticles->setChecked(m_feed->datetimeToAvoid().isValid()); + m_ui->m_dtDateTimeToAvoid->setDateTime(m_feed->datetimeToAvoid()); m_ui->m_cbDisableFeed->setChecked(m_feed->isSwitchedOff()); m_ui->m_cbSuppressFeed->setChecked(m_feed->isQuiet()); } diff --git a/src/librssguard/services/abstract/gui/formfeeddetails.ui b/src/librssguard/services/abstract/gui/formfeeddetails.ui index f874c739a..06cbe7ebb 100644 --- a/src/librssguard/services/abstract/gui/formfeeddetails.ui +++ b/src/librssguard/services/abstract/gui/formfeeddetails.ui @@ -59,6 +59,61 @@ + + + + Qt::Horizontal + + + + + + + Add articles with any date into the database + + + + + + + true + + + Avoid adding articles before this date into the database : + + + true + + + false + + + + + + dd/MM/yyyy HH:mm + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -66,17 +121,24 @@ Miscellaneous - + + + + Ignore notifications for this feed + + + + Disable this feed - - + + - Ignore notifications for this feed + Right-to-left layout @@ -111,8 +173,12 @@ m_cmbAutoUpdateType m_spinAutoUpdateInterval m_cbOpenArticlesAutomatically + m_cbAddAnyDateArticles + m_gbAvoidOldArticles + m_dtDateTimeToAvoid m_cbSuppressFeed m_cbDisableFeed + m_cbFeedRTL @@ -123,8 +189,8 @@ reject() - 331 - 556 + 340 + 580 286 diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 1c349af0c..2c5a3ebc9 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -385,6 +385,9 @@ QMap ServiceRoot::storeCustomFeedsData() { feed_custom_data.insert(QSL("is_off"), feed->isSwitchedOff()); feed_custom_data.insert(QSL("is_quiet"), feed->isQuiet()); feed_custom_data.insert(QSL("open_articles_directly"), feed->openArticlesDirectly()); + feed_custom_data.insert(QSL("is_rtl"), feed->isRtl()); + feed_custom_data.insert(QSL("add_any_datetime_articles"), feed->addAnyDatetimeArticles()); + feed_custom_data.insert(QSL("datetime_to_avoid"), feed->datetimeToAvoid().toMSecsSinceEpoch()); // NOTE: This is here specifically to be able to restore custom sort order. // Otherwise the information is lost when list of feeds/folders is refreshed from remote @@ -434,6 +437,9 @@ void ServiceRoot::restoreCustomFeedsData(const QMap& data, feed->setIsSwitchedOff(feed_custom_data.value(QSL("is_off")).toBool()); feed->setIsQuiet(feed_custom_data.value(QSL("is_quiet")).toBool()); feed->setOpenArticlesDirectly(feed_custom_data.value(QSL("open_articles_directly")).toBool()); + feed->setIsRtl(feed_custom_data.value(QSL("is_rtl")).toBool()); + feed->setAddAnyDatetimeArticles(feed_custom_data.value(QSL("add_any_datetime_articles")).toBool()); + feed->setDatetimeToAvoid(feed_custom_data.value(QSL("datetime_to_avoid")).toDateTime()); } } } diff --git a/src/librssguard/services/gmail/gui/emailpreviewer.cpp b/src/librssguard/services/gmail/gui/emailpreviewer.cpp index d35b722f8..b00ff50ab 100644 --- a/src/librssguard/services/gmail/gui/emailpreviewer.cpp +++ b/src/librssguard/services/gmail/gui/emailpreviewer.cpp @@ -49,8 +49,6 @@ void EmailPreviewer::clear() { } void EmailPreviewer::loadMessage(const Message& msg, RootItem* selected_item) { - Q_UNUSED(selected_item) - m_message = msg; m_webView->loadMessages({msg}, selected_item);