diff --git a/src/services/abstract/rootitem.cpp b/src/services/abstract/rootitem.cpp new file mode 100755 index 000000000..d9c4ca476 --- /dev/null +++ b/src/services/abstract/rootitem.cpp @@ -0,0 +1,373 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2015 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/rootitem.h" + +#include "services/abstract/serviceroot.h" +#include "services/abstract/feed.h" +#include "services/abstract/category.h" +#include "miscellaneous/application.h" + +#include + + +RootItem::RootItem(RootItem *parent_item) + : QObject(NULL), + m_kind(RootItemKind::Root), + m_id(NO_PARENT_CATEGORY), + m_title(QString()), + m_description(QString()), + m_icon(QIcon()), + m_creationDate(QDateTime()), + m_childItems(QList()), + m_parentItem(parent_item) { + setupFonts(); +} + +RootItem::~RootItem() { + qDeleteAll(m_childItems); +} + +QList RootItem::contextMenu() { + return QList(); +} + +bool RootItem::canBeEdited() { + return false; +} + +bool RootItem::editViaGui() { + return false; +} + +bool RootItem::canBeDeleted() { + return false; +} + +bool RootItem::deleteViaGui() { + return false; +} + +bool RootItem::canBeMarkedAsReadUnread(ReadStatus status) { + return true; +} + +bool RootItem::markAsReadUnread(ReadStatus status) { + bool result = true; + + foreach (RootItem *child, m_childItems) { + if (child->canBeMarkedAsReadUnread(status)) { + result &= child->markAsReadUnread(status); + } + } + + return result; +} + +bool RootItem::cleanMessages(bool clear_only_read) { + bool result = true; + + foreach (RootItem *child, m_childItems) { + result &= child->cleanMessages(clear_only_read); + } + + return result; +} + +void RootItem::updateCounts(bool including_total_count) { + foreach (RootItem *child, m_childItems) { + child->updateCounts(including_total_count); + } +} + +void RootItem::setupFonts() { + m_normalFont = Application::font("FeedsView"); + m_boldFont = m_normalFont; + m_boldFont.setBold(true); +} + +int RootItem::row() const { + if (m_parentItem) { + return m_parentItem->m_childItems.indexOf(const_cast(this)); + } + else { + // This item has no parent. Therefore, its row index is 0. + return 0; + } +} + +QVariant RootItem::data(int column, int role) const { + Q_UNUSED(column) + Q_UNUSED(role) + + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return m_title; + } + else if (column == FDS_MODEL_COUNTS_INDEX) { + //: Tooltip for "unread" column of feed list. + return tr("%n unread message(s).", 0, countOfUnreadMessages()); + } + else { + return QVariant(); + } + + case Qt::EditRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return m_title; + } + else if (column == FDS_MODEL_COUNTS_INDEX) { + return countOfUnreadMessages(); + } + else { + return QVariant(); + } + + case Qt::FontRole: + return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont; + + case Qt::DisplayRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return m_title; + } + else if (column == FDS_MODEL_COUNTS_INDEX) { + int count_all = countOfAllMessages(); + int count_unread = countOfUnreadMessages(); + + return qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::CountFormat)).toString() + .replace(PLACEHOLDER_UNREAD_COUNTS, count_unread < 0 ? QSL("-") : QString::number(count_unread)) + .replace(PLACEHOLDER_ALL_COUNTS, count_all < 0 ? QSL("-") : QString::number(count_all)); + } + else { + return QVariant(); + } + + case Qt::DecorationRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return icon(); + } + else { + return QVariant(); + } + + case Qt::TextAlignmentRole: + if (column == FDS_MODEL_COUNTS_INDEX) { + return Qt::AlignCenter; + } + else { + return QVariant(); + } + + default: + return QVariant(); + } +} + +Qt::ItemFlags RootItem::additionalFlags() const { + return Qt::NoItemFlags; +} + +bool RootItem::performDragDropChange(RootItem *target_item) { + return false; +} + +int RootItem::countOfAllMessages() const { + int total_count = 0; + + foreach (RootItem *child_item, m_childItems) { + total_count += child_item->countOfAllMessages(); + } + + return total_count; +} + +bool RootItem::isChildOf(RootItem *root) { + if (root == NULL) { + return false; + } + + RootItem *this_item = this; + + while (this_item->kind() != RootItemKind::Root) { + if (root->childItems().contains(this_item)) { + return true; + } + else { + this_item = this_item->parent(); + } + } + + return false; +} + +bool RootItem::isParentOf(RootItem *child) { + if (child == NULL) { + return false; + } + else { + return child->isChildOf(this); + } +} + +QList RootItem::getSubTree() { + QList children; + QList traversable_items; + + traversable_items.append(this); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem *active_item = traversable_items.takeFirst(); + + children.append(active_item); + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QList RootItem::getSubTree(RootItemKind::Kind kind_of_item) { + QList children; + QList traversable_items; + + traversable_items.append(this); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem *active_item = traversable_items.takeFirst(); + + if ((active_item->kind() & kind_of_item) > 0) { + children.append(active_item); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QList RootItem::getSubTreeCategories() { + QList children; + QList traversable_items; + + traversable_items.append(this); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem *active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Category) { + children.append(active_item->toCategory()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QHash RootItem::getHashedSubTreeCategories() { + QHash children; + QList traversable_items; + + traversable_items.append(this); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem *active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Category && !children.contains(active_item->id())) { + children.insert(active_item->id(), active_item->toCategory()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QList RootItem::getSubTreeFeeds() { + QList children; + QList traversable_items; + + traversable_items.append(this); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem *active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Feed) { + children.append(active_item->toFeed()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +ServiceRoot *RootItem::getParentServiceRoot() { + RootItem *working_parent = this; + + while (working_parent->kind() != RootItemKind::Root) { + if (working_parent->kind() == RootItemKind::ServiceRoot) { + return working_parent->toServiceRoot(); + } + else { + working_parent = working_parent->parent(); + } + } + + return NULL; +} + +bool RootItem::removeChild(RootItem *child) { + return m_childItems.removeOne(child); +} + +Category *RootItem::toCategory() { + return static_cast(this); +} + +Feed *RootItem::toFeed() { + return static_cast(this); +} + +ServiceRoot *RootItem::toServiceRoot() { + return static_cast(this); +} + +int RootItem::countOfUnreadMessages() const { + int total_count = 0; + + foreach (RootItem *child_item, m_childItems) { + total_count += child_item->countOfUnreadMessages(); + } + + return total_count; +} + +bool RootItem::removeChild(int index) { + if (index >= 0 && index < m_childItems.size()) { + m_childItems.removeAt(index); + return true; + } + else { + return false; + } +} diff --git a/src/services/abstract/rootitem.h b/src/services/abstract/rootitem.h new file mode 100755 index 000000000..876fe1149 --- /dev/null +++ b/src/services/abstract/rootitem.h @@ -0,0 +1,274 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2015 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 ROOTITEM_H +#define ROOTITEM_H + +#include +#include +#include + + +class Category; +class Feed; +class ServiceRoot; +class QAction; + +namespace RootItemKind { + // Describes the kind of the item. + enum Kind { + Root = 1, + Bin = 2, + Feed = 4, + Category = 8, + ServiceRoot = 16 + }; + + inline Kind operator|(Kind a, Kind b) { + return static_cast(static_cast(a) | static_cast(b)); + } +} + +// Represents ROOT item of FeedsModel. +// NOTE: This class is derived to add functionality for +// all other non-root items of FeedsModel. +class RootItem : public QObject { + Q_OBJECT + + public: + // Holds statuses for feeds/messages + // to be marked read/unread. + enum ReadStatus { + Unread = 0, + Read = 1 + }; + + // Holds statuses for messages + // to be switched importance (starred). + enum Importance { + NotImportant = 0, + Important = 1 + }; + + // Constructors and destructors. + explicit RootItem(RootItem *parent_item = NULL); + virtual ~RootItem(); + + ///////////////////////////////////////// + // /* Members to override. + ///////////////////////////////////////// + + // Returns list of specific actions which can be done with the item. + // Do not include general actions here like actions: Mark as read, Update, ... + // NOTE: Ownership of returned actions is not switched to caller, free them when needed. + virtual QList contextMenu(); + + // Can properties of this item be edited? + virtual bool canBeEdited(); + + // Performs editing of properties of this item (probably via dialog) + // and returns result status. + virtual bool editViaGui(); + + // Can the item be deleted? + virtual bool canBeDeleted(); + + // Performs deletion of the item, this + // method should NOT display any additional dialogs. + // Returns result status. + virtual bool deleteViaGui(); + + // Can this item be marked read/unread? + virtual bool canBeMarkedAsReadUnread(ReadStatus status); + + // Performs all needed steps (DB update, remote server update) + // to mark this item as read/unread. + virtual bool markAsReadUnread(ReadStatus status); + + // This method should "clean" all messages it contains. + // What "clean" means? It means delete messages -> move them to recycle bin + // or eventually remove them completely if there is no recycle bin functionality. + // If this method is called on "recycle bin" instance of your + // service account, it should "empty" the recycle bin. + virtual bool cleanMessages(bool clear_only_read); + + // Updates counts of all/unread messages for this feed. + virtual void updateCounts(bool including_total_count); + + virtual int row() const; + virtual QVariant data(int column, int role) const; + virtual Qt::ItemFlags additionalFlags() const; + virtual bool performDragDropChange(RootItem *target_item); + + // Each item offers "counts" of messages. + // Returns counts of messages of all child items summed up. + virtual int countOfUnreadMessages() const; + virtual int countOfAllMessages() const; + + ///////////////////////////////////////// + // Members to override. */ + ///////////////////////////////////////// + + inline RootItem *parent() const { + return m_parentItem; + } + + inline void setParent(RootItem *parent_item) { + m_parentItem = parent_item; + } + + inline RootItem *child(int row) { + return m_childItems.value(row); + } + + inline int childCount() const { + return m_childItems.size(); + } + + inline void appendChild(RootItem *child) { + m_childItems.append(child); + child->setParent(this); + } + + // Access to children. + inline QList childItems() const { + return m_childItems; + } + + // Removes all children from this item. + // NOTE: Children are NOT freed from the memory. + inline void clearChildren() { + m_childItems.clear(); + } + + inline void setChildItems(QList child_items) { + m_childItems = child_items; + } + + // Removes particular child at given index. + // NOTE: Child is NOT freed from the memory. + bool removeChild(int index); + bool removeChild(RootItem *child); + + // Checks whether "this" object is child (direct or indirect) + // of the given root. + bool isChildOf(RootItem *root); + + // Is "this" item parent (direct or indirect) if given child? + bool isParentOf(RootItem *child); + + // Returns flat list of all items from subtree where this item is a root. + // Returned list includes this item too. + QList getSubTree(); + QList getSubTree(RootItemKind::Kind kind_of_item); + QList getSubTreeCategories(); + QHash getHashedSubTreeCategories(); + QList getSubTreeFeeds(); + + // Returns the service root node which is direct or indirect parent of current item. + ServiceRoot *getParentServiceRoot(); + + inline RootItemKind::Kind kind() const { + return m_kind; + } + + inline void setKind(RootItemKind::Kind kind) { + m_kind = kind; + } + + // Each item can have icon. + inline QIcon icon() const { + return m_icon; + } + + inline void setIcon(const QIcon &icon) { + m_icon = icon; + } + + // Each item has some kind of id. Usually taken from primary key attribute from DB. + inline int id() const { + return m_id; + } + + inline void setId(int id) { + m_id = id; + } + + // Each item has its title. + inline QString title() const { + return m_title; + } + + inline void setTitle(const QString &title) { + m_title = title; + } + + inline QDateTime creationDate() const { + return m_creationDate; + } + + inline void setCreationDate(const QDateTime &creation_date) { + m_creationDate = creation_date; + } + + inline QString description() const { + return m_description; + } + + inline void setDescription(const QString &description) { + m_description = description; + } + + inline QFont normalFont() const { + return m_normalFont; + } + + inline void setNormalFont(const QFont &normal_font) { + m_normalFont = normal_font; + } + + inline QFont boldFont() const { + return m_boldFont; + } + + inline void setBoldFont(const QFont &bold_font) { + m_boldFont = bold_font; + } + + // Converters + Category *toCategory(); + Feed *toFeed(); + ServiceRoot *toServiceRoot(); + + private: + void setupFonts(); + + RootItemKind::Kind m_kind; + int m_id; + QString m_title; + QString m_description; + QIcon m_icon; + QDateTime m_creationDate; + + QFont m_normalFont; + QFont m_boldFont; + + QList m_childItems; + RootItem *m_parentItem; +}; + +#endif // ROOTITEM_H