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 
38 namespace Akregator {
39 
40 class 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 
54 TagNode::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 
72 TQString TagNode::icon() const
73 {
74  return d->icon;
75 }
76 
77 Tag TagNode::tag() const
78 {
79  return d->tag;
80 }
81 
82 TagNode::~TagNode()
83 {
84  emitSignalDestroyed();
85  delete d;
86  d = 0;
87 }
88 
89 bool TagNode::accept(TreeNodeVisitor* visitor)
90 {
91  if (visitor->visitTagNode(this))
92  return true;
93  else
94  return visitor->visitTreeNode(this);
95 }
96 
97 void 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 
111 int TagNode::unread() const
112 {
113  return d->unread;
114 }
115 
116 
117 int TagNode::totalCount() const
118 {
119  return d->articles.count();
120 }
121 
122 
123 TQValueList<Article> TagNode::articles(const TQString& tag)
124 {
125  return d->articles;
126 }
127 
128 TQStringList TagNode::tags() const
129 {
130  // TODO
131  return TQStringList();
132 }
133 
134 TQDomElement TagNode::toOPML( TQDomElement parent, TQDomDocument document ) const
135 {
136  return TQDomElement();
137 }
138 
139 TreeNode* 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 
155 void TagNode::slotDeleteExpiredArticles()
156 {
157 // not our business
158 }
159 
160 void 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 
169 void TagNode::slotAddToFetchQueue(FetchQueue* /*queue*/, bool /*intervalFetchOnly*/)
170 {
171 // not our business
172 }
173 
174 void 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 
194 void 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 
214 void 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 
250 void 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 
269 void TagNode::setTitle(const TQString& title)
270 {
271  if (d->tag.name() != title)
272  d->tag.setName(title);
273  TreeNode::setTitle(title);
274 }
275 
276 void TagNode::slotObservedDestroyed(TreeNode* /*observed*/)
277 {
278  d->removedArticlesNotify = d->articles;
279  d->articles.clear();
280  articlesModified();
281 }
282 
283 void 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"