2014-02-26 07:41:40 +01:00
|
|
|
// This file is part of RSS Guard.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2011-2014 by Martin Rotter <rotter.martinos@gmail.com>
|
|
|
|
//
|
|
|
|
// RSS Guard is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// RSS Guard is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with RSS Guard. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2013-11-12 20:24:19 +01:00
|
|
|
#include "core/feedsmodel.h"
|
2013-12-21 21:08:52 +01:00
|
|
|
|
2013-12-12 15:07:17 +01:00
|
|
|
#include "core/defs.h"
|
2013-12-13 20:48:45 +01:00
|
|
|
#include "core/databasefactory.h"
|
2014-03-26 08:33:50 +01:00
|
|
|
#include "core/feedsmodelcategory.h"
|
|
|
|
#include "core/feedsmodelfeed.h"
|
2014-01-04 14:09:38 +01:00
|
|
|
#include "core/textfactory.h"
|
2013-12-12 15:07:17 +01:00
|
|
|
#include "gui/iconthemefactory.h"
|
2013-12-14 11:45:43 +01:00
|
|
|
#include "gui/iconfactory.h"
|
2013-12-12 10:10:17 +01:00
|
|
|
|
2013-12-21 21:08:52 +01:00
|
|
|
#include <QSqlError>
|
|
|
|
#include <QSqlQuery>
|
|
|
|
#include <QSqlRecord>
|
|
|
|
#include <QPair>
|
|
|
|
|
2013-12-27 10:24:07 +01:00
|
|
|
#include <algorithm>
|
|
|
|
|
2013-11-12 20:24:19 +01:00
|
|
|
|
|
|
|
FeedsModel::FeedsModel(QObject *parent) : QAbstractItemModel(parent) {
|
2013-12-13 20:48:45 +01:00
|
|
|
setObjectName("FeedsModel");
|
|
|
|
|
2013-12-12 15:07:17 +01:00
|
|
|
m_rootItem = new FeedsModelRootItem();
|
2013-12-28 16:44:21 +01:00
|
|
|
m_rootItem->setId(NO_PARENT_CATEGORY);
|
2014-02-26 19:18:11 +01:00
|
|
|
|
|
|
|
//: Name of root item of feed list which can be seen in feed add/edit dialog.
|
2014-02-16 08:24:27 +01:00
|
|
|
m_rootItem->setTitle(tr("Root"));
|
2014-02-02 10:27:11 +01:00
|
|
|
m_rootItem->setIcon(IconThemeFactory::instance()->fromTheme("folder-root"));
|
|
|
|
m_countsIcon = IconThemeFactory::instance()->fromTheme("mail-mark-unread");
|
2014-02-26 19:18:11 +01:00
|
|
|
|
|
|
|
//: Title text in the feed list header.
|
2013-12-12 15:07:17 +01:00
|
|
|
m_headerData << tr("Title");
|
2014-02-26 19:18:11 +01:00
|
|
|
|
2014-02-27 18:15:16 +01:00
|
|
|
m_tooltipData << /*: Feed list header "titles" column tooltip.*/ tr("Titles of feeds/categories.") <<
|
|
|
|
/*: Feed list header "counts" column tooltip.*/ tr("Counts of unread/all meesages.");
|
2013-12-12 15:07:17 +01:00
|
|
|
|
2013-12-13 20:48:45 +01:00
|
|
|
loadFromDatabase();
|
2013-11-12 20:24:19 +01:00
|
|
|
}
|
2013-12-11 14:07:18 +01:00
|
|
|
|
|
|
|
FeedsModel::~FeedsModel() {
|
|
|
|
qDebug("Destroying FeedsModel instance.");
|
2013-12-31 14:25:49 +01:00
|
|
|
|
2014-01-10 08:36:08 +01:00
|
|
|
// Delete all model items.
|
2013-12-11 15:00:15 +01:00
|
|
|
delete m_rootItem;
|
2013-12-11 14:07:18 +01:00
|
|
|
}
|
2013-12-11 18:54:09 +01:00
|
|
|
|
2014-02-16 18:43:33 +01:00
|
|
|
QModelIndexList FeedsModel::persistentIndexList() const {
|
|
|
|
return QAbstractItemModel::persistentIndexList();
|
|
|
|
}
|
|
|
|
|
2013-12-11 18:54:09 +01:00
|
|
|
QVariant FeedsModel::headerData(int section,
|
|
|
|
Qt::Orientation orientation,
|
|
|
|
int role) const {
|
2013-12-12 15:07:17 +01:00
|
|
|
if (orientation != Qt::Horizontal) {
|
|
|
|
return QVariant();
|
|
|
|
}
|
2013-12-11 18:54:09 +01:00
|
|
|
|
2013-12-12 15:07:17 +01:00
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
2013-12-13 20:48:45 +01:00
|
|
|
if (section == FDS_MODEL_TITLE_INDEX) {
|
|
|
|
return m_headerData.at(FDS_MODEL_TITLE_INDEX);
|
2013-12-12 15:07:17 +01:00
|
|
|
}
|
|
|
|
else {
|
2013-12-11 18:54:09 +01:00
|
|
|
return QVariant();
|
2013-12-12 15:07:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case Qt::ToolTipRole:
|
2013-12-12 22:28:51 +01:00
|
|
|
return m_tooltipData.at(section);
|
2013-12-12 15:07:17 +01:00
|
|
|
|
|
|
|
case Qt::DecorationRole:
|
2013-12-13 20:48:45 +01:00
|
|
|
if (section == FDS_MODEL_COUNTS_INDEX) {
|
2013-12-12 15:07:17 +01:00
|
|
|
return m_countsIcon;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return QVariant();
|
2013-12-11 18:54:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex FeedsModel::index(int row, int column, const QModelIndex &parent) const {
|
|
|
|
if (!hasIndex(row, column, parent)) {
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
2013-12-12 08:27:05 +01:00
|
|
|
FeedsModelRootItem *parent_item;
|
2013-12-11 18:54:09 +01:00
|
|
|
|
|
|
|
if (!parent.isValid()) {
|
|
|
|
parent_item = m_rootItem;
|
|
|
|
}
|
|
|
|
else {
|
2013-12-12 08:27:05 +01:00
|
|
|
parent_item = static_cast<FeedsModelRootItem*>(parent.internalPointer());
|
2013-12-11 18:54:09 +01:00
|
|
|
}
|
|
|
|
|
2013-12-12 08:27:05 +01:00
|
|
|
FeedsModelRootItem *child_item = parent_item->child(row);
|
2013-12-11 18:54:09 +01:00
|
|
|
|
|
|
|
if (child_item) {
|
|
|
|
return createIndex(row, column, child_item);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex FeedsModel::parent(const QModelIndex &child) const {
|
|
|
|
if (!child.isValid()) {
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
2013-12-12 08:27:05 +01:00
|
|
|
FeedsModelRootItem *child_item = static_cast<FeedsModelRootItem*>(child.internalPointer());
|
|
|
|
FeedsModelRootItem *parent_item = child_item->parent();
|
2013-12-11 18:54:09 +01:00
|
|
|
|
|
|
|
if (parent_item == m_rootItem) {
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return createIndex(parent_item->row(), 0, parent_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int FeedsModel::rowCount(const QModelIndex &parent) const {
|
2013-12-12 08:27:05 +01:00
|
|
|
FeedsModelRootItem *parent_item;
|
2013-12-11 18:54:09 +01:00
|
|
|
|
|
|
|
if (parent.column() > 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!parent.isValid()) {
|
|
|
|
parent_item = m_rootItem;
|
|
|
|
}
|
|
|
|
else {
|
2013-12-12 08:27:05 +01:00
|
|
|
parent_item = static_cast<FeedsModelRootItem*>(parent.internalPointer());
|
2013-12-11 18:54:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return parent_item->childCount();
|
|
|
|
}
|
|
|
|
|
2014-01-28 19:19:33 +01:00
|
|
|
bool FeedsModel::removeItem(const QModelIndex &index) {
|
2014-01-17 19:10:10 +01:00
|
|
|
if (index.isValid()) {
|
|
|
|
QModelIndex parent_index = index.parent();
|
|
|
|
FeedsModelRootItem *deleting_item = itemForIndex(index);
|
2014-01-27 14:54:27 +01:00
|
|
|
FeedsModelRootItem *parent_item = deleting_item->parent();
|
2014-01-17 19:10:10 +01:00
|
|
|
|
2014-01-26 17:57:10 +01:00
|
|
|
// Try to persistently remove the item.
|
2014-01-25 17:21:13 +01:00
|
|
|
if (deleting_item->removeItself()) {
|
2014-01-26 17:57:10 +01:00
|
|
|
// Item was persistently removed.
|
|
|
|
// Remove it from the model.
|
2014-01-25 17:21:13 +01:00
|
|
|
beginRemoveRows(parent_index, index.row(), index.row());
|
2014-01-27 14:54:27 +01:00
|
|
|
parent_item->removeChild(deleting_item);
|
2014-01-25 17:21:13 +01:00
|
|
|
endRemoveRows();
|
2014-01-26 17:57:10 +01:00
|
|
|
|
2014-01-27 14:54:27 +01:00
|
|
|
delete deleting_item;
|
|
|
|
|
2014-01-26 17:57:10 +01:00
|
|
|
return true;
|
2014-01-25 17:21:13 +01:00
|
|
|
}
|
2014-01-26 17:57:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Item was not removed successfully.
|
|
|
|
return false;
|
|
|
|
}
|
2014-01-07 19:09:19 +01:00
|
|
|
|
2014-03-26 08:33:50 +01:00
|
|
|
bool FeedsModel::addCategory(FeedsModelCategory *category,
|
2014-01-26 17:57:10 +01:00
|
|
|
FeedsModelRootItem *parent) {
|
|
|
|
// Get index of parent item (parent standard category).
|
|
|
|
QModelIndex parent_index = indexForItem(parent);
|
|
|
|
|
|
|
|
// Now, add category to persistent storage.
|
|
|
|
// Children are removed, remove this standard category too.
|
|
|
|
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
|
|
|
|
DatabaseFactory::FromSettings);
|
|
|
|
QSqlQuery query_add(database);
|
|
|
|
|
|
|
|
query_add.setForwardOnly(true);
|
|
|
|
query_add.prepare("INSERT INTO Categories "
|
|
|
|
"(parent_id, title, description, date_created, icon, type) "
|
|
|
|
"VALUES (:parent_id, :title, :description, :date_created, :icon, :type);");
|
|
|
|
query_add.bindValue(":parent_id", parent->id());
|
|
|
|
query_add.bindValue(":title", category->title());
|
|
|
|
query_add.bindValue(":description", category->description());
|
|
|
|
query_add.bindValue(":date_created", category->creationDate().toMSecsSinceEpoch());
|
|
|
|
query_add.bindValue(":icon", IconFactory::toByteArray(category->icon()));
|
|
|
|
query_add.bindValue(":type", (int) FeedsModelCategory::Standard);
|
|
|
|
|
|
|
|
if (!query_add.exec()) {
|
|
|
|
// Query failed.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
query_add.prepare("SELECT id FROM Categories WHERE date_created = :date_created;");
|
|
|
|
query_add.bindValue(":date_created", category->creationDate().toMSecsSinceEpoch());
|
|
|
|
if (query_add.exec() && query_add.next()) {
|
|
|
|
// New category was added, fetch is primary id
|
|
|
|
// from the database.
|
|
|
|
category->setId(query_add.value(0).toInt());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Something failed.
|
|
|
|
return false;
|
2014-01-07 22:35:11 +01:00
|
|
|
}
|
2014-01-07 19:09:19 +01:00
|
|
|
|
2014-01-26 17:57:10 +01:00
|
|
|
// Category was added to the persistent storage,
|
|
|
|
// so add it to the model.
|
|
|
|
beginInsertRows(parent_index, parent->childCount(), parent->childCount());
|
|
|
|
parent->appendChild(category);
|
|
|
|
endInsertRows();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-26 08:33:50 +01:00
|
|
|
bool FeedsModel::editCategory(FeedsModelCategory *original_category,
|
|
|
|
FeedsModelCategory *new_category) {
|
2014-01-26 22:13:10 +01:00
|
|
|
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
|
|
|
|
DatabaseFactory::FromSettings);
|
|
|
|
QSqlQuery query_update_category(database);
|
2014-01-27 14:54:27 +01:00
|
|
|
FeedsModelRootItem *original_parent = original_category->parent();
|
|
|
|
FeedsModelRootItem *new_parent = new_category->parent();
|
2014-01-26 22:13:10 +01:00
|
|
|
|
|
|
|
query_update_category.setForwardOnly(true);
|
|
|
|
query_update_category.prepare("UPDATE Categories "
|
|
|
|
"SET title = :title, description = :description, icon = :icon, parent_id = :parent_id "
|
|
|
|
"WHERE id = :id;");
|
|
|
|
query_update_category.bindValue(":title", new_category->title());
|
|
|
|
query_update_category.bindValue(":description", new_category->description());
|
|
|
|
query_update_category.bindValue(":icon", IconFactory::toByteArray(new_category->icon()));
|
2014-01-27 14:54:27 +01:00
|
|
|
query_update_category.bindValue(":parent_id", new_parent->id());
|
2014-01-26 22:13:10 +01:00
|
|
|
query_update_category.bindValue(":id", original_category->id());
|
|
|
|
|
|
|
|
if (!query_update_category.exec()) {
|
2014-01-27 14:54:27 +01:00
|
|
|
// Persistent storage update failed, no way to continue now.
|
2014-01-26 22:13:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-27 14:54:27 +01:00
|
|
|
// Setup new model data for the original item.
|
|
|
|
original_category->setDescription(new_category->description());
|
|
|
|
original_category->setIcon(new_category->icon());
|
|
|
|
original_category->setTitle(new_category->title());
|
|
|
|
|
|
|
|
if (original_parent != new_parent) {
|
|
|
|
// User edited category and set it new parent item,
|
|
|
|
// se we need to move the item in the model too.
|
|
|
|
int original_index_of_category = original_parent->childItems().indexOf(original_category);
|
|
|
|
int new_index_of_category = new_parent->childCount();
|
|
|
|
|
|
|
|
// Remove the original item from the model...
|
|
|
|
beginRemoveRows(indexForItem(original_parent),
|
|
|
|
original_index_of_category,
|
|
|
|
original_index_of_category);
|
|
|
|
original_parent->removeChild(original_category);
|
2014-01-26 22:13:10 +01:00
|
|
|
endRemoveRows();
|
|
|
|
|
2014-01-27 14:54:27 +01:00
|
|
|
// ... and insert it under the new parent.
|
|
|
|
beginInsertRows(indexForItem(new_parent),
|
|
|
|
new_index_of_category,
|
|
|
|
new_index_of_category);
|
|
|
|
new_parent->appendChild(original_category);
|
2014-01-26 22:13:10 +01:00
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free temporary category from memory.
|
|
|
|
delete new_category;
|
|
|
|
|
2014-01-27 14:54:27 +01:00
|
|
|
// Editing is done.
|
2014-01-26 22:13:10 +01:00
|
|
|
return true;
|
2014-01-07 19:09:19 +01:00
|
|
|
}
|
|
|
|
|
2014-03-26 08:33:50 +01:00
|
|
|
bool FeedsModel::addFeed(FeedsModelFeed *feed,
|
2014-01-30 14:37:28 +01:00
|
|
|
FeedsModelRootItem *parent) {
|
2014-01-30 17:19:45 +01:00
|
|
|
// Get index of parent item (parent standard category).
|
|
|
|
QModelIndex parent_index = indexForItem(parent);
|
|
|
|
|
|
|
|
// Now, add category to persistent storage.
|
|
|
|
// Children are removed, remove this standard category too.
|
|
|
|
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
|
|
|
|
DatabaseFactory::FromSettings);
|
2014-01-30 17:49:51 +01:00
|
|
|
QSqlQuery query_add_feed(database);
|
|
|
|
|
|
|
|
query_add_feed.setForwardOnly(true);
|
|
|
|
query_add_feed.prepare("INSERT INTO Feeds "
|
2014-02-03 16:57:02 +01:00
|
|
|
"(title, description, date_created, icon, category, encoding, url, protected, username, password, update_type, update_interval, type) "
|
|
|
|
"VALUES (:title, :description, :date_created, :icon, :category, :encoding, :url, :protected, :username, :password, :update_type, :update_interval, :type);");
|
2014-01-30 17:49:51 +01:00
|
|
|
query_add_feed.bindValue(":title", feed->title());
|
|
|
|
query_add_feed.bindValue(":description", feed->description());
|
|
|
|
query_add_feed.bindValue(":date_created", feed->creationDate().toMSecsSinceEpoch());
|
|
|
|
query_add_feed.bindValue(":icon", IconFactory::toByteArray(feed->icon()));
|
|
|
|
query_add_feed.bindValue(":category", parent->id());
|
|
|
|
query_add_feed.bindValue(":encoding", feed->encoding());
|
|
|
|
query_add_feed.bindValue(":url", feed->url());
|
2014-02-01 10:05:28 +01:00
|
|
|
query_add_feed.bindValue(":protected", (int) feed->passwordProtected());
|
|
|
|
query_add_feed.bindValue(":username", feed->username());
|
|
|
|
query_add_feed.bindValue(":password", feed->password());
|
2014-02-03 16:57:02 +01:00
|
|
|
query_add_feed.bindValue(":update_type", (int) feed->autoUpdateType());
|
2014-02-04 10:02:07 +01:00
|
|
|
query_add_feed.bindValue(":update_interval", feed->autoUpdateInitialInterval());
|
2014-02-18 14:15:59 +01:00
|
|
|
query_add_feed.bindValue(":type", (int) feed->type());
|
2014-01-30 17:49:51 +01:00
|
|
|
|
|
|
|
if (!query_add_feed.exec()) {
|
2014-01-30 17:19:45 +01:00
|
|
|
// Query failed.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-30 17:49:51 +01:00
|
|
|
query_add_feed.prepare("SELECT id FROM Feeds WHERE date_created = :date_created;");
|
|
|
|
query_add_feed.bindValue(":date_created", feed->creationDate().toMSecsSinceEpoch());
|
|
|
|
if (query_add_feed.exec() && query_add_feed.next()) {
|
2014-01-30 17:19:45 +01:00
|
|
|
// New category was added, fetch is primary id
|
|
|
|
// from the database.
|
2014-01-30 17:49:51 +01:00
|
|
|
feed->setId(query_add_feed.value(0).toInt());
|
2014-01-30 17:19:45 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Something failed.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Category was added to the persistent storage,
|
|
|
|
// so add it to the model.
|
|
|
|
beginInsertRows(parent_index, parent->childCount(), parent->childCount());
|
|
|
|
parent->appendChild(feed);
|
|
|
|
endInsertRows();
|
|
|
|
|
|
|
|
return true;
|
2014-01-30 14:37:28 +01:00
|
|
|
}
|
|
|
|
|
2014-03-26 08:33:50 +01:00
|
|
|
bool FeedsModel::editFeed(FeedsModelFeed *original_feed,
|
|
|
|
FeedsModelFeed *new_feed) {
|
2014-01-30 17:49:51 +01:00
|
|
|
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
|
|
|
|
DatabaseFactory::FromSettings);
|
|
|
|
QSqlQuery query_update_feed(database);
|
|
|
|
FeedsModelRootItem *original_parent = original_feed->parent();
|
|
|
|
FeedsModelRootItem *new_parent = new_feed->parent();
|
|
|
|
|
|
|
|
query_update_feed.setForwardOnly(true);
|
|
|
|
query_update_feed.prepare("UPDATE Feeds "
|
2014-02-03 16:57:02 +01:00
|
|
|
"SET title = :title, description = :description, icon = :icon, category = :category, encoding = :encoding, url = :url, protected = :protected, username = :username, password = :password, update_type = :update_type, update_interval = :update_interval, type = :type "
|
2014-01-30 17:49:51 +01:00
|
|
|
"WHERE id = :id;");
|
|
|
|
query_update_feed.bindValue(":title", new_feed->title());
|
|
|
|
query_update_feed.bindValue(":description", new_feed->description());
|
|
|
|
query_update_feed.bindValue(":icon", IconFactory::toByteArray(new_feed->icon()));
|
|
|
|
query_update_feed.bindValue(":category", new_parent->id());
|
|
|
|
query_update_feed.bindValue(":encoding", new_feed->encoding());
|
|
|
|
query_update_feed.bindValue(":url", new_feed->url());
|
2014-02-01 10:05:28 +01:00
|
|
|
query_update_feed.bindValue(":protected", (int) new_feed->passwordProtected());
|
|
|
|
query_update_feed.bindValue(":username", new_feed->username());
|
|
|
|
query_update_feed.bindValue(":password", new_feed->password());
|
2014-02-03 16:57:02 +01:00
|
|
|
query_update_feed.bindValue(":update_type", (int) new_feed->autoUpdateType());
|
2014-02-04 10:02:07 +01:00
|
|
|
query_update_feed.bindValue(":update_interval", new_feed->autoUpdateInitialInterval());
|
2014-01-30 17:49:51 +01:00
|
|
|
query_update_feed.bindValue(":type", new_feed->type());
|
|
|
|
query_update_feed.bindValue(":id", original_feed->id());
|
|
|
|
|
|
|
|
if (!query_update_feed.exec()) {
|
|
|
|
// Persistent storage update failed, no way to continue now.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup new model data for the original item.
|
|
|
|
original_feed->setTitle(new_feed->title());
|
|
|
|
original_feed->setDescription(new_feed->description());
|
|
|
|
original_feed->setIcon(new_feed->icon());
|
|
|
|
original_feed->setEncoding(new_feed->encoding());
|
|
|
|
original_feed->setDescription(new_feed->description());
|
|
|
|
original_feed->setUrl(new_feed->url());
|
2014-02-01 10:05:28 +01:00
|
|
|
original_feed->setPasswordProtected(new_feed->passwordProtected());
|
|
|
|
original_feed->setUsername(new_feed->username());
|
|
|
|
original_feed->setPassword(new_feed->password());
|
2014-02-03 16:57:02 +01:00
|
|
|
original_feed->setAutoUpdateType(new_feed->autoUpdateType());
|
2014-02-04 10:02:07 +01:00
|
|
|
original_feed->setAutoUpdateInitialInterval(new_feed->autoUpdateInitialInterval());
|
2014-01-30 17:49:51 +01:00
|
|
|
original_feed->setType(new_feed->type());
|
|
|
|
|
|
|
|
if (original_parent != new_parent) {
|
|
|
|
// User edited category and set it new parent item,
|
|
|
|
// se we need to move the item in the model too.
|
|
|
|
int original_index_of_feed = original_parent->childItems().indexOf(original_feed);
|
|
|
|
int new_index_of_feed = new_parent->childCount();
|
|
|
|
|
|
|
|
// Remove the original item from the model...
|
|
|
|
beginRemoveRows(indexForItem(original_parent),
|
|
|
|
original_index_of_feed,
|
|
|
|
original_index_of_feed);
|
|
|
|
original_parent->removeChild(original_feed);
|
|
|
|
endRemoveRows();
|
|
|
|
|
|
|
|
// ... and insert it under the new parent.
|
|
|
|
beginInsertRows(indexForItem(new_parent),
|
|
|
|
new_index_of_feed,
|
|
|
|
new_index_of_feed);
|
|
|
|
new_parent->appendChild(original_feed);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free temporary category from memory.
|
|
|
|
delete new_feed;
|
|
|
|
|
|
|
|
// Editing is done.
|
|
|
|
return true;
|
2014-01-30 14:37:28 +01:00
|
|
|
}
|
|
|
|
|
2014-02-04 18:55:37 +01:00
|
|
|
QList<FeedsModelFeed*> FeedsModel::feedsForScheduledUpdate(bool auto_update_now) {
|
2014-02-04 10:02:07 +01:00
|
|
|
QList<FeedsModelFeed*> feeds_for_update;
|
|
|
|
|
|
|
|
foreach (FeedsModelFeed *feed, allFeeds()) {
|
2014-03-26 08:33:50 +01:00
|
|
|
switch (feed->autoUpdateType()) {
|
|
|
|
case FeedsModelFeed::DontAutoUpdate:
|
2014-02-04 10:02:07 +01:00
|
|
|
// Do not auto-update this feed ever.
|
|
|
|
continue;
|
|
|
|
|
2014-03-26 08:33:50 +01:00
|
|
|
case FeedsModelFeed::DefaultAutoUpdate:
|
2014-02-04 18:55:37 +01:00
|
|
|
if (auto_update_now) {
|
2014-02-04 10:02:07 +01:00
|
|
|
feeds_for_update.append(feed);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-03-26 08:33:50 +01:00
|
|
|
case FeedsModelFeed::SpecificAutoUpdate:
|
2014-02-04 10:02:07 +01:00
|
|
|
default:
|
2014-03-26 08:33:50 +01:00
|
|
|
int remaining_interval = feed->autoUpdateRemainingInterval();
|
2014-02-04 10:02:07 +01:00
|
|
|
|
|
|
|
if (--remaining_interval <= 0) {
|
|
|
|
// Interval of this feed passed, include this feed in the output list
|
|
|
|
// and reset the interval.
|
|
|
|
feeds_for_update.append(feed);
|
2014-03-26 08:33:50 +01:00
|
|
|
feed->setAutoUpdateRemainingInterval(feed->autoUpdateInitialInterval());
|
2014-02-04 10:02:07 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Interval did not pass, set new decremented interval and do NOT
|
|
|
|
// include this feed in the output list.
|
2014-03-26 08:33:50 +01:00
|
|
|
feed->setAutoUpdateRemainingInterval(remaining_interval);
|
2014-02-04 10:02:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return feeds_for_update;
|
|
|
|
}
|
|
|
|
|
2014-01-10 08:36:08 +01:00
|
|
|
QList<Message> FeedsModel::messagesForFeeds(const QList<FeedsModelFeed*> &feeds) {
|
2014-01-04 14:09:38 +01:00
|
|
|
QList<Message> messages;
|
|
|
|
|
2014-01-20 07:41:58 +01:00
|
|
|
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
|
2014-01-21 07:55:10 +01:00
|
|
|
DatabaseFactory::FromSettings);
|
2014-01-04 14:09:38 +01:00
|
|
|
QSqlQuery query_read_msg(database);
|
|
|
|
query_read_msg.setForwardOnly(true);
|
|
|
|
query_read_msg.prepare("SELECT title, url, author, date_created, contents "
|
|
|
|
"FROM Messages "
|
2014-02-07 16:57:16 +01:00
|
|
|
"WHERE is_deleted = 0 AND feed = :feed;");
|
2014-01-04 14:09:38 +01:00
|
|
|
|
|
|
|
foreach (FeedsModelFeed *feed, feeds) {
|
|
|
|
query_read_msg.bindValue(":feed", feed->id());
|
|
|
|
|
|
|
|
if (query_read_msg.exec()) {
|
|
|
|
while (query_read_msg.next()) {
|
|
|
|
Message message;
|
|
|
|
|
|
|
|
message.m_title = query_read_msg.value(0).toString();
|
|
|
|
message.m_url = query_read_msg.value(1).toString();
|
|
|
|
message.m_author = query_read_msg.value(2).toString();
|
|
|
|
message.m_created = TextFactory::parseDateTime(query_read_msg.value(3).value<qint64>());
|
|
|
|
message.m_contents = query_read_msg.value(4).toString();
|
|
|
|
|
|
|
|
messages.append(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
2013-12-11 18:54:09 +01:00
|
|
|
int FeedsModel::columnCount(const QModelIndex &parent) const {
|
|
|
|
if (parent.isValid()) {
|
2013-12-12 08:27:05 +01:00
|
|
|
return static_cast<FeedsModelRootItem*>(parent.internalPointer())->columnCount();
|
2013-12-11 18:54:09 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return m_rootItem->columnCount();
|
|
|
|
}
|
|
|
|
}
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2013-12-19 13:52:53 +01:00
|
|
|
FeedsModelRootItem *FeedsModel::itemForIndex(const QModelIndex &index) const {
|
2013-12-16 20:14:53 +01:00
|
|
|
if (index.isValid() && index.model() == this) {
|
|
|
|
return static_cast<FeedsModelRootItem*>(index.internalPointer());
|
|
|
|
}
|
|
|
|
else {
|
2013-12-31 14:25:49 +01:00
|
|
|
return m_rootItem;
|
2013-12-16 20:14:53 +01:00
|
|
|
}
|
2013-12-14 18:54:35 +01:00
|
|
|
}
|
|
|
|
|
2013-12-30 19:28:39 +01:00
|
|
|
FeedsModelCategory *FeedsModel::categoryForIndex(const QModelIndex &index) const {
|
|
|
|
FeedsModelRootItem *item = itemForIndex(index);
|
|
|
|
|
|
|
|
if (item->kind() == FeedsModelRootItem::Category) {
|
|
|
|
return static_cast<FeedsModelCategory*>(item);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-31 14:25:49 +01:00
|
|
|
|
2013-12-31 13:19:25 +01:00
|
|
|
QModelIndex FeedsModel::indexForItem(FeedsModelRootItem *item) const {
|
2013-12-31 14:25:49 +01:00
|
|
|
if (item == NULL || item->kind() == FeedsModelRootItem::RootItem) {
|
2013-12-31 13:19:25 +01:00
|
|
|
// Root item lies on invalid index.
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
2013-12-31 14:33:42 +01:00
|
|
|
QList<QModelIndex> parents;
|
2013-12-31 13:19:25 +01:00
|
|
|
|
2013-12-31 14:33:42 +01:00
|
|
|
// Start with root item.
|
|
|
|
parents << indexForItem(m_rootItem);
|
2013-12-31 13:19:25 +01:00
|
|
|
|
|
|
|
while (!parents.isEmpty()) {
|
|
|
|
QModelIndex active_index = parents.takeFirst();
|
|
|
|
int row_count = rowCount(active_index);
|
|
|
|
|
|
|
|
if (row_count > 0) {
|
|
|
|
// This index has children.
|
|
|
|
// Lets take a look if our target item is among them.
|
|
|
|
FeedsModelRootItem *active_item = itemForIndex(active_index);
|
|
|
|
int candidate_index = active_item->childItems().indexOf(item);
|
|
|
|
|
|
|
|
if (candidate_index >= 0) {
|
|
|
|
// We found our item.
|
|
|
|
return index(candidate_index, 0, active_index);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Item is not found, add all "categories" from active_item.
|
|
|
|
for (int i = 0; i < row_count; i++) {
|
|
|
|
FeedsModelRootItem *possible_category = active_item->child(i);
|
|
|
|
|
|
|
|
if (possible_category->kind() == FeedsModelRootItem::Category) {
|
|
|
|
parents << index(i, 0, active_index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:52:53 +01:00
|
|
|
void FeedsModel::reloadChangedLayout(QModelIndexList list) {
|
2013-12-18 14:15:34 +01:00
|
|
|
while (!list.isEmpty()) {
|
2014-01-03 09:00:51 +01:00
|
|
|
QModelIndex indx = list.takeFirst();
|
2014-01-03 07:45:42 +01:00
|
|
|
QModelIndex indx_parent = indx.parent();
|
2013-12-19 08:54:31 +01:00
|
|
|
|
|
|
|
// Underlying data are changed.
|
2014-01-03 07:45:42 +01:00
|
|
|
emit dataChanged(index(indx.row(), 0, indx_parent),
|
|
|
|
index(indx.row(), FDS_MODEL_COUNTS_INDEX, indx_parent));
|
2013-12-19 08:54:31 +01:00
|
|
|
|
2014-01-03 07:45:42 +01:00
|
|
|
if (indx_parent.isValid()) {
|
2013-12-19 08:54:31 +01:00
|
|
|
// Make sure that data of parent are changed too.
|
2014-01-03 09:00:51 +01:00
|
|
|
list.prepend(indx_parent);
|
2013-12-18 14:15:34 +01:00
|
|
|
}
|
|
|
|
}
|
2013-12-19 13:52:53 +01:00
|
|
|
}
|
2013-12-18 14:15:34 +01:00
|
|
|
|
2013-12-28 11:22:00 +01:00
|
|
|
QStringList FeedsModel::textualFeedIds(const QList<FeedsModelFeed*> &feeds) {
|
|
|
|
QStringList stringy_ids;
|
|
|
|
stringy_ids.reserve(feeds.size());
|
|
|
|
|
|
|
|
foreach (FeedsModelFeed *feed, feeds) {
|
|
|
|
stringy_ids.append(QString::number(feed->id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return stringy_ids;
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:52:53 +01:00
|
|
|
void FeedsModel::reloadWholeLayout() {
|
2013-12-18 08:51:35 +01:00
|
|
|
emit layoutAboutToBeChanged();
|
|
|
|
emit layoutChanged();
|
|
|
|
}
|
|
|
|
|
2013-12-13 20:48:45 +01:00
|
|
|
void FeedsModel::loadFromDatabase() {
|
2013-12-17 17:08:11 +01:00
|
|
|
// Delete all childs of the root node and clear them from the memory.
|
2013-12-16 10:22:23 +01:00
|
|
|
qDeleteAll(m_rootItem->childItems());
|
2014-01-14 14:42:20 +01:00
|
|
|
m_rootItem->clearChildren();
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2014-01-20 07:41:58 +01:00
|
|
|
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
|
2014-01-21 07:55:10 +01:00
|
|
|
DatabaseFactory::FromSettings);
|
2013-12-14 11:45:43 +01:00
|
|
|
CategoryAssignment categories;
|
|
|
|
FeedAssignment feeds;
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2013-12-14 11:45:43 +01:00
|
|
|
// Obtain data for categories from the database.
|
2013-12-31 17:33:42 +01:00
|
|
|
QSqlQuery query_categories(database);
|
|
|
|
query_categories.setForwardOnly(true);
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2013-12-31 17:33:42 +01:00
|
|
|
if (!query_categories.exec("SELECT * FROM Categories;") ||
|
|
|
|
query_categories.lastError().isValid()) {
|
2014-01-11 17:53:43 +01:00
|
|
|
qFatal("Query for obtaining categories failed. Error message: '%s'.",
|
|
|
|
qPrintable(query_categories.lastError().text()));
|
2013-12-13 20:48:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
while (query_categories.next()) {
|
|
|
|
// Process this category.
|
|
|
|
FeedsModelCategory::Type type = static_cast<FeedsModelCategory::Type>(query_categories.value(CAT_DB_TYPE_INDEX).toInt());
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case FeedsModelCategory::Standard: {
|
2013-12-14 11:45:43 +01:00
|
|
|
CategoryAssignmentItem pair;
|
2013-12-13 20:48:45 +01:00
|
|
|
pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt();
|
2014-03-26 08:33:50 +01:00
|
|
|
pair.second = FeedsModelCategory::loadFromRecord(query_categories.record());
|
2013-12-13 20:48:45 +01:00
|
|
|
|
|
|
|
categories << pair;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All categories are now loaded.
|
2013-12-31 17:33:42 +01:00
|
|
|
QSqlQuery query_feeds(database);
|
|
|
|
query_feeds.setForwardOnly(true);
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2013-12-31 17:33:42 +01:00
|
|
|
if (!query_feeds.exec("SELECT * FROM Feeds;") ||
|
|
|
|
query_feeds.lastError().isValid()) {
|
2013-12-13 20:48:45 +01:00
|
|
|
qFatal("Query for obtaining feeds failed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
while (query_feeds.next()) {
|
|
|
|
// Process this feed.
|
|
|
|
FeedsModelFeed::Type type = static_cast<FeedsModelFeed::Type>(query_feeds.value(FDS_DB_TYPE_INDEX).toInt());
|
|
|
|
|
|
|
|
switch (type) {
|
2014-03-26 08:33:50 +01:00
|
|
|
case FeedsModelFeed::Atom10:
|
|
|
|
case FeedsModelFeed::Rdf:
|
|
|
|
case FeedsModelFeed::Rss0X:
|
|
|
|
case FeedsModelFeed::Rss2X: {
|
2013-12-14 11:45:43 +01:00
|
|
|
FeedAssignmentItem pair;
|
2013-12-13 20:48:45 +01:00
|
|
|
pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt();
|
2014-03-26 08:33:50 +01:00
|
|
|
pair.second = FeedsModelFeed::loadFromRecord(query_feeds.record());
|
2013-12-14 11:45:43 +01:00
|
|
|
pair.second->setType(type);
|
|
|
|
|
|
|
|
feeds << pair;
|
2013-12-13 20:48:45 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-12-14 11:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// All data are now obtained, lets create the hierarchy.
|
|
|
|
assembleCategories(categories);
|
|
|
|
assembleFeeds(feeds);
|
|
|
|
}
|
|
|
|
|
2013-12-16 12:53:48 +01:00
|
|
|
QList<FeedsModelFeed*> FeedsModel::feedsForIndex(const QModelIndex &index) {
|
|
|
|
FeedsModelRootItem *item = itemForIndex(index);
|
2014-01-04 14:09:38 +01:00
|
|
|
return feedsForItem(item);
|
2013-12-16 12:53:48 +01:00
|
|
|
}
|
|
|
|
|
2013-12-30 19:28:39 +01:00
|
|
|
FeedsModelFeed *FeedsModel::feedForIndex(const QModelIndex &index) {
|
|
|
|
FeedsModelRootItem *item = itemForIndex(index);
|
|
|
|
|
|
|
|
if (item->kind() == FeedsModelRootItem::Feed) {
|
|
|
|
return static_cast<FeedsModelFeed*>(item);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-28 16:00:27 +01:00
|
|
|
QList<FeedsModelFeed*> FeedsModel::feedsForIndexes(const QModelIndexList &indexes) {
|
|
|
|
QList<FeedsModelFeed*> feeds;
|
|
|
|
|
|
|
|
// Get selected feeds for each index.
|
|
|
|
foreach (const QModelIndex &index, indexes) {
|
|
|
|
feeds.append(feedsForIndex(index));
|
|
|
|
}
|
2013-12-26 15:15:28 +01:00
|
|
|
|
2013-12-28 16:00:27 +01:00
|
|
|
// Now we obtained all feeds from corresponding indexes.
|
|
|
|
if (indexes.size() != feeds.size()) {
|
|
|
|
// Selection contains duplicate feeds (for
|
|
|
|
// example situation where feed and its parent category are both
|
|
|
|
// selected). So, remove duplicates from the list.
|
|
|
|
qSort(feeds.begin(), feeds.end(), FeedsModelRootItem::lessThan);
|
2014-01-10 08:36:08 +01:00
|
|
|
feeds.erase(std::unique(feeds.begin(),
|
|
|
|
feeds.end(), FeedsModelRootItem::isEqual),
|
2013-12-28 16:00:27 +01:00
|
|
|
feeds.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
return feeds;
|
2013-12-26 15:15:28 +01:00
|
|
|
}
|
|
|
|
|
2013-12-28 11:22:00 +01:00
|
|
|
bool FeedsModel::markFeedsRead(const QList<FeedsModelFeed*> &feeds,
|
|
|
|
int read) {
|
2014-01-20 07:41:58 +01:00
|
|
|
QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(),
|
2014-01-21 07:55:10 +01:00
|
|
|
DatabaseFactory::FromSettings);
|
2013-12-28 11:22:00 +01:00
|
|
|
|
|
|
|
if (!db_handle.transaction()) {
|
|
|
|
qWarning("Starting transaction for feeds read change.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSqlQuery query_read_msg(db_handle);
|
2013-12-31 17:33:42 +01:00
|
|
|
query_read_msg.setForwardOnly(true);
|
|
|
|
|
2014-02-07 16:57:16 +01:00
|
|
|
if (!query_read_msg.prepare(QString("UPDATE Messages SET is_read = :read "
|
|
|
|
"WHERE feed IN (%1) AND is_deleted = 0;").arg(textualFeedIds(feeds).join(", ")))) {
|
2013-12-28 11:22:00 +01:00
|
|
|
qWarning("Query preparation failed for feeds read change.");
|
|
|
|
|
|
|
|
db_handle.rollback();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
query_read_msg.bindValue(":read", read);
|
|
|
|
|
|
|
|
if (!query_read_msg.exec()) {
|
|
|
|
qDebug("Query execution for feeds read change failed.");
|
|
|
|
db_handle.rollback();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Commit changes.
|
|
|
|
if (db_handle.commit()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return db_handle.rollback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-02 14:44:00 +01:00
|
|
|
bool FeedsModel::markFeedsDeleted(const QList<FeedsModelFeed*> &feeds,
|
|
|
|
int deleted,
|
|
|
|
bool read_only) {
|
2014-01-20 07:41:58 +01:00
|
|
|
QSqlDatabase db_handle = DatabaseFactory::instance()->connection(objectName(),
|
2014-01-21 07:55:10 +01:00
|
|
|
DatabaseFactory::FromSettings);
|
2013-12-28 11:22:00 +01:00
|
|
|
|
|
|
|
if (!db_handle.transaction()) {
|
|
|
|
qWarning("Starting transaction for feeds clearing.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSqlQuery query_delete_msg(db_handle);
|
2013-12-31 17:33:42 +01:00
|
|
|
query_delete_msg.setForwardOnly(true);
|
|
|
|
|
2014-02-02 14:44:00 +01:00
|
|
|
if (read_only) {
|
2014-02-07 16:57:16 +01:00
|
|
|
if (!query_delete_msg.prepare(QString("UPDATE Messages SET is_deleted = :deleted "
|
|
|
|
"WHERE feed IN (%1) AND is_deleted = 0 AND is_read = 1;").arg(textualFeedIds(feeds).join(", ")))) {
|
2014-02-02 14:44:00 +01:00
|
|
|
qWarning("Query preparation failed for feeds clearing.");
|
2013-12-28 11:22:00 +01:00
|
|
|
|
2014-02-02 14:44:00 +01:00
|
|
|
db_handle.rollback();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2014-02-07 16:57:16 +01:00
|
|
|
if (!query_delete_msg.prepare(QString("UPDATE Messages SET is_deleted = :deleted "
|
|
|
|
"WHERE feed IN (%1) AND is_deleted = 0;").arg(textualFeedIds(feeds).join(", ")))) {
|
2014-02-02 14:44:00 +01:00
|
|
|
qWarning("Query preparation failed for feeds clearing.");
|
|
|
|
|
|
|
|
db_handle.rollback();
|
|
|
|
return false;
|
|
|
|
}
|
2013-12-28 11:22:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
query_delete_msg.bindValue(":deleted", deleted);
|
|
|
|
|
|
|
|
if (!query_delete_msg.exec()) {
|
|
|
|
qDebug("Query execution for feeds clearing failed.");
|
|
|
|
db_handle.rollback();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Commit changes.
|
|
|
|
if (db_handle.commit()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return db_handle.rollback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-04 14:09:38 +01:00
|
|
|
QHash<int, FeedsModelCategory*> FeedsModel::allCategories() {
|
|
|
|
return categoriesForItem(m_rootItem);
|
2013-12-22 21:31:36 +01:00
|
|
|
}
|
|
|
|
|
2014-01-04 14:09:38 +01:00
|
|
|
QHash<int, FeedsModelCategory*> FeedsModel::categoriesForItem(FeedsModelRootItem *root) {
|
2013-12-14 11:45:43 +01:00
|
|
|
QHash<int, FeedsModelCategory*> categories;
|
2014-01-02 22:07:02 +01:00
|
|
|
QList<FeedsModelRootItem*> parents;
|
2013-12-14 11:45:43 +01:00
|
|
|
|
2014-01-02 22:07:02 +01:00
|
|
|
parents.append(root->childItems());
|
2013-12-14 11:45:43 +01:00
|
|
|
|
2014-01-02 22:07:02 +01:00
|
|
|
while (!parents.isEmpty()) {
|
|
|
|
FeedsModelRootItem *item = parents.takeFirst();
|
|
|
|
|
|
|
|
if (item->kind() == FeedsModelRootItem::Category) {
|
|
|
|
// This item is category, add it to the output list and
|
|
|
|
// scan its children.
|
|
|
|
int category_id = item->id();
|
|
|
|
FeedsModelCategory *category = static_cast<FeedsModelCategory*>(item);
|
|
|
|
|
|
|
|
if (!categories.contains(category_id)) {
|
|
|
|
categories.insert(category_id, category);
|
|
|
|
}
|
2013-12-14 11:45:43 +01:00
|
|
|
|
2014-01-02 22:07:02 +01:00
|
|
|
parents.append(category->childItems());
|
2013-12-14 11:45:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return categories;
|
|
|
|
}
|
|
|
|
|
2014-01-04 14:09:38 +01:00
|
|
|
QList<FeedsModelFeed*> FeedsModel::allFeeds() {
|
|
|
|
return feedsForItem(m_rootItem);
|
2013-12-22 21:31:36 +01:00
|
|
|
}
|
|
|
|
|
2014-01-04 14:09:38 +01:00
|
|
|
QList<FeedsModelFeed*> FeedsModel::feedsForItem(FeedsModelRootItem *root) {
|
2013-12-22 21:31:36 +01:00
|
|
|
QList<FeedsModelFeed*> feeds;
|
|
|
|
|
|
|
|
if (root->kind() == FeedsModelRootItem::Feed) {
|
|
|
|
// Root itself is a FEED.
|
|
|
|
feeds.append(static_cast<FeedsModelFeed*>(root));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Root itself is a CATEGORY or ROOT item.
|
2013-12-31 17:33:42 +01:00
|
|
|
QList<FeedsModelRootItem*> traversable_items;
|
2013-12-22 21:31:36 +01:00
|
|
|
|
2013-12-31 17:33:42 +01:00
|
|
|
traversable_items.append(root);
|
2013-12-22 21:31:36 +01:00
|
|
|
|
|
|
|
// Iterate all nested categories.
|
|
|
|
while (!traversable_items.isEmpty()) {
|
2013-12-31 17:33:42 +01:00
|
|
|
FeedsModelRootItem *active_category = traversable_items.takeFirst();
|
2013-12-22 21:31:36 +01:00
|
|
|
|
|
|
|
foreach (FeedsModelRootItem *child, active_category->childItems()) {
|
|
|
|
if (child->kind() == FeedsModelRootItem::Feed) {
|
|
|
|
// This child is feed.
|
|
|
|
feeds.append(static_cast<FeedsModelFeed*>(child));
|
|
|
|
}
|
|
|
|
else if (child->kind() == FeedsModelRootItem::Category) {
|
|
|
|
// This child is category, add its child feeds too.
|
2013-12-31 17:33:42 +01:00
|
|
|
traversable_items.append(static_cast<FeedsModelCategory*>(child));
|
2013-12-22 21:31:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return feeds;
|
2013-12-14 11:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FeedsModel::assembleFeeds(FeedAssignment feeds) {
|
2014-01-04 14:09:38 +01:00
|
|
|
QHash<int, FeedsModelCategory*> categories = allCategories();
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2013-12-21 21:08:52 +01:00
|
|
|
foreach (const FeedAssignmentItem &feed, feeds) {
|
2013-12-14 11:45:43 +01:00
|
|
|
if (feed.first == NO_PARENT_CATEGORY) {
|
|
|
|
// This is top-level feed, add it to the root item.
|
|
|
|
m_rootItem->appendChild(feed.second);
|
|
|
|
}
|
2013-12-14 13:45:24 +01:00
|
|
|
else if (categories.contains(feed.first)) {
|
2013-12-22 21:31:36 +01:00
|
|
|
// This feed belongs to this category.
|
2013-12-14 11:45:43 +01:00
|
|
|
categories.value(feed.first)->appendChild(feed.second);
|
|
|
|
}
|
2013-12-14 13:45:24 +01:00
|
|
|
else {
|
|
|
|
qWarning("Feed '%s' is loose, skipping it.",
|
|
|
|
qPrintable(feed.second->title()));
|
|
|
|
}
|
2013-12-13 20:48:45 +01:00
|
|
|
}
|
2013-12-14 11:45:43 +01:00
|
|
|
}
|
2013-12-13 20:48:45 +01:00
|
|
|
|
2013-12-14 11:45:43 +01:00
|
|
|
void FeedsModel::assembleCategories(CategoryAssignment categories) {
|
|
|
|
QHash<int, FeedsModelRootItem*> assignments;
|
2013-12-21 09:14:03 +01:00
|
|
|
assignments.insert(NO_PARENT_CATEGORY, m_rootItem);
|
2013-12-14 11:45:43 +01:00
|
|
|
|
|
|
|
// Add top-level categories.
|
|
|
|
while (!categories.isEmpty()) {
|
|
|
|
for (int i = 0; i < categories.size(); i++) {
|
|
|
|
if (assignments.contains(categories.at(i).first)) {
|
|
|
|
// Parent category of this category is already added.
|
|
|
|
assignments.value(categories.at(i).first)->appendChild(categories.at(i).second);
|
|
|
|
|
|
|
|
// Now, added category can be parent for another categories, add it.
|
|
|
|
assignments.insert(categories.at(i).second->id(),
|
|
|
|
categories.at(i).second);
|
|
|
|
|
|
|
|
// Remove the category from the list, because it was
|
|
|
|
// added to the final collection.
|
|
|
|
categories.removeAt(i);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-13 20:48:45 +01:00
|
|
|
}
|