Added possibility to remove feeds and categories.

Leonardo Robol [2011-10-27 14:24]
Added possibility to remove feeds and categories.
Filename
include/feedmodel.h
include/mainwindow.h
include/rssparser.h
larss/feedmodel.cpp
larss/feedpoller.cpp
larss/mainwindow.cpp
larss/rssparser.cpp
ui/mainwindow.ui
diff --git a/include/feedmodel.h b/include/feedmodel.h
index 039970b..ae37145 100644
--- a/include/feedmodel.h
+++ b/include/feedmodel.h
@@ -48,6 +48,16 @@ public:
     bool addFeed (QString name, QString url, FeedNode* categoryNode);

     /**
+     * @brief Remove rows from the treeview
+     */
+    bool removeElements (int row, int count, const QModelIndex &parent = QModelIndex());
+
+    /**
+     * @brief Remove a single row from the treeview.
+     */
+    bool removeElement (int row, const QModelIndex &parent = QModelIndex());
+
+    /**
      * @brief Select data from the database.
      */
     void select();
diff --git a/include/mainwindow.h b/include/mainwindow.h
index 283f191..ab44d93 100644
--- a/include/mainwindow.h
+++ b/include/mainwindow.h
@@ -36,6 +36,14 @@ private slots:

     void on_actionNext_unread_news_triggered();

+    void on_actionUpdate_this_feed_triggered();
+
+    void on_feedTreeView_customContextMenuRequested(const QPoint &pos);
+
+    void removeSelectedFeed ();
+
+    void removeSelectedCategory ();
+
 public slots:
     /**
      * @brief Callback for the start of an update of a feed.
diff --git a/include/rssparser.h b/include/rssparser.h
index 53cca45..e9ba405 100644
--- a/include/rssparser.h
+++ b/include/rssparser.h
@@ -78,6 +78,11 @@ namespace Larss {
         int getNextUnread (const QModelIndex& index);

         /**
+         * @brief Delete all news that are from the given feed.
+         */
+        void removeNewsForFeed (const QModelIndex& index);
+
+        /**
          * @brief Database where all the news will be loaded and saved.
          */
         QSqlDatabase db;
diff --git a/larss/feedmodel.cpp b/larss/feedmodel.cpp
index e79b57b..143e196 100644
--- a/larss/feedmodel.cpp
+++ b/larss/feedmodel.cpp
@@ -109,6 +109,63 @@ FeedModel::setData(const QModelIndex &index, const QVariant &value, int role)
 }

 bool
+FeedModel::removeElements(int row, int count, const QModelIndex &parent)
+{
+    for(int i = 0; i < count; i++)
+    {
+        if (!removeElement(row, parent))
+            return false;
+    }
+    return true;
+}
+
+bool
+FeedModel::removeElement(int row, const QModelIndex &parent)
+{
+    if (parent.isValid())
+    {
+        // This means that this is a feed.
+        QModelIndex index = parent.child(row, 0);
+        FeedNode *node = itemFromIndex(index);
+
+        QSqlQuery query(db);
+        query.prepare ("DELETE FROM feeds WHERE id=:feed;");
+        query.bindValue("feed", node->id());
+
+        if (!query.exec())
+        {
+            qDebug() << "Error removing a row";
+            return false;
+        }
+        else
+            QStandardItemModel::removeRow(row, parent);
+    }
+    else
+    {
+        // This means that this is a feed.
+        QModelIndex index = this->index(row, 0, parent);
+        FeedNode *node = itemFromIndex(index);
+        QSqlQuery query(db);
+        query.prepare ("DELETE FROM categories WHERE id=:category");
+        query.bindValue("category", node->id());
+
+        if (!query.exec())
+        {
+            qDebug() << "Error removing a row";
+            return false;
+        }
+        else
+        {
+            QStandardItemModel::removeRow(row, parent);
+        }
+    }
+
+    return true;
+}
+
+
+
+bool
 FeedModel::addCategory(QString name)
 {
     // First push the data in the database.
diff --git a/larss/feedpoller.cpp b/larss/feedpoller.cpp
index d8f479d..be46756 100644
--- a/larss/feedpoller.cpp
+++ b/larss/feedpoller.cpp
@@ -197,11 +197,11 @@ FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
             }
         }

-        // Commit the changes.
-        parser->db.commit();
-
         if (!parser->submitAll())
             qDebug() << "Error submitting new data";
+
+        // Commit the changes.
+        parser->db.commit();
     }
     else
         qDebug () << "Error parsing the document";
diff --git a/larss/mainwindow.cpp b/larss/mainwindow.cpp
index cbbfe59..53a968b 100644
--- a/larss/mainwindow.cpp
+++ b/larss/mainwindow.cpp
@@ -31,7 +31,8 @@ MainWindow::MainWindow(QWidget *parent) :
     ui->feedTreeView->setModel(feedModel);
     ui->feedTreeView->setItemDelegate (new UnReadCountItemDelegate(feedModel, rssParser));

-    // Load the RSSParser, hiding the unnecessary columns
+    // Load the RSSParser, hiding the unnecessary columns and applying
+    // some custom style :)
     ui->newsTableView->setModel(rssParser);
     ui->newsTableView->setColumnHidden(0, true); // ID
     ui->newsTableView->setColumnHidden(1, true); // Feed ID
@@ -41,6 +42,7 @@ MainWindow::MainWindow(QWidget *parent) :
     ui->newsTableView->setColumnHidden(7, true); // Read state
     ui->newsTableView->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch);
     ui->newsTableView->horizontalHeader()->setResizeMode(6, QHeaderView::ResizeToContents);
+    ui->newsTableView->setAlternatingRowColors(true);

     // Show nothing for now.
     rssParser->setFilter("1 = 0");
@@ -50,7 +52,8 @@ MainWindow::MainWindow(QWidget *parent) :
                     SLOT(loadingFeedStart(QString)));
     poller->start();

-    // Install event filter to handle particular events.
+    // Install event filter to handle particular events, such as mouseclicks that
+    // triggers original article loading.
     ui->webViewTitleLabel->installEventFilter(this);
     loadedNews = "";

@@ -201,3 +204,67 @@ void Larss::MainWindow::on_actionNext_unread_news_triggered()
         loadFeed (ui->newsTableView->selectionModel()->currentIndex());
     }
 }
+
+void Larss::MainWindow::on_actionUpdate_this_feed_triggered()
+{
+    QModelIndex index = ui->feedTreeView->selectionModel()->currentIndex();
+    if (index.isValid())
+    {
+        poller->queueWork(index);
+    }
+}
+
+void Larss::MainWindow::on_feedTreeView_customContextMenuRequested(const QPoint &pos)
+{
+    QModelIndex index = ui->feedTreeView->indexAt(pos);
+    if (index.isValid())
+    {
+        // Check if this is a category or not.
+        FeedNode *node = feedModel->itemFromIndex(index);
+
+        QMenu popupMenu (this);
+
+        if (node->type() == FeedNode::Feed)
+        {
+            popupMenu.addAction(tr("Update"), this, SLOT(on_actionUpdate_this_feed_triggered()));
+            popupMenu.addAction(tr("Remove"), this,
+                                SLOT(removeSelectedFeed()));
+        }
+        else if (node->type() == FeedNode::Category)
+        {
+            popupMenu.addAction(tr("Remove"), this,
+                                SLOT(removeSelectedCategory()));
+        }
+
+        popupMenu.exec(ui->feedTreeView->mapToGlobal(pos));
+    }
+}
+
+void Larss::MainWindow::removeSelectedFeed()
+{
+    QModelIndex index = ui->feedTreeView->currentIndex();
+
+    // We need to remove all the feeds from that category
+    rssParser->removeNewsForFeed(index);
+
+    // And then remove the feed itself.
+    feedModel->removeElement(index.row(), index.parent());
+}
+
+void Larss::MainWindow::removeSelectedCategory()
+{
+    // Check that the category is empty before removing it
+    QModelIndex index = ui->feedTreeView->currentIndex();
+    if (feedModel->rowCount(index) > 0)
+    {
+        QMessageBox messageBox;
+        messageBox.setText("Impossible to remove category");
+        messageBox.setInformativeText("Please remove feeds in this category before trying to remove it.");
+
+        messageBox.exec ();
+    }
+    else
+    {
+        feedModel->removeElement(index.row());
+    }
+}
diff --git a/larss/rssparser.cpp b/larss/rssparser.cpp
index be2e6b5..62c0b66 100644
--- a/larss/rssparser.cpp
+++ b/larss/rssparser.cpp
@@ -183,3 +183,22 @@ Larss::RssParser::getNextUnread(const QModelIndex& starting)

     return -1;
 }
+
+void
+Larss::RssParser::removeNewsForFeed(const QModelIndex& index)
+{
+    if (!index.isValid())
+        return;
+    FeedNode *node = model->itemFromIndex(index);
+
+    if (node->type() == FeedNode::Feed)
+    {
+        int id = node->id();
+        QSqlQuery query(db);
+        query.prepare("DELETE FROM news WHERE feed=:feed");
+        query.bindValue("feed", id);
+
+        if (!query.exec())
+            qDebug() << "Error deleting news from feed " << node->name();
+    }
+}
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index 286a875..5c4de59 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -20,7 +20,11 @@
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
-      <widget class="QTreeView" name="feedTreeView"/>
+      <widget class="QTreeView" name="feedTreeView">
+       <property name="contextMenuPolicy">
+        <enum>Qt::CustomContextMenu</enum>
+       </property>
+      </widget>
       <widget class="QSplitter" name="splitter">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
@@ -44,6 +48,12 @@
         <attribute name="verticalHeaderVisible">
          <bool>false</bool>
         </attribute>
+        <attribute name="verticalHeaderDefaultSectionSize">
+         <number>24</number>
+        </attribute>
+        <attribute name="verticalHeaderMinimumSectionSize">
+         <number>24</number>
+        </attribute>
         <attribute name="verticalHeaderStretchLastSection">
          <bool>false</bool>
         </attribute>
@@ -123,6 +133,7 @@
      <string>Feed</string>
     </property>
     <addaction name="actionNext_unread_news"/>
+    <addaction name="actionUpdate_this_feed"/>
    </widget>
    <addaction name="menuFile"/>
    <addaction name="menuFeed"/>
@@ -151,6 +162,11 @@
     <string>N</string>
    </property>
   </action>
+  <action name="actionUpdate_this_feed">
+   <property name="text">
+    <string>Update</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>
ViewGit