From 683517948d84c229ba2201dfaf82aae03daabfeb Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 09:11:55 +0100
Subject: [PATCH 01/11] Many changes, working Sync in, fixed some problems with
 item expanding, items now can inform model that it wants some items expanded
 or not.

---
 resources/misc/db_init_mysql.sql              |  10 +-
 resources/misc/db_init_sqlite.sql             |  10 +-
 resources/misc/db_update_mysql_3_4.sql        |  15 +++
 resources/misc/db_update_sqlite_3_4.sql       |  50 ++++++++
 src/core/feedsmodel.cpp                       |   1 +
 src/core/feedsmodel.h                         |   3 +
 src/gui/feedsview.cpp                         |  19 +++-
 src/gui/feedsview.h                           |   1 +
 src/services/abstract/serviceroot.cpp         |   6 +-
 src/services/abstract/serviceroot.h           |   5 +-
 .../standard/standardserviceentrypoint.cpp    |   2 -
 src/services/standard/standardserviceroot.cpp |   2 +
 src/services/tt-rss/gui/formeditaccount.cpp   |   5 +-
 .../tt-rss/network/ttrssnetworkfactory.cpp    | 102 ++++++++++-------
 .../tt-rss/network/ttrssnetworkfactory.h      |   5 +-
 .../tt-rss/ttrssserviceentrypoint.cpp         |   1 -
 src/services/tt-rss/ttrssserviceroot.cpp      | 107 +++++++++++++++++-
 src/services/tt-rss/ttrssserviceroot.h        |   8 +-
 18 files changed, 282 insertions(+), 70 deletions(-)

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/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp
index ce322eb5e..b6040476e 100755
--- a/src/core/feedsmodel.cpp
+++ b/src/core/feedsmodel.cpp
@@ -697,6 +697,7 @@ bool FeedsModel::addServiceAccount(ServiceRoot *root) {
   connect(root, SIGNAL(readFeedsFilterInvalidationRequested()), this, SIGNAL(readFeedsFilterInvalidationRequested()));
   connect(root, SIGNAL(dataChanged(QList<RootItem*>)), this, SLOT(onItemDataChanged(QList<RootItem*>)));
   connect(root, SIGNAL(reloadMessageListRequested(bool)), this, SIGNAL(reloadMessageListRequested(bool)));
+  connect(root, SIGNAL(itemExpandRequested(QList<RootItem*>,bool)), this, SIGNAL(itemExpandRequested(QList<RootItem*>,bool)));
 
   root->start();
   return true;
diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h
index 83df548e5..1e94efc93 100755
--- a/src/core/feedsmodel.h
+++ b/src/core/feedsmodel.h
@@ -206,6 +206,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<RootItem*> items, bool expand);
+
     // Emitted when there is a need of reloading of displayed messages.
     void reloadMessageListRequested(bool mark_selected_messages_read);
 
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<RootItem*>,bool)), this, SLOT(onItemExpandRequested(QList<RootItem*>,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<RootItem*> 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<RootItem*> &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<RootItem*> &items, bool exp);
 
   private:
     // Initializes context menus.
diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp
index 2a804ce18..cd25632b1 100755
--- a/src/services/abstract/serviceroot.cpp
+++ b/src/services/abstract/serviceroot.cpp
@@ -58,7 +58,7 @@ bool ServiceRoot::deleteViaGui() {
   return data_removed;
 }
 
-void ServiceRoot::itemChanged(QList<RootItem*> items) {
+void ServiceRoot::itemChanged(const QList<RootItem *> &items) {
   emit dataChanged(items);
 }
 
@@ -70,6 +70,10 @@ void ServiceRoot::requestFeedReadFilterReload() {
   emit readFeedsFilterInvalidationRequested();
 }
 
+void ServiceRoot::requestItemExpand(const QList<RootItem *> &items, bool expand) {
+  emit itemExpandRequested(items, expand);
+}
+
 void ServiceRoot::requestItemReassignment(RootItem *item, RootItem *new_parent) {
   emit itemReassignmentRequested(item, new_parent);
 }
diff --git a/src/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h
index 3dbd13475..30498fc73 100755
--- a/src/services/abstract/serviceroot.h
+++ b/src/services/abstract/serviceroot.h
@@ -138,10 +138,10 @@ class ServiceRoot : public RootItem {
     /////////////////////////////////////////
 
     // Obvious methods to wrap signals.
-    void itemChanged(QList<RootItem*> items);
+    void itemChanged(const QList<RootItem*> &items);
     void requestReloadMessageList(bool mark_selected_messages_read);
     void requestFeedReadFilterReload();
-
+    void requestItemExpand(const QList<RootItem*> &items, bool expand);
     void requestItemReassignment(RootItem *item, RootItem *new_parent);
     void requestItemRemoval(RootItem *item);
 
@@ -154,6 +154,7 @@ class ServiceRoot : public RootItem {
     void dataChanged(QList<RootItem*> items);
     void readFeedsFilterInvalidationRequested();
     void reloadMessageListRequested(bool mark_selected_messages_read);
+    void itemExpandRequested(QList<RootItem*> items, bool expand);
 
     void itemReassignmentRequested(RootItem *item, RootItem *new_parent);
     void itemRemovalRequested(RootItem *item);
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<ServiceRoot*> 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..c261155f0 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),
diff --git a/src/services/tt-rss/gui/formeditaccount.cpp b/src/services/tt-rss/gui/formeditaccount.cpp
index 43068321c..865e2df0e 100755
--- a/src/services/tt-rss/gui/formeditaccount.cpp
+++ b/src/services/tt-rss/gui/formeditaccount.cpp
@@ -148,7 +148,7 @@ void FormEditAccount::onClickedOk() {
   m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text());
   m_editableRoot->network()->setUsername(m_ui->m_txtUsername->lineEdit()->text());
   m_editableRoot->network()->setPassword(m_ui->m_txtPassword->lineEdit()->text());
-  m_editableRoot->saveToDatabase();
+  m_editableRoot->saveAccountDataToDatabase();
 
   accept();
 }
@@ -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 6576b9c08..5915f545e 100755
--- a/src/services/tt-rss/network/ttrssnetworkfactory.cpp
+++ b/src/services/tt-rss/network/ttrssnetworkfactory.cpp
@@ -92,15 +92,22 @@ 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();
+  }
 }
 
 TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories(QNetworkReply::NetworkError &error) {
@@ -210,9 +217,12 @@ TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString &
 TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() {
 }
 
-RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories() {
+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<QVariant> items_to_process = m_rawContent["content"].toMap()["categories"].toMap()["items"].toList();
@@ -227,49 +237,61 @@ RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories() {
       RootItem *act_parent = pair.first;
       QMap<QString,QVariant> 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<RootItem*,QVariant>(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<RootItem*,QVariant>(parent, child_feed));
+            category->setIcon(qApp->icons()->fromTheme(QSL("folder-category")));
+            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<RootItem*,QVariant>(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<RootItem*,QVariant>(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);
-      }
-
     }
   }
 
diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.h b/src/services/tt-rss/network/ttrssnetworkfactory.h
index 5bc259209..8e76f3b09 100755
--- a/src/services/tt-rss/network/ttrssnetworkfactory.h
+++ b/src/services/tt-rss/network/ttrssnetworkfactory.h
@@ -58,7 +58,10 @@ class TtRssGetFeedsCategoriesResponse : public TtRssResponse {
     explicit TtRssGetFeedsCategoriesResponse(const QString &raw_content = QString());
     virtual ~TtRssGetFeedsCategoriesResponse();
 
-    RootItem *feedsCategories();
+    // 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 TtRssNetworkFactory {
diff --git a/src/services/tt-rss/ttrssserviceentrypoint.cpp b/src/services/tt-rss/ttrssserviceentrypoint.cpp
index 02c03c88a..6af16e2a8 100755
--- a/src/services/tt-rss/ttrssserviceentrypoint.cpp
+++ b/src/services/tt-rss/ttrssserviceentrypoint.cpp
@@ -87,7 +87,6 @@ QList<ServiceRoot*> TtRssServiceEntryPoint::initializeSubtree() {
       root->network()->setPassword(query.value(2).toString());
       root->network()->setUrl(query.value(3).toString());
       root->updateTitle();
-      root->loadFromDatabase();
       roots.append(root);
     }
   }
diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp
index 48575f313..bffdd96e0 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -21,6 +21,8 @@
 #include "miscellaneous/settings.h"
 #include "gui/dialogs/formmain.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"
 
@@ -42,13 +44,16 @@ TtRssServiceRoot::~TtRssServiceRoot() {
 }
 
 void TtRssServiceRoot::start() {
-  if (childItems().isEmpty()) {
-    syncIn();
-  }
+  // TODO: posunout starty rootů až je okno uděláno
+  loadFromDatabase();
+
+  // TODO: pokud tady není nic načteno, tak
+  // syncIn
 }
 
 void TtRssServiceRoot::stop() {
-
+  QNetworkReply::NetworkError error;
+  m_network->logout(error);
 }
 
 QString TtRssServiceRoot::code() {
@@ -164,7 +169,7 @@ TtRssNetworkFactory *TtRssServiceRoot::network() const {
   return m_network;
 }
 
-void TtRssServiceRoot::saveToDatabase() {
+void TtRssServiceRoot::saveAccountDataToDatabase() {
   if (accountId() != NO_PARENT_CATEGORY) {
     // We are overwritting previously saved data.
     QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
@@ -228,7 +233,97 @@ void TtRssServiceRoot::syncIn() {
   // 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 err;
+  TtRssGetFeedsCategoriesResponse feed_cats_response = m_network->getFeedsCategories(err);
 
+  if (err == QNetworkReply::NoError) {
+    RootItem *new_tree = feed_cats_response.feedsCategories(true, m_network->url());
 
-  RootItem *aa = m_network->getFeedsCategories(err).feedsCategories();
+    // Purge old data from SQL and clean all model items.
+    removeOldFeedTree();
+    cleanAllItems();
+
+    // 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);
+    }
+
+    new_tree->clearChildren();
+    new_tree->deleteLater();
+    itemChanged(QList<RootItem*>() << this);
+    requestFeedReadFilterReload();
+    requestReloadMessageList(true);
+    requestItemExpand(getSubTree(), true);
+  }
+}
+
+void TtRssServiceRoot::removeOldFeedTree() {
+  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::cleanAllItems() {
+  foreach (RootItem *top_level_item, childItems()) {
+    requestItemRemoval(top_level_item);
+  }
+}
+
+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<TtRssCategory*>(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<TtRssFeed*>(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());
+
+        // TODO: updatecounts;
+      }
+      else {
+        // TODO: logovat.
+      }
+    }
+  }
 }
diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h
index 00ba6446e..a549d1374 100755
--- a/src/services/tt-rss/ttrssserviceroot.h
+++ b/src/services/tt-rss/ttrssserviceroot.h
@@ -66,14 +66,18 @@ class TtRssServiceRoot : public ServiceRoot {
 
     TtRssNetworkFactory *network() const;
 
-    void saveToDatabase();
-    void loadFromDatabase();
+    void saveAccountDataToDatabase();
     void updateTitle();
 
   private slots:
     void syncIn();
 
   private:
+    void removeOldFeedTree();
+    void cleanAllItems();
+    void storeNewFeedTree(RootItem *root);
+    void loadFromDatabase();
+
     QAction *m_actionSyncIn;
     QList<QAction*> m_serviceMenu;
 

From 11dfe67b4a7a0bcfb9123ffa31d6edfbf5c414b1 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 09:34:14 +0100
Subject: [PATCH 02/11] Account initial starting is now delayed after main form
 construction, better now. Also some more stuff.

---
 src/core/feedsmodel.cpp                  |  6 +++++-
 src/core/feedsmodel.h                    |  6 +++---
 src/gui/feedmessageviewer.cpp            |  2 --
 src/main.cpp                             |  4 ++++
 src/services/abstract/serviceroot.h      |  4 ++--
 src/services/tt-rss/ttrssserviceroot.cpp | 10 +++++++---
 6 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp
index b6040476e..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;
 }
diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h
index 1e94efc93..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.
@@ -221,9 +224,6 @@ class FeedsModel : public QAbstractItemModel {
     // which are suitable as IN clause for SQL queries.
     QStringList textualFeedIds(const QList<Feed*> &feeds);
 
-    // Loads feed/categories from the database.
-    void loadActivatedServiceAccounts();
-
     RootItem *m_rootItem;
     QList<QString> m_headerData;
     QList<QString> m_tooltipData;
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/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/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h
index 30498fc73..ed0295daa 100755
--- a/src/services/abstract/serviceroot.h
+++ b/src/services/abstract/serviceroot.h
@@ -64,6 +64,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 +81,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,
diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp
index bffdd96e0..0f6680779 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -20,6 +20,7 @@
 #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"
@@ -44,16 +45,19 @@ TtRssServiceRoot::~TtRssServiceRoot() {
 }
 
 void TtRssServiceRoot::start() {
-  // TODO: posunout starty rootů až je okno uděláno
   loadFromDatabase();
 
-  // TODO: pokud tady není nic načteno, tak
-  // syncIn
+  if (childCount() == 0) {
+    // TODO: pokud tady není nic načteno, tak
+    // 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() {

From 04028ef6bb8bc41fb7796dc938f3ccceea6326fa Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 09:58:05 +0100
Subject: [PATCH 03/11] Fixed expanding when importing, newer readme.

---
 README.md                                     |  2 +
 src/gui/dialogs/formmain.cpp                  |  2 +
 .../standard/gui/formstandardimportexport.cpp |  1 +
 src/services/standard/standardserviceroot.cpp |  5 +-
 src/services/tt-rss/ttrssfeed.cpp             | 46 ++++++++++++++++++-
 src/services/tt-rss/ttrssfeed.h               | 11 +++++
 src/services/tt-rss/ttrssserviceroot.cpp      |  5 +-
 7 files changed, 68 insertions(+), 4 deletions(-)
 mode change 100644 => 100755 src/services/tt-rss/ttrssfeed.cpp
 mode change 100644 => 100755 src/services/tt-rss/ttrssfeed.h

diff --git a/README.md b/README.md
index 716888781..b3cfb76d5 100644
--- a/README.md
+++ b/README.md
@@ -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/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp
index 278a791c8..ce207f599 100755
--- a/src/gui/dialogs/formmain.cpp
+++ b/src/gui/dialogs/formmain.cpp
@@ -80,6 +80,8 @@ FormMain::FormMain(QWidget *parent, Qt::WindowFlags f)
   setupIcons();
   loadSize();
 
+  statusBar()->setVisible(false);
+
   // Initialize the web factory.
   WebFactory::instance()->loadState();
 }
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/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp
index c261155f0..e86be599b 100755
--- a/src/services/standard/standardserviceroot.cpp
+++ b/src/services/standard/standardserviceroot.cpp
@@ -85,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());
diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp
old mode 100644
new mode 100755
index 45d90dc31..5c8dc0810
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -18,14 +18,58 @@
 #include "services/tt-rss/ttrssfeed.h"
 
 #include "definitions/definitions.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/databasefactory.h"
+#include "services/tt-rss/ttrssserviceroot.h"
+
+#include <QSqlQuery>
 
 
-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() {
 }
 
+TtRssServiceRoot *TtRssFeed::serviceRoot() {
+  return qobject_cast<TtRssServiceRoot*>(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;
 }
diff --git a/src/services/tt-rss/ttrssfeed.h b/src/services/tt-rss/ttrssfeed.h
old mode 100644
new mode 100755
index 314ad880d..a66335781
--- a/src/services/tt-rss/ttrssfeed.h
+++ b/src/services/tt-rss/ttrssfeed.h
@@ -21,11 +21,20 @@
 #include "services/abstract/feed.h"
 
 
+class TtRssServiceRoot;
+
 class TtRssFeed : public Feed {
   public:
     explicit TtRssFeed(RootItem *parent = NULL);
     virtual ~TtRssFeed();
 
+    TtRssServiceRoot *serviceRoot();
+
+    void updateCounts(bool including_total_count);
+
+    int countOfAllMessages();
+    int countOfUnreadMessages();
+
     int update();
     QList<Message> undeletedMessages() const;
 
@@ -34,6 +43,8 @@ class TtRssFeed : public Feed {
 
   private:
     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 0f6680779..50eeb2bd3 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -254,8 +254,11 @@ void TtRssServiceRoot::syncIn() {
       appendChild(top_level_item);
     }
 
+    updateCounts(true);
+
     new_tree->clearChildren();
     new_tree->deleteLater();
+
     itemChanged(QList<RootItem*>() << this);
     requestFeedReadFilterReload();
     requestReloadMessageList(true);
@@ -322,8 +325,6 @@ void TtRssServiceRoot::storeNewFeedTree(RootItem *root) {
 
       if (query_feed.exec()) {
         feed->setId(query_feed.lastInsertId().toInt());
-
-        // TODO: updatecounts;
       }
       else {
         // TODO: logovat.

From a66c9055fd5f1687eb3906ed3b8dfc66a2e86422 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 09:59:51 +0100
Subject: [PATCH 04/11] README.

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index b3cfb76d5..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,7 +66,7 @@ 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,
+* **support for online feed synchronization via plugins**,
     * Tiny Tiny RSS (from RSS Guard 3.0.0).
 * multiplatformity,
 * support for all feed formats,

From cac3aee503c04744044b1cd928c3fa9887be41e4 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 10:13:05 +0100
Subject: [PATCH 05/11] Added ability to hide status bar.

---
 resources/text/CHANGELOG       |  1 +
 src/gui/dialogs/formmain.cpp   | 13 ++++++-------
 src/gui/dialogs/formmain.h     |  3 ---
 src/gui/dialogs/formmain.ui    | 12 ++++++++++++
 src/miscellaneous/settings.cpp |  3 +++
 src/miscellaneous/settings.h   |  3 +++
 6 files changed, 25 insertions(+), 10 deletions(-)

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 @@
 	  <ul>
 		  <li style="color: red;">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.</li>
       <li>Added ability to completely disable notifications (bug #128).</li>
+      <li>Added ability to hide status bar.</li>
       <li>Added ability to go to next <font style="font-weight: bold;">unread</font> message. (partially bug #112)</li>
 	  </ul>
 	  
diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp
index ce207f599..94801af62 100755
--- a/src/gui/dialogs/formmain.cpp
+++ b/src/gui/dialogs/formmain.cpp
@@ -80,8 +80,6 @@ FormMain::FormMain(QWidget *parent, Qt::WindowFlags f)
   setupIcons();
   loadSize();
 
-  statusBar()->setVisible(false);
-
   // Initialize the web factory.
   WebFactory::instance()->loadState();
 }
@@ -107,6 +105,7 @@ QList<QAction*> 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
@@ -174,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();
@@ -328,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")));
 
@@ -410,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());
@@ -433,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();
 }
@@ -454,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..b392a808b 100755
--- a/src/gui/dialogs/formmain.ui
+++ b/src/gui/dialogs/formmain.ui
@@ -85,6 +85,7 @@
      <addaction name="m_actionSwitchMainMenu"/>
      <addaction name="m_actionSwitchToolBars"/>
      <addaction name="m_actionSwitchListHeaders"/>
+     <addaction name="m_actionSwitchStatusBar"/>
     </widget>
     <addaction name="m_menuShowHide"/>
     <addaction name="m_actionSwitchMainWindow"/>
@@ -742,6 +743,17 @@
     <string notr="true"/>
    </property>
   </action>
+  <action name="m_actionSwitchStatusBar">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Status bar</string>
+   </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>
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;
 

From c4a8497471315608e80e98b22048a25c4c06bc63 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 10:55:03 +0100
Subject: [PATCH 06/11] Refactored assembling methods, TT-RSS is now able to
 load stored feeds.

---
 src/core/rootitem.cpp                         | 20 +++++++
 src/core/rootitem.h                           |  1 +
 src/gui/dialogs/formmain.ui                   |  3 ++
 src/services/abstract/serviceroot.cpp         | 45 ++++++++++++++++
 src/services/abstract/serviceroot.h           |  9 ++++
 src/services/standard/standardfeed.cpp        |  3 +-
 src/services/standard/standardserviceroot.cpp | 54 +++----------------
 src/services/standard/standardserviceroot.h   | 11 ----
 src/services/tt-rss/ttrsscategory.cpp         | 11 ++++
 src/services/tt-rss/ttrsscategory.h           |  3 ++
 src/services/tt-rss/ttrssfeed.cpp             | 10 ++++
 src/services/tt-rss/ttrssfeed.h               |  3 ++
 src/services/tt-rss/ttrssserviceroot.cpp      | 43 +++++++++++++++
 src/services/tt-rss/ttrssserviceroot.h        |  2 +
 14 files changed, 157 insertions(+), 61 deletions(-)
 mode change 100644 => 100755 src/services/tt-rss/ttrsscategory.cpp
 mode change 100644 => 100755 src/services/tt-rss/ttrsscategory.h

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<Category*> RootItem::getSubTreeCategories() {
   return children;
 }
 
+QHash<int,Category*> RootItem::getHashedSubTreeCategories() {
+  QHash<int,Category*> children;
+  QList<RootItem*> 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<Feed*> RootItem::getSubTreeFeeds() {
   QList<Feed*> children;
   QList<RootItem*> 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<RootItem*> getSubTree();
     QList<RootItem*> getSubTree(RootItemKind::Kind kind_of_item);
     QList<Category*> getSubTreeCategories();
+    QHash<int,Category*> getHashedSubTreeCategories();
     QList<Feed*> getSubTreeFeeds();
 
     // Returns the service root node which is direct or indirect parent of current item.
diff --git a/src/gui/dialogs/formmain.ui b/src/gui/dialogs/formmain.ui
index b392a808b..7e9a1ef3c 100755
--- a/src/gui/dialogs/formmain.ui
+++ b/src/gui/dialogs/formmain.ui
@@ -753,6 +753,9 @@
    <property name="text">
     <string>Status bar</string>
    </property>
+   <property name="shortcut">
+    <string notr="true"/>
+   </property>
   </action>
  </widget>
  <customwidgets>
diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp
index cd25632b1..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 <QSqlQuery>
 
@@ -89,3 +90,47 @@ int ServiceRoot::accountId() const {
 void ServiceRoot::setAccountId(int account_id) {
   m_accountId = account_id;
 }
+
+void ServiceRoot::assembleFeeds(Assignment feeds) {
+  QHash<int,Category*> 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<int,RootItem*> 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 ed0295daa..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<QPair<int,RootItem*> > Assignment;
+typedef QPair<int,RootItem*> 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.
@@ -149,6 +153,11 @@ 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<RootItem*> items);
diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp
index a0f4332f3..0b22d15a9 100755
--- a/src/services/standard/standardfeed.cpp
+++ b/src/services/standard/standardfeed.cpp
@@ -763,7 +763,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 +781,6 @@ StandardFeed::StandardFeed(const QSqlRecord &record) : Feed(NULL) {
   }
 
 
-  setAutoUpdateType(static_cast<StandardFeed::AutoUpdateType>(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
+  setAutoUpdateType(static_cast<Feed::AutoUpdateType>(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
   setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
 }
diff --git a/src/services/standard/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp
index e86be599b..6a6c44884 100755
--- a/src/services/standard/standardserviceroot.cpp
+++ b/src/services/standard/standardserviceroot.cpp
@@ -331,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);
@@ -344,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());
 
@@ -369,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<StandardFeed*>(pair.second)->setType(type);
 
         feeds << pair;
         break;
@@ -419,6 +419,7 @@ QHash<int,StandardCategory*> StandardServiceRoot::categoriesForItem(RootItem *ro
 }
 
 QHash<int,StandardCategory*> StandardServiceRoot::allCategories() {
+  // TODO: změnit na qlist, použít getsubtree možná
   return categoriesForItem(this);
 }
 
@@ -436,26 +437,6 @@ QList<QAction*> StandardServiceRoot::getContextMenuForFeed(StandardFeed *feed) {
   return m_feedContextMenu;
 }
 
-void StandardServiceRoot::assembleFeeds(FeedAssignment feeds) {
-  QHash<int,StandardCategory*> 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<RootItem*> original_parents; original_parents.push(this);
   QStack<RootItem*> new_parents; new_parents.push(model->rootItem());
@@ -696,26 +677,3 @@ bool StandardServiceRoot::onAfterMessagesRestoredFromBin(RootItem *selected_item
   requestFeedReadFilterReload();
   return true;
 }
-
-void StandardServiceRoot::assembleCategories(CategoryAssignment categories) {
-  QHash<int,RootItem*> 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<QPair<int, StandardCategory*> > CategoryAssignment;
-typedef QPair<int, StandardCategory*> CategoryAssignmentItem;
-
-typedef QList<QPair<int, StandardFeed*> > FeedAssignment;
-typedef QPair<int, StandardFeed*> 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<Feed *> &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/ttrsscategory.cpp b/src/services/tt-rss/ttrsscategory.cpp
old mode 100644
new mode 100755
index 7b485288e..9334a347d
--- a/src/services/tt-rss/ttrsscategory.cpp
+++ b/src/services/tt-rss/ttrsscategory.cpp
@@ -18,11 +18,22 @@
 #include "services/tt-rss/ttrsscategory.h"
 
 #include "definitions/definitions.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/iconfactory.h"
+
+#include <QVariant>
 
 
 TtRssCategory::TtRssCategory(RootItem *parent) : Category(parent), m_customId(NO_PARENT_CATEGORY) {
 }
 
+TtRssCategory::TtRssCategory(const QSqlRecord &record) : Category(NULL) {
+  setId(record.value(CAT_DB_ID_INDEX).toInt());
+  setTitle(record.value(CAT_DB_TITLE_INDEX).toString());
+  setIcon(qApp->icons()->fromByteArray(record.value(CAT_DB_ICON_INDEX).toByteArray()));
+  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 <QSqlRecord>
+
 
 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
index 5c8dc0810..f45cd427f 100755
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -20,6 +20,7 @@
 #include "definitions/definitions.h"
 #include "miscellaneous/application.h"
 #include "miscellaneous/databasefactory.h"
+#include "miscellaneous/iconfactory.h"
 #include "services/tt-rss/ttrssserviceroot.h"
 
 #include <QSqlQuery>
@@ -29,6 +30,15 @@ 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<Feed::AutoUpdateType>(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() {
 }
 
diff --git a/src/services/tt-rss/ttrssfeed.h b/src/services/tt-rss/ttrssfeed.h
index a66335781..a934f9743 100755
--- a/src/services/tt-rss/ttrssfeed.h
+++ b/src/services/tt-rss/ttrssfeed.h
@@ -20,12 +20,15 @@
 
 #include "services/abstract/feed.h"
 
+#include <QSqlRecord>
+
 
 class TtRssServiceRoot;
 
 class TtRssFeed : public Feed {
   public:
     explicit TtRssFeed(RootItem *parent = NULL);
+    explicit TtRssFeed(const QSqlRecord &record);
     virtual ~TtRssFeed();
 
     TtRssServiceRoot *serviceRoot();
diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp
index 50eeb2bd3..32882d7c6 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -30,6 +30,7 @@
 #include <QSqlQuery>
 #include <QSqlError>
 #include <QPointer>
+#include <QPair>
 
 
 TtRssServiceRoot::TtRssServiceRoot(RootItem *parent)
@@ -220,6 +221,48 @@ 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() {
diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h
index a549d1374..5ff7cffb6 100755
--- a/src/services/tt-rss/ttrssserviceroot.h
+++ b/src/services/tt-rss/ttrssserviceroot.h
@@ -23,6 +23,8 @@
 #include <QCoreApplication>
 
 
+class TtRssCategory;
+class TtRssFeed;
 class TtRssNetworkFactory;
 
 class TtRssServiceRoot : public ServiceRoot {

From 523adcf4b7d39d93544dec3a900262658620d4b3 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 11:00:43 +0100
Subject: [PATCH 07/11] Fixed icons.

---
 src/services/tt-rss/network/ttrssnetworkfactory.cpp | 1 -
 src/services/tt-rss/ttrsscategory.cpp               | 3 ++-
 src/services/tt-rss/ttrssfeed.cpp                   | 1 +
 src/services/tt-rss/ttrssserviceroot.cpp            | 6 +-----
 4 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.cpp b/src/services/tt-rss/network/ttrssnetworkfactory.cpp
index 5915f545e..5dcfb7b8b 100755
--- a/src/services/tt-rss/network/ttrssnetworkfactory.cpp
+++ b/src/services/tt-rss/network/ttrssnetworkfactory.cpp
@@ -253,7 +253,6 @@ RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QS
           else {
             TtRssCategory *category = new TtRssCategory();
 
-            category->setIcon(qApp->icons()->fromTheme(QSL("folder-category")));
             category->setTitle(item["name"].toString());
             category->setCustomId(item_id);
             act_parent->appendChild(category);
diff --git a/src/services/tt-rss/ttrsscategory.cpp b/src/services/tt-rss/ttrsscategory.cpp
index 9334a347d..7dc127ea6 100755
--- a/src/services/tt-rss/ttrsscategory.cpp
+++ b/src/services/tt-rss/ttrsscategory.cpp
@@ -25,12 +25,13 @@
 
 
 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());
-  setIcon(qApp->icons()->fromByteArray(record.value(CAT_DB_ICON_INDEX).toByteArray()));
   setCustomId(record.value(CAT_DB_CUSTOM_ID_INDEX).toInt());
 }
 
diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp
index f45cd427f..9b6916c86 100755
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -81,6 +81,7 @@ int TtRssFeed::countOfUnreadMessages() {
 }
 
 int TtRssFeed::update() {
+  // TODO: přes getHeadlines provede stažení kompletnich zprav.
   return 0;
 }
 
diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp
index 32882d7c6..532610aa7 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -49,8 +49,7 @@ void TtRssServiceRoot::start() {
   loadFromDatabase();
 
   if (childCount() == 0) {
-    // TODO: pokud tady není nic načteno, tak
-    // syncIn
+    syncIn();
   }
 }
 
@@ -276,9 +275,6 @@ 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 err;
   TtRssGetFeedsCategoriesResponse feed_cats_response = m_network->getFeedsCategories(err);
 

From eb1eef3a504afe4ebe185816fc5242a4d489b28a Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 11:13:35 +0100
Subject: [PATCH 08/11] Added ability to load messages.

---
 src/services/tt-rss/ttrssserviceroot.cpp | 20 +++++++++++++++++++-
 src/services/tt-rss/ttrssserviceroot.h   |  4 ++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp
index 532610aa7..5ac50a600 100755
--- a/src/services/tt-rss/ttrssserviceroot.cpp
+++ b/src/services/tt-rss/ttrssserviceroot.cpp
@@ -27,6 +27,7 @@
 #include "services/tt-rss/network/ttrssnetworkfactory.h"
 #include "services/tt-rss/gui/formeditaccount.h"
 
+#include <QSqlTableModel>
 #include <QSqlQuery>
 #include <QSqlError>
 #include <QPointer>
@@ -118,7 +119,13 @@ RecycleBin *TtRssServiceRoot::recycleBin() {
 }
 
 bool TtRssServiceRoot::loadMessagesForItem(RootItem *item, QSqlTableModel *model) {
-  return false;
+  QList<Feed*> 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<QAction*> TtRssServiceRoot::serviceMenu() {
@@ -305,6 +312,17 @@ void TtRssServiceRoot::syncIn() {
   }
 }
 
+QStringList TtRssServiceRoot::textualFeedIds(const QList<Feed*> &feeds) {
+  QStringList stringy_ids;
+  stringy_ids.reserve(feeds.size());
+
+  foreach (Feed *feed, feeds) {
+    stringy_ids.append(QString("'%1'").arg(QString::number(static_cast<TtRssFeed*>(feed)->customId())));
+  }
+
+  return stringy_ids;
+}
+
 void TtRssServiceRoot::removeOldFeedTree() {
   QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
   QSqlQuery query(database);
diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h
index 5ff7cffb6..56519886f 100755
--- a/src/services/tt-rss/ttrssserviceroot.h
+++ b/src/services/tt-rss/ttrssserviceroot.h
@@ -75,6 +75,10 @@ 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<Feed*> &feeds);
+
     void removeOldFeedTree();
     void cleanAllItems();
     void storeNewFeedTree(RootItem *root);

From c9bb406a509cd88f0a46be20ff2f27656fdef85a Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 12:12:39 +0100
Subject: [PATCH 09/11] Added undeletedMessages() implementation.

---
 src/services/standard/standardfeed.cpp |  3 ++-
 src/services/tt-rss/ttrssfeed.cpp      | 33 +++++++++++++++++++++++++-
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp
index 0b22d15a9..c7ea6fecc 100755
--- a/src/services/standard/standardfeed.cpp
+++ b/src/services/standard/standardfeed.cpp
@@ -135,7 +135,7 @@ QList<Message> 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<Message> StandardFeed::undeletedMessages() const {
       message.m_author = query_read_msg.value(2).toString();
       message.m_created = TextFactory::parseDateTime(query_read_msg.value(3).value<qint64>());
       message.m_contents = query_read_msg.value(4).toString();
+      message.m_enclosures = Enclosures::decodeEnclosuresFromString(query_read_msg.value(5).toString());
 
       messages.append(message);
     }
diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp
index 9b6916c86..849b67ace 100755
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -21,6 +21,7 @@
 #include "miscellaneous/application.h"
 #include "miscellaneous/databasefactory.h"
 #include "miscellaneous/iconfactory.h"
+#include "miscellaneous/textfactory.h"
 #include "services/tt-rss/ttrssserviceroot.h"
 
 #include <QSqlQuery>
@@ -86,7 +87,37 @@ int TtRssFeed::update() {
 }
 
 QList<Message> TtRssFeed::undeletedMessages() const {
-  return QList<Message>();
+  QList<Message> messages;
+  int account_id = const_cast<TtRssFeed*>(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 "
+                         "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 = 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<qint64>());
+      message.m_contents = query_read_msg.value(4).toString();
+      message.m_enclosures = Enclosures::decodeEnclosuresFromString(query_read_msg.value(5).toString());
+
+      messages.append(message);
+    }
+  }
+
+  return messages;
 }
 
 int TtRssFeed::customId() const {

From 34e2f702190b1c58389f01e329f08bf557e43e25 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 13:37:26 +0100
Subject: [PATCH 10/11] Working on TT-RSS messages obtaining.

---
 src/core/message.cpp                          |  4 +-
 src/core/message.h                            |  5 +-
 src/core/messagesmodel.cpp                    |  3 +-
 src/services/abstract/feed.h                  |  1 +
 src/services/tt-rss/definitions.h             |  3 +
 .../tt-rss/network/ttrssnetworkfactory.cpp    | 75 +++++++++++++++++++
 .../tt-rss/network/ttrssnetworkfactory.h      | 15 ++++
 src/services/tt-rss/ttrssfeed.cpp             | 36 ++++++++-
 src/services/tt-rss/ttrssfeed.h               |  2 +
 9 files changed, 137 insertions(+), 7 deletions(-)

diff --git a/src/core/message.cpp b/src/core/message.cpp
index 0338a5477..6c8833400 100755
--- a/src/core/message.cpp
+++ b/src/core/message.cpp
@@ -61,7 +61,7 @@ QString Enclosures::encodeEnclosuresToString(const QList<Enclosure> &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<Enclosure>();
+  m_isImportant = 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<Enclosure> 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<qint64>()).toLocalTime();
 
   return message;
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/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/network/ttrssnetworkfactory.cpp b/src/services/tt-rss/network/ttrssnetworkfactory.cpp
index 5dcfb7b8b..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 <QPair>
@@ -133,6 +134,37 @@ TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories(QNetwork
   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;
+  return result;
+}
+
 TtRssResponse::TtRssResponse(const QString &raw_content) {
   m_rawContent = QtJson::parse(raw_content).toMap();
 }
@@ -296,3 +328,46 @@ RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QS
 
   return parent;
 }
+
+
+TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString &raw_content) : TtRssResponse(raw_content) {
+}
+
+TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() {
+}
+
+QList<Message> TtRssGetHeadlinesResponse::messages() {
+  QList<Message> messages;
+
+  foreach (QVariant item, m_rawContent["content"].toList()) {
+    QMap<QString,QVariant> 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<qint64>());
+    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<QString,QVariant> 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 8e76f3b09..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 <QString>
 #include <QPair>
 #include <QNetworkReply>
@@ -64,6 +66,14 @@ class TtRssGetFeedsCategoriesResponse : public TtRssResponse {
     RootItem *feedsCategories(bool obtain_icons, QString base_address = QString());
 };
 
+class TtRssGetHeadlinesResponse : public TtRssResponse {
+  public:
+    explicit TtRssGetHeadlinesResponse(const QString &raw_content = QString());
+    virtual ~TtRssGetHeadlinesResponse();
+
+    QList<Message> messages();
+};
+
 class TtRssNetworkFactory {
   public:
     explicit TtRssNetworkFactory();
@@ -89,6 +99,11 @@ class TtRssNetworkFactory {
     // Gets feeds from the server.
     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;
     QString m_username;
diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp
index 849b67ace..7a8c64383 100755
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -22,7 +22,9 @@
 #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 <QSqlQuery>
 
@@ -83,7 +85,30 @@ int TtRssFeed::countOfUnreadMessages() {
 
 int TtRssFeed::update() {
   // TODO: přes getHeadlines provede stažení kompletnich zprav.
-  return 0;
+  QNetworkReply::NetworkError error;
+  QList<Message> 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<Message> new_messages = headlines.messages();
+
+      messages.append(new_messages);
+      skip += new_messages.size();
+    }
+  }
+  while (newly_added_messages > 0);
+
+  return updateMessages(messages);
 }
 
 QList<Message> TtRssFeed::undeletedMessages() const {
@@ -92,7 +117,7 @@ QList<Message> TtRssFeed::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, enclosures "
+  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;");
 
@@ -105,13 +130,14 @@ QList<Message> TtRssFeed::undeletedMessages() const {
     while (query_read_msg.next()) {
       Message message;
 
-      message.m_feedId = account_id;
+      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<qint64>());
       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);
     }
@@ -127,3 +153,7 @@ int TtRssFeed::customId() const {
 void TtRssFeed::setCustomId(int custom_id) {
   m_customId = custom_id;
 }
+
+int TtRssFeed::updateMessages(const QList<Message> &messages) {
+  return 0;
+}
diff --git a/src/services/tt-rss/ttrssfeed.h b/src/services/tt-rss/ttrssfeed.h
index a934f9743..92fae6ef2 100755
--- a/src/services/tt-rss/ttrssfeed.h
+++ b/src/services/tt-rss/ttrssfeed.h
@@ -45,6 +45,8 @@ class TtRssFeed : public Feed {
     void setCustomId(int custom_id);
 
   private:
+    int updateMessages(const QList<Message> &messages);
+
     int m_customId;
     int m_totalCount;
     int m_unreadCount;

From 44528a79d751f5df0f846f3d846f750dae926411 Mon Sep 17 00:00:00 2001
From: Martin Rotter <rotter.martinos@gmail.com>
Date: Tue, 8 Dec 2015 13:44:57 +0100
Subject: [PATCH 11/11] Work on msgs gettttttttttttttttttttting.

---
 src/services/tt-rss/ttrssfeed.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp
index 7a8c64383..33c7f1bc6 100755
--- a/src/services/tt-rss/ttrssfeed.cpp
+++ b/src/services/tt-rss/ttrssfeed.cpp
@@ -103,7 +103,8 @@ int TtRssFeed::update() {
       QList<Message> new_messages = headlines.messages();
 
       messages.append(new_messages);
-      skip += new_messages.size();
+      newly_added_messages = new_messages.size();
+      skip += newly_added_messages;
     }
   }
   while (newly_added_messages > 0);
@@ -155,5 +156,7 @@ void TtRssFeed::setCustomId(int custom_id) {
 }
 
 int TtRssFeed::updateMessages(const QList<Message> &messages) {
+  // TODO: pokračovat tady
+
   return 0;
 }