#include #include #include #include #include "core/feedsmodel.h" #include "core/feedsmodelstandardcategory.h" #include "core/feedsmodelstandardfeed.h" #include "core/defs.h" #include "core/databasefactory.h" #include "gui/iconthemefactory.h" #include "gui/iconfactory.h" FeedsModel::FeedsModel(QObject *parent) : QAbstractItemModel(parent) { setObjectName("FeedsModel"); m_rootItem = new FeedsModelRootItem(); m_countsIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-unread"); m_headerData << tr("Title"); m_tooltipData << tr("Titles of feeds/categories.") << tr("Counts of unread/all meesages."); loadFromDatabase(); /* FeedsModelStandardCategory *cat1 = new FeedsModelStandardCategory(); FeedsModelStandardCategory *cat2 = new FeedsModelStandardCategory(); FeedsModelStandardFeed *feed1 = new FeedsModelStandardFeed(); FeedsModelStandardFeed *feed2 = new FeedsModelStandardFeed(); FeedsModelStandardFeed *feed3 = new FeedsModelStandardFeed(); FeedsModelStandardFeed *feed4 = new FeedsModelStandardFeed(); FeedsModelStandardFeed *feed5 = new FeedsModelStandardFeed(); feed1->setTitle("aaa"); feed2->setTitle("aaa"); feed3->setTitle("aaa"); feed4->setTitle("aaa"); feed5->setTitle("aaa"); cat1->appendChild(feed1); cat1->appendChild(feed2); cat1->appendChild(cat2); cat2->appendChild(feed4); cat2->appendChild(feed5); m_rootItem->appendChild(cat1); m_rootItem->appendChild(feed3); */ } FeedsModel::~FeedsModel() { qDebug("Destroying FeedsModel instance."); delete m_rootItem; DatabaseFactory::getInstance()->removeConnection(objectName()); } QVariant FeedsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } FeedsModelRootItem *item = static_cast(index.internalPointer()); return item->data(index.column(), role); } QVariant FeedsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal) { return QVariant(); } switch (role) { case Qt::DisplayRole: if (section == FDS_MODEL_TITLE_INDEX) { return m_headerData.at(FDS_MODEL_TITLE_INDEX); } else { return QVariant(); } case Qt::ToolTipRole: return m_tooltipData.at(section); case Qt::DecorationRole: if (section == FDS_MODEL_COUNTS_INDEX) { return m_countsIcon; } else { return QVariant(); } default: return QVariant(); } } QModelIndex FeedsModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } FeedsModelRootItem *parent_item; if (!parent.isValid()) { parent_item = m_rootItem; } else { parent_item = static_cast(parent.internalPointer()); } FeedsModelRootItem *child_item = parent_item->child(row); if (child_item) { return createIndex(row, column, child_item); } else { return QModelIndex(); } } QModelIndex FeedsModel::parent(const QModelIndex &child) const { if (!child.isValid()) { return QModelIndex(); } FeedsModelRootItem *child_item = static_cast(child.internalPointer()); FeedsModelRootItem *parent_item = child_item->parent(); if (parent_item == m_rootItem) { return QModelIndex(); } else { return createIndex(parent_item->row(), 0, parent_item); } } int FeedsModel::rowCount(const QModelIndex &parent) const { FeedsModelRootItem *parent_item; if (parent.column() > 0) { return 0; } if (!parent.isValid()) { parent_item = m_rootItem; } else { parent_item = static_cast(parent.internalPointer()); } return parent_item->childCount(); } int FeedsModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return static_cast(parent.internalPointer())->columnCount(); } else { return m_rootItem->columnCount(); } } FeedsModelRootItem *FeedsModel::itemForIndex(const QModelIndex &index) { if (index.isValid() && index.model() == this) { return static_cast(index.internalPointer()); } else { return NULL; } } void FeedsModel::changeLayout(QModelIndexList list) { // TODO: Udělat analyzu pres cachegrind // esli je lepsi tohle, nebo zakomentovana cast // zakomentovana cast udela to ze view se dopta na všecky // polozky while (!list.isEmpty()) { QModelIndex ix = list.takeLast(); emit dataChanged(index(ix.row(), 0, ix.parent()), index(ix.row(), FDS_MODEL_COUNTS_INDEX, ix.parent())); if (ix.parent().isValid()) { list.append(ix.parent()); } } /* emit layoutAboutToBeChanged(); // TODO: kouknout do dokumentace na signal layoutChanged, // protoze ten signal layoutAboutToBeChanged by se měl volat PRED // zmenou dat v modelu emit layoutChanged(); */ } void FeedsModel::loadFromDatabase() { // Delete all childs of the root node and clear them from the memory. qDeleteAll(m_rootItem->childItems()); m_rootItem->clearChilds(); QSqlDatabase database = DatabaseFactory::getInstance()->addConnection(objectName()); CategoryAssignment categories; FeedAssignment feeds; // Obtain data for categories from the database. QSqlQuery query_categories = database.exec("SELECT * FROM Categories;"); if (query_categories.lastError().isValid()) { qFatal("Query for obtaining categories failed."); } while (query_categories.next()) { // Process this category. FeedsModelCategory::Type type = static_cast(query_categories.value(CAT_DB_TYPE_INDEX).toInt()); switch (type) { case FeedsModelCategory::Standard: { CategoryAssignmentItem pair; pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt(); pair.second = FeedsModelStandardCategory::loadFromRecord(query_categories.record()); categories << pair; break; } case FeedsModelCategory::Feedly: case FeedsModelCategory::TinyTinyRss: default: // NOTE: Not yet implemented. break; } } // All categories are now loaded. QSqlQuery query_feeds = database.exec("SELECT * FROM Feeds;"); if (query_feeds.lastError().isValid()) { qFatal("Query for obtaining feeds failed."); } while (query_feeds.next()) { // Process this feed. FeedsModelFeed::Type type = static_cast(query_feeds.value(FDS_DB_TYPE_INDEX).toInt()); switch (type) { case FeedsModelFeed::StandardAtom: case FeedsModelFeed::StandardRdf: case FeedsModelFeed::StandardRss0X: case FeedsModelFeed::StandardRss1X: case FeedsModelFeed::StandardRss2X: { FeedAssignmentItem pair; pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt(); pair.second = FeedsModelStandardFeed::loadFromRecord(query_feeds.record()); pair.second->setType(type); feeds << pair; break; } default: break; } } // All data are now obtained, lets create the hierarchy. assembleCategories(categories); assembleFeeds(feeds); } QList FeedsModel::feedsForIndex(const QModelIndex &index) { QList feeds; FeedsModelRootItem *item = itemForIndex(index); switch (item->kind()) { case FeedsModelRootItem::Category: // This item is a category, add all its child feeds. feeds.append(static_cast(item)->feeds()); break; case FeedsModelRootItem::Feed: // This item is feed (it SURELY subclasses FeedsModelFeed), add it. feeds.append(static_cast(item)); break; default: break; } return feeds; } QList FeedsModel::feedsForIndexes(const QModelIndexList &indexes) { QList feeds; foreach (const QModelIndex &index, indexes) { feeds.append(feedsForIndex(index)); } return feeds; } QHash FeedsModel::getCategories(FeedsModelRootItem *root) { QHash categories; foreach (FeedsModelRootItem *child, root->childItems()) { FeedsModelCategory *converted = dynamic_cast(child); if (converted != NULL) { // This child is some kind of category. categories.insert(converted->id(), converted); // Moreover, add all child categories of this category. categories.unite(getCategories(converted)); } } return categories; } QHash FeedsModel::getCategories() { return getCategories(m_rootItem); } void FeedsModel::assembleFeeds(FeedAssignment feeds) { QHash categories = getCategories(); foreach (FeedAssignmentItem feed, feeds) { if (feed.first == NO_PARENT_CATEGORY) { // This is top-level feed, add it to the root item. m_rootItem->appendChild(feed.second); } else if (categories.contains(feed.first)) { // This feed belongs to some category. categories.value(feed.first)->appendChild(feed.second); } else { qWarning("Feed '%s' is loose, skipping it.", qPrintable(feed.second->title())); } } } void FeedsModel::assembleCategories(CategoryAssignment categories) { QHash assignments; assignments.insert(-1, m_rootItem); // 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--; } } } }