akregator/src

tagnode.cpp
1/*
2 This file is part of Akregator.
3
4 Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 As a special exception, permission is given to link this program
21 with any edition of TQt, and distribute the resulting executable,
22 without including the source code for TQt in the source distribution.
23*/
24
25#include "article.h"
26#include "articlefilter.h"
27#include "fetchqueue.h"
28#include "folder.h"
29#include "tag.h"
30#include "tagnode.h"
31#include "treenode.h"
32#include "treenodevisitor.h"
33
34#include <tqdom.h>
35#include <tqstring.h>
36#include <tqvaluelist.h>
37
38namespace Akregator {
39
40class TagNode::TagNodePrivate
41{
42 public:
43 Filters::TagMatcher filter;
44 TreeNode* observed;
45 int unread;
46 TQString icon;
47 Tag tag;
48 TQValueList<Article> articles;
49 TQValueList<Article> addedArticlesNotify;
50 TQValueList<Article> removedArticlesNotify;
51 TQValueList<Article> updatedArticlesNotify;
52};
53
54TagNode::TagNode(const Tag& tag, TreeNode* observed) : d(new TagNodePrivate)
55{
56 d->tag = tag;
57 d->icon = tag.icon();
58 d->filter = Filters::TagMatcher(tag.id());
59 setTitle(tag.name());
60 d->observed = observed;
61 d->unread = 0;
62
63 connect(observed, TQ_SIGNAL(signalDestroyed(TreeNode*)), this, TQ_SLOT(slotObservedDestroyed(TreeNode*)));
64 connect(observed, TQ_SIGNAL(signalArticlesAdded(TreeNode*, const TQValueList<Article>&)), this, TQ_SLOT(slotArticlesAdded(TreeNode*, const TQValueList<Article>&)) );
65 connect(observed, TQ_SIGNAL(signalArticlesUpdated(TreeNode*, const TQValueList<Article>&)), this, TQ_SLOT(slotArticlesUpdated(TreeNode*, const TQValueList<Article>&)) );
66 connect(observed, TQ_SIGNAL(signalArticlesRemoved(TreeNode*, const TQValueList<Article>&)), this, TQ_SLOT(slotArticlesRemoved(TreeNode*, const TQValueList<Article>&)) );
67
68 d->articles = observed->articles(tag.id());
69 calcUnread();
70}
71
72TQString TagNode::icon() const
73{
74 return d->icon;
75}
76
77Tag TagNode::tag() const
78{
79 return d->tag;
80}
81
82TagNode::~TagNode()
83{
84 emitSignalDestroyed();
85 delete d;
86 d = 0;
87}
88
89bool TagNode::accept(TreeNodeVisitor* visitor)
90{
91 if (visitor->visitTagNode(this))
92 return true;
93 else
94 return visitor->visitTreeNode(this);
95}
96
97void TagNode::calcUnread()
98{
99 int unread = 0;
100 TQValueList<Article>::Iterator en = d->articles.end();
101 for (TQValueList<Article>::Iterator it = d->articles.begin(); it != en; ++it)
102 if ((*it).status() != Article::Read)
103 ++unread;
104 if (d->unread != unread)
105 {
106 d->unread = unread;
107 nodeModified();
108 }
109}
110
111int TagNode::unread() const
112{
113 return d->unread;
114}
115
116
117int TagNode::totalCount() const
118{
119 return d->articles.count();
120}
121
122
123TQValueList<Article> TagNode::articles(const TQString& tag)
124{
125 return d->articles;
126}
127
128TQStringList TagNode::tags() const
129{
130 // TODO
131 return TQStringList();
132}
133
134TQDomElement TagNode::toOPML( TQDomElement parent, TQDomDocument document ) const
135{
136 return TQDomElement();
137}
138
139TreeNode* TagNode::next()
140{
141 if ( nextSibling() )
142 return nextSibling();
143
144 Folder* p = parent();
145 while (p)
146 {
147 if ( p->nextSibling() )
148 return p->nextSibling();
149 else
150 p = p->parent();
151 }
152 return 0;
153}
154
155void TagNode::slotDeleteExpiredArticles()
156{
157// not our business
158}
159
160void TagNode::slotMarkAllArticlesAsRead()
161{
162 setNotificationMode(false);
163 TQValueList<Article>::Iterator en = d->articles.end();
164 for (TQValueList<Article>::Iterator it = d->articles.begin(); it != en; ++it)
165 (*it).setStatus(Article::Read);
166 setNotificationMode(true);
167}
168
169void TagNode::slotAddToFetchQueue(FetchQueue* /*queue*/, bool /*intervalFetchOnly*/)
170{
171// not our business
172}
173
174void TagNode::doArticleNotification()
175{
176 if (!d->addedArticlesNotify.isEmpty())
177 {
178 emit signalArticlesAdded(this, d->addedArticlesNotify);
179 d->addedArticlesNotify.clear();
180 }
181 if (!d->updatedArticlesNotify.isEmpty())
182 {
183 emit signalArticlesUpdated(this, d->updatedArticlesNotify);
184 d->updatedArticlesNotify.clear();
185 }
186 if (!d->removedArticlesNotify.isEmpty())
187 {
188 emit signalArticlesRemoved(this, d->removedArticlesNotify);
189 d->removedArticlesNotify.clear();
190 }
191 TreeNode::doArticleNotification();
192}
193
194void TagNode::slotArticlesAdded(TreeNode* node, const TQValueList<Article>& list)
195{
196 bool added = false;
197 for (TQValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it)
198 {
199 if (!d->articles.contains(*it) && d->filter.matches(*it))
200 {
201 d->articles.append(*it);
202 d->addedArticlesNotify.append(*it);
203 added = true;
204 }
205 }
206
207 if (added)
208 {
209 calcUnread();
210 articlesModified();
211 }
212}
213
214void TagNode::slotArticlesUpdated(TreeNode* node, const TQValueList<Article>& list)
215{
216 bool updated = false;
217 for (TQValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it)
218 {
219 if (d->articles.contains(*it))
220 {
221 if (!d->filter.matches(*it)) // articles is in list, but doesn't match our criteria anymore -> remove it
222 {
223 d->articles.remove(*it);
224 d->removedArticlesNotify.append(*it);
225 updated = true;
226 }
227 else // otherwise the article remains in the list and we just forward the update
228 {
229 d->updatedArticlesNotify.append(*it);
230 updated = true;
231 }
232 }
233 else // article not in list
234 {
235 if (d->filter.matches(*it)) // articles is not in list, but matches our criteria -> add it
236 {
237 d->articles.append(*it);
238 d->addedArticlesNotify.append(*it);
239 updated = true;
240 }
241 }
242 }
243 if (updated)
244 {
245 calcUnread();
246 articlesModified();
247 }
248}
249
250void TagNode::slotArticlesRemoved(TreeNode* node, const TQValueList<Article>& list)
251{
252 bool removed = false;
253 for (TQValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it)
254 {
255 if (d->articles.contains(*it))
256 {
257 d->articles.remove(*it);
258 d->removedArticlesNotify.append(*it);
259 removed = true;
260 }
261 }
262 if (removed)
263 {
264 calcUnread();
265 articlesModified();
266 }
267}
268
269void TagNode::setTitle(const TQString& title)
270{
271 if (d->tag.name() != title)
272 d->tag.setName(title);
273 TreeNode::setTitle(title);
274}
275
276void TagNode::slotObservedDestroyed(TreeNode* /*observed*/)
277{
278 d->removedArticlesNotify = d->articles;
279 d->articles.clear();
280 articlesModified();
281}
282
283void TagNode::tagChanged()
284{
285 bool changed = false;
286 if (title() != d->tag.name())
287 {
288 setTitle(d->tag.name());
289 changed = true;
290 }
291
292 if (d->icon != d->tag.icon())
293 {
294 d->icon = d->tag.icon();
295 changed = true;
296 }
297
298 if (changed)
299 nodeModified();
300}
301
302} // namespace Akregator
303
304#include "tagnode.moc"
TreeNode()
Standard constructor.
Definition treenode.cpp:48