File indexing completed on 2024-05-12 16:01:52
0001 /************************************************************************** 0002 ** 0003 ** This file is part of Qt Creator 0004 ** 0005 ** SPDX-FileCopyrightText: 2011 Nokia Corporation and /or its subsidiary(-ies). 0006 ** 0007 ** Contact: Nokia Corporation (qt-info@nokia.com) 0008 ** 0009 ** 0010 ** SPDX-License-Identifier: LGPL-2.1-only 0011 ** 0012 ** In addition, as a special exception, Nokia gives you certain additional 0013 ** rights. These rights are described in the Nokia Qt LGPL Exception 0014 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 0015 ** 0016 ** Other Usage 0017 ** 0018 ** Alternatively, this file may be used in accordance with the terms and 0019 ** conditions contained in a signed written agreement between you and Nokia. 0020 ** 0021 ** If you have questions regarding the use of this file, please contact 0022 ** Nokia at qt-info@nokia.com. 0023 ** 0024 **************************************************************************/ 0025 0026 #include "KisMultiFeedRSSModel.h" 0027 0028 #include <QTimer> 0029 #include <QThread> 0030 #include <QXmlStreamReader> 0031 #include <QCoreApplication> 0032 #include <QLocale> 0033 #include <QFile> 0034 #include <QTextBlock> 0035 #include <QTextDocument> 0036 0037 #include <QNetworkRequest> 0038 #include <QNetworkReply> 0039 #include <KisNetworkAccessManager.h> 0040 0041 #include <KisRssReader.h> 0042 0043 MultiFeedRssModel::MultiFeedRssModel(QObject *parent) : 0044 QAbstractListModel(parent), 0045 m_networkAccessManager(new KisNetworkAccessManager), 0046 m_articleCount(0) 0047 { 0048 initialize(); 0049 } 0050 0051 MultiFeedRssModel::MultiFeedRssModel(KisNetworkAccessManager* nam, QObject* parent) 0052 : QAbstractListModel(parent), 0053 m_networkAccessManager(nam), 0054 m_articleCount(0) 0055 { 0056 initialize(); 0057 } 0058 0059 0060 MultiFeedRssModel::~MultiFeedRssModel() 0061 { 0062 delete m_networkAccessManager; 0063 } 0064 0065 QHash<int, QByteArray> MultiFeedRssModel::roleNames() const 0066 { 0067 QHash<int, QByteArray> roleNames; 0068 roleNames[KisRssReader::RssRoles::TitleRole] = "title"; 0069 roleNames[KisRssReader::RssRoles::DescriptionRole] = "description"; 0070 roleNames[KisRssReader::RssRoles::PubDateRole] = "pubDate"; 0071 roleNames[KisRssReader::RssRoles::LinkRole] = "link"; 0072 roleNames[KisRssReader::RssRoles::CategoryRole] = "category"; 0073 roleNames[KisRssReader::RssRoles::BlogNameRole] = "blogName"; 0074 roleNames[KisRssReader::RssRoles::BlogIconRole] = "blogIcon"; 0075 return roleNames; 0076 } 0077 0078 void MultiFeedRssModel::addFeed(const QString& feed) 0079 { 0080 if (m_sites.contains(feed)) { 0081 // do not add the feed twice 0082 return; 0083 } 0084 0085 m_sites << feed; 0086 const QUrl feedUrl(feed); 0087 m_networkAccessManager->getUrl(feedUrl); 0088 } 0089 0090 bool sortForPubDate(const RssItem& item1, const RssItem& item2) 0091 { 0092 return item1.pubDate > item2.pubDate; 0093 } 0094 0095 void MultiFeedRssModel::appendFeedData(QNetworkReply *reply) 0096 { 0097 beginResetModel(); 0098 KisRssReader reader; 0099 m_aggregatedFeed.append(reader.parse(reply)); 0100 sortAggregatedFeed(); 0101 setArticleCount(m_aggregatedFeed.size()); 0102 endResetModel(); 0103 0104 emit feedDataChanged(); 0105 } 0106 0107 void MultiFeedRssModel::sortAggregatedFeed() 0108 { 0109 std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate); 0110 } 0111 0112 void MultiFeedRssModel::initialize() 0113 { 0114 connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), 0115 SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection); 0116 } 0117 0118 void MultiFeedRssModel::removeFeed(const QString &feed) 0119 { 0120 bool isRemoved = m_sites.removeOne(feed); 0121 if (isRemoved) { 0122 beginResetModel(); 0123 QMutableListIterator<RssItem> it(m_aggregatedFeed); 0124 while (it.hasNext()) { 0125 RssItem item = it.next(); 0126 if (item.source == feed) 0127 it.remove(); 0128 } 0129 setArticleCount(m_aggregatedFeed.size()); 0130 endResetModel(); 0131 0132 emit feedDataChanged(); 0133 } 0134 } 0135 0136 int MultiFeedRssModel::rowCount(const QModelIndex &) const 0137 { 0138 return m_aggregatedFeed.size(); 0139 } 0140 0141 QVariant MultiFeedRssModel::data(const QModelIndex &index, int role) const 0142 { 0143 0144 RssItem item = m_aggregatedFeed.at(index.row()); 0145 0146 switch (role) { 0147 case Qt::DisplayRole: 0148 { 0149 QTextDocument doc; 0150 doc.setHtml(item.description); 0151 // Extract the first text block, which is the `<p>` element containing 0152 // the shortened post text, excluding the "This post [...] appeared 0153 // first on [...]" text. 0154 QString text = doc.firstBlock().text(); 0155 if (text.length() > 92) { 0156 text.truncate(90); 0157 text.append("..."); 0158 } 0159 return QString("<b><a href=\"" + item.link + "\">" + item.title + "</a></b>" 0160 "<br><small>(" + item.pubDate.toLocalTime().toString(Qt::DefaultLocaleShortDate) + ") " 0161 "<p style=\"margin-top: 4px\">" + text + "</p></small>"); 0162 } 0163 case KisRssReader::RssRoles::TitleRole: 0164 return item.title; 0165 case KisRssReader::RssRoles::DescriptionRole: 0166 return item.description; 0167 case KisRssReader::RssRoles::PubDateRole: 0168 return item.pubDate.toString("dd-MM-yyyy hh:mm"); 0169 case KisRssReader::RssRoles::LinkRole: 0170 return item.link; 0171 case KisRssReader::RssRoles::CategoryRole: 0172 return item.category; 0173 case KisRssReader::RssRoles::BlogNameRole: 0174 return item.blogName; 0175 case KisRssReader::RssRoles::BlogIconRole: 0176 return item.blogIcon; 0177 } 0178 0179 return QVariant(); 0180 }