Source reorganization.

Leonardo Robol [2011-10-24 06:10]
Source reorganization.
Filename
Larss.pro
feedmodel.cpp
feedmodel.h
feedpoller.cpp
feedpoller.h
include/feedmodel.h
include/feedpoller.h
include/mainwindow.h
include/rssparser.h
larss/feedmodel.cpp
larss/feedpoller.cpp
larss/main.cpp
larss/mainwindow.cpp
larss/rssparser.cpp
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
rssparser.cpp
rssparser.h
ui/mainwindow.ui
diff --git a/Larss.pro b/Larss.pro
index 74bcf7e..006626b 100644
--- a/Larss.pro
+++ b/Larss.pro
@@ -10,15 +10,15 @@ TARGET = Larss
 TEMPLATE = app


-SOURCES += main.cpp\
-        mainwindow.cpp \
-    feedmodel.cpp \
-    rssparser.cpp \
-    feedpoller.cpp
+SOURCES += larss/main.cpp\
+        larss/mainwindow.cpp \
+    larss/feedmodel.cpp \
+    larss/rssparser.cpp \
+    larss/feedpoller.cpp

-HEADERS  += mainwindow.h \
-    feedmodel.h \
-    rssparser.h \
-    feedpoller.h
+HEADERS  += include/mainwindow.h \
+    include/feedmodel.h \
+    include/rssparser.h \
+    include/feedpoller.h

-FORMS    += mainwindow.ui
+FORMS    += ui/mainwindow.ui
diff --git a/feedmodel.cpp b/feedmodel.cpp
deleted file mode 100644
index 7382f1e..0000000
--- a/feedmodel.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-#include "feedmodel.h"
-#include <QAbstractItemModel>
-#include <QSqlDatabase>
-#include <QSqlQuery>
-#include <QSqlError>
-#include <QDebug>
-#include <QStringList>
-#include <QLabel>
-
-using namespace Larss;
-
-FeedModel::FeedModel(QSqlDatabase db, QObject *parent) : QAbstractItemModel (parent)
-{
-    this->db = db;
-
-    // Check that the right tables are present in the db
-    if (!db.tables().contains("categories"))
-    {
-        QSqlQuery query(db);
-        query.prepare("CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT);");
-        if (!query.exec())
-            qDebug() << "Error while creating the categories table in the db";
-    }
-
-    if (!db.tables().contains("feeds"))
-    {
-        QSqlQuery query(db);
-        query.prepare("CREATE TABLE feeds (id INTEGER PRIMARY KEY, category INTEGER, name TEXT, url TEXT);");
-        if (!query.exec())
-            qDebug() << "Error while creating the feeds table in the db";
-    }
-}
-
-FeedModel::~FeedModel()
-{
-}
-
-
-QModelIndex
-FeedModel::index(int row, int column, const QModelIndex &parent) const
-{
-    if (parent.internalId() == 0)
-    {
-        QSqlQuery query (db);
-        query.prepare("SELECT id from categories ORDER by id;");
-        if (query.exec())
-        {
-            if (!query.first())
-                return QModelIndex();
-            for (int i = 0; i < row; i++)
-            {
-                if (!query.next ())
-                    return QModelIndex();
-            }
-            return createIndex(row, column, query.value(0).toInt());
-        }
-        else
-            return QModelIndex();
-    }
-    else
-    {
-        QSqlQuery query(db);
-        query.prepare ("SELECT id from feeds WHERE category=:category ORDER BY id;");
-        query.bindValue("category", parent.internalId());
-        if (query.exec())
-        {
-            if (!query.first())
-                return QModelIndex();
-            else
-            {
-                for(int i = 0; i < row; i++)
-                {
-                    if (!query.next())
-                        return QModelIndex();
-                }
-                return createIndex(row, column, query.value(0).toInt() + FEEDMODEL_MAX_CATEGORIES);
-            }
-        }
-        else
-            return QModelIndex();
-    }
-}
-
-bool
-FeedModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
-    if (role != Qt::EditRole)
-        return false;
-
-    QSqlQuery query(db);
-
-    if (index.internalId() < FEEDMODEL_MAX_CATEGORIES && index.internalId() != 0)
-    {
-        // We are trying to modify a category
-        query.prepare("UPDATE categories SET name=:value WHERE id=:id;");
-        query.bindValue("value", value.toString());
-        query.bindValue("id", index.internalId());
-    }
-    else
-    {
-        // We are trying to modify a feed
-        query.prepare("UPDATE feeds SET name=:value WHERE id=:id;");
-        query.bindValue("value", value.toString());
-        query.bindValue("id", index.internalId() - FEEDMODEL_MAX_CATEGORIES);
-    }
-
-    if (!query.exec())
-    {
-        qDebug() << "Query failed" << query.lastError() << query.executedQuery();
-        return false;
-    }
-    else
-    {
-        // Emit the datachanged signal
-        dataChanged(index, index);
-        return true;
-    }
-}
-
-Qt::ItemFlags
-FeedModel::flags(const QModelIndex &index) const
-{
-    Q_UNUSED(index);
-    return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
-}
-
-int
-FeedModel::rowCount(const QModelIndex &parent) const
-{
-    if (!parent.isValid())
-    {
-        // Categories count
-        QSqlQuery query(db);
-        query.prepare ("SELECT id from categories ORDER by id;");
-        if (query.exec())
-        {
-            int row_count = 1;
-            if (!query.first())
-                return 0;
-            else
-                while (query.next())
-                    row_count++;
-            return row_count;
-        }
-        else
-            return 0;
-    }
-    else
-    {
-        int category_id = parent.internalId();
-        QSqlQuery query(db);
-        query.prepare("SELECT id from feeds where category=:category;");
-        query.bindValue("category", category_id);
-        if (query.exec())
-        {
-            int row_count = 1;
-            if (!query.first())
-                return 0;
-            else
-                while (query.next())
-                    row_count++;
-            return row_count;
-        }
-        else
-            return 0;
-    }
-}
-
-bool
-FeedModel::addCategory(QString name)
-{
-    QSqlQuery query(db);
-    query.prepare("INSERT INTO categories VALUES (NULL, :name);");
-    query.bindValue("name", name);
-
-    bool successful = query.exec();
-    if (successful)
-        reset();
-    return successful;
-}
-
-bool
-FeedModel::addFeed(QString name, QString url, quint32 category_id)
-{
-    QSqlQuery query(db);
-    query.prepare("INSERT INTO feeds VALUES (NULL, :category, :name, :url);");
-    query.bindValue("category", category_id);
-    query.bindValue("name", name);
-    query.bindValue("url", url);
-
-    bool successful = query.exec();
-    if (successful)
-        reset();
-    return successful;
-}
-
-int
-FeedModel::columnCount(const QModelIndex &parent) const
-{
-    Q_UNUSED(parent);
-    return 1;
-}
-
-QVariant
-FeedModel::data(const QModelIndex &index, int role) const
-{
-    if (role == Qt::DisplayRole)
-    {
-        if (index.internalId() == 0)
-            return QString ("Root");
-        if (index.internalId() < FEEDMODEL_MAX_CATEGORIES)
-        {
-            QSqlQuery query(db);
-            query.prepare ("SELECT id, name from categories WHERE id=:category;");
-            query.bindValue("category", index.internalId());
-            if (query.exec())
-            {
-                if (!query.first())
-                    return QVariant(QVariant::Invalid);
-                else
-                {
-                    return query.value(1).toString();
-                }
-            }
-            else
-                return QVariant(QVariant::Invalid);
-        }
-        else
-        {
-            QSqlQuery query(db);
-            query.prepare ("SELECT id, category, name, url from feeds WHERE id=:feed;");
-            query.bindValue("feed", index.internalId() - FEEDMODEL_MAX_CATEGORIES);
-            if (query.exec())
-            {
-                if (query.first())
-                    return query.value(2).toString();
-                else
-                    return QVariant(QVariant::Invalid);
-            }
-            else
-                return QVariant(QVariant::Invalid);
-        }
-    }
-    else
-        return QVariant (QVariant::Invalid);
-}
-
-QModelIndex
-FeedModel::parent(const QModelIndex &child) const
-{
-    if (!child.isValid())
-        return QModelIndex ();
-
-    quint32 row;
-    quint32 id = child.internalId();
-
-    if (id == 0)
-        return QModelIndex();
-    else if (id < FEEDMODEL_MAX_CATEGORIES)
-    {
-        // Get the position of the category
-        QSqlQuery query (db);
-        query.prepare ("SELECT id from category;");
-        if (query.exec ())
-        {
-            if (query.first ())
-                row = 1;
-            else
-                return QModelIndex ();
-            while (query.next ())
-            {
-                row++;
-                if ((quint64) query.value(0).toInt() == id)
-                    break;
-            }
-
-            return createIndex (row, 1, 0);
-        }
-        else
-            return QModelIndex();
-    }
-    else
-    {
-        quint32 category_id;
-        // We have a feed here, that actually has a real parent.
-        // We need to get the ID of the category
-        id -= FEEDMODEL_MAX_CATEGORIES;
-        QSqlQuery query (db);
-        query.prepare ("SELECT category from feeds WHERE id=:id;");
-        query.bindValue("id", id);
-        if (query.exec())
-        {
-            if (!query.first ())
-                return QModelIndex();
-            else
-            {
-                category_id = query.value(0).toInt();
-
-                // We need to get the position of the feed in the category
-                query.prepare("SELECT id from feeds WHERE category=:category;");
-                query.bindValue("category", category_id);
-                if (query.exec())
-                {
-                    row = 1;
-                    if (!query.first())
-                        return QModelIndex();
-                    else
-                    {
-                        while (query.next())
-                            row++;
-                        return createIndex(row, 1, category_id);
-                    }
-                }
-                else
-                    return QModelIndex();
-            }
-        }
-        else
-            return QModelIndex();
-    }
-}
-
-QVariant
-FeedModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
-    // We have a header data only for the first column, horizontal mode.
-    if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section == 0)
-        return tr("Feed");
-    else
-        return QVariant ();
-}
-
-QString
-FeedModel::getUrl(const QModelIndex &index)
-{
-    quint64 id = index.internalId();
-    if (id < FEEDMODEL_MAX_CATEGORIES)
-        return "";
-    else
-    {
-        QSqlQuery query(db);
-        query.prepare("SELECT url from feeds WHERE id=:id;");
-        query.bindValue("id", id - FEEDMODEL_MAX_CATEGORIES);
-        if (query.exec())
-        {
-            if (query.first())
-                return query.value(0).toString();
-            else
-                return "";
-        }
-        else
-            return "";
-    }
-}
-
diff --git a/feedmodel.h b/feedmodel.h
deleted file mode 100644
index 6ead440..0000000
--- a/feedmodel.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef FEEDMODEL_H
-#define FEEDMODEL_H
-
-#include <QObject>
-#include <QAbstractItemModel>
-#include <QSqlDatabase>
-#include <QtXml>
-
-namespace Larss {
-
-#define FEEDMODEL_MAX_CATEGORIES 1024
-
-class FeedModel : public QAbstractItemModel {
-    Q_OBJECT
-
-public:
-    explicit FeedModel(QSqlDatabase db, QObject *parent = 0);
-
-    /**
-     * @brief Destructor for the FeedModel
-     */
-    ~FeedModel();
-
-    /**
-     * @brief Get the ModelIndex associated with a given row and column.
-     */
-    QModelIndex index(int row, int column, const QModelIndex &parent) const;
-
-    /**
-     * @brief Get the number of rows in the list.
-     */
-    int rowCount(const QModelIndex &parent) const;
-
-    /**
-     * @brief Get the number of column
-     */
-    int columnCount(const QModelIndex &parent) const;
-
-    /**
-     * @brief Get the data associated to a given node.
-     */
-    QVariant data(const QModelIndex &index, int role) const;
-
-    /**
-     * @brief Get the parent of a given node.
-     */
-    QModelIndex parent(const QModelIndex &child) const;
-
-    /**
-     * @brief Return the flags for the given item.
-     */
-    Qt::ItemFlags flags(const QModelIndex &index) const;
-
-    /**
-     * @brief Return the data to be inserted in the column header of the treeview.
-     */
-    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-
-    /**
-     * @brief Return the URL associated with a given ModelIndex
-     */
-    QString getUrl (const QModelIndex& index);
-
-    /**
-     * @brief Method used to change the data in the database
-     */
-    bool setData(const QModelIndex &index, const QVariant &value, int role);
-
-    /**
-     * @brief Add a category.
-     */
-    bool addCategory (QString name);
-
-    /**
-     * @brief Add a new feed in the specified category.
-     */
-    bool addFeed (QString name, QString url, quint32 category_id);
-
-signals:
-
-public slots:
-
-private:
-    /**
-     * @brief Database containing the data of the feeds.
-     */
-    QSqlDatabase db;
-
-
-};
-
-}
-
-#endif // FEEDMODEL_H
diff --git a/feedpoller.cpp b/feedpoller.cpp
deleted file mode 100644
index 11a6834..0000000
--- a/feedpoller.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-#include "feedpoller.h"
-#include <QtXml>
-#include "feedmodel.h"
-
-using namespace Larss;
-
-FeedPoller::FeedPoller(QObject *parent, RssParser *parser, FeedModel *model) :
-    QThread(parent)
-{
-    this->parser = parser;
-    this->model = model;
-
-    workQueue = new QList<QModelIndex> ();
-    nowLoading = 0;
-
-    rssContent = new QHash<quint32, QString>();
-    poll_active = true;
-
-    // Create the QNetworkAccessManager and connect the loaded signal
-    // with our handler.
-    manager = new QNetworkAccessManager ();
-    manager->connect(manager, SIGNAL(finished(QNetworkReply*)),
-                     this, SLOT(networkManagerReplyFinished(QNetworkReply*)));
-}
-
-void
-FeedPoller::run()
-{
-
-    // Create the timer that will call the function every second.
-    QTimer *timer = new QTimer ();
-    timer->setInterval(1000);
-    timer->connect(timer, SIGNAL(timeout()),
-                   this, SLOT(poll()));
-    timer->start();
-
-    QThread::exec();
-}
-
-bool
-FeedPoller::poll()
-{
-    // Poll indefinitely until we are requested to exit.
-    if (nowLoading == 0)
-    {
-        if (workQueue->isEmpty())
-            return false;
-        else
-        {
-            QModelIndex next_item = workQueue->takeFirst();
-            nowLoading = next_item.internalId();
-            manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
-            return true;
-        }
-    }
-}
-
-void
-FeedPoller::stopPolling ()
-{
-    poll_active = false;
-    QThread::exit();
-}
-
-void
-FeedPoller::queueWork(const QModelIndex &index)
-{
-    if (index.internalId() < FEEDMODEL_MAX_CATEGORIES)
-        return;
-    workQueue->append(index);
-}
-
-void
-FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
-{
-    // Assume that the string is UTF-8 encoded. This is likely to be
-    // true, but I should check it in some way.
-    rssContent->insert (nowLoading, QString::fromUtf8(reply->readAll()));
-
-    // Now update the database with the new data obtained.
-    QDomDocument doc;
-    if (doc.setContent(rssContent->value(nowLoading)))
-    {
-        // Try to catch other news_feed with the same link, so preload all of them.
-        QSqlQuery query(parser->db);
-        query.prepare ("SELECT link from news WHERE feed=:feed");
-        query.bindValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
-        if (!query.exec ())
-            return;
-
-        QStringList links;
-        if (query.first())
-        {
-            links.append(query.value(0).toString());
-            while (query.next())
-                links.append(query.value(0).toString());
-        }
-
-        QDomElement doc_el = doc.documentElement();
-        QDomNodeList items = doc_el.elementsByTagName("item");
-
-        for (quint32 i = 0; i < items.length(); i++)
-        {
-            // Get the i-th news
-            QDomNode item = items.item(i);
-            QDomElement element = item.toElement();
-
-            // Get the data in it
-            QString link = element.elementsByTagName("link").item(0).firstChild().nodeValue();
-            QString title = element.elementsByTagName("title").item(0).firstChild().nodeValue();
-            QString description = element.elementsByTagName("description").item(0).firstChild().nodeValue();
-            QString content = element.elementsByTagName("content:encoded").item(0).firstChild().nodeValue();
-
-            // We should enable this for RSS 2.0
-            // QString guid = element.elementsByTagName("guid").item(0).firstChild().nodeValue();
-            // QString pubDate = element.elementsByTagName("pubDate").item(0).firstChild().nodeValue();
-
-            if (!links.contains(link))
-            {
-                // That means that no results were found, so let's insert this one.
-                QSqlRecord record = parser->record();
-                record.setValue("time", 0);
-                record.setValue("read", 0);
-                record.setValue("title", title);
-                record.setValue("link", link);
-                record.setValue("description", description);
-                record.setValue("content", content);
-                record.setValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
-
-                if (!parser->insertRecord(-1, record))
-                    qDebug () << "Error inserting record";
-            }
-        }
-
-        if (!parser->submitAll())
-            qDebug() << "Error submitting new data";
-    }
-    else
-        qDebug () << "Error parsing the document";
-
-    nowLoading = 0;
-    return;
-
-    // Check if there is work in the queue
-    if (!workQueue->isEmpty())
-    {
-        QModelIndex next_item = workQueue->takeFirst();
-        nowLoading = next_item.internalId();
-        manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
-    }
-}
diff --git a/feedpoller.h b/feedpoller.h
deleted file mode 100644
index 4494249..0000000
--- a/feedpoller.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef FEEDPOLLER_H
-#define FEEDPOLLER_H
-
-#include <QObject>
-#include <QThread>
-#include <QtSql>
-#include <QtNetwork>
-#include "rssparser.h"
-#include "feedmodel.h"
-
-namespace Larss {
-
-    class FeedPoller : public QThread
-    {
-        Q_OBJECT
-    public:
-        explicit FeedPoller(QObject *parent, RssParser* parser, FeedModel *model);
-        void queueWork (const QModelIndex& index);
-        void stopPolling ();
-
-    signals:
-
-    public slots:
-        void networkManagerReplyFinished(QNetworkReply* reply);
-        bool poll();
-
-    private:
-        /**
-         * @brief The parser of this instance of Larss.
-         */
-        RssParser *parser;
-
-        /**
-         * @brief The FeedModel of this instance of Larss.
-         */
-        FeedModel *model;
-
-        /**
-         * @brief The content of the rss loaded from the various
-         * items in the feedmodel.
-         */
-        QHash<quint32, QString> *rssContent;
-
-        /**
-         * @brief The NetworkAccessManager that will be used to retrieve
-         * the data from sourceUrl.
-         */
-        QNetworkAccessManager *manager;
-
-        /**
-         * @brief An index of the RSS that is currently being loaded, or 0
-         * if there is nothing in the queue.
-         */
-        quint32 nowLoading;
-
-        /**
-         * @brief Queue of item that needs to be refreshed.
-         */
-        QList<QModelIndex> *workQueue;
-
-        /**
-         * @brief If the thread is requested to polling or not.
-         */
-        bool poll_active;
-
-        /**
-         * @brief Real work of the thread.
-         */
-        void run();
-
-    };
-}
-
-#endif // FEEDPOLLER_H
diff --git a/include/feedmodel.h b/include/feedmodel.h
new file mode 100644
index 0000000..6ead440
--- /dev/null
+++ b/include/feedmodel.h
@@ -0,0 +1,94 @@
+#ifndef FEEDMODEL_H
+#define FEEDMODEL_H
+
+#include <QObject>
+#include <QAbstractItemModel>
+#include <QSqlDatabase>
+#include <QtXml>
+
+namespace Larss {
+
+#define FEEDMODEL_MAX_CATEGORIES 1024
+
+class FeedModel : public QAbstractItemModel {
+    Q_OBJECT
+
+public:
+    explicit FeedModel(QSqlDatabase db, QObject *parent = 0);
+
+    /**
+     * @brief Destructor for the FeedModel
+     */
+    ~FeedModel();
+
+    /**
+     * @brief Get the ModelIndex associated with a given row and column.
+     */
+    QModelIndex index(int row, int column, const QModelIndex &parent) const;
+
+    /**
+     * @brief Get the number of rows in the list.
+     */
+    int rowCount(const QModelIndex &parent) const;
+
+    /**
+     * @brief Get the number of column
+     */
+    int columnCount(const QModelIndex &parent) const;
+
+    /**
+     * @brief Get the data associated to a given node.
+     */
+    QVariant data(const QModelIndex &index, int role) const;
+
+    /**
+     * @brief Get the parent of a given node.
+     */
+    QModelIndex parent(const QModelIndex &child) const;
+
+    /**
+     * @brief Return the flags for the given item.
+     */
+    Qt::ItemFlags flags(const QModelIndex &index) const;
+
+    /**
+     * @brief Return the data to be inserted in the column header of the treeview.
+     */
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+    /**
+     * @brief Return the URL associated with a given ModelIndex
+     */
+    QString getUrl (const QModelIndex& index);
+
+    /**
+     * @brief Method used to change the data in the database
+     */
+    bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+    /**
+     * @brief Add a category.
+     */
+    bool addCategory (QString name);
+
+    /**
+     * @brief Add a new feed in the specified category.
+     */
+    bool addFeed (QString name, QString url, quint32 category_id);
+
+signals:
+
+public slots:
+
+private:
+    /**
+     * @brief Database containing the data of the feeds.
+     */
+    QSqlDatabase db;
+
+
+};
+
+}
+
+#endif // FEEDMODEL_H
diff --git a/include/feedpoller.h b/include/feedpoller.h
new file mode 100644
index 0000000..4494249
--- /dev/null
+++ b/include/feedpoller.h
@@ -0,0 +1,74 @@
+#ifndef FEEDPOLLER_H
+#define FEEDPOLLER_H
+
+#include <QObject>
+#include <QThread>
+#include <QtSql>
+#include <QtNetwork>
+#include "rssparser.h"
+#include "feedmodel.h"
+
+namespace Larss {
+
+    class FeedPoller : public QThread
+    {
+        Q_OBJECT
+    public:
+        explicit FeedPoller(QObject *parent, RssParser* parser, FeedModel *model);
+        void queueWork (const QModelIndex& index);
+        void stopPolling ();
+
+    signals:
+
+    public slots:
+        void networkManagerReplyFinished(QNetworkReply* reply);
+        bool poll();
+
+    private:
+        /**
+         * @brief The parser of this instance of Larss.
+         */
+        RssParser *parser;
+
+        /**
+         * @brief The FeedModel of this instance of Larss.
+         */
+        FeedModel *model;
+
+        /**
+         * @brief The content of the rss loaded from the various
+         * items in the feedmodel.
+         */
+        QHash<quint32, QString> *rssContent;
+
+        /**
+         * @brief The NetworkAccessManager that will be used to retrieve
+         * the data from sourceUrl.
+         */
+        QNetworkAccessManager *manager;
+
+        /**
+         * @brief An index of the RSS that is currently being loaded, or 0
+         * if there is nothing in the queue.
+         */
+        quint32 nowLoading;
+
+        /**
+         * @brief Queue of item that needs to be refreshed.
+         */
+        QList<QModelIndex> *workQueue;
+
+        /**
+         * @brief If the thread is requested to polling or not.
+         */
+        bool poll_active;
+
+        /**
+         * @brief Real work of the thread.
+         */
+        void run();
+
+    };
+}
+
+#endif // FEEDPOLLER_H
diff --git a/include/mainwindow.h b/include/mainwindow.h
new file mode 100644
index 0000000..6c71c04
--- /dev/null
+++ b/include/mainwindow.h
@@ -0,0 +1,45 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include "rssparser.h"
+#include "feedmodel.h"
+#include "feedpoller.h"
+#include <QSqlDatabase>
+
+namespace Ui {
+    class MainWindow;
+}
+
+namespace Larss {
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindow(QWidget *parent = 0);
+    ~MainWindow();
+
+private slots:
+    void on_actionExit_activated();
+
+    void on_feedTreeView_clicked(const QModelIndex &index);
+
+    void on_newsTableView_clicked(const QModelIndex &index);
+
+    void on_newsTableView_activated(const QModelIndex &index);
+
+private:
+    Ui::MainWindow *ui;
+    void do_exit();
+
+    QSqlDatabase db;
+    FeedModel *feedModel;
+    RssParser *rssParser;
+    FeedPoller *poller;
+};
+
+}
+
+#endif // MAINWINDOW_H
diff --git a/include/rssparser.h b/include/rssparser.h
new file mode 100644
index 0000000..1070b43
--- /dev/null
+++ b/include/rssparser.h
@@ -0,0 +1,88 @@
+#ifndef RSSPARSER_H
+#define RSSPARSER_H
+
+#include <QObject>
+#include <QtNetwork/QNetworkAccessManager>
+#include <QList>
+#include <QSqlTableModel>
+#include "feedmodel.h"
+
+namespace Larss {
+
+    class RssParser : public QSqlTableModel
+    {
+        Q_OBJECT
+    public:
+        /**
+         * @brief RssParser constructor.
+         */
+        explicit RssParser(QSqlDatabase db, FeedModel * model = NULL, QObject *parent = 0);
+
+        /**
+         * @brief Default destructor.
+         */
+        ~RssParser();
+
+        /**
+         * @brief Function that tells the views that use this model
+         * what to display in the title of the columns.
+         */
+        QVariant headerData (int section, Qt::Orientation orientation, int role) const;
+
+        /**
+         * @brief Get the link associated with a given ModelIndex.
+         */
+        QString getLink (const QModelIndex& index);
+
+        /**
+         * @brief Get the id of the feed pointed by the QModelIndex or
+         * 0 if there is no feed pointed (i.e. is a category or
+         * the root of the tree).
+         */
+        quint64 getFeed (const QModelIndex& index);
+
+        /**
+         * @brief Get the content associated with the feed.
+         */
+        QString getContent (const QModelIndex& index);
+
+        /**
+         * @brief Get the title of a news pointed by index
+         */
+        QString getTitle (const QModelIndex& index);
+
+        /**
+         * @brief Set the read status on a news.
+         */
+        void setReadStatus (const QModelIndex& index, bool read);
+
+        /**
+         * @brief Reimplement data to make unread post bold.
+         */
+        QVariant data(const QModelIndex &idx, int role) const;
+
+        /**
+         * @brief Set the active category to display.
+         */
+        void selectActiveFeed (quint64 feed_id);
+
+        /**
+         * @brief Database where all the news will be loaded and saved.
+         */
+        QSqlDatabase db;
+
+    signals:
+
+    public slots:
+
+    private:
+
+        /**
+         * @brief The FeedModel
+         */
+        FeedModel *model;
+    };
+
+}
+
+#endif // RSSPARSER_H
diff --git a/larss/feedmodel.cpp b/larss/feedmodel.cpp
new file mode 100644
index 0000000..7382f1e
--- /dev/null
+++ b/larss/feedmodel.cpp
@@ -0,0 +1,355 @@
+#include "feedmodel.h"
+#include <QAbstractItemModel>
+#include <QSqlDatabase>
+#include <QSqlQuery>
+#include <QSqlError>
+#include <QDebug>
+#include <QStringList>
+#include <QLabel>
+
+using namespace Larss;
+
+FeedModel::FeedModel(QSqlDatabase db, QObject *parent) : QAbstractItemModel (parent)
+{
+    this->db = db;
+
+    // Check that the right tables are present in the db
+    if (!db.tables().contains("categories"))
+    {
+        QSqlQuery query(db);
+        query.prepare("CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT);");
+        if (!query.exec())
+            qDebug() << "Error while creating the categories table in the db";
+    }
+
+    if (!db.tables().contains("feeds"))
+    {
+        QSqlQuery query(db);
+        query.prepare("CREATE TABLE feeds (id INTEGER PRIMARY KEY, category INTEGER, name TEXT, url TEXT);");
+        if (!query.exec())
+            qDebug() << "Error while creating the feeds table in the db";
+    }
+}
+
+FeedModel::~FeedModel()
+{
+}
+
+
+QModelIndex
+FeedModel::index(int row, int column, const QModelIndex &parent) const
+{
+    if (parent.internalId() == 0)
+    {
+        QSqlQuery query (db);
+        query.prepare("SELECT id from categories ORDER by id;");
+        if (query.exec())
+        {
+            if (!query.first())
+                return QModelIndex();
+            for (int i = 0; i < row; i++)
+            {
+                if (!query.next ())
+                    return QModelIndex();
+            }
+            return createIndex(row, column, query.value(0).toInt());
+        }
+        else
+            return QModelIndex();
+    }
+    else
+    {
+        QSqlQuery query(db);
+        query.prepare ("SELECT id from feeds WHERE category=:category ORDER BY id;");
+        query.bindValue("category", parent.internalId());
+        if (query.exec())
+        {
+            if (!query.first())
+                return QModelIndex();
+            else
+            {
+                for(int i = 0; i < row; i++)
+                {
+                    if (!query.next())
+                        return QModelIndex();
+                }
+                return createIndex(row, column, query.value(0).toInt() + FEEDMODEL_MAX_CATEGORIES);
+            }
+        }
+        else
+            return QModelIndex();
+    }
+}
+
+bool
+FeedModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+    if (role != Qt::EditRole)
+        return false;
+
+    QSqlQuery query(db);
+
+    if (index.internalId() < FEEDMODEL_MAX_CATEGORIES && index.internalId() != 0)
+    {
+        // We are trying to modify a category
+        query.prepare("UPDATE categories SET name=:value WHERE id=:id;");
+        query.bindValue("value", value.toString());
+        query.bindValue("id", index.internalId());
+    }
+    else
+    {
+        // We are trying to modify a feed
+        query.prepare("UPDATE feeds SET name=:value WHERE id=:id;");
+        query.bindValue("value", value.toString());
+        query.bindValue("id", index.internalId() - FEEDMODEL_MAX_CATEGORIES);
+    }
+
+    if (!query.exec())
+    {
+        qDebug() << "Query failed" << query.lastError() << query.executedQuery();
+        return false;
+    }
+    else
+    {
+        // Emit the datachanged signal
+        dataChanged(index, index);
+        return true;
+    }
+}
+
+Qt::ItemFlags
+FeedModel::flags(const QModelIndex &index) const
+{
+    Q_UNUSED(index);
+    return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+}
+
+int
+FeedModel::rowCount(const QModelIndex &parent) const
+{
+    if (!parent.isValid())
+    {
+        // Categories count
+        QSqlQuery query(db);
+        query.prepare ("SELECT id from categories ORDER by id;");
+        if (query.exec())
+        {
+            int row_count = 1;
+            if (!query.first())
+                return 0;
+            else
+                while (query.next())
+                    row_count++;
+            return row_count;
+        }
+        else
+            return 0;
+    }
+    else
+    {
+        int category_id = parent.internalId();
+        QSqlQuery query(db);
+        query.prepare("SELECT id from feeds where category=:category;");
+        query.bindValue("category", category_id);
+        if (query.exec())
+        {
+            int row_count = 1;
+            if (!query.first())
+                return 0;
+            else
+                while (query.next())
+                    row_count++;
+            return row_count;
+        }
+        else
+            return 0;
+    }
+}
+
+bool
+FeedModel::addCategory(QString name)
+{
+    QSqlQuery query(db);
+    query.prepare("INSERT INTO categories VALUES (NULL, :name);");
+    query.bindValue("name", name);
+
+    bool successful = query.exec();
+    if (successful)
+        reset();
+    return successful;
+}
+
+bool
+FeedModel::addFeed(QString name, QString url, quint32 category_id)
+{
+    QSqlQuery query(db);
+    query.prepare("INSERT INTO feeds VALUES (NULL, :category, :name, :url);");
+    query.bindValue("category", category_id);
+    query.bindValue("name", name);
+    query.bindValue("url", url);
+
+    bool successful = query.exec();
+    if (successful)
+        reset();
+    return successful;
+}
+
+int
+FeedModel::columnCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    return 1;
+}
+
+QVariant
+FeedModel::data(const QModelIndex &index, int role) const
+{
+    if (role == Qt::DisplayRole)
+    {
+        if (index.internalId() == 0)
+            return QString ("Root");
+        if (index.internalId() < FEEDMODEL_MAX_CATEGORIES)
+        {
+            QSqlQuery query(db);
+            query.prepare ("SELECT id, name from categories WHERE id=:category;");
+            query.bindValue("category", index.internalId());
+            if (query.exec())
+            {
+                if (!query.first())
+                    return QVariant(QVariant::Invalid);
+                else
+                {
+                    return query.value(1).toString();
+                }
+            }
+            else
+                return QVariant(QVariant::Invalid);
+        }
+        else
+        {
+            QSqlQuery query(db);
+            query.prepare ("SELECT id, category, name, url from feeds WHERE id=:feed;");
+            query.bindValue("feed", index.internalId() - FEEDMODEL_MAX_CATEGORIES);
+            if (query.exec())
+            {
+                if (query.first())
+                    return query.value(2).toString();
+                else
+                    return QVariant(QVariant::Invalid);
+            }
+            else
+                return QVariant(QVariant::Invalid);
+        }
+    }
+    else
+        return QVariant (QVariant::Invalid);
+}
+
+QModelIndex
+FeedModel::parent(const QModelIndex &child) const
+{
+    if (!child.isValid())
+        return QModelIndex ();
+
+    quint32 row;
+    quint32 id = child.internalId();
+
+    if (id == 0)
+        return QModelIndex();
+    else if (id < FEEDMODEL_MAX_CATEGORIES)
+    {
+        // Get the position of the category
+        QSqlQuery query (db);
+        query.prepare ("SELECT id from category;");
+        if (query.exec ())
+        {
+            if (query.first ())
+                row = 1;
+            else
+                return QModelIndex ();
+            while (query.next ())
+            {
+                row++;
+                if ((quint64) query.value(0).toInt() == id)
+                    break;
+            }
+
+            return createIndex (row, 1, 0);
+        }
+        else
+            return QModelIndex();
+    }
+    else
+    {
+        quint32 category_id;
+        // We have a feed here, that actually has a real parent.
+        // We need to get the ID of the category
+        id -= FEEDMODEL_MAX_CATEGORIES;
+        QSqlQuery query (db);
+        query.prepare ("SELECT category from feeds WHERE id=:id;");
+        query.bindValue("id", id);
+        if (query.exec())
+        {
+            if (!query.first ())
+                return QModelIndex();
+            else
+            {
+                category_id = query.value(0).toInt();
+
+                // We need to get the position of the feed in the category
+                query.prepare("SELECT id from feeds WHERE category=:category;");
+                query.bindValue("category", category_id);
+                if (query.exec())
+                {
+                    row = 1;
+                    if (!query.first())
+                        return QModelIndex();
+                    else
+                    {
+                        while (query.next())
+                            row++;
+                        return createIndex(row, 1, category_id);
+                    }
+                }
+                else
+                    return QModelIndex();
+            }
+        }
+        else
+            return QModelIndex();
+    }
+}
+
+QVariant
+FeedModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    // We have a header data only for the first column, horizontal mode.
+    if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section == 0)
+        return tr("Feed");
+    else
+        return QVariant ();
+}
+
+QString
+FeedModel::getUrl(const QModelIndex &index)
+{
+    quint64 id = index.internalId();
+    if (id < FEEDMODEL_MAX_CATEGORIES)
+        return "";
+    else
+    {
+        QSqlQuery query(db);
+        query.prepare("SELECT url from feeds WHERE id=:id;");
+        query.bindValue("id", id - FEEDMODEL_MAX_CATEGORIES);
+        if (query.exec())
+        {
+            if (query.first())
+                return query.value(0).toString();
+            else
+                return "";
+        }
+        else
+            return "";
+    }
+}
+
diff --git a/larss/feedpoller.cpp b/larss/feedpoller.cpp
new file mode 100644
index 0000000..11a6834
--- /dev/null
+++ b/larss/feedpoller.cpp
@@ -0,0 +1,151 @@
+#include "feedpoller.h"
+#include <QtXml>
+#include "feedmodel.h"
+
+using namespace Larss;
+
+FeedPoller::FeedPoller(QObject *parent, RssParser *parser, FeedModel *model) :
+    QThread(parent)
+{
+    this->parser = parser;
+    this->model = model;
+
+    workQueue = new QList<QModelIndex> ();
+    nowLoading = 0;
+
+    rssContent = new QHash<quint32, QString>();
+    poll_active = true;
+
+    // Create the QNetworkAccessManager and connect the loaded signal
+    // with our handler.
+    manager = new QNetworkAccessManager ();
+    manager->connect(manager, SIGNAL(finished(QNetworkReply*)),
+                     this, SLOT(networkManagerReplyFinished(QNetworkReply*)));
+}
+
+void
+FeedPoller::run()
+{
+
+    // Create the timer that will call the function every second.
+    QTimer *timer = new QTimer ();
+    timer->setInterval(1000);
+    timer->connect(timer, SIGNAL(timeout()),
+                   this, SLOT(poll()));
+    timer->start();
+
+    QThread::exec();
+}
+
+bool
+FeedPoller::poll()
+{
+    // Poll indefinitely until we are requested to exit.
+    if (nowLoading == 0)
+    {
+        if (workQueue->isEmpty())
+            return false;
+        else
+        {
+            QModelIndex next_item = workQueue->takeFirst();
+            nowLoading = next_item.internalId();
+            manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
+            return true;
+        }
+    }
+}
+
+void
+FeedPoller::stopPolling ()
+{
+    poll_active = false;
+    QThread::exit();
+}
+
+void
+FeedPoller::queueWork(const QModelIndex &index)
+{
+    if (index.internalId() < FEEDMODEL_MAX_CATEGORIES)
+        return;
+    workQueue->append(index);
+}
+
+void
+FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
+{
+    // Assume that the string is UTF-8 encoded. This is likely to be
+    // true, but I should check it in some way.
+    rssContent->insert (nowLoading, QString::fromUtf8(reply->readAll()));
+
+    // Now update the database with the new data obtained.
+    QDomDocument doc;
+    if (doc.setContent(rssContent->value(nowLoading)))
+    {
+        // Try to catch other news_feed with the same link, so preload all of them.
+        QSqlQuery query(parser->db);
+        query.prepare ("SELECT link from news WHERE feed=:feed");
+        query.bindValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
+        if (!query.exec ())
+            return;
+
+        QStringList links;
+        if (query.first())
+        {
+            links.append(query.value(0).toString());
+            while (query.next())
+                links.append(query.value(0).toString());
+        }
+
+        QDomElement doc_el = doc.documentElement();
+        QDomNodeList items = doc_el.elementsByTagName("item");
+
+        for (quint32 i = 0; i < items.length(); i++)
+        {
+            // Get the i-th news
+            QDomNode item = items.item(i);
+            QDomElement element = item.toElement();
+
+            // Get the data in it
+            QString link = element.elementsByTagName("link").item(0).firstChild().nodeValue();
+            QString title = element.elementsByTagName("title").item(0).firstChild().nodeValue();
+            QString description = element.elementsByTagName("description").item(0).firstChild().nodeValue();
+            QString content = element.elementsByTagName("content:encoded").item(0).firstChild().nodeValue();
+
+            // We should enable this for RSS 2.0
+            // QString guid = element.elementsByTagName("guid").item(0).firstChild().nodeValue();
+            // QString pubDate = element.elementsByTagName("pubDate").item(0).firstChild().nodeValue();
+
+            if (!links.contains(link))
+            {
+                // That means that no results were found, so let's insert this one.
+                QSqlRecord record = parser->record();
+                record.setValue("time", 0);
+                record.setValue("read", 0);
+                record.setValue("title", title);
+                record.setValue("link", link);
+                record.setValue("description", description);
+                record.setValue("content", content);
+                record.setValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
+
+                if (!parser->insertRecord(-1, record))
+                    qDebug () << "Error inserting record";
+            }
+        }
+
+        if (!parser->submitAll())
+            qDebug() << "Error submitting new data";
+    }
+    else
+        qDebug () << "Error parsing the document";
+
+    nowLoading = 0;
+    return;
+
+    // Check if there is work in the queue
+    if (!workQueue->isEmpty())
+    {
+        QModelIndex next_item = workQueue->takeFirst();
+        nowLoading = next_item.internalId();
+        manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
+    }
+}
diff --git a/larss/main.cpp b/larss/main.cpp
new file mode 100644
index 0000000..1eea1f3
--- /dev/null
+++ b/larss/main.cpp
@@ -0,0 +1,10 @@
+#include <QtGui/QApplication>
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+    QApplication lars_application(argc, argv);
+    Larss::MainWindow main_window;
+    main_window.show();
+    return lars_application.exec();
+}
diff --git a/larss/mainwindow.cpp b/larss/mainwindow.cpp
new file mode 100644
index 0000000..626ec1a
--- /dev/null
+++ b/larss/mainwindow.cpp
@@ -0,0 +1,104 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include <QDebug>
+#include <QtGui>
+
+using namespace Larss;
+
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::MainWindow)
+{
+    ui->setupUi(this);
+
+    // Open the database
+    db = QSqlDatabase::addDatabase("QSQLITE");
+    db.setDatabaseName("/home/leonardo/larss.db");
+    db.open();
+
+    // Load feedModel that will wrap the SQLite database
+    feedModel = new FeedModel(db, this);
+    ui->feedTreeView->setModel(feedModel);
+    ui->feedTreeView->setEditTriggers(QTreeView::DoubleClicked);
+
+    // Load the RSSParser, hiding the unnecessary columns
+    rssParser = new RssParser(db, feedModel, this);
+    ui->newsTableView->setModel(rssParser);
+    ui->newsTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+    ui->newsTableView->setColumnHidden(0, true); // ID
+    ui->newsTableView->setColumnHidden(1, true); // Feed ID
+    ui->newsTableView->setColumnHidden(3, true); // Link
+    ui->newsTableView->setColumnHidden(4, true); // Description
+    ui->newsTableView->setColumnHidden(5, true); // Content
+    ui->newsTableView->setColumnHidden(6, true); // Time
+    ui->newsTableView->setColumnHidden(7, true); // Read state
+    ui->newsTableView->setEditTriggers(QTableView::NoEditTriggers);
+    ui->newsTableView->verticalHeader()->setHidden(true);
+    ui->newsTableView->horizontalHeader()->setHidden(true);
+    ui->newsTableView->horizontalHeader()->setStretchLastSection(false);
+    ui->newsTableView->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch);
+
+    // Show only unread elements
+    rssParser->setFilter("read=0");
+
+    poller = new FeedPoller (this, rssParser, feedModel);
+    poller->start();
+}
+
+MainWindow::~MainWindow()
+{
+    db.close();
+    delete ui;
+}
+
+void MainWindow::do_exit()
+{
+    poller->stopPolling();
+    poller->wait();
+    QApplication::exit();
+}
+
+void MainWindow::on_actionExit_activated()
+{
+    // Exit the application
+    do_exit();
+}
+
+void Larss::MainWindow::on_feedTreeView_clicked(const QModelIndex &index)
+{
+    // Trigger refresh of selected item
+    poller->queueWork(index);
+
+    // Set the active filter
+    quint64 feed_id;
+    if ((feed_id = rssParser->getFeed (index)))
+    {
+        rssParser->selectActiveFeed(feed_id);
+
+        // Reset the title
+        ui->webViewTitleLabel->setText("");
+    }
+
+}
+
+void Larss::MainWindow::on_newsTableView_clicked(const QModelIndex &index)
+{
+    // Get the number of the row, since index.row () is likely to change
+    // while we set the read status on the post.
+    quint32 row_number = index.row();
+
+    // A row got activated, so open it in the webview.
+    ui->webView->setHtml(rssParser->getContent(index));
+
+    // Select the right title
+    ui->webViewTitleLabel->setText(QString("<b>%1</b>").arg(rssParser->getTitle (index)));
+
+    // And then mark it as read
+    rssParser->setReadStatus(index, true);
+    ui->newsTableView->selectRow(row_number);
+}
+
+void Larss::MainWindow::on_newsTableView_activated(const QModelIndex &index)
+{
+    on_newsTableView_clicked(index);
+}
diff --git a/larss/rssparser.cpp b/larss/rssparser.cpp
new file mode 100644
index 0000000..03e1ac1
--- /dev/null
+++ b/larss/rssparser.cpp
@@ -0,0 +1,139 @@
+#include "rssparser.h"
+#include <QDebug>
+#include <QtXml>
+#include <QtSql>
+#include <QtNetwork>
+#include <QtGui>
+#include <QtCore>
+
+using namespace Larss;
+
+Larss::RssParser::RssParser(QSqlDatabase db, FeedModel *model, QObject *parent) :
+    QSqlTableModel (parent, db)
+{
+    // Create the database if it does not exists.
+    if (!db.tables().contains ("news"))
+    {
+        qDebug () << "Creating table news that is not in database...";
+        QSqlQuery query(db);
+        query.prepare("CREATE TABLE news (id INTEGER PRIMARY KEY, feed INTEGER, title TEXT, link TEXT, description TEXT, content TEXT, time INTEGER, read INTEGER);");
+        if (!query.exec())
+            qDebug () << "Error occurred while creating the database:" << query.lastError();
+    }
+
+    // Set init parameters for the QSqlDataTable
+    setTable("news");
+
+    // Select manual submit so user cannot modify content directly
+    setEditStrategy(QSqlTableModel::OnManualSubmit);
+    this->model = model;
+    select();
+}
+
+QVariant
+Larss::RssParser::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (role == Qt::DisplayRole)
+    {
+        if (orientation == Qt::Horizontal)
+        {
+            switch (section)
+            {
+            case 0:
+                return tr("ID");
+                break;
+            case 1:
+                return tr("Feed");
+                break;
+            case 2:
+                return tr("Title");
+                break;
+            case 3:
+                return tr("Link");
+                break;
+            case 4:
+                return tr("Description");
+                break;
+            case 5:
+                return tr("Content");
+                break;
+            case 6:
+                return tr("Time");
+                break;
+            case 7:
+                return tr("Read");
+            }
+        }
+    }
+
+    return QVariant (QVariant::Invalid);
+}
+
+Larss::RssParser::~RssParser()
+{
+}
+
+QVariant
+Larss::RssParser::data(const QModelIndex &idx, int role) const
+{
+    if (role == Qt::FontRole)
+    {
+        // Get default font
+        QFont default_font = QSqlTableModel::data(idx, role).toString();
+
+        // Check if this news is read or not
+        QSqlRecord record = this->record(idx.row());
+        if (record.value("read") == 0)
+            default_font.setBold(true);
+        return default_font;
+    }
+    // Call the default implementaton in almost every case
+    return QSqlTableModel::data(idx, role);
+}
+
+QString
+Larss::RssParser::getLink(const QModelIndex &index)
+{
+    QSqlRecord record = this->record(index.row());
+    return record.value("link").toString();
+}
+
+void
+Larss::RssParser::setReadStatus(const QModelIndex& index, bool read)
+{
+    QModelIndex read_index = createIndex(index.row(), 7, index.internalPointer());
+    setData(read_index, read ? 1 : 0);
+    if (!submitAll())
+        qDebug() << "Error while setting the read flag";
+}
+
+quint64
+Larss::RssParser::getFeed(const QModelIndex &index)
+{
+    quint64 id = index.internalId();
+    if (id < FEEDMODEL_MAX_CATEGORIES)
+        return 0;
+    else
+        return (id - FEEDMODEL_MAX_CATEGORIES);
+}
+
+QString
+Larss::RssParser::getContent(const QModelIndex &index)
+{
+    QModelIndex description_index = createIndex(index.row(), 5, index.internalPointer());
+    return data(description_index, Qt::DisplayRole).toString();
+}
+
+QString
+Larss::RssParser::getTitle(const QModelIndex &index)
+{
+    QModelIndex title_index = createIndex(index.row(), 2, index.internalPointer());
+    return data(title_index, Qt::DisplayRole).toString();
+}
+
+void
+Larss::RssParser::selectActiveFeed(quint64 feed_id)
+{
+    // Show only the news from the given feed
+    setFilter(QString("feed='%1'").arg(feed_id));
+}
diff --git a/main.cpp b/main.cpp
deleted file mode 100644
index 1eea1f3..0000000
--- a/main.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <QtGui/QApplication>
-#include "mainwindow.h"
-
-int main(int argc, char *argv[])
-{
-    QApplication lars_application(argc, argv);
-    Larss::MainWindow main_window;
-    main_window.show();
-    return lars_application.exec();
-}
diff --git a/mainwindow.cpp b/mainwindow.cpp
deleted file mode 100644
index 626ec1a..0000000
--- a/mainwindow.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-#include <QDebug>
-#include <QtGui>
-
-using namespace Larss;
-
-MainWindow::MainWindow(QWidget *parent) :
-    QMainWindow(parent),
-    ui(new Ui::MainWindow)
-{
-    ui->setupUi(this);
-
-    // Open the database
-    db = QSqlDatabase::addDatabase("QSQLITE");
-    db.setDatabaseName("/home/leonardo/larss.db");
-    db.open();
-
-    // Load feedModel that will wrap the SQLite database
-    feedModel = new FeedModel(db, this);
-    ui->feedTreeView->setModel(feedModel);
-    ui->feedTreeView->setEditTriggers(QTreeView::DoubleClicked);
-
-    // Load the RSSParser, hiding the unnecessary columns
-    rssParser = new RssParser(db, feedModel, this);
-    ui->newsTableView->setModel(rssParser);
-    ui->newsTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
-    ui->newsTableView->setColumnHidden(0, true); // ID
-    ui->newsTableView->setColumnHidden(1, true); // Feed ID
-    ui->newsTableView->setColumnHidden(3, true); // Link
-    ui->newsTableView->setColumnHidden(4, true); // Description
-    ui->newsTableView->setColumnHidden(5, true); // Content
-    ui->newsTableView->setColumnHidden(6, true); // Time
-    ui->newsTableView->setColumnHidden(7, true); // Read state
-    ui->newsTableView->setEditTriggers(QTableView::NoEditTriggers);
-    ui->newsTableView->verticalHeader()->setHidden(true);
-    ui->newsTableView->horizontalHeader()->setHidden(true);
-    ui->newsTableView->horizontalHeader()->setStretchLastSection(false);
-    ui->newsTableView->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch);
-
-    // Show only unread elements
-    rssParser->setFilter("read=0");
-
-    poller = new FeedPoller (this, rssParser, feedModel);
-    poller->start();
-}
-
-MainWindow::~MainWindow()
-{
-    db.close();
-    delete ui;
-}
-
-void MainWindow::do_exit()
-{
-    poller->stopPolling();
-    poller->wait();
-    QApplication::exit();
-}
-
-void MainWindow::on_actionExit_activated()
-{
-    // Exit the application
-    do_exit();
-}
-
-void Larss::MainWindow::on_feedTreeView_clicked(const QModelIndex &index)
-{
-    // Trigger refresh of selected item
-    poller->queueWork(index);
-
-    // Set the active filter
-    quint64 feed_id;
-    if ((feed_id = rssParser->getFeed (index)))
-    {
-        rssParser->selectActiveFeed(feed_id);
-
-        // Reset the title
-        ui->webViewTitleLabel->setText("");
-    }
-
-}
-
-void Larss::MainWindow::on_newsTableView_clicked(const QModelIndex &index)
-{
-    // Get the number of the row, since index.row () is likely to change
-    // while we set the read status on the post.
-    quint32 row_number = index.row();
-
-    // A row got activated, so open it in the webview.
-    ui->webView->setHtml(rssParser->getContent(index));
-
-    // Select the right title
-    ui->webViewTitleLabel->setText(QString("<b>%1</b>").arg(rssParser->getTitle (index)));
-
-    // And then mark it as read
-    rssParser->setReadStatus(index, true);
-    ui->newsTableView->selectRow(row_number);
-}
-
-void Larss::MainWindow::on_newsTableView_activated(const QModelIndex &index)
-{
-    on_newsTableView_clicked(index);
-}
diff --git a/mainwindow.h b/mainwindow.h
deleted file mode 100644
index 6c71c04..0000000
--- a/mainwindow.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
-
-#include <QMainWindow>
-#include "rssparser.h"
-#include "feedmodel.h"
-#include "feedpoller.h"
-#include <QSqlDatabase>
-
-namespace Ui {
-    class MainWindow;
-}
-
-namespace Larss {
-
-class MainWindow : public QMainWindow
-{
-    Q_OBJECT
-
-public:
-    explicit MainWindow(QWidget *parent = 0);
-    ~MainWindow();
-
-private slots:
-    void on_actionExit_activated();
-
-    void on_feedTreeView_clicked(const QModelIndex &index);
-
-    void on_newsTableView_clicked(const QModelIndex &index);
-
-    void on_newsTableView_activated(const QModelIndex &index);
-
-private:
-    Ui::MainWindow *ui;
-    void do_exit();
-
-    QSqlDatabase db;
-    FeedModel *feedModel;
-    RssParser *rssParser;
-    FeedPoller *poller;
-};
-
-}
-
-#endif // MAINWINDOW_H
diff --git a/mainwindow.ui b/mainwindow.ui
deleted file mode 100644
index f297349..0000000
--- a/mainwindow.ui
+++ /dev/null
@@ -1,122 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>720</width>
-    <height>480</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Larss 0.1</string>
-  </property>
-  <widget class="QWidget" name="centralWidget">
-   <layout class="QVBoxLayout" name="verticalLayout_3">
-    <item>
-     <widget class="QSplitter" name="splitter_2">
-      <property name="orientation">
-       <enum>Qt::Horizontal</enum>
-      </property>
-      <widget class="QTreeView" name="feedTreeView"/>
-      <widget class="QSplitter" name="splitter">
-       <property name="orientation">
-        <enum>Qt::Vertical</enum>
-       </property>
-       <widget class="QTableView" name="newsTableView">
-        <property name="showGrid">
-         <bool>false</bool>
-        </property>
-        <property name="gridStyle">
-         <enum>Qt::DashLine</enum>
-        </property>
-        <attribute name="verticalHeaderStretchLastSection">
-         <bool>false</bool>
-        </attribute>
-       </widget>
-       <widget class="QWidget" name="">
-        <layout class="QVBoxLayout" name="verticalLayout_2">
-         <item>
-          <widget class="QLabel" name="webViewTitleLabel">
-           <property name="text">
-            <string/>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QFrame" name="frame">
-           <property name="sizePolicy">
-            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-             <horstretch>0</horstretch>
-             <verstretch>0</verstretch>
-            </sizepolicy>
-           </property>
-           <property name="frameShape">
-            <enum>QFrame::StyledPanel</enum>
-           </property>
-           <property name="frameShadow">
-            <enum>QFrame::Raised</enum>
-           </property>
-           <layout class="QVBoxLayout" name="verticalLayout">
-            <property name="spacing">
-             <number>0</number>
-            </property>
-            <property name="margin">
-             <number>0</number>
-            </property>
-            <item>
-             <widget class="QWebView" name="webView">
-              <property name="url">
-               <url>
-                <string>about:blank</string>
-               </url>
-              </property>
-             </widget>
-            </item>
-           </layout>
-          </widget>
-         </item>
-        </layout>
-       </widget>
-      </widget>
-     </widget>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menuBar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>720</width>
-     <height>24</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuFile">
-    <property name="title">
-     <string>File</string>
-    </property>
-    <addaction name="actionExit"/>
-   </widget>
-   <addaction name="menuFile"/>
-  </widget>
-  <widget class="QStatusBar" name="statusBar"/>
-  <action name="actionExit">
-   <property name="text">
-    <string>Exit</string>
-   </property>
-  </action>
- </widget>
- <layoutdefault spacing="6" margin="11"/>
- <customwidgets>
-  <customwidget>
-   <class>QWebView</class>
-   <extends>QWidget</extends>
-   <header>QtWebKit/QWebView</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/rssparser.cpp b/rssparser.cpp
deleted file mode 100644
index 03e1ac1..0000000
--- a/rssparser.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "rssparser.h"
-#include <QDebug>
-#include <QtXml>
-#include <QtSql>
-#include <QtNetwork>
-#include <QtGui>
-#include <QtCore>
-
-using namespace Larss;
-
-Larss::RssParser::RssParser(QSqlDatabase db, FeedModel *model, QObject *parent) :
-    QSqlTableModel (parent, db)
-{
-    // Create the database if it does not exists.
-    if (!db.tables().contains ("news"))
-    {
-        qDebug () << "Creating table news that is not in database...";
-        QSqlQuery query(db);
-        query.prepare("CREATE TABLE news (id INTEGER PRIMARY KEY, feed INTEGER, title TEXT, link TEXT, description TEXT, content TEXT, time INTEGER, read INTEGER);");
-        if (!query.exec())
-            qDebug () << "Error occurred while creating the database:" << query.lastError();
-    }
-
-    // Set init parameters for the QSqlDataTable
-    setTable("news");
-
-    // Select manual submit so user cannot modify content directly
-    setEditStrategy(QSqlTableModel::OnManualSubmit);
-    this->model = model;
-    select();
-}
-
-QVariant
-Larss::RssParser::headerData(int section, Qt::Orientation orientation, int role) const
-{
-    if (role == Qt::DisplayRole)
-    {
-        if (orientation == Qt::Horizontal)
-        {
-            switch (section)
-            {
-            case 0:
-                return tr("ID");
-                break;
-            case 1:
-                return tr("Feed");
-                break;
-            case 2:
-                return tr("Title");
-                break;
-            case 3:
-                return tr("Link");
-                break;
-            case 4:
-                return tr("Description");
-                break;
-            case 5:
-                return tr("Content");
-                break;
-            case 6:
-                return tr("Time");
-                break;
-            case 7:
-                return tr("Read");
-            }
-        }
-    }
-
-    return QVariant (QVariant::Invalid);
-}
-
-Larss::RssParser::~RssParser()
-{
-}
-
-QVariant
-Larss::RssParser::data(const QModelIndex &idx, int role) const
-{
-    if (role == Qt::FontRole)
-    {
-        // Get default font
-        QFont default_font = QSqlTableModel::data(idx, role).toString();
-
-        // Check if this news is read or not
-        QSqlRecord record = this->record(idx.row());
-        if (record.value("read") == 0)
-            default_font.setBold(true);
-        return default_font;
-    }
-    // Call the default implementaton in almost every case
-    return QSqlTableModel::data(idx, role);
-}
-
-QString
-Larss::RssParser::getLink(const QModelIndex &index)
-{
-    QSqlRecord record = this->record(index.row());
-    return record.value("link").toString();
-}
-
-void
-Larss::RssParser::setReadStatus(const QModelIndex& index, bool read)
-{
-    QModelIndex read_index = createIndex(index.row(), 7, index.internalPointer());
-    setData(read_index, read ? 1 : 0);
-    if (!submitAll())
-        qDebug() << "Error while setting the read flag";
-}
-
-quint64
-Larss::RssParser::getFeed(const QModelIndex &index)
-{
-    quint64 id = index.internalId();
-    if (id < FEEDMODEL_MAX_CATEGORIES)
-        return 0;
-    else
-        return (id - FEEDMODEL_MAX_CATEGORIES);
-}
-
-QString
-Larss::RssParser::getContent(const QModelIndex &index)
-{
-    QModelIndex description_index = createIndex(index.row(), 5, index.internalPointer());
-    return data(description_index, Qt::DisplayRole).toString();
-}
-
-QString
-Larss::RssParser::getTitle(const QModelIndex &index)
-{
-    QModelIndex title_index = createIndex(index.row(), 2, index.internalPointer());
-    return data(title_index, Qt::DisplayRole).toString();
-}
-
-void
-Larss::RssParser::selectActiveFeed(quint64 feed_id)
-{
-    // Show only the news from the given feed
-    setFilter(QString("feed='%1'").arg(feed_id));
-}
diff --git a/rssparser.h b/rssparser.h
deleted file mode 100644
index 1070b43..0000000
--- a/rssparser.h
+++ /dev/null
@@ -1,88 +0,0 @@
-#ifndef RSSPARSER_H
-#define RSSPARSER_H
-
-#include <QObject>
-#include <QtNetwork/QNetworkAccessManager>
-#include <QList>
-#include <QSqlTableModel>
-#include "feedmodel.h"
-
-namespace Larss {
-
-    class RssParser : public QSqlTableModel
-    {
-        Q_OBJECT
-    public:
-        /**
-         * @brief RssParser constructor.
-         */
-        explicit RssParser(QSqlDatabase db, FeedModel * model = NULL, QObject *parent = 0);
-
-        /**
-         * @brief Default destructor.
-         */
-        ~RssParser();
-
-        /**
-         * @brief Function that tells the views that use this model
-         * what to display in the title of the columns.
-         */
-        QVariant headerData (int section, Qt::Orientation orientation, int role) const;
-
-        /**
-         * @brief Get the link associated with a given ModelIndex.
-         */
-        QString getLink (const QModelIndex& index);
-
-        /**
-         * @brief Get the id of the feed pointed by the QModelIndex or
-         * 0 if there is no feed pointed (i.e. is a category or
-         * the root of the tree).
-         */
-        quint64 getFeed (const QModelIndex& index);
-
-        /**
-         * @brief Get the content associated with the feed.
-         */
-        QString getContent (const QModelIndex& index);
-
-        /**
-         * @brief Get the title of a news pointed by index
-         */
-        QString getTitle (const QModelIndex& index);
-
-        /**
-         * @brief Set the read status on a news.
-         */
-        void setReadStatus (const QModelIndex& index, bool read);
-
-        /**
-         * @brief Reimplement data to make unread post bold.
-         */
-        QVariant data(const QModelIndex &idx, int role) const;
-
-        /**
-         * @brief Set the active category to display.
-         */
-        void selectActiveFeed (quint64 feed_id);
-
-        /**
-         * @brief Database where all the news will be loaded and saved.
-         */
-        QSqlDatabase db;
-
-    signals:
-
-    public slots:
-
-    private:
-
-        /**
-         * @brief The FeedModel
-         */
-        FeedModel *model;
-    };
-
-}
-
-#endif // RSSPARSER_H
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
new file mode 100644
index 0000000..f297349
--- /dev/null
+++ b/ui/mainwindow.ui
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>720</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Larss 0.1</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QVBoxLayout" name="verticalLayout_3">
+    <item>
+     <widget class="QSplitter" name="splitter_2">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <widget class="QTreeView" name="feedTreeView"/>
+      <widget class="QSplitter" name="splitter">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <widget class="QTableView" name="newsTableView">
+        <property name="showGrid">
+         <bool>false</bool>
+        </property>
+        <property name="gridStyle">
+         <enum>Qt::DashLine</enum>
+        </property>
+        <attribute name="verticalHeaderStretchLastSection">
+         <bool>false</bool>
+        </attribute>
+       </widget>
+       <widget class="QWidget" name="">
+        <layout class="QVBoxLayout" name="verticalLayout_2">
+         <item>
+          <widget class="QLabel" name="webViewTitleLabel">
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QFrame" name="frame">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="frameShape">
+            <enum>QFrame::StyledPanel</enum>
+           </property>
+           <property name="frameShadow">
+            <enum>QFrame::Raised</enum>
+           </property>
+           <layout class="QVBoxLayout" name="verticalLayout">
+            <property name="spacing">
+             <number>0</number>
+            </property>
+            <property name="margin">
+             <number>0</number>
+            </property>
+            <item>
+             <widget class="QWebView" name="webView">
+              <property name="url">
+               <url>
+                <string>about:blank</string>
+               </url>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>720</width>
+     <height>24</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionExit"/>
+   </widget>
+   <addaction name="menuFile"/>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+  <action name="actionExit">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+  <customwidget>
+   <class>QWebView</class>
+   <extends>QWidget</extends>
+   <header>QtWebKit/QWebView</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
ViewGit