diff --git a/README.md b/README.md
index 716888781..6ec89a9b8 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@ RSS Guard
=========
Welcome to RSS Guard website. You can find here basic information.
-RSS Guard is simple and easy-to-use RSS/ATOM feed aggregator developed using Qt framework.
+RSS Guard is simple and easy-to-use RSS/ATOM feed aggregator developed using Qt framework which supports online feed synchronization.
- - -
Contacts
@@ -66,6 +66,8 @@ RSS Guard is simple (yet powerful) feed reader. It is able to fetch the most kno
RSS Guard is written in C++. It is pretty fast even with tons of messages loaded. The core features are:
+* **support for online feed synchronization via plugins**,
+ * Tiny Tiny RSS (from RSS Guard 3.0.0).
* multiplatformity,
* support for all feed formats,
* simplicity,
diff --git a/resources/misc/db_init_mysql.sql b/resources/misc/db_init_mysql.sql
index b3eb5e2cd..d1bbd370d 100644
--- a/resources/misc/db_init_mysql.sql
+++ b/resources/misc/db_init_mysql.sql
@@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS Categories (
parent_id INTEGER NOT NULL,
title VARCHAR(100) NOT NULL CHECK (title != ''),
description TEXT,
- date_created BIGINT NOT NULL CHECK (date_created != 0),
+ date_created BIGINT,
icon BLOB,
account_id INTEGER NOT NULL,
custom_id TEXT,
@@ -50,17 +50,17 @@ CREATE TABLE IF NOT EXISTS Feeds (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
title TEXT NOT NULL CHECK (title != ''),
description TEXT,
- date_created BIGINT NOT NULL CHECK (date_created != 0),
+ date_created BIGINT,
icon BLOB,
category INTEGER NOT NULL CHECK (category >= -1),
- encoding TEXT NOT NULL CHECK (encoding != ''),
- url VARCHAR(100) NOT NULL CHECK (url != ''),
+ encoding TEXT,
+ url VARCHAR(100),
protected INTEGER(1) NOT NULL CHECK (protected >= 0 AND protected <= 1),
username TEXT,
password TEXT,
update_type INTEGER(1) NOT NULL CHECK (update_type >= 0),
update_interval INTEGER NOT NULL DEFAULT 15 CHECK (update_interval >= 5),
- type INTEGER NOT NULL CHECK (type >= 0),
+ type INTEGER,
account_id INTEGER NOT NULL,
custom_id TEXT,
diff --git a/resources/misc/db_init_sqlite.sql b/resources/misc/db_init_sqlite.sql
index 80a091205..a0e357296 100644
--- a/resources/misc/db_init_sqlite.sql
+++ b/resources/misc/db_init_sqlite.sql
@@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS Categories (
parent_id INTEGER NOT NULL,
title TEXT NOT NULL CHECK (title != ''),
description TEXT,
- date_created INTEGER NOT NULL CHECK (date_created != 0),
+ date_created INTEGER,
icon BLOB,
account_id INTEGER NOT NULL,
custom_id TEXT,
@@ -45,17 +45,17 @@ CREATE TABLE IF NOT EXISTS Feeds (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL CHECK (title != ''),
description TEXT,
- date_created INTEGER NOT NULL CHECK (date_created != 0),
+ date_created INTEGER,
icon BLOB,
category INTEGER NOT NULL CHECK (category >= -1),
- encoding TEXT NOT NULL CHECK (encoding != ''),
- url TEXT NOT NULL CHECK (url != ''),
+ encoding TEXT,
+ url TEXT,
protected INTEGER(1) NOT NULL CHECK (protected >= 0 AND protected <= 1),
username TEXT,
password TEXT,
update_type INTEGER(1) NOT NULL CHECK (update_type >= 0),
update_interval INTEGER NOT NULL CHECK (update_interval >= 5) DEFAULT 15,
- type INTEGER NOT NULL CHECK (type >= 0),
+ type INTEGER,
account_id INTEGER NOT NULL,
custom_id TEXT,
diff --git a/resources/misc/db_update_mysql_3_4.sql b/resources/misc/db_update_mysql_3_4.sql
index 892d54579..25038fd1e 100644
--- a/resources/misc/db_update_mysql_3_4.sql
+++ b/resources/misc/db_update_mysql_3_4.sql
@@ -40,4 +40,19 @@ DROP FOREIGN KEY feed;
ALTER TABLE Messages
MODIFY Feeds TEXT;
-- !
+ALTER TABLE Feeds
+MODIFY date_created BIGINT;
+-- !
+ALTER TABLE Feeds
+MODIFY encoding TEXT;
+-- !
+ALTER TABLE Feeds
+MODIFY url VARCHAR(100);
+-- !
+ALTER TABLE Feeds
+MODIFY type INTEGER;
+-- !
+ALTER TABLE Categories
+MODIFY date_created BIGINT;
+-- !
UPDATE Information SET inf_value = '4' WHERE inf_key = 'schema_version';
\ No newline at end of file
diff --git a/resources/misc/db_update_sqlite_3_4.sql b/resources/misc/db_update_sqlite_3_4.sql
index 6a6433312..6765c6491 100644
--- a/resources/misc/db_update_sqlite_3_4.sql
+++ b/resources/misc/db_update_sqlite_3_4.sql
@@ -61,4 +61,54 @@ INSERT INTO Messages SELECT * FROM backup_Messages;
-- !
DROP TABLE backup_Messages;
-- !
+CREATE TABLE backup_Feeds AS SELECT * FROM Feeds;
+-- !
+DROP TABLE Feeds;
+-- !
+CREATE TABLE Feeds (
+ id INTEGER PRIMARY KEY,
+ title TEXT NOT NULL CHECK (title != ''),
+ description TEXT,
+ date_created INTEGER,
+ icon BLOB,
+ category INTEGER NOT NULL CHECK (category >= -1),
+ encoding TEXT,
+ url TEXT,
+ protected INTEGER(1) NOT NULL CHECK (protected >= 0 AND protected <= 1),
+ username TEXT,
+ password TEXT,
+ update_type INTEGER(1) NOT NULL CHECK (update_type >= 0),
+ update_interval INTEGER NOT NULL CHECK (update_interval >= 5) DEFAULT 15,
+ type INTEGER,
+ account_id INTEGER NOT NULL,
+ custom_id TEXT,
+
+ FOREIGN KEY (account_id) REFERENCES Accounts (id)
+);
+-- !
+INSERT INTO Feeds SELECT * FROM backup_Feeds;
+-- !
+DROP TABLE backup_Feeds;
+-- !
+CREATE TABLE backup_Categories AS SELECT * FROM Categories;
+-- !
+DROP TABLE Categories;
+-- !
+CREATE TABLE Categories (
+ id INTEGER PRIMARY KEY,
+ parent_id INTEGER NOT NULL,
+ title TEXT NOT NULL CHECK (title != ''),
+ description TEXT,
+ date_created INTEGER,
+ icon BLOB,
+ account_id INTEGER NOT NULL,
+ custom_id TEXT,
+
+ FOREIGN KEY (account_id) REFERENCES Accounts (id)
+);
+-- !
+INSERT INTO Categories SELECT * FROM backup_Categories;
+-- !
+DROP TABLE backup_Categories;
+-- !
UPDATE Information SET inf_value = '4' WHERE inf_key = 'schema_version';
\ No newline at end of file
diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG
index 8718de143..1ad0bd8f0 100644
--- a/resources/text/CHANGELOG
+++ b/resources/text/CHANGELOG
@@ -18,6 +18,7 @@
- Brand new "service plugin system" - HIGHLY EXPERIMENTAL and REWRITTEN from scratch. Expect bugs and misunderstandings now! Major parts of RSS Guard were completely rewritten. Note that some functionality might be TEMPORARILY removed.
- Added ability to completely disable notifications (bug #128).
+ - Added ability to hide status bar.
- Added ability to go to next unread message. (partially bug #112)
diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp
index ce322eb5e..6b4ee2a30 100755
--- a/src/core/feedsmodel.cpp
+++ b/src/core/feedsmodel.cpp
@@ -70,7 +70,7 @@ FeedsModel::FeedsModel(QObject *parent)
connect(m_autoUpdateTimer, SIGNAL(timeout()), this, SLOT(executeNextAutoUpdate()));
- loadActivatedServiceAccounts();
+ //loadActivatedServiceAccounts();
updateAutoUpdateStatus();
if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) {
@@ -82,6 +82,10 @@ FeedsModel::FeedsModel(QObject *parent)
FeedsModel::~FeedsModel() {
qDebug("Destroying FeedsModel instance.");
+ foreach (ServiceRoot *account, serviceRoots()) {
+ account->stop();
+ }
+
// Delete all model items.
delete m_rootItem;
}
@@ -697,6 +701,7 @@ bool FeedsModel::addServiceAccount(ServiceRoot *root) {
connect(root, SIGNAL(readFeedsFilterInvalidationRequested()), this, SIGNAL(readFeedsFilterInvalidationRequested()));
connect(root, SIGNAL(dataChanged(QList)), this, SLOT(onItemDataChanged(QList)));
connect(root, SIGNAL(reloadMessageListRequested(bool)), this, SIGNAL(reloadMessageListRequested(bool)));
+ connect(root, SIGNAL(itemExpandRequested(QList,bool)), this, SIGNAL(itemExpandRequested(QList,bool)));
root->start();
return true;
diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h
index 83df548e5..b561aa506 100755
--- a/src/core/feedsmodel.h
+++ b/src/core/feedsmodel.h
@@ -151,6 +151,9 @@ class FeedsModel : public QAbstractItemModel {
// Adds given service root account.
bool addServiceAccount(ServiceRoot *root);
+ // Loads feed/categories from the database.
+ void loadActivatedServiceAccounts();
+
public slots:
// Checks if new parent node is different from one used by original node.
// If it is, then it reassigns original_node to new parent.
@@ -206,6 +209,9 @@ class FeedsModel : public QAbstractItemModel {
// Emitted if counts of messages are changed.
void messageCountsChanged(int unread_messages, int total_messages, bool any_feed_has_unread_messages);
+ // Emitted if any item requested that any view should expand it.
+ void itemExpandRequested(QList items, bool expand);
+
// Emitted when there is a need of reloading of displayed messages.
void reloadMessageListRequested(bool mark_selected_messages_read);
@@ -218,9 +224,6 @@ class FeedsModel : public QAbstractItemModel {
// which are suitable as IN clause for SQL queries.
QStringList textualFeedIds(const QList &feeds);
- // Loads feed/categories from the database.
- void loadActivatedServiceAccounts();
-
RootItem *m_rootItem;
QList m_headerData;
QList m_tooltipData;
diff --git a/src/core/message.cpp b/src/core/message.cpp
index 0338a5477..a0a6d4448 100755
--- a/src/core/message.cpp
+++ b/src/core/message.cpp
@@ -61,7 +61,7 @@ QString Enclosures::encodeEnclosuresToString(const QList &enclosures)
}
Message::Message() {
- m_title = m_url = m_author = m_contents = m_customId = "";
- m_feedId = 0;
+ m_title = m_url = m_author = m_contents = m_feedId = m_customId = "";
m_enclosures = QList();
+ m_isRead = m_isImportant = false;
}
diff --git a/src/core/message.h b/src/core/message.h
index f0cd4ab3b..eab142d15 100755
--- a/src/core/message.h
+++ b/src/core/message.h
@@ -49,9 +49,12 @@ class Message {
QString m_author;
QString m_contents;
QDateTime m_created;
- int m_feedId;
+ QString m_feedId;
QString m_customId;
+ bool m_isRead;
+ bool m_isImportant;
+
QList m_enclosures;
// Is true if "created" date was obtained directly
diff --git a/src/core/messagesmodel.cpp b/src/core/messagesmodel.cpp
index 62af8ae12..ba0968bbe 100755
--- a/src/core/messagesmodel.cpp
+++ b/src/core/messagesmodel.cpp
@@ -139,7 +139,8 @@ Message MessagesModel::messageAt(int row_index) const {
message.m_enclosures = Enclosures::decodeEnclosuresFromString(rec.value(MSG_DB_ENCLOSURES_INDEX).toString());
message.m_title = rec.value(MSG_DB_TITLE_INDEX).toString();
message.m_url = rec.value(MSG_DB_URL_INDEX).toString();
- message.m_feedId = rec.value(MSG_DB_FEED_INDEX).toInt();
+ message.m_feedId = rec.value(MSG_DB_FEED_INDEX).toString();
+ message.m_customId = rec.value(MSG_DB_CUSTOM_ID_INDEX).toString();
message.m_created = TextFactory::parseDateTime(rec.value(MSG_DB_DCREATED_INDEX).value()).toLocalTime();
return message;
diff --git a/src/core/rootitem.cpp b/src/core/rootitem.cpp
index 879ad8839..60f7482ed 100755
--- a/src/core/rootitem.cpp
+++ b/src/core/rootitem.cpp
@@ -281,6 +281,26 @@ QList RootItem::getSubTreeCategories() {
return children;
}
+QHash RootItem::getHashedSubTreeCategories() {
+ QHash children;
+ QList traversable_items;
+
+ traversable_items.append(this);
+
+ // Iterate all nested items.
+ while (!traversable_items.isEmpty()) {
+ RootItem *active_item = traversable_items.takeFirst();
+
+ if (active_item->kind() == RootItemKind::Category && !children.contains(active_item->id())) {
+ children.insert(active_item->id(), active_item->toCategory());
+ }
+
+ traversable_items.append(active_item->childItems());
+ }
+
+ return children;
+}
+
QList RootItem::getSubTreeFeeds() {
QList children;
QList traversable_items;
diff --git a/src/core/rootitem.h b/src/core/rootitem.h
index 800bbc3cf..9ad382c32 100755
--- a/src/core/rootitem.h
+++ b/src/core/rootitem.h
@@ -177,6 +177,7 @@ class RootItem : public QObject {
QList getSubTree();
QList getSubTree(RootItemKind::Kind kind_of_item);
QList getSubTreeCategories();
+ QHash getHashedSubTreeCategories();
QList getSubTreeFeeds();
// Returns the service root node which is direct or indirect parent of current item.
diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp
index 278a791c8..94801af62 100755
--- a/src/gui/dialogs/formmain.cpp
+++ b/src/gui/dialogs/formmain.cpp
@@ -105,6 +105,7 @@ QList FormMain::allActions() {
actions << m_ui->m_actionSwitchMainMenu;
actions << m_ui->m_actionSwitchToolBars;
actions << m_ui->m_actionSwitchListHeaders;
+ actions << m_ui->m_actionSwitchStatusBar;
actions << m_ui->m_actionSwitchMessageListOrientation;
// Add web browser actions
@@ -172,10 +173,6 @@ void FormMain::switchFullscreenMode() {
}
}
-void FormMain::switchMainMenu() {
- m_ui->m_menuBar->setVisible(m_ui->m_actionSwitchMainMenu->isChecked());
-}
-
void FormMain::updateAddItemMenu() {
// NOTE: Clear here deletes items from memory but only those OWNED by the menu.
m_ui->m_menuAddItem->clear();
@@ -326,6 +323,7 @@ void FormMain::setupIcons() {
m_ui->m_actionSwitchMainMenu->setIcon(icon_theme_factory->fromTheme(QSL("view-switch-menu")));
m_ui->m_actionSwitchToolBars->setIcon(icon_theme_factory->fromTheme(QSL("view-switch-list")));
m_ui->m_actionSwitchListHeaders->setIcon(icon_theme_factory->fromTheme(QSL("view-switch-list")));
+ m_ui->m_actionSwitchStatusBar->setIcon(icon_theme_factory->fromTheme(QSL("dialog-information")));
m_ui->m_actionSwitchMessageListOrientation->setIcon(icon_theme_factory->fromTheme(QSL("view-switch-layout-direction")));
m_ui->m_menuShowHide->setIcon(icon_theme_factory->fromTheme(QSL("view-switch")));
@@ -408,6 +406,7 @@ void FormMain::loadSize() {
m_ui->m_tabWidget->feedMessageViewer()->loadSize();
m_ui->m_actionSwitchToolBars->setChecked(settings->value(GROUP(GUI), SETTING(GUI::ToolbarsVisible)).toBool());
m_ui->m_actionSwitchListHeaders->setChecked(settings->value(GROUP(GUI), SETTING(GUI::ListHeadersVisible)).toBool());
+ m_ui->m_actionSwitchStatusBar->setChecked(settings->value(GROUP(GUI), SETTING(GUI::StatusBarVisible)).toBool());
// Make sure that only unread feeds are shown if user has that feature set on.
m_ui->m_actionShowOnlyUnreadItems->setChecked(settings->value(GROUP(Feeds), SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool());
@@ -431,6 +430,7 @@ void FormMain::saveSize() {
settings->setValue(GROUP(GUI), GUI::MainWindowInitialSize, size());
settings->setValue(GROUP(GUI), GUI::MainWindowStartsMaximized, is_maximized);
settings->setValue(GROUP(GUI), GUI::MainWindowStartsFullscreen, is_fullscreen);
+ settings->setValue(GROUP(GUI), GUI::StatusBarVisible, m_ui->m_actionSwitchStatusBar->isChecked());
m_ui->m_tabWidget->feedMessageViewer()->saveSize();
}
@@ -452,8 +452,9 @@ void FormMain::createConnections() {
// Menu "View" connections.
connect(m_ui->m_actionFullscreen, SIGNAL(toggled(bool)), this, SLOT(switchFullscreenMode()));
- connect(m_ui->m_actionSwitchMainMenu, SIGNAL(toggled(bool)), this, SLOT(switchMainMenu()));
+ connect(m_ui->m_actionSwitchMainMenu, SIGNAL(toggled(bool)), m_ui->m_menuBar, SLOT(setVisible(bool)));
connect(m_ui->m_actionSwitchMainWindow, SIGNAL(triggered()), this, SLOT(switchVisibility()));
+ connect(m_ui->m_actionSwitchStatusBar, SIGNAL(toggled(bool)), statusBar(), SLOT(setVisible(bool)));
// Menu "Tools" connections.
connect(m_ui->m_actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
diff --git a/src/gui/dialogs/formmain.h b/src/gui/dialogs/formmain.h
index 1320a832f..140d9ad79 100755
--- a/src/gui/dialogs/formmain.h
+++ b/src/gui/dialogs/formmain.h
@@ -73,9 +73,6 @@ class FormMain : public QMainWindow {
// Turns on/off fullscreen mode
void switchFullscreenMode();
- // Switches visibility of main menu.
- void switchMainMenu();
-
private slots:
void updateAddItemMenu();
void updateRecycleBinMenu();
diff --git a/src/gui/dialogs/formmain.ui b/src/gui/dialogs/formmain.ui
index fe4f645ce..7e9a1ef3c 100755
--- a/src/gui/dialogs/formmain.ui
+++ b/src/gui/dialogs/formmain.ui
@@ -85,6 +85,7 @@
+
@@ -742,6 +743,20 @@
+
+
+ true
+
+
+ true
+
+
+ Status bar
+
+
+
+
+
diff --git a/src/gui/feedmessageviewer.cpp b/src/gui/feedmessageviewer.cpp
index 69a978993..c772e7548 100755
--- a/src/gui/feedmessageviewer.cpp
+++ b/src/gui/feedmessageviewer.cpp
@@ -103,8 +103,6 @@ void FeedMessageViewer::loadSize() {
Settings *settings = qApp->settings();
int default_msg_section_size = m_messagesView->header()->defaultSectionSize();
- m_feedsView->loadExpandedStates();
-
// Restore offsets of splitters.
m_feedSplitter->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::SplitterFeeds)).toString().toLocal8Bit()));
m_messageSplitter->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::SplitterMessages)).toString().toLocal8Bit()));
diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp
index d976b6e84..39a48d211 100755
--- a/src/gui/feedsview.cpp
+++ b/src/gui/feedsview.cpp
@@ -55,6 +55,7 @@ FeedsView::FeedsView(QWidget *parent)
// Connections.
connect(m_sourceModel, SIGNAL(requireItemValidationAfterDragDrop(QModelIndex)), this, SLOT(validateItemAfterDragDrop(QModelIndex)));
+ connect(m_sourceModel, SIGNAL(itemExpandRequested(QList,bool)), this, SLOT(onItemExpandRequested(QList,bool)));
connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSortState(int,Qt::SortOrder)));
setModel(m_proxyModel);
@@ -102,11 +103,11 @@ void FeedsView::saveExpandedStates() {
Settings *settings = qApp->settings();
QList expandable_items;
- expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category));
+ expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot));
// Iterate all categories and save their expand statuses.
foreach (RootItem *item, expandable_items) {
- QString setting_name = QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id());
+ QString setting_name = QString::number(item->kind()) + QL1S("-") + QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id());
settings->setValue(GROUP(Categories),
setting_name,
@@ -122,10 +123,10 @@ void FeedsView::loadExpandedStates() {
// Iterate all categories and save their expand statuses.
foreach (RootItem *item, expandable_items) {
- QString setting_name = QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id());
+ QString setting_name = QString::number(item->kind()) + QL1S("-") + QString::number(qHash(item->title())) + QL1S("-") + QString::number(item->id());
setExpanded(model()->mapFromSource(sourceModel()->indexForItem(item)),
- settings->value(GROUP(Categories), setting_name, true).toBool());
+ settings->value(GROUP(Categories), setting_name, item->childCount() > 0).toBool());
}
}
@@ -466,3 +467,13 @@ void FeedsView::validateItemAfterDragDrop(const QModelIndex &source_index) {
setCurrentIndex(mapped);
}
}
+
+void FeedsView::onItemExpandRequested(const QList &items, bool exp) {
+ foreach (RootItem *item, items) {
+ QModelIndex source_index = m_sourceModel->indexForItem(item);
+ QModelIndex proxy_index = m_proxyModel->mapFromSource(source_index);
+
+ setExpanded(proxy_index, !exp);
+ setExpanded(proxy_index, exp);
+ }
+}
diff --git a/src/gui/feedsview.h b/src/gui/feedsview.h
index 05f9f8a31..f80a9a250 100755
--- a/src/gui/feedsview.h
+++ b/src/gui/feedsview.h
@@ -104,6 +104,7 @@ class FeedsView : public QTreeView {
void saveSortState(int column, Qt::SortOrder order);
void validateItemAfterDragDrop(const QModelIndex &source_index);
+ void onItemExpandRequested(const QList &items, bool exp);
private:
// Initializes context menus.
diff --git a/src/main.cpp b/src/main.cpp
index f78478c22..94736dd08 100755
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -121,6 +121,10 @@ int main(int argc, char *argv[]) {
QTimer::singleShot(STARTUP_UPDATE_DELAY, application.system(), SLOT(checkForUpdatesOnStartup()));
}
+ // Load activated accounts.
+ qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->loadActivatedServiceAccounts();
+ qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadExpandedStates();
+
// Setup single-instance behavior.
QObject::connect(&application, SIGNAL(messageReceived(QString)), &application, SLOT(processExecutionMessage(QString)));
diff --git a/src/miscellaneous/settings.cpp b/src/miscellaneous/settings.cpp
index bc040936e..bf1179f07 100755
--- a/src/miscellaneous/settings.cpp
+++ b/src/miscellaneous/settings.cpp
@@ -105,6 +105,9 @@ DVALUE(bool) GUI::ToolbarsVisibleDef = true;
DKEY GUI::ListHeadersVisible = "enable_list_headers";
DVALUE(bool) GUI::ListHeadersVisibleDef = true;
+DKEY GUI::StatusBarVisible = "enable_status_bar";
+DVALUE(bool) GUI::StatusBarVisibleDef = true;
+
DKEY GUI::HideMainWindowWhenMinimized = "hide_when_minimized";
DVALUE(bool) GUI::HideMainWindowWhenMinimizedDef = false;
diff --git a/src/miscellaneous/settings.h b/src/miscellaneous/settings.h
index b3a977520..35746dd5b 100755
--- a/src/miscellaneous/settings.h
+++ b/src/miscellaneous/settings.h
@@ -120,6 +120,9 @@ namespace GUI {
KEY ListHeadersVisible;
VALUE(bool) ListHeadersVisibleDef;
+ KEY StatusBarVisible;
+ VALUE(bool) StatusBarVisibleDef;
+
KEY HideMainWindowWhenMinimized;
VALUE(bool) HideMainWindowWhenMinimizedDef;
diff --git a/src/services/abstract/feed.h b/src/services/abstract/feed.h
index 0ab9671e5..aaed9266a 100755
--- a/src/services/abstract/feed.h
+++ b/src/services/abstract/feed.h
@@ -55,6 +55,7 @@ class Feed : public RootItem {
/////////////////////////////////////////
// Performs synchronous update and returns number of newly updated messages.
+ // NOTE: This is called from worker thread, not from main UI thread.
// NOTE: This should COMPLETELY download ALL messages from online source
// into locale "Messages" table, INCLUDING contents (or excerpts) of those
// messages.
diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp
index 2a804ce18..c3c9413a3 100755
--- a/src/services/abstract/serviceroot.cpp
+++ b/src/services/abstract/serviceroot.cpp
@@ -19,6 +19,7 @@
#include "core/feedsmodel.h"
#include "miscellaneous/application.h"
+#include "services/abstract/category.h"
#include
@@ -58,7 +59,7 @@ bool ServiceRoot::deleteViaGui() {
return data_removed;
}
-void ServiceRoot::itemChanged(QList items) {
+void ServiceRoot::itemChanged(const QList &items) {
emit dataChanged(items);
}
@@ -70,6 +71,10 @@ void ServiceRoot::requestFeedReadFilterReload() {
emit readFeedsFilterInvalidationRequested();
}
+void ServiceRoot::requestItemExpand(const QList &items, bool expand) {
+ emit itemExpandRequested(items, expand);
+}
+
void ServiceRoot::requestItemReassignment(RootItem *item, RootItem *new_parent) {
emit itemReassignmentRequested(item, new_parent);
}
@@ -85,3 +90,47 @@ int ServiceRoot::accountId() const {
void ServiceRoot::setAccountId(int account_id) {
m_accountId = account_id;
}
+
+void ServiceRoot::assembleFeeds(Assignment feeds) {
+ QHash categories = getHashedSubTreeCategories();
+
+ foreach (const AssignmentItem &feed, feeds) {
+ if (feed.first == NO_PARENT_CATEGORY) {
+ // This is top-level feed, add it to the root item.
+ appendChild(feed.second);
+ feed.second->updateCounts(true);
+ }
+ else if (categories.contains(feed.first)) {
+ // This feed belongs to this category.
+ categories.value(feed.first)->appendChild(feed.second);
+ feed.second->updateCounts(true);
+ }
+ else {
+ qWarning("Feed '%s' is loose, skipping it.", qPrintable(feed.second->title()));
+ }
+ }
+}
+
+void ServiceRoot::assembleCategories(Assignment categories) {
+ QHash assignments;
+ assignments.insert(NO_PARENT_CATEGORY, this);
+
+ // Add top-level categories.
+ while (!categories.isEmpty()) {
+ for (int i = 0; i < categories.size(); i++) {
+ if (assignments.contains(categories.at(i).first)) {
+ // Parent category of this category is already added.
+ assignments.value(categories.at(i).first)->appendChild(categories.at(i).second);
+
+ // Now, added category can be parent for another categories, add it.
+ assignments.insert(categories.at(i).second->id(), categories.at(i).second);
+
+ // Remove the category from the list, because it was
+ // added to the final collection.
+ categories.removeAt(i);
+ i--;
+ }
+ }
+ }
+}
+
diff --git a/src/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h
index 3dbd13475..aca550619 100755
--- a/src/services/abstract/serviceroot.h
+++ b/src/services/abstract/serviceroot.h
@@ -30,6 +30,10 @@ class RecycleBin;
class QAction;
class QSqlTableModel;
+// Car here represents ID of the item.
+typedef QList > Assignment;
+typedef QPair AssignmentItem;
+
// THIS IS the root node of the service.
// NOTE: The root usually contains some core functionality of the
// service like service account username/password etc.
@@ -64,6 +68,8 @@ class ServiceRoot : public RootItem {
// Start/stop services.
// Start method is called when feed model gets initialized OR after user adds new service.
+ // Account should synchronously initialize its children (load them from DB is recommended
+ // here).
//
// Stop method is called just before application exits OR when
// user explicitly deletes existing service instance.
@@ -79,8 +85,6 @@ class ServiceRoot : public RootItem {
// and then use method QSqlTableModel::setFilter(....).
// NOTE: It would be more preferable if all messages are downloaded
// right when feeds are updated.
- // TODO: toto možná udělat asynchronně, zobrazit
- // "loading" dialog přes view a toto zavolat, nasledně signalovat
virtual bool loadMessagesForItem(RootItem *item, QSqlTableModel *model) = 0;
// Called BEFORE this read status update (triggered by user in message list) is stored in DB,
@@ -138,10 +142,10 @@ class ServiceRoot : public RootItem {
/////////////////////////////////////////
// Obvious methods to wrap signals.
- void itemChanged(QList items);
+ void itemChanged(const QList &items);
void requestReloadMessageList(bool mark_selected_messages_read);
void requestFeedReadFilterReload();
-
+ void requestItemExpand(const QList &items, bool expand);
void requestItemReassignment(RootItem *item, RootItem *new_parent);
void requestItemRemoval(RootItem *item);
@@ -149,11 +153,17 @@ class ServiceRoot : public RootItem {
int accountId() const;
void setAccountId(int account_id);
+ protected:
+ // Takes lists of feeds/categories and assembles them into the tree structure.
+ void assembleCategories(Assignment categories);
+ void assembleFeeds(Assignment feeds);
+
signals:
// Emitted if data in any item belonging to this root are changed.
void dataChanged(QList items);
void readFeedsFilterInvalidationRequested();
void reloadMessageListRequested(bool mark_selected_messages_read);
+ void itemExpandRequested(QList items, bool expand);
void itemReassignmentRequested(RootItem *item, RootItem *new_parent);
void itemRemovalRequested(RootItem *item);
diff --git a/src/services/standard/gui/formstandardimportexport.cpp b/src/services/standard/gui/formstandardimportexport.cpp
index 9c10ac444..6e7f869cd 100755
--- a/src/services/standard/gui/formstandardimportexport.cpp
+++ b/src/services/standard/gui/formstandardimportexport.cpp
@@ -233,6 +233,7 @@ void FormStandardImportExport::importFeeds() {
QString output_message;
if (m_serviceRoot->mergeImportExportModel(m_model, output_message)) {
+ m_serviceRoot->requestItemExpand(m_serviceRoot->getSubTree(), true);
m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, output_message, output_message);
}
else {
diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp
index a0f4332f3..c7ea6fecc 100755
--- a/src/services/standard/standardfeed.cpp
+++ b/src/services/standard/standardfeed.cpp
@@ -135,7 +135,7 @@ QList StandardFeed::undeletedMessages() const {
QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
QSqlQuery query_read_msg(database);
query_read_msg.setForwardOnly(true);
- query_read_msg.prepare("SELECT title, url, author, date_created, contents "
+ query_read_msg.prepare("SELECT title, url, author, date_created, contents, enclosures "
"FROM Messages "
"WHERE is_deleted = 0 AND feed = :feed AND account_id = :account_id;");
@@ -154,6 +154,7 @@ QList StandardFeed::undeletedMessages() const {
message.m_author = query_read_msg.value(2).toString();
message.m_created = TextFactory::parseDateTime(query_read_msg.value(3).value());
message.m_contents = query_read_msg.value(4).toString();
+ message.m_enclosures = Enclosures::decodeEnclosuresFromString(query_read_msg.value(5).toString());
messages.append(message);
}
@@ -763,7 +764,6 @@ QNetworkReply::NetworkError StandardFeed::networkError() const {
}
StandardFeed::StandardFeed(const QSqlRecord &record) : Feed(NULL) {
- setKind(RootItemKind::Feed);
setTitle(record.value(FDS_DB_TITLE_INDEX).toString());
setId(record.value(FDS_DB_ID_INDEX).toInt());
setDescription(record.value(FDS_DB_DESCRIPTION_INDEX).toString());
@@ -782,6 +782,6 @@ StandardFeed::StandardFeed(const QSqlRecord &record) : Feed(NULL) {
}
- setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
+ setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
}
diff --git a/src/services/standard/standardserviceentrypoint.cpp b/src/services/standard/standardserviceentrypoint.cpp
index ca3615619..f852f6bb5 100755
--- a/src/services/standard/standardserviceentrypoint.cpp
+++ b/src/services/standard/standardserviceentrypoint.cpp
@@ -75,7 +75,6 @@ ServiceRoot *StandardServiceEntryPoint::createNewRoot() {
SERVICE_CODE_STD_RSS))) {
StandardServiceRoot *root = new StandardServiceRoot();
root->setAccountId(id_to_assing);
- root->loadFromDatabase();
return root;
}
else {
@@ -93,7 +92,6 @@ QList StandardServiceEntryPoint::initializeSubtree() {
while (query.next()) {
StandardServiceRoot *root = new StandardServiceRoot();
root->setAccountId(query.value(0).toInt());
- root->loadFromDatabase();
roots.append(root);
}
}
diff --git a/src/services/standard/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp
index ecebe24bf..6a6c44884 100755
--- a/src/services/standard/standardserviceroot.cpp
+++ b/src/services/standard/standardserviceroot.cpp
@@ -61,6 +61,8 @@ StandardServiceRoot::~StandardServiceRoot() {
}
void StandardServiceRoot::start() {
+ loadFromDatabase();
+
if (qApp->isFirstRun()) {
if (MessageBox::show(qApp->mainForm(), QMessageBox::Question, QObject::tr("Load initial set of feeds"),
tr("You started %1 for the first time, now you can load initial set of feeds.").arg(APP_NAME),
@@ -83,7 +85,10 @@ void StandardServiceRoot::start() {
try {
model.importAsOPML20(IOFactory::readTextFile(file_to_load));
model.checkAllItems();
- mergeImportExportModel(&model, output_msg);
+
+ if (mergeImportExportModel(&model, output_msg)) {
+ requestItemExpand(getSubTree(), true);
+ }
}
catch (ApplicationException &ex) {
MessageBox::show(qApp->mainForm(), QMessageBox::Critical, tr("Error when loading initial feeds"), ex.message());
@@ -326,8 +331,8 @@ bool StandardServiceRoot::emptyBin() {
void StandardServiceRoot::loadFromDatabase(){
QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
- CategoryAssignment categories;
- FeedAssignment feeds;
+ Assignment categories;
+ Assignment feeds;
// Obtain data for categories from the database.
QSqlQuery query_categories(database);
@@ -339,7 +344,7 @@ void StandardServiceRoot::loadFromDatabase(){
}
while (query_categories.next()) {
- CategoryAssignmentItem pair;
+ AssignmentItem pair;
pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt();
pair.second = new StandardCategory(query_categories.record());
@@ -364,10 +369,10 @@ void StandardServiceRoot::loadFromDatabase(){
case StandardFeed::Rdf:
case StandardFeed::Rss0X:
case StandardFeed::Rss2X: {
- FeedAssignmentItem pair;
+ AssignmentItem pair;
pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt();
pair.second = new StandardFeed(query_feeds.record());
- pair.second->setType(type);
+ qobject_cast(pair.second)->setType(type);
feeds << pair;
break;
@@ -414,6 +419,7 @@ QHash StandardServiceRoot::categoriesForItem(RootItem *ro
}
QHash StandardServiceRoot::allCategories() {
+ // TODO: změnit na qlist, použít getsubtree možná
return categoriesForItem(this);
}
@@ -431,26 +437,6 @@ QList StandardServiceRoot::getContextMenuForFeed(StandardFeed *feed) {
return m_feedContextMenu;
}
-void StandardServiceRoot::assembleFeeds(FeedAssignment feeds) {
- QHash categories = categoriesForItem(this);
-
- foreach (const FeedAssignmentItem &feed, feeds) {
- if (feed.first == NO_PARENT_CATEGORY) {
- // This is top-level feed, add it to the root item.
- appendChild(feed.second);
- feed.second->updateCounts(true);
- }
- else if (categories.contains(feed.first)) {
- // This feed belongs to this category.
- categories.value(feed.first)->appendChild(feed.second);
- feed.second->updateCounts(true);
- }
- else {
- qWarning("Feed '%s' is loose, skipping it.", qPrintable(feed.second->title()));
- }
- }
-}
-
bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel *model, QString &output_message) {
QStack original_parents; original_parents.push(this);
QStack new_parents; new_parents.push(model->rootItem());
@@ -691,26 +677,3 @@ bool StandardServiceRoot::onAfterMessagesRestoredFromBin(RootItem *selected_item
requestFeedReadFilterReload();
return true;
}
-
-void StandardServiceRoot::assembleCategories(CategoryAssignment categories) {
- QHash assignments;
- assignments.insert(NO_PARENT_CATEGORY, this);
-
- // Add top-level categories.
- while (!categories.isEmpty()) {
- for (int i = 0; i < categories.size(); i++) {
- if (assignments.contains(categories.at(i).first)) {
- // Parent category of this category is already added.
- assignments.value(categories.at(i).first)->appendChild(categories.at(i).second);
-
- // Now, added category can be parent for another categories, add it.
- assignments.insert(categories.at(i).second->id(), categories.at(i).second);
-
- // Remove the category from the list, because it was
- // added to the final collection.
- categories.removeAt(i);
- i--;
- }
- }
- }
-}
diff --git a/src/services/standard/standardserviceroot.h b/src/services/standard/standardserviceroot.h
index 1a54f7b02..55552ff54 100755
--- a/src/services/standard/standardserviceroot.h
+++ b/src/services/standard/standardserviceroot.h
@@ -30,12 +30,6 @@ class StandardFeed;
class FeedsImportExportModel;
class QMenu;
-typedef QList > CategoryAssignment;
-typedef QPair CategoryAssignmentItem;
-
-typedef QList > FeedAssignment;
-typedef QPair FeedAssignmentItem;
-
class StandardServiceRoot : public ServiceRoot {
Q_OBJECT
@@ -117,11 +111,6 @@ class StandardServiceRoot : public ServiceRoot {
// which are suitable as IN clause for SQL queries.
QStringList textualFeedIds(const QList &feeds);
- // Takes lists of feeds/categories and assembles
- // them into the tree structure.
- void assembleCategories(CategoryAssignment categories);
- void assembleFeeds(FeedAssignment feeds);
-
StandardRecycleBin *m_recycleBin;
// Menus.
diff --git a/src/services/tt-rss/definitions.h b/src/services/tt-rss/definitions.h
index 6aae31bea..391188c76 100755
--- a/src/services/tt-rss/definitions.h
+++ b/src/services/tt-rss/definitions.h
@@ -11,6 +11,9 @@
#define UNKNOWN_METHOD "UNKNOWN_METHOD" // Given "op" is not recognized.
#define INCORRECT_USAGE "INCORRECT_USAGE" // Given "op" was used with bad parameters.
+// Limitations
+#define MAX_MESSAGES 200
+
// General return status codes.
#define API_STATUS_OK 0
#define API_STATUS_ERR 1
diff --git a/src/services/tt-rss/gui/formeditaccount.cpp b/src/services/tt-rss/gui/formeditaccount.cpp
index 57fbfed10..865e2df0e 100755
--- a/src/services/tt-rss/gui/formeditaccount.cpp
+++ b/src/services/tt-rss/gui/formeditaccount.cpp
@@ -185,6 +185,9 @@ void FormEditAccount::onUrlChanged() {
if (url.isEmpty()) {
m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty."));
}
+ else if (!url.endsWith(QL1S("api/"))) {
+ m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL must end with \"api/\"."));
+ }
else {
m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay."));
}
diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.cpp b/src/services/tt-rss/network/ttrssnetworkfactory.cpp
index 3c854f1bd..4d5c8ec03 100755
--- a/src/services/tt-rss/network/ttrssnetworkfactory.cpp
+++ b/src/services/tt-rss/network/ttrssnetworkfactory.cpp
@@ -24,6 +24,7 @@
#include "services/tt-rss/ttrsscategory.h"
#include "miscellaneous/application.h"
#include "miscellaneous/iconfactory.h"
+#include "miscellaneous/textfactory.h"
#include "network-web/networkfactory.h"
#include
@@ -92,18 +93,25 @@ TtRssLoginResponse TtRssNetworkFactory::login(QNetworkReply::NetworkError &error
}
TtRssResponse TtRssNetworkFactory::logout(QNetworkReply::NetworkError &error) {
- QtJson::JsonObject json;
- json["op"] = "logout";
- json["sid"] = m_sessionId;
+ if (!m_sessionId.isEmpty()) {
- QByteArray result_raw;
- NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
+ QtJson::JsonObject json;
+ json["op"] = "logout";
+ json["sid"] = m_sessionId;
- error = network_reply.first;
- return TtRssResponse(QString::fromUtf8(result_raw));
+ QByteArray result_raw;
+ NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
+
+ error = network_reply.first;
+ return TtRssResponse(QString::fromUtf8(result_raw));
+ }
+ else {
+ error = QNetworkReply::NoError;
+ return TtRssResponse();
+ }
}
-TtRssGetFeedsTreeResponse TtRssNetworkFactory::getFeedsTree(QNetworkReply::NetworkError &error) {
+TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories(QNetworkReply::NetworkError &error) {
QtJson::JsonObject json;
json["op"] = "getFeedTree";
json["sid"] = m_sessionId;
@@ -111,7 +119,7 @@ TtRssGetFeedsTreeResponse TtRssNetworkFactory::getFeedsTree(QNetworkReply::Netwo
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
- TtRssGetFeedsTreeResponse result(QString::fromUtf8(result_raw));
+ TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
@@ -119,7 +127,38 @@ TtRssGetFeedsTreeResponse TtRssNetworkFactory::getFeedsTree(QNetworkReply::Netwo
json["sid"] = m_sessionId;
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
- result = TtRssGetFeedsTreeResponse(QString::fromUtf8(result_raw));
+ result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw));
+ }
+
+ error = network_reply.first;
+ return result;
+}
+
+TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, bool force_update, int limit, int skip,
+ bool show_content, bool include_attachments,
+ bool sanitize, QNetworkReply::NetworkError &error) {
+ QtJson::JsonObject json;
+ json["op"] = "getHeadlines";
+ json["sid"] = m_sessionId;
+ json["feed_id"] = feed_id;
+ json["force_update"] = force_update;
+ json["limit"] = limit;
+ json["skip"] = skip;
+ json["show_content"] = show_content;
+ json["include_attachments"] = include_attachments;
+ json["sanitize"] = sanitize;
+
+ QByteArray result_raw;
+ NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
+ TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw));
+
+ if (result.isNotLoggedIn()) {
+ // We are not logged in.
+ login(error);
+ json["sid"] = m_sessionId;
+
+ network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
+ result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw));
}
error = network_reply.first;
@@ -203,15 +242,19 @@ bool TtRssResponse::hasError() const {
}
-TtRssGetFeedsTreeResponse::TtRssGetFeedsTreeResponse(const QString &raw_content) : TtRssResponse(raw_content) {
+TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString &raw_content) : TtRssResponse(raw_content) {
+
}
-TtRssGetFeedsTreeResponse::~TtRssGetFeedsTreeResponse() {
+TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() {
}
-RootItem *TtRssGetFeedsTreeResponse::feedsTree() {
+RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QString base_address) {
RootItem *parent = new RootItem();
+ // Chop the "api/" from the end of the address.
+ base_address.chop(4);
+
if (status() == API_STATUS_OK) {
// We have data, construct object tree according to data.
QList items_to_process = m_rawContent["content"].toMap()["categories"].toMap()["items"].toList();
@@ -226,51 +269,105 @@ RootItem *TtRssGetFeedsTreeResponse::feedsTree() {
RootItem *act_parent = pair.first;
QMap item = pair.second.toMap();
- if (item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY) {
- // Add category to the parent, go through children.
- int item_bare_id = item["bare_id"].toInt();
+ int item_id = item["bare_id"].toInt();
+ bool is_category = item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY;
- if (item_bare_id < 0) {
- // Ignore virtual categories or feeds.
- continue;
- }
+ if (item_id >= 0) {
+ if (is_category) {
+ if (item_id == 0) {
+ // This is "Uncategorized" category, all its feeds belong to top-level root.
+ if (item.contains("items")) {
+ foreach (QVariant child_feed, item["items"].toList()) {
+ pairs.append(QPair(parent, child_feed));
+ }
+ }
+ }
+ else {
+ TtRssCategory *category = new TtRssCategory();
- if (item_bare_id == 0) {
- // This is "Uncategorized" category, all its feeds belong to total parent.
- if (item.contains("items")) {
- foreach (QVariant child_feed, item["items"].toList()) {
- pairs.append(QPair(parent, child_feed));
+ category->setTitle(item["name"].toString());
+ category->setCustomId(item_id);
+ act_parent->appendChild(category);
+
+ if (item.contains("items")) {
+ foreach (QVariant child, item["items"].toList()) {
+ pairs.append(QPair(category, child));
+ }
}
}
}
- else if (item_bare_id > 0) {
- TtRssCategory *category = new TtRssCategory();
+ else {
+ // We have feed.
+ TtRssFeed *feed = new TtRssFeed();
- category->setIcon(qApp->icons()->fromTheme(QSL("folder-category")));
- category->setTitle(item["name"].toString());
- category->setCustomId(item_bare_id);
- act_parent->appendChild(category);
+ if (obtain_icons) {
+ QString icon_path = item["icon"].type() == QVariant::String ? item["icon"].toString() : QString();
- if (item.contains("items")) {
- foreach (QVariant child, item["items"].toList()) {
- pairs.append(QPair(category, child));
+ if (!icon_path.isEmpty()) {
+ // Chop the "api/" suffix out and append
+ QString full_icon_address = base_address + QL1C('/') + icon_path;
+ QByteArray icon_data;
+
+ if (NetworkFactory::downloadFile(full_icon_address, DOWNLOAD_TIMEOUT, icon_data).first == QNetworkReply::NoError) {
+ // Icon downloaded, set it up.
+ QPixmap icon_pixmap;
+ icon_pixmap.loadFromData(icon_data);
+ feed->setIcon(QIcon(icon_pixmap));
+ }
}
}
+
+ // TODO: stahnout a nastavit ikonu
+ feed->setTitle(item["name"].toString());
+ feed->setCustomId(item_id);
+ act_parent->appendChild(feed);
}
}
- else {
- // We have feed.
- int item_bare_id = item["bare_id"].toInt();
- TtRssFeed *feed = new TtRssFeed();
-
- // TODO: stahnout a nastavit ikonu
- feed->setTitle(item["name"].toString());
- feed->setCustomId(item_bare_id);
- act_parent->appendChild(feed);
- }
-
}
}
return parent;
}
+
+
+TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString &raw_content) : TtRssResponse(raw_content) {
+}
+
+TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() {
+}
+
+QList TtRssGetHeadlinesResponse::messages() {
+ QList messages;
+
+ foreach (QVariant item, m_rawContent["content"].toList()) {
+ QMap mapped = item.toMap();
+ Message message;
+
+ message.m_author = mapped["author"].toString();
+ message.m_isRead = !mapped["unread"].toBool();
+ message.m_isImportant = mapped["marked"].toBool();
+ message.m_contents = mapped["content"].toString();
+ message.m_created = TextFactory::parseDateTime(mapped["updated"].value());
+ message.m_createdFromFeed = true;
+ message.m_customId = mapped["id"].toString();
+ message.m_feedId = mapped["feed_id"].toString();
+ message.m_title = mapped["title"].toString();
+ message.m_url = mapped["link"].toString();
+
+ if (mapped.contains(QSL("attachments"))) {
+ // Process enclosures.
+ foreach (QVariant attachment, mapped["attachments"].toList()) {
+ QMap mapped_attachemnt = attachment.toMap();
+ Enclosure enclosure;
+
+ enclosure.m_mimeType = mapped_attachemnt["content_type"].toString();
+ enclosure.m_url = mapped_attachemnt["content_url"].toString();
+ message.m_enclosures.append(enclosure);
+ }
+ }
+
+ messages.append(message);
+ }
+
+ return messages;
+}
diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.h b/src/services/tt-rss/network/ttrssnetworkfactory.h
index 6db415d73..8d9374f20 100755
--- a/src/services/tt-rss/network/ttrssnetworkfactory.h
+++ b/src/services/tt-rss/network/ttrssnetworkfactory.h
@@ -20,6 +20,8 @@
#include "qt-json/json.h"
+#include "core/message.h"
+
#include
#include
#include
@@ -53,12 +55,23 @@ class TtRssLoginResponse : public TtRssResponse {
QString sessionId() const;
};
-class TtRssGetFeedsTreeResponse : public TtRssResponse {
+class TtRssGetFeedsCategoriesResponse : public TtRssResponse {
public:
- explicit TtRssGetFeedsTreeResponse(const QString &raw_content = QString());
- virtual ~TtRssGetFeedsTreeResponse();
+ explicit TtRssGetFeedsCategoriesResponse(const QString &raw_content = QString());
+ virtual ~TtRssGetFeedsCategoriesResponse();
- RootItem *feedsTree();
+ // Returns tree of feeds/categories.
+ // Top-level root of the tree is not needed here.
+ // Returned items do not have primary IDs assigned.
+ RootItem *feedsCategories(bool obtain_icons, QString base_address = QString());
+};
+
+class TtRssGetHeadlinesResponse : public TtRssResponse {
+ public:
+ explicit TtRssGetHeadlinesResponse(const QString &raw_content = QString());
+ virtual ~TtRssGetHeadlinesResponse();
+
+ QList messages();
};
class TtRssNetworkFactory {
@@ -84,7 +97,12 @@ class TtRssNetworkFactory {
TtRssResponse logout(QNetworkReply::NetworkError &error);
// Gets feeds from the server.
- TtRssGetFeedsTreeResponse getFeedsTree(QNetworkReply::NetworkError &error);
+ TtRssGetFeedsCategoriesResponse getFeedsCategories(QNetworkReply::NetworkError &error);
+
+ // Gets headlines (messages) from the server.
+ TtRssGetHeadlinesResponse getHeadlines(int feed_id, bool force_update, int limit, int skip,
+ bool show_content, bool include_attachments,
+ bool sanitize, QNetworkReply::NetworkError &error);
private:
QString m_url;
diff --git a/src/services/tt-rss/ttrsscategory.cpp b/src/services/tt-rss/ttrsscategory.cpp
old mode 100644
new mode 100755
index 7b485288e..7dc127ea6
--- a/src/services/tt-rss/ttrsscategory.cpp
+++ b/src/services/tt-rss/ttrsscategory.cpp
@@ -18,9 +18,21 @@
#include "services/tt-rss/ttrsscategory.h"
#include "definitions/definitions.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/iconfactory.h"
+
+#include
TtRssCategory::TtRssCategory(RootItem *parent) : Category(parent), m_customId(NO_PARENT_CATEGORY) {
+ setIcon(qApp->icons()->fromTheme(QSL("folder-category")));
+}
+
+TtRssCategory::TtRssCategory(const QSqlRecord &record) : Category(NULL) {
+ setIcon(qApp->icons()->fromTheme(QSL("folder-category")));
+ setId(record.value(CAT_DB_ID_INDEX).toInt());
+ setTitle(record.value(CAT_DB_TITLE_INDEX).toString());
+ setCustomId(record.value(CAT_DB_CUSTOM_ID_INDEX).toInt());
}
TtRssCategory::~TtRssCategory() {
diff --git a/src/services/tt-rss/ttrsscategory.h b/src/services/tt-rss/ttrsscategory.h
old mode 100644
new mode 100755
index fb7df9a24..c029d3435
--- a/src/services/tt-rss/ttrsscategory.h
+++ b/src/services/tt-rss/ttrsscategory.h
@@ -20,10 +20,13 @@
#include "services/abstract/category.h"
+#include
+
class TtRssCategory : public Category {
public:
explicit TtRssCategory(RootItem *parent = NULL);
+ explicit TtRssCategory(const QSqlRecord &record);
virtual ~TtRssCategory();
int customId() const;
diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp
old mode 100644
new mode 100755
index 45d90dc31..33c7f1bc6
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -18,20 +18,133 @@
#include "services/tt-rss/ttrssfeed.h"
#include "definitions/definitions.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/databasefactory.h"
+#include "miscellaneous/iconfactory.h"
+#include "miscellaneous/textfactory.h"
+#include "services/tt-rss/definitions.h"
+#include "services/tt-rss/ttrssserviceroot.h"
+#include "services/tt-rss/network/ttrssnetworkfactory.h"
+
+#include
-TtRssFeed::TtRssFeed(RootItem *parent) : Feed(parent), m_customId(NO_PARENT_CATEGORY) {
+TtRssFeed::TtRssFeed(RootItem *parent)
+ : Feed(parent), m_customId(NO_PARENT_CATEGORY), m_totalCount(0), m_unreadCount(0) {
+}
+
+TtRssFeed::TtRssFeed(const QSqlRecord &record) : Feed(NULL), m_totalCount(0), m_unreadCount(0) {
+ setTitle(record.value(FDS_DB_TITLE_INDEX).toString());
+ setId(record.value(FDS_DB_ID_INDEX).toInt());
+ setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray()));
+ setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
+ setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
+ setCustomId(record.value(FDS_DB_CUSTOM_ID_INDEX).toInt());
}
TtRssFeed::~TtRssFeed() {
}
+TtRssServiceRoot *TtRssFeed::serviceRoot() {
+ return qobject_cast(getParentServiceRoot());
+}
+
+void TtRssFeed::updateCounts(bool including_total_count) {
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
+ QSqlQuery query_all(database);
+
+ query_all.setForwardOnly(true);
+
+ if (including_total_count) {
+ if (query_all.exec(QString("SELECT count(*) FROM Messages WHERE feed = '%1' AND is_deleted = 0 AND account_id = %2;").arg(QString::number(customId()),
+ QString::number(serviceRoot()->accountId()))) && query_all.next()) {
+ m_totalCount = query_all.value(0).toInt();
+ }
+ }
+
+ // Obtain count of unread messages.
+ if (query_all.exec(QString("SELECT count(*) FROM Messages WHERE feed = '%1' AND is_deleted = 0 AND is_read = 0 AND account_id = %2;").arg(QString::number(customId()),
+ QString::number(serviceRoot()->accountId()))) && query_all.next()) {
+ int new_unread_count = query_all.value(0).toInt();
+
+ if (status() == NewMessages && new_unread_count < m_unreadCount) {
+ setStatus(Normal);
+ }
+
+ m_unreadCount = new_unread_count;
+ }
+}
+
+int TtRssFeed::countOfAllMessages() {
+ return m_totalCount;
+}
+
+int TtRssFeed::countOfUnreadMessages() {
+ return m_unreadCount;
+}
+
int TtRssFeed::update() {
- return 0;
+ // TODO: přes getHeadlines provede stažení kompletnich zprav.
+ QNetworkReply::NetworkError error;
+ QList messages;
+ int newly_added_messages = 0;
+ int limit = MAX_MESSAGES;
+ int skip = 0;
+
+ do {
+ TtRssGetHeadlinesResponse headlines = serviceRoot()->network()->getHeadlines(customId(), true, limit, skip,
+ true, true, false, error);
+
+ if (error != QNetworkReply::NoError) {
+ setStatus(Feed::NetworkError);
+ return 0;
+ }
+ else {
+ QList new_messages = headlines.messages();
+
+ messages.append(new_messages);
+ newly_added_messages = new_messages.size();
+ skip += newly_added_messages;
+ }
+ }
+ while (newly_added_messages > 0);
+
+ return updateMessages(messages);
}
QList TtRssFeed::undeletedMessages() const {
- return QList();
+ QList messages;
+ int account_id = const_cast(this)->serviceRoot()->accountId();
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
+ QSqlQuery query_read_msg(database);
+ query_read_msg.setForwardOnly(true);
+ query_read_msg.prepare("SELECT title, url, author, date_created, contents, enclosures, custom_id "
+ "FROM Messages "
+ "WHERE is_deleted = 0 AND feed = :feed AND account_id = :account_id;");
+
+ query_read_msg.bindValue(QSL(":feed"), id());
+ query_read_msg.bindValue(QSL(":account_id"), account_id);
+
+ // FIXME: Fix those const functions, this is fucking ugly.
+
+ if (query_read_msg.exec()) {
+ while (query_read_msg.next()) {
+ Message message;
+
+ message.m_feedId = QString::number(account_id);
+ message.m_title = query_read_msg.value(0).toString();
+ message.m_url = query_read_msg.value(1).toString();
+ message.m_author = query_read_msg.value(2).toString();
+ message.m_created = TextFactory::parseDateTime(query_read_msg.value(3).value());
+ message.m_contents = query_read_msg.value(4).toString();
+ message.m_enclosures = Enclosures::decodeEnclosuresFromString(query_read_msg.value(5).toString());
+ message.m_customId = query_read_msg.value(6).toString();
+
+ messages.append(message);
+ }
+ }
+
+ return messages;
}
int TtRssFeed::customId() const {
@@ -41,3 +154,9 @@ int TtRssFeed::customId() const {
void TtRssFeed::setCustomId(int custom_id) {
m_customId = custom_id;
}
+
+int TtRssFeed::updateMessages(const QList &messages) {
+ // TODO: pokračovat tady
+
+ return 0;
+}
diff --git a/src/services/tt-rss/ttrssfeed.h b/src/services/tt-rss/ttrssfeed.h
old mode 100644
new mode 100755
index 314ad880d..92fae6ef2
--- a/src/services/tt-rss/ttrssfeed.h
+++ b/src/services/tt-rss/ttrssfeed.h
@@ -20,12 +20,24 @@
#include "services/abstract/feed.h"
+#include
+
+
+class TtRssServiceRoot;
class TtRssFeed : public Feed {
public:
explicit TtRssFeed(RootItem *parent = NULL);
+ explicit TtRssFeed(const QSqlRecord &record);
virtual ~TtRssFeed();
+ TtRssServiceRoot *serviceRoot();
+
+ void updateCounts(bool including_total_count);
+
+ int countOfAllMessages();
+ int countOfUnreadMessages();
+
int update();
QList undeletedMessages() const;
@@ -33,7 +45,11 @@ class TtRssFeed : public Feed {
void setCustomId(int custom_id);
private:
+ int updateMessages(const QList &messages);
+
int m_customId;
+ int m_totalCount;
+ int m_unreadCount;
};
#endif // TTRSSFEED_H
diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp
index 7f1086a5b..5ac50a600 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -20,13 +20,18 @@
#include "miscellaneous/application.h"
#include "miscellaneous/settings.h"
#include "gui/dialogs/formmain.h"
+#include "network-web/networkfactory.h"
#include "services/tt-rss/ttrssserviceentrypoint.h"
+#include "services/tt-rss/ttrssfeed.h"
+#include "services/tt-rss/ttrsscategory.h"
#include "services/tt-rss/network/ttrssnetworkfactory.h"
#include "services/tt-rss/gui/formeditaccount.h"
+#include
#include
#include
#include
+#include
TtRssServiceRoot::TtRssServiceRoot(RootItem *parent)
@@ -42,15 +47,18 @@ TtRssServiceRoot::~TtRssServiceRoot() {
}
void TtRssServiceRoot::start() {
- loadFeedTreeFromDatabase();
+ loadFromDatabase();
- if (childItems().isEmpty()) {
+ if (childCount() == 0) {
syncIn();
}
}
void TtRssServiceRoot::stop() {
+ QNetworkReply::NetworkError error;
+ m_network->logout(error);
+ qDebug("Stopping Tiny Tiny RSS account, logging out with result '%d'.", (int) error);
}
QString TtRssServiceRoot::code() {
@@ -111,7 +119,13 @@ RecycleBin *TtRssServiceRoot::recycleBin() {
}
bool TtRssServiceRoot::loadMessagesForItem(RootItem *item, QSqlTableModel *model) {
- return false;
+ QList children = item->getSubTreeFeeds();
+ QString filter_clause = textualFeedIds(children).join(QSL(", "));
+
+ model->setFilter(QString(QSL("feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0")).arg(filter_clause));
+ qDebug("Loading messages from feeds: %s.", qPrintable(filter_clause));
+
+ return true;
}
QList TtRssServiceRoot::serviceMenu() {
@@ -211,6 +225,52 @@ void TtRssServiceRoot::saveAccountDataToDatabase() {
}
}
+void TtRssServiceRoot::loadFromDatabase() {
+ // TODO: Load feeds/categories from DB.
+
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
+ Assignment categories;
+ Assignment feeds;
+
+ // Obtain data for categories from the database.
+ QSqlQuery query_categories(database);
+ query_categories.setForwardOnly(true);
+
+ if (!query_categories.exec(QString("SELECT * FROM Categories WHERE account_id = %1;").arg(accountId())) || query_categories.lastError().isValid()) {
+ qFatal("Query for obtaining categories failed. Error message: '%s'.",
+ qPrintable(query_categories.lastError().text()));
+ }
+
+ while (query_categories.next()) {
+ AssignmentItem pair;
+ pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt();
+ pair.second = new TtRssCategory(query_categories.record());
+
+ categories << pair;
+ }
+
+ // All categories are now loaded.
+ QSqlQuery query_feeds(database);
+ query_feeds.setForwardOnly(true);
+
+ if (!query_feeds.exec(QString("SELECT * FROM Feeds WHERE account_id = %1;").arg(accountId())) || query_feeds.lastError().isValid()) {
+ qFatal("Query for obtaining feeds failed. Error message: '%s'.",
+ qPrintable(query_feeds.lastError().text()));
+ }
+
+ while (query_feeds.next()) {
+ AssignmentItem pair;
+ pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt();
+ pair.second = new TtRssFeed(query_feeds.record());
+
+ feeds << pair;
+ }
+
+ // All data are now obtained, lets create the hierarchy.
+ assembleCategories(categories);
+ assembleFeeds(feeds);
+}
+
void TtRssServiceRoot::updateTitle() {
QString host = QUrl(m_network->url()).host();
@@ -222,37 +282,110 @@ void TtRssServiceRoot::updateTitle() {
}
void TtRssServiceRoot::syncIn() {
- // TODO: provede stažení kanálů/kategorií
- // ze serveru, a sloučení s aktuálními
- // neprovádí aktualizace kanálů ani stažení počtu nepřečtených zpráv
- QNetworkReply::NetworkError error;
- RootItem *new_feeds = m_network->getFeedsTree(error).feedsTree();
+ QNetworkReply::NetworkError err;
+ TtRssGetFeedsCategoriesResponse feed_cats_response = m_network->getFeedsCategories(err);
- if (error == QNetworkReply::NoError) {
- // We have new feeds, purge old and set new to DB.
+ if (err == QNetworkReply::NoError) {
+ RootItem *new_tree = feed_cats_response.feedsCategories(true, m_network->url());
+
+ // Purge old data from SQL and clean all model items.
removeOldFeedTree();
+ cleanAllItems();
- foreach (RootItem *child, childItems()) {
- requestItemRemoval(child);
+ // Model is clean, now store new tree into DB and
+ // set primary IDs of the items.
+ storeNewFeedTree(new_tree);
+
+ foreach (RootItem *top_level_item, new_tree->childItems()) {
+ appendChild(top_level_item);
}
- clearChildren();
+ updateCounts(true);
- // Old stuff is gone.
- storeNewFeedTree(new_feeds);
- loadFeedTreeFromDatabase();
- //itemChanged(QList() << this);
+ new_tree->clearChildren();
+ new_tree->deleteLater();
+
+ itemChanged(QList() << this);
+ requestFeedReadFilterReload();
+ requestReloadMessageList(true);
+ requestItemExpand(getSubTree(), true);
}
}
+QStringList TtRssServiceRoot::textualFeedIds(const QList &feeds) {
+ QStringList stringy_ids;
+ stringy_ids.reserve(feeds.size());
+
+ foreach (Feed *feed, feeds) {
+ stringy_ids.append(QString("'%1'").arg(QString::number(static_cast(feed)->customId())));
+ }
+
+ return stringy_ids;
+}
+
void TtRssServiceRoot::removeOldFeedTree() {
- // TODO: vymazat kanaly a kategorie.
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
+ QSqlQuery query(database);
+ query.setForwardOnly(true);
+
+ query.prepare(QSL("DELETE FROM Feeds WHERE account_id = :account_id;"));
+ query.bindValue(QSL(":account_id"), accountId());
+ query.exec();
+
+ query.prepare(QSL("DELETE FROM Categories WHERE account_id = :account_id;"));
+ query.bindValue(QSL(":account_id"), accountId());
+ query.exec();
}
-void TtRssServiceRoot::storeNewFeedTree(RootItem *tree_root) {
- // TODO: ulozit do db.
+void TtRssServiceRoot::cleanAllItems() {
+ foreach (RootItem *top_level_item, childItems()) {
+ requestItemRemoval(top_level_item);
+ }
}
-void TtRssServiceRoot::loadFeedTreeFromDatabase() {
- // TODO: nacist kanaly a kategorie z db
+void TtRssServiceRoot::storeNewFeedTree(RootItem *root) {
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
+ QSqlQuery query_category(database);
+ QSqlQuery query_feed(database);
+
+ query_category.prepare("INSERT INTO Categories (parent_id, title, account_id, custom_id) "
+ "VALUES (:parent_id, :title, :account_id, :custom_id);");
+ query_feed.prepare("INSERT INTO Feeds (title, icon, category, protected, update_type, update_interval, account_id, custom_id) "
+ "VALUES (:title, :icon, :category, :protected, :update_type, :update_interval, :account_id, :custom_id);");
+
+ // Iterate all children.
+ foreach (RootItem *child, root->getSubTree()) {
+ if (child->kind() == RootItemKind::Category) {
+ query_category.bindValue(QSL(":parent_id"), child->parent()->id());
+ query_category.bindValue(QSL(":title"), child->title());
+ query_category.bindValue(QSL(":account_id"), accountId());
+ query_category.bindValue(QSL(":custom_id"), QString::number(static_cast(child)->customId()));
+
+ if (query_category.exec()) {
+ child->setId(query_category.lastInsertId().toInt());
+ }
+ else {
+ // TODO: logovat
+ }
+ }
+ else if (child->kind() == RootItemKind::Feed) {
+ TtRssFeed *feed = static_cast(child);
+
+ query_feed.bindValue(QSL(":title"), feed->title());
+ query_feed.bindValue(QSL(":icon"), qApp->icons()->toByteArray(feed->icon()));
+ query_feed.bindValue(QSL(":category"), feed->parent()->id());
+ query_feed.bindValue(QSL(":protected"), 0);
+ query_feed.bindValue(QSL(":update_type"), (int) feed->autoUpdateType());
+ query_feed.bindValue(QSL(":update_interval"), feed->autoUpdateInitialInterval());
+ query_feed.bindValue(QSL(":account_id"), accountId());
+ query_feed.bindValue(QSL(":custom_id"), feed->customId());
+
+ if (query_feed.exec()) {
+ feed->setId(query_feed.lastInsertId().toInt());
+ }
+ else {
+ // TODO: logovat.
+ }
+ }
+ }
}
diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h
index f34fba2b3..56519886f 100755
--- a/src/services/tt-rss/ttrssserviceroot.h
+++ b/src/services/tt-rss/ttrssserviceroot.h
@@ -23,6 +23,8 @@
#include
+class TtRssCategory;
+class TtRssFeed;
class TtRssNetworkFactory;
class TtRssServiceRoot : public ServiceRoot {
@@ -73,9 +75,14 @@ class TtRssServiceRoot : public ServiceRoot {
void syncIn();
private:
+ // Returns converted ids of given feeds
+ // which are suitable as IN clause for SQL queries.
+ QStringList textualFeedIds(const QList &feeds);
+
void removeOldFeedTree();
- void storeNewFeedTree(RootItem *tree_root);
- void loadFeedTreeFromDatabase();
+ void cleanAllItems();
+ void storeNewFeedTree(RootItem *root);
+ void loadFromDatabase();
QAction *m_actionSyncIn;
QList m_serviceMenu;