Changeset View
Changeset View
Standalone View
Standalone View
src/subscription/subscriptionlistmodel.cpp
Show All 34 Lines | |||||
35 | 35 | | |||
36 | #include <QByteArray> | 36 | #include <QByteArray> | ||
37 | #include <QDataStream> | 37 | #include <QDataStream> | ||
38 | #include <QIcon> | 38 | #include <QIcon> | ||
39 | #include <QList> | 39 | #include <QList> | ||
40 | #include <QMimeData> | 40 | #include <QMimeData> | ||
41 | #include <QUrl> | 41 | #include <QUrl> | ||
42 | #include <QVariant> | 42 | #include <QVariant> | ||
43 | #include <QItemSelection> | ||||
43 | 44 | | |||
44 | #include <cassert> | 45 | #include <cassert> | ||
45 | 46 | | |||
46 | using namespace Akregator; | 47 | using namespace Akregator; | ||
47 | using namespace Syndication; | 48 | using namespace Syndication; | ||
48 | 49 | | |||
49 | #define AKREGATOR_TREENODE_MIMETYPE QStringLiteral("akregator/treenode-id") | 50 | #define AKREGATOR_TREENODE_MIMETYPE QStringLiteral("akregator/treenode-id") | ||
50 | 51 | | |||
51 | namespace { | 52 | namespace { | ||
53 | static uint nodeIdForIndex(const QModelIndex &idx) | ||||
54 | { | ||||
55 | return idx.isValid() ? idx.internalId() : 0; | ||||
56 | } | ||||
57 | | ||||
52 | static QString errorCodeToString(Syndication::ErrorCode err) | 58 | static QString errorCodeToString(Syndication::ErrorCode err) | ||
53 | { | 59 | { | ||
54 | switch (err) { | 60 | switch (err) { | ||
55 | case Timeout: | 61 | case Timeout: | ||
56 | return i18n("Timeout on remote server"); | 62 | return i18n("Timeout on remote server"); | ||
57 | case UnknownHost: | 63 | case UnknownHost: | ||
58 | return i18n("Unknown host"); | 64 | return i18n("Unknown host"); | ||
59 | case FileNotFound: | 65 | case FileNotFound: | ||
Show All 12 Lines | |||||
72 | } | 78 | } | ||
73 | 79 | | |||
74 | static const Akregator::TreeNode *nodeForIndex(const QModelIndex &index, const FeedList *feedList) | 80 | static const Akregator::TreeNode *nodeForIndex(const QModelIndex &index, const FeedList *feedList) | ||
75 | { | 81 | { | ||
76 | return (!index.isValid() || !feedList) ? 0 : feedList->findByID(index.internalId()); | 82 | return (!index.isValid() || !feedList) ? 0 : feedList->findByID(index.internalId()); | ||
77 | } | 83 | } | ||
78 | } | 84 | } | ||
79 | 85 | | |||
86 | Akregator::FilterUnreadProxyModel::FilterUnreadProxyModel(QObject *parent) | ||||
87 | : QSortFilterProxyModel(parent) | ||||
88 | , m_doFilter(false) | ||||
89 | , m_selectedHierarchy() | ||||
90 | { | ||||
91 | setDynamicSortFilter(true); | ||||
92 | } | ||||
93 | | ||||
94 | bool Akregator::FilterUnreadProxyModel::doFilter() const | ||||
95 | { | ||||
96 | return m_doFilter; | ||||
dvratil: Use brackets (`{` and `}` even with one-line statements | |||||
97 | } | ||||
98 | | ||||
99 | void Akregator::FilterUnreadProxyModel::setDoFilter(bool v) | ||||
100 | { | ||||
101 | m_doFilter = v; | ||||
102 | invalidateFilter(); | ||||
103 | } | ||||
104 | | ||||
Is this recursive? In other words, does this return true even if idx is a folder with an unread feed? Otherwise you'll need to traverse the hierarchy here in order not to hide those folders. dvratil: Is this recursive? In other words, does this return `true` even if `idx` is a folder with an… | |||||
sagara: Yes this works with nested folders just fine. | |||||
105 | void Akregator::FilterUnreadProxyModel::setSourceModel(QAbstractItemModel *src) | ||||
106 | { | ||||
107 | clearCache(); | ||||
108 | QSortFilterProxyModel::setSourceModel(src); | ||||
109 | } | ||||
110 | | ||||
111 | bool Akregator::FilterUnreadProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const | ||||
112 | { | ||||
113 | if (!m_doFilter) { | ||||
114 | return true; | ||||
115 | } | ||||
116 | | ||||
117 | QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); | ||||
118 | | ||||
119 | if (m_selectedHierarchy.contains(idx)) | ||||
120 | return true; | ||||
121 | | ||||
122 | QVariant v = idx.data(SubscriptionListModel::HasUnreadRole); | ||||
123 | if (v.isNull()) | ||||
124 | return true; | ||||
125 | | ||||
126 | return v.toBool(); | ||||
dvratil: `!desel.isEmpty()` (`QList` does not cache size) | |||||
127 | } | ||||
128 | | ||||
129 | /** | ||||
130 | * This caches the hierarchy of the selected node. Its purpose is to allow | ||||
131 | * feeds/folders with no unread content not to be filtered out immediately, | ||||
132 | * which would occur otherwise (we'd select the last article to read, it would | ||||
133 | * become unread, and disappear from the list without letting us view it). | ||||
134 | **/ | ||||
135 | void Akregator::FilterUnreadProxyModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) | ||||
136 | { | ||||
137 | QModelIndexList desel = mapSelectionToSource(deselected).indexes(); | ||||
138 | //calling invalidateFilter causes refiltering at the call point, so we should | ||||
139 | //call it ONLY after we recreate our node cache | ||||
140 | bool doInvalidate = false; | ||||
141 | | ||||
142 | //if we're deselecting an empty feed/folder, we need to hide it | ||||
143 | if (!desel.isEmpty()) { | ||||
144 | if (m_selectedHierarchy.contains(desel.at(0))) { | ||||
145 | doInvalidate = true; | ||||
146 | } | ||||
147 | } | ||||
148 | | ||||
149 | clearCache(); | ||||
150 | | ||||
151 | QModelIndexList sel = mapSelectionToSource(selected).indexes(); | ||||
152 | if (!sel.isEmpty()) { | ||||
153 | //XXX add support for multiple selections? this doesn't generally make sense in this case honestly | ||||
154 | QModelIndex current = sel.at(0); | ||||
155 | while (current.isValid()) { | ||||
156 | m_selectedHierarchy.insert(current); | ||||
157 | current = current.parent(); | ||||
158 | } | ||||
159 | } | ||||
160 | | ||||
161 | if (doInvalidate && doFilter()) | ||||
162 | invalidateFilter(); | ||||
163 | } | ||||
164 | | ||||
165 | void Akregator::FilterUnreadProxyModel::clearCache() | ||||
166 | { | ||||
167 | m_selectedHierarchy.clear(); | ||||
168 | } | ||||
169 | | ||||
80 | Akregator::SubscriptionListModel::SubscriptionListModel(const QSharedPointer<const FeedList> &feedList, QObject *parent) : QAbstractItemModel(parent) | 170 | Akregator::SubscriptionListModel::SubscriptionListModel(const QSharedPointer<const FeedList> &feedList, QObject *parent) : QAbstractItemModel(parent) | ||
81 | , m_feedList(feedList) | 171 | , m_feedList(feedList) | ||
82 | , m_beganRemoval(false) | 172 | , m_beganRemoval(false) | ||
83 | { | 173 | { | ||
84 | if (!m_feedList) { | 174 | if (!m_feedList) { | ||
85 | return; | 175 | return; | ||
86 | } | 176 | } | ||
87 | connect(m_feedList.data(), &FeedList::signalNodeAdded, | 177 | connect(m_feedList.data(), &FeedList::signalNodeAdded, | ||
Show All 22 Lines | 199 | { | |||
110 | if (!parent.isValid()) { | 200 | if (!parent.isValid()) { | ||
111 | return 1; | 201 | return 1; | ||
112 | } | 202 | } | ||
113 | 203 | | |||
114 | const Akregator::TreeNode *const node = nodeForIndex(parent, m_feedList.data()); | 204 | const Akregator::TreeNode *const node = nodeForIndex(parent, m_feedList.data()); | ||
115 | return node ? node->children().count() : 0; | 205 | return node ? node->children().count() : 0; | ||
116 | } | 206 | } | ||
117 | 207 | | |||
118 | uint Akregator::SubscriptionListModel::nodeIdForIndex(const QModelIndex &idx) const | | |||
119 | { | | |||
120 | return idx.isValid() ? idx.internalId() : 0; | | |||
121 | } | | |||
122 | | ||||
123 | QVariant Akregator::SubscriptionListModel::data(const QModelIndex &index, int role) const | 208 | QVariant Akregator::SubscriptionListModel::data(const QModelIndex &index, int role) const | ||
124 | { | 209 | { | ||
125 | if (!index.isValid()) { | 210 | if (!index.isValid()) { | ||
126 | return QVariant(); | 211 | return QVariant(); | ||
127 | } | 212 | } | ||
128 | 213 | | |||
129 | const Akregator::TreeNode *const node = nodeForIndex(index, m_feedList.data()); | 214 | const Akregator::TreeNode *const node = nodeForIndex(index, m_feedList.data()); | ||
130 | 215 | | |||
▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Line(s) | 422 | { | |||
338 | setExpanded(idx, false); | 423 | setExpanded(idx, false); | ||
339 | } | 424 | } | ||
340 | 425 | | |||
341 | void Akregator::FolderExpansionHandler::setExpanded(const QModelIndex &idx, bool expanded) | 426 | void Akregator::FolderExpansionHandler::setExpanded(const QModelIndex &idx, bool expanded) | ||
342 | { | 427 | { | ||
343 | if (!m_feedList || !m_model) { | 428 | if (!m_feedList || !m_model) { | ||
344 | return; | 429 | return; | ||
345 | } | 430 | } | ||
346 | Akregator::TreeNode *const node = m_feedList->findByID(m_model->nodeIdForIndex(idx)); | 431 | Akregator::TreeNode *const node = m_feedList->findByID(nodeIdForIndex(idx)); | ||
347 | if (!node || !node->isGroup()) { | 432 | if (!node || !node->isGroup()) { | ||
348 | return; | 433 | return; | ||
349 | } | 434 | } | ||
350 | 435 | | |||
351 | Akregator::Folder *const folder = qobject_cast<Akregator::Folder *>(node); | 436 | Akregator::Folder *const folder = qobject_cast<Akregator::Folder *>(node); | ||
352 | Q_ASSERT(folder); | 437 | Q_ASSERT(folder); | ||
353 | folder->setOpen(expanded); | 438 | folder->setOpen(expanded); | ||
354 | } | 439 | } | ||
355 | 440 | | |||
356 | FolderExpansionHandler::FolderExpansionHandler(QObject *parent) : QObject(parent) | 441 | FolderExpansionHandler::FolderExpansionHandler(QObject *parent) : QObject(parent) | ||
357 | , m_feedList() | 442 | , m_feedList() | ||
358 | , m_model(0) | 443 | , m_model(0) | ||
359 | { | 444 | { | ||
360 | } | 445 | } | ||
361 | 446 | | |||
362 | void FolderExpansionHandler::setModel(Akregator::SubscriptionListModel *model) | 447 | void FolderExpansionHandler::setModel(QAbstractItemModel *model) | ||
363 | { | 448 | { | ||
364 | m_model = model; | 449 | m_model = model; | ||
365 | } | 450 | } | ||
366 | 451 | | |||
367 | void FolderExpansionHandler::setFeedList(const QSharedPointer<FeedList> &feedList) | 452 | void FolderExpansionHandler::setFeedList(const QSharedPointer<FeedList> &feedList) | ||
368 | { | 453 | { | ||
369 | m_feedList = feedList; | 454 | m_feedList = feedList; | ||
370 | } | 455 | } | ||
▲ Show 20 Lines • Show All 121 Lines • Show Last 20 Lines |
Use brackets ({ and } even with one-line statements