From 99ec71f4c4e44206a7e0cb61eaf778445c67f0b3 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Mon, 30 May 2016 07:36:34 +0200 Subject: [PATCH] =?UTF-8?q?Refactored=20feed=20c=E1=B8=A7ecking=20models.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/abstract/accountcheckmodel.cpp | 283 ++++++++++++++++++ src/services/abstract/accountcheckmodel.h | 68 +++++ .../standardfeedsimportexportmodel.cpp | 253 +--------------- .../standard/standardfeedsimportexportmodel.h | 41 +-- 4 files changed, 358 insertions(+), 287 deletions(-) create mode 100644 src/services/abstract/accountcheckmodel.cpp create mode 100644 src/services/abstract/accountcheckmodel.h diff --git a/src/services/abstract/accountcheckmodel.cpp b/src/services/abstract/accountcheckmodel.cpp new file mode 100644 index 000000000..e806554f1 --- /dev/null +++ b/src/services/abstract/accountcheckmodel.cpp @@ -0,0 +1,283 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// 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 . + +#include "services/abstract/accountcheckmodel.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" + + +AccountCheckModel::AccountCheckModel(QObject *parent) + : QAbstractItemModel(parent), m_checkStates(QHash()), + m_rootItem(NULL), m_recursiveChange(false) { +} + +AccountCheckModel::~AccountCheckModel() { +} + +RootItem *AccountCheckModel::itemForIndex(const QModelIndex &index) const { + if (index.isValid() && index.model() == this) { + return static_cast(index.internalPointer()); + } + else { + return m_rootItem; + } +} + +RootItem *AccountCheckModel::rootItem() const { + return m_rootItem; +} + +void AccountCheckModel::setRootItem(RootItem *root_item) { + if (m_rootItem != NULL) { + delete m_rootItem; + } + + m_rootItem = root_item; +} + +void AccountCheckModel::checkAllItems() { + if (m_rootItem != NULL) { + foreach (RootItem *root_child, m_rootItem->childItems()) { + if (root_child->kind() == RootItemKind::Feed || root_child->kind() == RootItemKind::Category) { + setItemChecked(root_child, Qt::Checked); + } + } + } +} + +void AccountCheckModel::uncheckAllItems() { + if (m_rootItem != NULL) { + foreach (RootItem *root_child, m_rootItem->childItems()) { + if (root_child->kind() == RootItemKind::Feed || root_child->kind() == RootItemKind::Category) { + setData(indexForItem(root_child), Qt::Unchecked, Qt::CheckStateRole); + } + } + } +} + +QModelIndex AccountCheckModel::index(int row, int column, const QModelIndex &parent) const { + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + RootItem *parent_item = itemForIndex(parent); + RootItem *child_item = parent_item->child(row); + + if (child_item) { + return createIndex(row, column, child_item); + } + else { + return QModelIndex(); + } +} + +QModelIndex AccountCheckModel::indexForItem(RootItem *item) const { + if (item == NULL || item->kind() == RootItemKind::ServiceRoot || item->kind() == RootItemKind::Root) { + // Root item lies on invalid index. + return QModelIndex(); + } + + QList parents; + + // Start with root item (which obviously has invalid index). + parents << indexForItem(m_rootItem); + + 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. + RootItem *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++) { + RootItem *possible_category = active_item->child(i); + + if (possible_category->kind() == RootItemKind::Category) { + parents << index(i, 0, active_index); + } + } + } + } + } + + return QModelIndex(); +} + +QModelIndex AccountCheckModel::parent(const QModelIndex &child) const { + if (!child.isValid()) { + return QModelIndex(); + } + + RootItem *child_item = itemForIndex(child); + RootItem *parent_item = child_item->parent(); + + if (parent_item == m_rootItem) { + return QModelIndex(); + } + else { + return createIndex(parent_item->row(), 0, parent_item); + } +} + +int AccountCheckModel::rowCount(const QModelIndex &parent) const { + if (parent.column() > 0) { + return 0; + } + else { + RootItem *item = itemForIndex(parent); + + if (item != NULL) { + return item->childCount(); + } + else { + return 0; + } + } +} + +int AccountCheckModel::columnCount(const QModelIndex &parent) const { + Q_UNUSED(parent) + return 1; +} + +QVariant AccountCheckModel::data(const QModelIndex &index, int role) const { + if (index.column() != 0) { + return QVariant(); + } + + RootItem *item = itemForIndex(index); + + if (role == Qt::CheckStateRole) { + if (m_checkStates.contains(item)) { + return m_checkStates.value(item); + } + else { + return static_cast(Qt::Unchecked); + } + } + else if (role == Qt::DecorationRole) { + switch (item->kind()) { + case RootItemKind::Category: + case RootItemKind::Bin: + case RootItemKind::Feed: + return item->icon(); + + default: + return QVariant(); + } + } + else if (role == Qt::DisplayRole) { + switch (item->kind()) { + case RootItemKind::Category: + return QVariant(item->data(index.column(), role).toString() + tr(" (category)")); + + case RootItemKind::Feed: + return QVariant(item->data(index.column(), role).toString() + tr(" (feed)")); + + default: + return item->title(); + } + } + else { + return QVariant(); + } +} + +bool AccountCheckModel::setData(const QModelIndex &index, const QVariant &value, int role) { + if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) { + RootItem *item = itemForIndex(index); + + if (item == m_rootItem) { + // Cannot set data on root item. + return false; + } + + // Change data for the actual item. + m_checkStates[item] = static_cast(value.toInt()); + emit dataChanged(index, index); + + if (m_recursiveChange) { + return true; + } + + // Set new data for all descendants of this actual item. + foreach(RootItem *child, item->childItems()) { + setData(indexForItem(child), value, Qt::CheckStateRole); + } + + // Now we need to change new data to all parents. + QModelIndex parent_index = index; + m_recursiveChange = true; + + // Iterate all valid parents. + while ((parent_index = parent_index.parent()).isValid()) { + // We now have parent index. Get parent item too. + item = item->parent(); + + // Check children of this new parent item. + Qt::CheckState parent_state = Qt::Unchecked; + foreach (RootItem *child_of_parent, item->childItems()) { + if (m_checkStates.contains(child_of_parent) && m_checkStates[child_of_parent] == Qt::Checked) { + // We found out, that some child of this item is checked, + // therefore this item must be checked too. + parent_state = Qt::Checked; + break; + } + } + + setData(parent_index, parent_state, Qt::CheckStateRole); + } + + m_recursiveChange = false; + return true; + } + + return false; +} + +Qt::ItemFlags AccountCheckModel::flags(const QModelIndex &index) const { + if (!index.isValid() || itemForIndex(index)->kind() == RootItemKind::Bin) { + return Qt::NoItemFlags; + } + + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if ( index.column() == 0 ) { + flags |= Qt::ItemIsUserCheckable; + } + + return flags; +} + +bool AccountCheckModel::isItemChecked(RootItem *item) { + return m_checkStates.contains(item) && m_checkStates.value(item, Qt::Unchecked); +} + +bool AccountCheckModel::setItemChecked(RootItem *item, Qt::CheckState check) { + return setData(indexForItem(item), check, Qt::CheckStateRole); +} diff --git a/src/services/abstract/accountcheckmodel.h b/src/services/abstract/accountcheckmodel.h new file mode 100644 index 000000000..48bdbef39 --- /dev/null +++ b/src/services/abstract/accountcheckmodel.h @@ -0,0 +1,68 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// 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 . + +#ifndef ACCOUNTCHECKMODEL_H +#define ACCOUNTCHECKMODEL_H + +#include + +#include "services/abstract/rootitem.h" + + +class AccountCheckModel : public QAbstractItemModel { + Q_OBJECT + + public: + // Constructors and destructors. + explicit AccountCheckModel(QObject *parent = 0); + virtual ~AccountCheckModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + Qt::ItemFlags flags(const QModelIndex &index) const; + + bool isItemChecked(RootItem *item); + bool setItemChecked(RootItem *item, Qt::CheckState check); + + // Returns feed/category which lies at the specified index or + // root item if index is invalid. + RootItem *itemForIndex(const QModelIndex &index) const; + + // Returns source QModelIndex on which lies given item. + QModelIndex indexForItem(RootItem *item) const; + + // Root item manipulators. + RootItem *rootItem() const; + void setRootItem(RootItem *root_item); + + public slots: + void checkAllItems(); + void uncheckAllItems(); + + protected: + RootItem *m_rootItem; + + private: + QHash m_checkStates; + bool m_recursiveChange; +}; + +#endif // ACCOUNTCHECKMODEL_H diff --git a/src/services/standard/standardfeedsimportexportmodel.cpp b/src/services/standard/standardfeedsimportexportmodel.cpp index 3032809d6..351264ac4 100755 --- a/src/services/standard/standardfeedsimportexportmodel.cpp +++ b/src/services/standard/standardfeedsimportexportmodel.cpp @@ -32,8 +32,7 @@ FeedsImportExportModel::FeedsImportExportModel(QObject *parent) - : QAbstractItemModel(parent), m_checkStates(QHash()), - m_rootItem(NULL), m_recursiveChange(false), m_mode(Import) { + : AccountCheckModel(parent), m_mode(Import) { } FeedsImportExportModel::~FeedsImportExportModel() { @@ -44,27 +43,6 @@ FeedsImportExportModel::~FeedsImportExportModel() { } } -RootItem *FeedsImportExportModel::itemForIndex(const QModelIndex &index) const { - if (index.isValid() && index.model() == this) { - return static_cast(index.internalPointer()); - } - else { - return m_rootItem; - } -} - -RootItem *FeedsImportExportModel::rootItem() const { - return m_rootItem; -} - -void FeedsImportExportModel::setRootItem(RootItem *root_item) { - if (m_rootItem != NULL) { - delete m_rootItem; - } - - m_rootItem = root_item; -} - bool FeedsImportExportModel::exportToOMPL20(QByteArray &result) { QDomDocument opml_document; QDomProcessingInstruction xml_declaration = opml_document.createProcessingInstruction(QSL("xml"), @@ -99,7 +77,7 @@ bool FeedsImportExportModel::exportToOMPL20(QByteArray &result) { RootItem *active_item = items_to_process.pop(); foreach (RootItem *child_item, active_item->childItems()) { - if (!m_checkStates.contains(child_item) || m_checkStates[child_item] != Qt::Checked) { + if (!isItemChecked(child_item)) { continue; } @@ -361,230 +339,3 @@ FeedsImportExportModel::Mode FeedsImportExportModel::mode() const { void FeedsImportExportModel::setMode(const FeedsImportExportModel::Mode &mode) { m_mode = mode; } - -void FeedsImportExportModel::checkAllItems() { - if (m_rootItem != NULL) { - foreach (RootItem *root_child, m_rootItem->childItems()) { - if (root_child->kind() != RootItemKind::Bin) { - setData(indexForItem(root_child), Qt::Checked, Qt::CheckStateRole); - } - } - } -} - -void FeedsImportExportModel::uncheckAllItems() { - if (m_rootItem != NULL) { - foreach (RootItem *root_child, m_rootItem->childItems()) { - if (root_child->kind() != RootItemKind::Bin) { - setData(indexForItem(root_child), Qt::Unchecked, Qt::CheckStateRole); - } - } - } -} - -QModelIndex FeedsImportExportModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) { - return QModelIndex(); - } - - RootItem *parent_item = itemForIndex(parent); - RootItem *child_item = parent_item->child(row); - - if (child_item) { - return createIndex(row, column, child_item); - } - else { - return QModelIndex(); - } -} - -QModelIndex FeedsImportExportModel::indexForItem(RootItem *item) const { - if (item == NULL || item->kind() == RootItemKind::ServiceRoot || item->kind() == RootItemKind::Root) { - // Root item lies on invalid index. - return QModelIndex(); - } - - QList parents; - - // Start with root item (which obviously has invalid index). - parents << indexForItem(m_rootItem); - - 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. - RootItem *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++) { - RootItem *possible_category = active_item->child(i); - - if (possible_category->kind() == RootItemKind::Category) { - parents << index(i, 0, active_index); - } - } - } - } - } - - return QModelIndex(); -} - -QModelIndex FeedsImportExportModel::parent(const QModelIndex &child) const { - if (!child.isValid()) { - return QModelIndex(); - } - - RootItem *child_item = itemForIndex(child); - RootItem *parent_item = child_item->parent(); - - if (parent_item == m_rootItem) { - return QModelIndex(); - } - else { - return createIndex(parent_item->row(), 0, parent_item); - } -} - -int FeedsImportExportModel::rowCount(const QModelIndex &parent) const { - if (parent.column() > 0) { - return 0; - } - else { - RootItem *item = itemForIndex(parent); - - if (item != NULL) { - return item->childCount(); - } - else { - return 0; - } - } -} - -int FeedsImportExportModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent) - return 1; -} - -QVariant FeedsImportExportModel::data(const QModelIndex &index, int role) const { - if (index.column() != 0) { - return QVariant(); - } - - RootItem *item = itemForIndex(index); - - if (role == Qt::CheckStateRole) { - if (m_checkStates.contains(item)) { - return m_checkStates.value(item); - } - else { - return static_cast(Qt::Unchecked); - } - } - else if (role == Qt::DecorationRole) { - switch (item->kind()) { - case RootItemKind::Category: - case RootItemKind::Bin: - case RootItemKind::Feed: - return item->icon(); - - default: - return QVariant(); - } - } - else if (role == Qt::DisplayRole) { - switch (item->kind()) { - case RootItemKind::Category: - return QVariant(item->data(index.column(), role).toString() + tr(" (category)")); - - case RootItemKind::Feed: - return QVariant(item->data(index.column(), role).toString() + tr(" (feed)")); - - default: - return item->title(); - } - } - else { - return QVariant(); - } -} - -bool FeedsImportExportModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) { - RootItem *item = itemForIndex(index); - - if (item == m_rootItem) { - // Cannot set data on root item. - return false; - } - - // Change data for the actual item. - m_checkStates[item] = static_cast(value.toInt()); - emit dataChanged(index, index); - - if (m_recursiveChange) { - return true; - } - - // Set new data for all descendants of this actual item. - foreach(RootItem *child, item->childItems()) { - setData(indexForItem(child), value, Qt::CheckStateRole); - } - - // Now we need to change new data to all parents. - QModelIndex parent_index = index; - m_recursiveChange = true; - - // Iterate all valid parents. - while ((parent_index = parent_index.parent()).isValid()) { - // We now have parent index. Get parent item too. - item = item->parent(); - - // Check children of this new parent item. - Qt::CheckState parent_state = Qt::Unchecked; - foreach (RootItem *child_of_parent, item->childItems()) { - if (m_checkStates.contains(child_of_parent) && m_checkStates[child_of_parent] == Qt::Checked) { - // We found out, that some child of this item is checked, - // therefore this item must be checked too. - parent_state = Qt::Checked; - break; - } - } - - setData(parent_index, parent_state, Qt::CheckStateRole); - } - - m_recursiveChange = false; - return true; - } - - return false; -} - -Qt::ItemFlags FeedsImportExportModel::flags(const QModelIndex &index) const { - if (!index.isValid() || itemForIndex(index)->kind() == RootItemKind::Bin) { - return Qt::NoItemFlags; - } - - Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; - - if ( index.column() == 0 ) { - flags |= Qt::ItemIsUserCheckable; - } - - return flags; -} - -bool FeedsImportExportModel::isItemChecked(RootItem *item) { - return m_checkStates.contains(item) && m_checkStates.value(item, Qt::Unchecked); -} diff --git a/src/services/standard/standardfeedsimportexportmodel.h b/src/services/standard/standardfeedsimportexportmodel.h index ea81c0021..f1512f82e 100755 --- a/src/services/standard/standardfeedsimportexportmodel.h +++ b/src/services/standard/standardfeedsimportexportmodel.h @@ -15,15 +15,13 @@ // You should have received a copy of the GNU General Public License // along with RSS Guard. If not, see . -#ifndef FEEDIMPORTEXPORTMODEL_H -#define FEEDIMPORTEXPORTMODEL_H +#ifndef STANDARDFEEDSIMPORTEXPORTMODEL_H +#define STANDARDFEEDSIMPORTEXPORTMODEL_H -#include - -#include "services/abstract/rootitem.h" +#include "services/abstract/accountcheckmodel.h" -class FeedsImportExportModel : public QAbstractItemModel { +class FeedsImportExportModel : public AccountCheckModel { Q_OBJECT public: @@ -36,27 +34,6 @@ class FeedsImportExportModel : public QAbstractItemModel { explicit FeedsImportExportModel(QObject *parent = 0); virtual ~FeedsImportExportModel(); - QModelIndex index(int row, int column, const QModelIndex &parent) const; - QModelIndex parent(const QModelIndex &child) const; - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - Qt::ItemFlags flags(const QModelIndex &index) const; - - bool isItemChecked(RootItem *item); - - // Returns feed/category which lies at the specified index or - // root item if index is invalid. - RootItem *itemForIndex(const QModelIndex &index) const; - - // Returns source QModelIndex on which lies given item. - QModelIndex indexForItem(RootItem *item) const; - - // Root item manipulators. - RootItem *rootItem() const; - void setRootItem(RootItem *root_item); - // Exports to OPML 2.0 // NOTE: http://dev.opml.org/spec2.html bool exportToOMPL20(QByteArray &result); @@ -70,10 +47,6 @@ class FeedsImportExportModel : public QAbstractItemModel { Mode mode() const; void setMode(const Mode &mode); - public slots: - void checkAllItems(); - void uncheckAllItems(); - signals: // These signals are emitted when user selects some data // to be imported/parsed into the model. @@ -82,11 +55,7 @@ class FeedsImportExportModel : public QAbstractItemModel { void parsingFinished(int count_failed, int count_succeeded, bool parsing_error); private: - QHash m_checkStates; - RootItem *m_rootItem; - - bool m_recursiveChange; Mode m_mode; }; -#endif // FEEDIMPORTEXPORTMODEL_H +#endif // STANDARDFEEDSIMPORTEXPORTMODEL_H