Migration to QStandardItemModel to clean underlying interfaces.

Leonardo Robol [2011-10-25 06:06]
Migration to QStandardItemModel to clean underlying interfaces.
Filename
Larss.pro
include/feedmodel.h
include/feednode.h
include/feedproxymodel.h
larss/feedmodel.cpp
larss/feednode.cpp
larss/feedpoller.cpp
larss/feedproxymodel.cpp
larss/mainwindow.cpp
larss/rssparser.cpp
diff --git a/Larss.pro b/Larss.pro
index 95761ad..437cd54 100644
--- a/Larss.pro
+++ b/Larss.pro
@@ -17,7 +17,7 @@ SOURCES += larss/main.cpp\
     larss/feedpoller.cpp \
     larss/editfeeddialog.cpp \
     larss/editcategorydialog.cpp \
-    larss/feedproxymodel.cpp
+    larss/feednode.cpp

 HEADERS  += include/mainwindow.h \
     include/feedmodel.h \
@@ -25,7 +25,7 @@ HEADERS  += include/mainwindow.h \
     include/feedpoller.h \
     include/editfeeddialog.h \
     include/editcategorydialog.h \
-    include/feedproxymodel.h
+    include/feednode.h

 FORMS    += ui/mainwindow.ui \
     ui/editfeeddialog.ui \
diff --git a/include/feedmodel.h b/include/feedmodel.h
index 6ead440..2c4334d 100644
--- a/include/feedmodel.h
+++ b/include/feedmodel.h
@@ -2,15 +2,16 @@
 #define FEEDMODEL_H

 #include <QObject>
-#include <QAbstractItemModel>
+#include <QStandardItemModel>
 #include <QSqlDatabase>
 #include <QtXml>
+#include "feednode.h"

 namespace Larss {

 #define FEEDMODEL_MAX_CATEGORIES 1024

-class FeedModel : public QAbstractItemModel {
+class FeedModel : public QStandardItemModel {
     Q_OBJECT

 public:
@@ -22,36 +23,6 @@ public:
     ~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;
@@ -76,6 +47,13 @@ public:
      */
     bool addFeed (QString name, QString url, quint32 category_id);

+    /**
+     * @brief Select data from the database.
+     */
+    void select();
+
+    FeedNode * itemFromIndex (const QModelIndex& index);
+
 signals:

 public slots:
@@ -86,6 +64,8 @@ private:
      */
     QSqlDatabase db;

+    FeedNode *rootNode;
+

 };

diff --git a/include/feednode.h b/include/feednode.h
new file mode 100644
index 0000000..5120491
--- /dev/null
+++ b/include/feednode.h
@@ -0,0 +1,54 @@
+#ifndef FEEDNODE_H
+#define FEEDNODE_H
+
+#include <QObject>
+#include <QStandardItem>
+
+namespace Larss {
+
+    class FeedNode : public QStandardItem
+    {
+
+    public:
+        explicit FeedNode(quint64 id, QString name, QString url = "");
+
+        enum ItemType {
+            Root = 1001,
+            Category = 1002,
+            Feed = 1003
+        };
+
+        /**
+         * @brief Return the type of this element.
+         */
+        ItemType type ();
+
+        quint64 id();
+        QString name();
+        QString url();
+
+    private:
+
+        /**
+         * @brief The id of the item in the database.
+         */
+        quint64 nodeId;
+
+        /**
+         * @brief The name of the item.
+         */
+        QString nodeName;
+
+        /**
+         * @brief The Url associated with the feed, if any
+         * (Category do not have this one).
+         */
+        QString nodeUrl;
+
+    private:
+
+    };
+
+}
+
+#endif // FEEDNODE_H
diff --git a/include/feedproxymodel.h b/include/feedproxymodel.h
deleted file mode 100644
index ee4b5f4..0000000
--- a/include/feedproxymodel.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef FEEDPROXYMODEL_H
-#define FEEDPROXYMODEL_H
-
-#include <QObject>
-#include <QProxyModel>
-#include "rssparser.h"
-
-namespace Larss {
-
-    class FeedProxyModel : public QProxyModel
-    {
-        Q_OBJECT
-    public:
-        explicit FeedProxyModel(QObject *parent = 0);
-        QVariant data (const QModelIndex &index, int role) const;
-        void setParser (RssParser * parser);
-
-    private:
-        RssParser *parser;
-
-    signals:
-
-    public slots:
-        void onRssParserDataChanged (QModelIndex index, QModelIndex index2);
-
-    };
-
-}
-
-#endif // FEEDPROXYMODEL_H
diff --git a/larss/feedmodel.cpp b/larss/feedmodel.cpp
index 104e010..81048dd 100644
--- a/larss/feedmodel.cpp
+++ b/larss/feedmodel.cpp
@@ -1,5 +1,5 @@
 #include "feedmodel.h"
-#include <QAbstractItemModel>
+#include "feednode.h"
 #include <QSqlDatabase>
 #include <QSqlQuery>
 #include <QSqlError>
@@ -9,7 +9,7 @@

 using namespace Larss;

-FeedModel::FeedModel(QSqlDatabase db, QObject *parent) : QAbstractItemModel (parent)
+FeedModel::FeedModel(QSqlDatabase db, QObject *parent) : QStandardItemModel (parent)
 {
     this->db = db;

@@ -29,141 +29,58 @@ FeedModel::FeedModel(QSqlDatabase db, QObject *parent) : QAbstractItemModel (par
         if (!query.exec())
             qDebug() << "Error while creating the feeds table in the db";
     }
+
+    select();
 }

 FeedModel::~FeedModel()
 {
 }

-
-QModelIndex
-FeedModel::index(int row, int column, const QModelIndex &parent) const
+void
+FeedModel::select()
 {
-    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
+    // Clear the content of the model
+    clear();
+
+    // Get the root of the tree
+    QStandardItem *root = invisibleRootItem();
+
+    // Set the fake rootNode
+    rootNode = new FeedNode (0, "");
+
+    QSqlQuery query(db);
+    query.prepare("SELECT id, name from categories;");
+    if (query.exec() && query.first())
     {
-        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
+        do {
+            // Insert the new category in the tree
+            FeedNode* node = new FeedNode(query.value(0).toInt(), query.value(1).toString());
+            root->appendRow(node);
+
+            // Find the feeds associated with this category and add the as childs.
+            QSqlQuery feedQuery(db);
+            feedQuery.prepare("SELECT id, name, url FROM feeds WHERE category=:category");
+            feedQuery.bindValue("category", node->id());
+
+            if (feedQuery.exec() && feedQuery.first())
             {
-                for(int i = 0; i < row; i++)
+                do
                 {
-                    if (!query.next())
-                        return QModelIndex();
-                }
-                return createIndex(row, column, query.value(0).toInt() + FEEDMODEL_MAX_CATEGORIES);
+                    FeedNode *feedNode = new FeedNode(feedQuery.value(0).toInt(),
+                                                      feedQuery.value(1).toString(),
+                                                      feedQuery.value(2).toString());
+                    node->appendRow(feedNode);
+                } while (feedQuery.next());
             }
-        }
-        else
-            return QModelIndex();
+        } while (query.next());
     }
 }

 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;
-    }
+    return QStandardItemModel::setData(index, value, role);
 }

 bool
@@ -199,132 +116,6 @@ FeedModel::addFeed(QString name, QString url, quint32 category_id)
     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
 {
@@ -338,23 +129,16 @@ FeedModel::headerData(int section, Qt::Orientation orientation, int role) const
 QString
 FeedModel::getUrl(const QModelIndex &index)
 {
-    quint64 id = index.internalId();
-    if (id < FEEDMODEL_MAX_CATEGORIES)
-        return "";
+    FeedNode *node = itemFromIndex(index);
+    return node->url();
+}
+
+FeedNode*
+FeedModel::itemFromIndex(const QModelIndex &index)
+{
+    if (index.isValid())
+        return (FeedNode*) QStandardItemModel::itemFromIndex(index);
     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 "";
-    }
+        return rootNode;
 }

diff --git a/larss/feednode.cpp b/larss/feednode.cpp
new file mode 100644
index 0000000..670c050
--- /dev/null
+++ b/larss/feednode.cpp
@@ -0,0 +1,40 @@
+#include "include/feednode.h"
+#include <QDebug>
+
+using namespace Larss;
+
+FeedNode::FeedNode(quint64 id, QString name, QString url) :
+    nodeId(id), nodeName(name), nodeUrl(url)
+{
+    setText (name);
+    qDebug() << "Created node, ID: " << id << " Name: " << name << " Url: " << url;
+}
+
+FeedNode::ItemType
+FeedNode::type()
+{
+    if (nodeId == 0)
+        return Root;
+    if (nodeUrl != "")
+        return Feed;
+    else
+        return Category;
+}
+
+quint64
+FeedNode::id()
+{
+    return nodeId;
+}
+
+QString
+FeedNode::name()
+{
+    return nodeName;
+}
+
+QString
+FeedNode::url()
+{
+    return nodeUrl;
+}
diff --git a/larss/feedpoller.cpp b/larss/feedpoller.cpp
index d25ff93..b6f56f1 100644
--- a/larss/feedpoller.cpp
+++ b/larss/feedpoller.cpp
@@ -1,6 +1,7 @@
 #include "feedpoller.h"
 #include <QtXml>
 #include "feedmodel.h"
+#include "feednode.h"

 using namespace Larss;

@@ -48,7 +49,8 @@ FeedPoller::poll()
         else
         {
             QModelIndex next_item = workQueue->takeFirst();
-            nowLoading = next_item.internalId();
+            FeedNode *node = (FeedNode*) next_item.internalPointer();
+            nowLoading = node->id();
             manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
             return true;
         }
@@ -67,7 +69,11 @@ FeedPoller::stopPolling ()
 void
 FeedPoller::queueWork(const QModelIndex &index)
 {
-    if (index.internalId() < FEEDMODEL_MAX_CATEGORIES)
+    qDebug() << index;
+    if (!index.isValid())
+        return;
+    FeedNode *node = model->itemFromIndex(index);
+    if (node->type() != FeedNode::Feed)
         return;
     workQueue->append(index);
 }
@@ -86,7 +92,7 @@ FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
         // 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);
+        query.bindValue("feed", nowLoading);
         if (!query.exec ())
             return;

@@ -142,7 +148,7 @@ FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
                 record.setValue("link", link);
                 record.setValue("description", description);
                 record.setValue("content", content);
-                record.setValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
+                record.setValue("feed", nowLoading);

                 if (!parser->insertRecord(-1, record))
                     qDebug () << "Error inserting record";
@@ -162,7 +168,8 @@ FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
     if (!workQueue->isEmpty())
     {
         QModelIndex next_item = workQueue->takeFirst();
-        nowLoading = next_item.internalId();
+        FeedNode *node = (FeedNode*) next_item.internalPointer();
+        nowLoading = node->id();
         manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
     }
 }
diff --git a/larss/feedproxymodel.cpp b/larss/feedproxymodel.cpp
deleted file mode 100644
index db8f21e..0000000
--- a/larss/feedproxymodel.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "include/feedproxymodel.h"
-#include <QFont>
-#include <QSqlRecord>
-
-using namespace Larss;
-
-FeedProxyModel::FeedProxyModel(QObject *parent) :
-    QProxyModel(parent)
-{
-
-}
-
-QVariant
-FeedProxyModel::data(const QModelIndex &index, int role) const
-{
-    // Check if this is  a feed, and in that case check the number of unread items.
-    if (index.internalId() > FEEDMODEL_MAX_CATEGORIES)
-    {
-        if (role == Qt::DisplayRole)
-        {
-            QString feedName = this->model()->data(index, role).toString();
-            quint32 unreadCount = parser->getUnreadCount(index);
-            if (unreadCount > 0)
-                return QString("%1 (%2)").arg(feedName).arg(unreadCount);
-            else
-                return feedName;
-        }
-        else if (role == Qt::FontRole)
-        {
-            quint32 unreadCount = parser->getUnreadCount(index);
-            QFont font = this->model()->data(index, role).toString();
-            if (unreadCount > 0)
-                font.setBold(true);
-            return font;
-        }
-
-        return this->model()->data(index, role);
-    }
-    else
-        return this->model()->data(index, role);
-}
-
-void
-FeedProxyModel::setParser(RssParser *parser)
-{
-    this->parser = parser;
-    parser->connect(parser, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
-                    this, SLOT(onRssParserDataChanged(QModelIndex,QModelIndex)));
-}
-
-void
-FeedProxyModel::onRssParserDataChanged(QModelIndex index, QModelIndex index2)
-{
-    Q_UNUSED(index);
-    Q_UNUSED(index2);
-    // We need to get the feed where the news is, so we can emit dataChanged
-    // for it. For now we simply propagate the change.
-    QModelIndex rootIndex = this->model()->index(1, 1, QModelIndex());
-    dataChanged(rootIndex, rootIndex);
-}
diff --git a/larss/mainwindow.cpp b/larss/mainwindow.cpp
index 5ad4a06..579993c 100644
--- a/larss/mainwindow.cpp
+++ b/larss/mainwindow.cpp
@@ -2,7 +2,6 @@
 #include "ui_mainwindow.h"
 #include "editfeeddialog.h"
 #include "editcategorydialog.h"
-#include "feedproxymodel.h"
 #include <QDebug>
 #include <QtGui>

@@ -27,11 +26,7 @@ MainWindow::MainWindow(QWidget *parent) :
     feedModel = new FeedModel(db, this);
     rssParser = new RssParser(db, feedModel, this);

-    FeedProxyModel *proxyModel = new FeedProxyModel(this);
-    proxyModel->setModel(feedModel);
-    proxyModel->setParser(rssParser);
-
-    ui->feedTreeView->setModel(proxyModel);
+    ui->feedTreeView->setModel(feedModel);

     // Load the RSSParser, hiding the unnecessary columns
     ui->newsTableView->setModel(rssParser);
@@ -90,6 +85,7 @@ void Larss::MainWindow::on_feedTreeView_clicked(const QModelIndex &index)
         ui->webViewTitleLabel->setText("");
     }

+
 }

 void Larss::MainWindow::on_newsTableView_clicked(const QModelIndex &index)
diff --git a/larss/rssparser.cpp b/larss/rssparser.cpp
index f6f84ac..e6ede52 100644
--- a/larss/rssparser.cpp
+++ b/larss/rssparser.cpp
@@ -1,4 +1,5 @@
 #include "rssparser.h"
+#include "feednode.h"
 #include <QDebug>
 #include <QtXml>
 #include <QtSql>
@@ -121,11 +122,11 @@ Larss::RssParser::setReadStatus(const QModelIndex& index, bool read)
 quint64
 Larss::RssParser::getFeed(const QModelIndex &index)
 {
-    quint64 id = index.internalId();
-    if (id < FEEDMODEL_MAX_CATEGORIES)
+    FeedNode *node = model->itemFromIndex (index);
+    if (node->type() == FeedNode::Category)
         return 0;
     else
-        return (id - FEEDMODEL_MAX_CATEGORIES);
+        return node->id();
 }

 QString
ViewGit