Merge branch 'master' of github.com:martinrotter/rssguard
|
@ -15,6 +15,8 @@ RSS Guard is simple, light and easy-to-use RSS/ATOM feed aggregator developed us
|
||||||
* [Nextcloud News](https://apps.nextcloud.com/apps/news),
|
* [Nextcloud News](https://apps.nextcloud.com/apps/news),
|
||||||
* [Gmail API](https://developers.google.com/gmail/api).
|
* [Gmail API](https://developers.google.com/gmail/api).
|
||||||
|
|
||||||
|
Application icon was kindly contributed by Siddharth Yadav - @Siddharth_yd (Instagram), illustrationdesignsid@gmail.com (e-mail).
|
||||||
|
|
||||||
Development builds can be downloaded [here for Windows](https://bintray.com/martinrotter/rssguard/Development/Windows) and [here for Linux/Mac](https://bintray.com/martinrotter/rssguard/Development/LinuxMacOs).
|
Development builds can be downloaded [here for Windows](https://bintray.com/martinrotter/rssguard/Development/Windows) and [here for Linux/Mac](https://bintray.com/martinrotter/rssguard/Development/LinuxMacOs).
|
||||||
|
|
||||||
Documentation is [here](https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md).
|
Documentation is [here](https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md).
|
||||||
|
|
|
@ -48,6 +48,19 @@ function filterMessage() {
|
||||||
return MSG_ACCEPT;
|
return MSG_ACCEPT;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Make sure that your receive only one message with particular URL and all other messages with same URL are subsequently ignored.
|
||||||
|
```js
|
||||||
|
function filterMessage() {
|
||||||
|
if (msg.isDuplicateWithAttribute(2)) {
|
||||||
|
return MSG_IGNORE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return MSG_ACCEPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `Message filters` dialog
|
## `Message filters` dialog
|
||||||
The dialog is accessible from menu `Messages -> Message filters` and is the central place for message filters management within RSS Guard. It allows you to:
|
The dialog is accessible from menu `Messages -> Message filters` and is the central place for message filters management within RSS Guard. It allows you to:
|
||||||
* add or remove message filters,
|
* add or remove message filters,
|
||||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 24 KiB |
BIN
resources/graphics/original_sizes/rssguard.png
Executable file
After Width: | Height: | Size: 140 KiB |
BIN
resources/graphics/original_sizes/rssguard_mono.png
Executable file
After Width: | Height: | Size: 140 KiB |
BIN
resources/graphics/original_sizes/rssguard_plain.png
Executable file
After Width: | Height: | Size: 69 KiB |
BIN
resources/graphics/original_sizes/rssguard_plain_mono.png
Executable file
After Width: | Height: | Size: 68 KiB |
BIN
resources/graphics/rssguard.ico
Normal file → Executable file
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 122 KiB |
BIN
resources/graphics/rssguard.png
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
BIN
resources/graphics/rssguard_plain.png
Normal file → Executable file
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 10 KiB |
BIN
resources/macosx/rssguard.icns
Normal file → Executable file
|
@ -1,3 +1,17 @@
|
||||||
|
3.8.1
|
||||||
|
—————
|
||||||
|
|
||||||
|
Added:
|
||||||
|
▪ New applications icons. Contributed by Siddharth Yadav:
|
||||||
|
▪ @Siddharth_yd (Instagram)
|
||||||
|
▪ illustrationdesignsid@gmail.com (e-mail)
|
||||||
|
▪
|
||||||
|
▪
|
||||||
|
|
||||||
|
Fixed/changed:
|
||||||
|
▪
|
||||||
|
▪
|
||||||
|
|
||||||
3.8.0
|
3.8.0
|
||||||
—————
|
—————
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,7 @@ QList<QAction*> FormMain::allActions() const {
|
||||||
actions << m_ui->m_actionClearAllItems;
|
actions << m_ui->m_actionClearAllItems;
|
||||||
actions << m_ui->m_actionShowOnlyUnreadItems;
|
actions << m_ui->m_actionShowOnlyUnreadItems;
|
||||||
actions << m_ui->m_actionShowTreeBranches;
|
actions << m_ui->m_actionShowTreeBranches;
|
||||||
|
actions << m_ui->m_actionAutoExpandItemsWhenSelected;
|
||||||
actions << m_ui->m_actionShowOnlyUnreadMessages;
|
actions << m_ui->m_actionShowOnlyUnreadMessages;
|
||||||
actions << m_ui->m_actionMarkSelectedMessagesAsRead;
|
actions << m_ui->m_actionMarkSelectedMessagesAsRead;
|
||||||
actions << m_ui->m_actionMarkSelectedMessagesAsUnread;
|
actions << m_ui->m_actionMarkSelectedMessagesAsUnread;
|
||||||
|
@ -596,6 +597,8 @@ void FormMain::loadSize() {
|
||||||
SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool());
|
SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool());
|
||||||
m_ui->m_actionShowTreeBranches->setChecked(settings->value(GROUP(Feeds),
|
m_ui->m_actionShowTreeBranches->setChecked(settings->value(GROUP(Feeds),
|
||||||
SETTING(Feeds::ShowTreeBranches)).toBool());
|
SETTING(Feeds::ShowTreeBranches)).toBool());
|
||||||
|
m_ui->m_actionAutoExpandItemsWhenSelected->setChecked(settings->value(GROUP(Feeds),
|
||||||
|
SETTING(Feeds::AutoExpandOnSelection)).toBool());
|
||||||
m_ui->m_actionShowOnlyUnreadMessages->setChecked(settings->value(GROUP(Messages),
|
m_ui->m_actionShowOnlyUnreadMessages->setChecked(settings->value(GROUP(Messages),
|
||||||
SETTING(Messages::ShowOnlyUnreadMessages)).toBool());
|
SETTING(Messages::ShowOnlyUnreadMessages)).toBool());
|
||||||
m_ui->m_actionAlternateColorsInLists->setChecked(settings->value(GROUP(GUI),
|
m_ui->m_actionAlternateColorsInLists->setChecked(settings->value(GROUP(GUI),
|
||||||
|
@ -763,6 +766,8 @@ void FormMain::createConnections() {
|
||||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds);
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds);
|
||||||
connect(m_ui->m_actionShowTreeBranches, &QAction::toggled,
|
connect(m_ui->m_actionShowTreeBranches, &QAction::toggled,
|
||||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowFeedTreeBranches);
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowFeedTreeBranches);
|
||||||
|
connect(m_ui->m_actionAutoExpandItemsWhenSelected, &QAction::toggled,
|
||||||
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleItemsAutoExpandingOnSelection);
|
||||||
connect(m_ui->m_actionAlternateColorsInLists, &QAction::toggled,
|
connect(m_ui->m_actionAlternateColorsInLists, &QAction::toggled,
|
||||||
tabWidget()->feedMessageViewer(), &FeedMessageViewer::alternateRowColorsInLists);
|
tabWidget()->feedMessageViewer(), &FeedMessageViewer::alternateRowColorsInLists);
|
||||||
connect(m_ui->m_actionShowOnlyUnreadMessages, &QAction::toggled,
|
connect(m_ui->m_actionShowOnlyUnreadMessages, &QAction::toggled,
|
||||||
|
|
|
@ -116,6 +116,7 @@
|
||||||
<addaction name="m_actionDeleteSelectedItem"/>
|
<addaction name="m_actionDeleteSelectedItem"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="m_actionShowOnlyUnreadItems"/>
|
<addaction name="m_actionShowOnlyUnreadItems"/>
|
||||||
|
<addaction name="m_actionAutoExpandItemsWhenSelected"/>
|
||||||
<addaction name="m_actionShowTreeBranches"/>
|
<addaction name="m_actionShowTreeBranches"/>
|
||||||
<addaction name="m_actionExpandCollapseItem"/>
|
<addaction name="m_actionExpandCollapseItem"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
@ -784,6 +785,14 @@
|
||||||
<string>Alternate row colors in lists</string>
|
<string>Alternate row colors in lists</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="m_actionAutoExpandItemsWhenSelected">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatically &expand items when selected</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|
|
@ -188,6 +188,12 @@ void FeedMessageViewer::toggleShowFeedTreeBranches() {
|
||||||
qApp->settings()->setValue(GROUP(Feeds), Feeds::ShowTreeBranches, origin->isChecked());
|
qApp->settings()->setValue(GROUP(Feeds), Feeds::ShowTreeBranches, origin->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedMessageViewer::toggleItemsAutoExpandingOnSelection() {
|
||||||
|
const QAction* origin = qobject_cast<QAction*>(sender());
|
||||||
|
|
||||||
|
qApp->settings()->setValue(GROUP(Feeds), Feeds::AutoExpandOnSelection, origin->isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
void FeedMessageViewer::alternateRowColorsInLists() {
|
void FeedMessageViewer::alternateRowColorsInLists() {
|
||||||
const QAction* origin = qobject_cast<QAction*>(sender());
|
const QAction* origin = qobject_cast<QAction*>(sender());
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ class RSSGUARD_DLLSPEC FeedMessageViewer : public TabContent {
|
||||||
void toggleShowOnlyUnreadMessages();
|
void toggleShowOnlyUnreadMessages();
|
||||||
void toggleShowOnlyUnreadFeeds();
|
void toggleShowOnlyUnreadFeeds();
|
||||||
void toggleShowFeedTreeBranches();
|
void toggleShowFeedTreeBranches();
|
||||||
|
void toggleItemsAutoExpandingOnSelection();
|
||||||
void alternateRowColorsInLists();
|
void alternateRowColorsInLists();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
|
@ -713,6 +713,11 @@ void FeedsView::selectionChanged(const QItemSelection& selected, const QItemSele
|
||||||
emit itemSelected(selected_item);
|
emit itemSelected(selected_item);
|
||||||
|
|
||||||
m_proxyModel->invalidateReadFeedsFilter();
|
m_proxyModel->invalidateReadFeedsFilter();
|
||||||
|
|
||||||
|
if (!selectedIndexes().isEmpty() &&
|
||||||
|
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoExpandOnSelection)).toBool()) {
|
||||||
|
expand(selectedIndexes().first());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedsView::keyPressEvent(QKeyEvent* event) {
|
void FeedsView::keyPressEvent(QKeyEvent* event) {
|
||||||
|
|
|
@ -124,26 +124,26 @@ void SystemTrayIcon::setNumber(int number, bool any_new_message) {
|
||||||
// Numbers with more than 2 digits won't be readable, display
|
// Numbers with more than 2 digits won't be readable, display
|
||||||
// infinity symbol in that case.
|
// infinity symbol in that case.
|
||||||
if (number > 999) {
|
if (number > 999) {
|
||||||
m_font.setPixelSize(100);
|
m_font.setPixelSize(background.width() * 0.78);
|
||||||
tray_painter.setFont(m_font);
|
tray_painter.setFont(m_font);
|
||||||
tray_painter.drawText(QRect(0, 0, 128, 128), Qt::AlignVCenter | Qt::AlignCenter, QChar(8734));
|
tray_painter.drawText(background.rect(), Qt::AlignVCenter | Qt::AlignCenter, QChar(8734));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Smaller number if it has 3 digits.
|
// Smaller number if it has 3 digits.
|
||||||
if (number > 99) {
|
if (number > 99) {
|
||||||
m_font.setPixelSize(55);
|
m_font.setPixelSize(background.width() * 0.43);
|
||||||
}
|
}
|
||||||
else if (number > 9) {
|
else if (number > 9) {
|
||||||
m_font.setPixelSize(80);
|
m_font.setPixelSize(background.width() * 0.56);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bigger number if it has just one digit.
|
// Bigger number if it has just one digit.
|
||||||
else {
|
else {
|
||||||
m_font.setPixelSize(100);
|
m_font.setPixelSize(background.width() * 0.78);
|
||||||
}
|
}
|
||||||
|
|
||||||
tray_painter.setFont(m_font);
|
tray_painter.setFont(m_font);
|
||||||
tray_painter.drawText(QRect(0, 0, 128, 128),
|
tray_painter.drawText(background.rect(),
|
||||||
Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignCenter,
|
Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignCenter,
|
||||||
QString::number(number));
|
QString::number(number));
|
||||||
}
|
}
|
||||||
|
|
|
@ -888,9 +888,9 @@ int DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||||
}
|
}
|
||||||
|
|
||||||
int id_existing_message = -1;
|
int id_existing_message = -1;
|
||||||
qint64 date_existing_message;
|
qint64 date_existing_message = 0;
|
||||||
bool is_read_existing_message;
|
bool is_read_existing_message = false;
|
||||||
bool is_important_existing_message;
|
bool is_important_existing_message = false;
|
||||||
QString contents_existing_message;
|
QString contents_existing_message;
|
||||||
QString feed_id_existing_message;
|
QString feed_id_existing_message;
|
||||||
|
|
||||||
|
@ -973,19 +973,22 @@ int DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||||
// Message is already in the DB.
|
// Message is already in the DB.
|
||||||
//
|
//
|
||||||
// Now, we update it if at least one of next conditions is true:
|
// Now, we update it if at least one of next conditions is true:
|
||||||
// 1) Message has custom ID AND (its date OR read status OR starred status are changed).
|
// 1) Message has custom ID AND (its date OR read status OR starred status are changed or message
|
||||||
|
// was moved from one feed to another - this can particularly happen in Gmail feeds).
|
||||||
|
//
|
||||||
// 2) Message has its date fetched from feed AND its date is different from date in DB and contents is changed.
|
// 2) Message has its date fetched from feed AND its date is different from date in DB and contents is changed.
|
||||||
if (/* 1 */ (!message.m_customId.isEmpty() && (message.m_created.toMSecsSinceEpoch() != date_existing_message ||
|
if (/* 1 */ (!message.m_customId.isEmpty() && (message.m_created.toMSecsSinceEpoch() != date_existing_message ||
|
||||||
message.m_isRead != is_read_existing_message ||
|
message.m_isRead != is_read_existing_message ||
|
||||||
message.m_isImportant != is_important_existing_message ||
|
message.m_isImportant != is_important_existing_message ||
|
||||||
message.m_feedId != feed_id_existing_message)) ||
|
message.m_feedId != feed_id_existing_message ||
|
||||||
|
message.m_contents != contents_existing_message)) ||
|
||||||
|
|
||||||
/* 2 */ (message.m_createdFromFeed && message.m_created.toMSecsSinceEpoch() != date_existing_message
|
/* 2 */ (message.m_createdFromFeed && message.m_created.toMSecsSinceEpoch() != date_existing_message
|
||||||
&& message.m_contents != contents_existing_message)) {
|
&& message.m_contents != contents_existing_message)) {
|
||||||
// Message exists, it is changed, update it.
|
// Message exists, it is changed, update it.
|
||||||
query_update.bindValue(QSL(":title"), unnulifyString(message.m_title));
|
query_update.bindValue(QSL(":title"), unnulifyString(message.m_title));
|
||||||
query_update.bindValue(QSL(":is_read"), (int) message.m_isRead);
|
query_update.bindValue(QSL(":is_read"), int(message.m_isRead));
|
||||||
query_update.bindValue(QSL(":is_important"), (int) message.m_isImportant);
|
query_update.bindValue(QSL(":is_important"), int(message.m_isImportant));
|
||||||
query_update.bindValue(QSL(":url"), unnulifyString(message.m_url));
|
query_update.bindValue(QSL(":url"), unnulifyString(message.m_url));
|
||||||
query_update.bindValue(QSL(":author"), unnulifyString(message.m_author));
|
query_update.bindValue(QSL(":author"), unnulifyString(message.m_author));
|
||||||
query_update.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch());
|
query_update.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch());
|
||||||
|
@ -1019,8 +1022,8 @@ int DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||||
// Message with this URL is not fetched in this feed yet.
|
// Message with this URL is not fetched in this feed yet.
|
||||||
query_insert.bindValue(QSL(":feed"), unnulifyString(feed_custom_id));
|
query_insert.bindValue(QSL(":feed"), unnulifyString(feed_custom_id));
|
||||||
query_insert.bindValue(QSL(":title"), unnulifyString(message.m_title));
|
query_insert.bindValue(QSL(":title"), unnulifyString(message.m_title));
|
||||||
query_insert.bindValue(QSL(":is_read"), (int) message.m_isRead);
|
query_insert.bindValue(QSL(":is_read"), int(message.m_isRead));
|
||||||
query_insert.bindValue(QSL(":is_important"), (int) message.m_isImportant);
|
query_insert.bindValue(QSL(":is_important"), int(message.m_isImportant));
|
||||||
query_insert.bindValue(QSL(":url"), unnulifyString( message.m_url));
|
query_insert.bindValue(QSL(":url"), unnulifyString( message.m_url));
|
||||||
query_insert.bindValue(QSL(":author"), unnulifyString(message.m_author));
|
query_insert.bindValue(QSL(":author"), unnulifyString(message.m_author));
|
||||||
query_insert.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch());
|
query_insert.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch());
|
||||||
|
|
|
@ -60,6 +60,9 @@ DVALUE(bool) Feeds::ShowOnlyUnreadFeedsDef = false;
|
||||||
DKEY Feeds::ShowTreeBranches = "show_tree_branches";
|
DKEY Feeds::ShowTreeBranches = "show_tree_branches";
|
||||||
DVALUE(bool) Feeds::ShowTreeBranchesDef = true;
|
DVALUE(bool) Feeds::ShowTreeBranchesDef = true;
|
||||||
|
|
||||||
|
DKEY Feeds::AutoExpandOnSelection = "auto_expand_on_selection";
|
||||||
|
DVALUE(bool) Feeds::AutoExpandOnSelectionDef = false;
|
||||||
|
|
||||||
DKEY Feeds::ListFont = "list_font";
|
DKEY Feeds::ListFont = "list_font";
|
||||||
|
|
||||||
// Messages.
|
// Messages.
|
||||||
|
|
|
@ -79,6 +79,9 @@ namespace Feeds {
|
||||||
KEY ShowTreeBranches;
|
KEY ShowTreeBranches;
|
||||||
VALUE(bool) ShowTreeBranchesDef;
|
VALUE(bool) ShowTreeBranchesDef;
|
||||||
|
|
||||||
|
KEY AutoExpandOnSelection;
|
||||||
|
VALUE(bool) AutoExpandOnSelectionDef;
|
||||||
|
|
||||||
KEY ListFont;
|
KEY ListFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -435,7 +435,6 @@ QList<Message> StandardFeed::obtainNewMessages(bool* error_during_obtaining) {
|
||||||
QList<QPair<QByteArray, QByteArray>> headers;
|
QList<QPair<QByteArray, QByteArray>> headers;
|
||||||
|
|
||||||
headers << NetworkFactory::generateBasicAuthHeader(username(), password());
|
headers << NetworkFactory::generateBasicAuthHeader(username(), password());
|
||||||
|
|
||||||
m_networkError = NetworkFactory::performNetworkOperation(url(),
|
m_networkError = NetworkFactory::performNetworkOperation(url(),
|
||||||
download_timeout,
|
download_timeout,
|
||||||
QByteArray(),
|
QByteArray(),
|
||||||
|
|