akregator/src

feed.cpp
1/*
2 This file is part of Akregator.
3
4 Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
5 2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 As a special exception, permission is given to link this program
22 with any edition of TQt, and distribute the resulting executable,
23 without including the source code for TQt in the source distribution.
24*/
25
26#include <tqtimer.h>
27#include <tqdatetime.h>
28#include <tqlistview.h>
29#include <tqdom.h>
30#include <tqmap.h>
31#include <tqpixmap.h>
32#include <tqvaluelist.h>
33
34#include <kurl.h>
35#include <kdebug.h>
36#include <tdeglobal.h>
37#include <tdestandarddirs.h>
38
39#include "akregatorconfig.h"
40#include "article.h"
41#include "articleinterceptor.h"
42#include "feed.h"
43#include "folder.h"
44#include "fetchqueue.h"
45#include "feediconmanager.h"
46#include "feedstorage.h"
47#include "storage.h"
48#include "treenodevisitor.h"
49#include "utils.h"
50
51#include "librss/librss.h"
52
53namespace Akregator {
54
55class Feed::FeedPrivate
56{
57 public:
58 bool autoFetch;
59 int fetchInterval;
61 int maxArticleAge;
64 bool useNotification;
65 bool loadLinkedWebsite;
66
67 bool fetchError;
68
69 int lastErrorFetch; // save time of last fetch that went wrong.
70 // != lastFetch property from the archive
71 // (that saves the last _successfull fetch!)
72 // workaround for 3.5.x
73
74 int fetchTries;
75 bool followDiscovery;
76 RSS::Loader* loader;
77 bool articlesLoaded;
78 Backend::FeedStorage* archive;
79
80 TQString xmlUrl;
81 TQString htmlUrl;
82 TQString description;
83
85 TQMap<TQString, Article> articles;
86
88 TQMap<TQString, TQStringList> taggedArticles;
89
91 TQValueList<Article> deletedArticles;
92
95 TQValueList<Article> addedArticlesNotify;
96 TQValueList<Article> removedArticlesNotify;
97 TQValueList<Article> updatedArticlesNotify;
98
99 TQPixmap imagePixmap;
100 RSS::Image image;
101 TQPixmap favicon;
102};
103
105{
106 switch (mode)
107 {
108 case keepAllArticles:
109 return "keepAllArticles";
110 case disableArchiving:
111 return "disableArchiving";
112 case limitArticleNumber:
113 return "limitArticleNumber";
114 case limitArticleAge:
115 return "limitArticleAge";
116 default:
117 return "globalDefault";
118 }
119
120 // in a perfect world, this is never reached
121
122 return "globalDefault";
123}
124
125Feed* Feed::fromOPML(TQDomElement e)
126{
127
128 Feed* feed = 0;
129
130 if( e.hasAttribute("xmlUrl") || e.hasAttribute("xmlurl") || e.hasAttribute("xmlURL") )
131 {
132 TQString title = e.hasAttribute("text") ? e.attribute("text") : e.attribute("title");
133
134 TQString xmlUrl = e.hasAttribute("xmlUrl") ? e.attribute("xmlUrl") : e.attribute("xmlurl");
135 if (xmlUrl.isEmpty())
136 xmlUrl = e.attribute("xmlURL");
137
138 bool useCustomFetchInterval = e.attribute("useCustomFetchInterval") == "true" || e.attribute("autoFetch") == "true";
139 // "autoFetch" is used in 3.4
140 // Will be removed in KDE4
141
142 TQString htmlUrl = e.attribute("htmlUrl");
143 TQString description = e.attribute("description");
144 int fetchInterval = e.attribute("fetchInterval").toInt();
145 ArchiveMode archiveMode = stringToArchiveMode(e.attribute("archiveMode"));
146 int maxArticleAge = e.attribute("maxArticleAge").toUInt();
147 int maxArticleNumber = e.attribute("maxArticleNumber").toUInt();
148 bool markImmediatelyAsRead = e.attribute("markImmediatelyAsRead") == "true";
149 bool useNotification = e.attribute("useNotification") == "true";
150 bool loadLinkedWebsite = e.attribute("loadLinkedWebsite") == "true";
151 uint id = e.attribute("id").toUInt();
152
153 feed = new Feed();
154 feed->setTitle(title);
155 feed->setXmlUrl(xmlUrl);
157 feed->setHtmlUrl(htmlUrl);
158 feed->setId(id);
161 feed->setUseNotification(useNotification);
165 feed->setMarkImmediatelyAsRead(markImmediatelyAsRead);
166 feed->setLoadLinkedWebsite(loadLinkedWebsite);
167 feed->loadArticles(); // TODO: make me fly: make this delayed
168 feed->loadImage();
169 }
170
171 return feed;
172}
173
174bool Feed::accept(TreeNodeVisitor* visitor)
175{
176 if (visitor->visitFeed(this))
177 return true;
178 else
179 return visitor->visitTreeNode(this);
180}
181
182TQStringList Feed::tags() const
183{
184 return d->archive->tags();
185}
186
187Article Feed::findArticle(const TQString& guid) const
188{
189 return d->articles[guid];
190}
191
192TQValueList<Article> Feed::articles(const TQString& tag)
193{
194 if (!d->articlesLoaded)
195 loadArticles();
196 if (tag.isNull())
197 return d->articles.values();
198 else
199 {
200 TQValueList<Article> tagged;
201 TQStringList guids = d->archive->articles(tag);
202 for (TQStringList::ConstIterator it = guids.begin(); it != guids.end(); ++it)
203 tagged += d->articles[*it];
204 return tagged;
205
206 }
207}
208
210{
211 TQString imageFileName = TDEGlobal::dirs()->saveLocation("cache", "akregator/Media/")
212 + Utils::fileNameForUrl(d->xmlUrl) +
213".png";
214 d->imagePixmap.load(imageFileName, "PNG");
215}
216
218{
219 if (d->articlesLoaded)
220 return;
221
222 if (!d->archive)
223 d->archive = Backend::Storage::getInstance()->archiveFor(xmlUrl());
224
225 TQStringList list = d->archive->articles();
226 for ( TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
227 {
228 Article mya(*it, this);
229 d->articles[mya.guid()] = mya;
230 if (mya.isDeleted())
231 d->deletedArticles.append(mya);
232 }
233
234 d->articlesLoaded = true;
235 enforceLimitArticleNumber();
236 recalcUnreadCount();
237}
238
239void Feed::recalcUnreadCount()
240{
241 TQValueList<Article> tarticles = articles();
242 TQValueList<Article>::Iterator it;
243 TQValueList<Article>::Iterator en = tarticles.end();
244
245 int oldUnread = d->archive->unread();
246
247 int unread = 0;
248
249 for (it = tarticles.begin(); it != en; ++it)
250 if (!(*it).isDeleted() && (*it).status() != Article::Read)
251 ++unread;
252
253 if (unread != oldUnread)
254 {
255 d->archive->setUnread(unread);
256 nodeModified();
257 }
258}
259
261{
262 if (str == "globalDefault")
263 return globalDefault;
264 if (str == "keepAllArticles")
265 return keepAllArticles;
266 if (str == "disableArchiving")
267 return disableArchiving;
268 if (str == "limitArticleNumber")
269 return limitArticleNumber;
270 if (str == "limitArticleAge")
271 return limitArticleAge;
272
273 return globalDefault;
274}
275
276Feed::Feed() : TreeNode(), d(new FeedPrivate)
277{
278 d->autoFetch = false;
279 d->fetchInterval = 30;
280 d->archiveMode = globalDefault;
281 d->maxArticleAge = 60;
282 d->maxArticleNumber = 1000;
283 d->markImmediatelyAsRead = false;
284 d->useNotification = false;
285 d->fetchError = false;
286 d->lastErrorFetch = 0;
287 d->fetchTries = 0;
288 d->loader = 0;
289 d->articlesLoaded = false;
290 d->archive = 0;
291 d->loadLinkedWebsite = false;
292}
293
294Feed::~Feed()
295{
296 slotAbortFetch();
297 emitSignalDestroyed();
298 delete d;
299 d = 0;
300}
301
302bool Feed::useCustomFetchInterval() const { return d->autoFetch; }
303
304void Feed::setCustomFetchIntervalEnabled(bool enabled) { d->autoFetch = enabled; }
305
306int Feed::fetchInterval() const { return d->fetchInterval; }
307
308void Feed::setFetchInterval(int interval) { d->fetchInterval = interval; }
309
310int Feed::maxArticleAge() const { return d->maxArticleAge; }
311
312void Feed::setMaxArticleAge(int maxArticleAge) { d->maxArticleAge = maxArticleAge; }
313
314int Feed::maxArticleNumber() const { return d->maxArticleNumber; }
315
316void Feed::setMaxArticleNumber(int maxArticleNumber) { d->maxArticleNumber = maxArticleNumber; }
317
318bool Feed::markImmediatelyAsRead() const { return d->markImmediatelyAsRead; }
319
320void Feed::setMarkImmediatelyAsRead(bool enabled)
321{
322 d->markImmediatelyAsRead = enabled;
323 if (enabled)
325}
326
327void Feed::setUseNotification(bool enabled)
328{
329 d->useNotification = enabled;
330}
331
332bool Feed::useNotification() const
333{
334 return d->useNotification;
335}
336
338{
339 d->loadLinkedWebsite = enabled;
340}
341
342bool Feed::loadLinkedWebsite() const
343{
344 return d->loadLinkedWebsite;
345}
346
347const TQPixmap& Feed::favicon() const { return d->favicon; }
348
349const TQPixmap& Feed::image() const { return d->imagePixmap; }
350
351const TQString& Feed::xmlUrl() const { return d->xmlUrl; }
352
353void Feed::setXmlUrl(const TQString& s) { d->xmlUrl = s; }
354
355const TQString& Feed::htmlUrl() const { return d->htmlUrl; }
356
357void Feed::setHtmlUrl(const TQString& s) { d->htmlUrl = s; }
358
359const TQString& Feed::description() const { return d->description; }
360
361void Feed::setDescription(const TQString& s) { d->description = s; }
362
363bool Feed::fetchErrorOccurred() { return d->fetchError; }
364
365bool Feed::isArticlesLoaded() const { return d->articlesLoaded; }
366
367
368TQDomElement Feed::toOPML( TQDomElement parent, TQDomDocument document ) const
369{
370 TQDomElement el = document.createElement( "outline" );
371 el.setAttribute( "text", title() );
372 el.setAttribute( "title", title() );
373 el.setAttribute( "xmlUrl", d->xmlUrl );
374 el.setAttribute( "htmlUrl", d->htmlUrl );
375 el.setAttribute( "id", TQString::number(id()) );
376 el.setAttribute( "description", d->description );
377 el.setAttribute( "useCustomFetchInterval", (useCustomFetchInterval() ? "true" : "false") );
378 el.setAttribute( "fetchInterval", TQString::number(fetchInterval()) );
379 el.setAttribute( "archiveMode", archiveModeToString(d->archiveMode) );
380 el.setAttribute( "maxArticleAge", d->maxArticleAge );
381 el.setAttribute( "maxArticleNumber", d->maxArticleNumber );
382 if (d->markImmediatelyAsRead)
383 el.setAttribute( "markImmediatelyAsRead", "true" );
384 if (d->useNotification)
385 el.setAttribute( "useNotification", "true" );
386 if (d->loadLinkedWebsite)
387 el.setAttribute( "loadLinkedWebsite", "true" );
388 el.setAttribute( "maxArticleNumber", d->maxArticleNumber );
389 el.setAttribute( "type", "rss" ); // despite some additional fields, its still "rss" OPML
390 el.setAttribute( "version", "RSS" );
391 parent.appendChild( el );
392 return el;
393}
394
396{
397 if (unread() > 0)
398 {
399 setNotificationMode(false, true);
400 TQValueList<Article> tarticles = articles();
401 TQValueList<Article>::Iterator it;
402 TQValueList<Article>::Iterator en = tarticles.end();
403
404 for (it = tarticles.begin(); it != en; ++it)
405 (*it).setStatus(Article::Read);
406 setNotificationMode(true, true);
407 }
408}
409void Feed::slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchOnly)
410{
411 if (!intervalFetchOnly)
412 queue->addFeed(this);
413 else
414 {
415 uint now = TQDateTime::currentDateTime().toTime_t();
416
417 // workaround for 3.5.x: if the last fetch went wrong, try again after 30 minutes
418 // this fixes annoying behaviour of akregator, especially when the host is reachable
419 // but Akregator can't parse the feed (the host is hammered every minute then)
420 if ( fetchErrorOccurred() && now - d->lastErrorFetch <= 30*60 )
421 return;
422
423 int interval = -1;
424
426 interval = fetchInterval() * 60;
427 else
428 if ( Settings::useIntervalFetch() )
429 interval = Settings::autoFetchInterval() * 60;
430
431 uint lastFetch = d->archive->lastFetch();
432
433 if ( interval > 0 && now - lastFetch >= (uint)interval )
434 queue->addFeed(this);
435 }
436}
437
438
439void Feed::appendArticles(const RSS::Document &doc)
440{
441 bool changed = false;
442
443 RSS::Article::List d_articles = doc.articles();
444 RSS::Article::List::ConstIterator it;
445 RSS::Article::List::ConstIterator en = d_articles.end();
446
447 int nudge=0;
448
449 TQValueList<Article> deletedArticles = d->deletedArticles;
450
451 for (it = d_articles.begin(); it != en; ++it)
452 {
453 if ( !d->articles.contains((*it).guid()) ) // article not in list
454 {
455 Article mya(*it, this);
456 mya.offsetPubDate(nudge);
457 nudge--;
458 appendArticle(mya);
459
460 TQValueList<ArticleInterceptor*> interceptors = ArticleInterceptorManager::self()->interceptors();
461 for (TQValueList<ArticleInterceptor*>::ConstIterator it = interceptors.begin(); it != interceptors.end(); ++it)
462 (*it)->processArticle(mya);
463
464 d->addedArticlesNotify.append(mya);
465
466 if (!mya.isDeleted() && !markImmediatelyAsRead())
467 mya.setStatus(Article::New);
468 else
469 mya.setStatus(Article::Read);
470
471 changed = true;
472 }
473 else // article is in list
474 {
475 // if the article's guid is no hash but an ID, we have to check if the article was updated. That's done by comparing the hash values.
476 Article old = d->articles[(*it).guid()];
477 Article mya(*it, this);
478 if (!mya.guidIsHash() && mya.hash() != old.hash() && !old.isDeleted())
479 {
480 mya.setKeep(old.keep());
481 int oldstatus = old.status();
482 old.setStatus(Article::Read);
483
484 d->articles.remove(old.guid());
485 appendArticle(mya);
486
487 mya.setStatus(oldstatus);
488
489 d->updatedArticlesNotify.append(mya);
490 changed = true;
491 }
492 else if (old.isDeleted())
493 deletedArticles.remove(mya);
494 }
495 }
496
497 TQValueList<Article>::ConstIterator dit = deletedArticles.begin();
498 TQValueList<Article>::ConstIterator dtmp;
499 TQValueList<Article>::ConstIterator den = deletedArticles.end();
500
501 // delete articles with delete flag set completely from archive, which aren't in the current feed source anymore
502 while (dit != den)
503 {
504 dtmp = dit;
505 ++dit;
506 d->articles.remove((*dtmp).guid());
507 d->archive->deleteArticle((*dtmp).guid());
508 d->deletedArticles.remove(*dtmp);
509 }
510
511 if (changed)
513}
514
515bool Feed::usesExpiryByAge() const
516{
517 return ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge) || d->archiveMode == limitArticleAge;
518}
519
520bool Feed::isExpired(const Article& a) const
521{
522 TQDateTime now = TQDateTime::currentDateTime();
523 int expiryAge = -1;
524// check whether the feed uses the global default and the default is limitArticleAge
525 if ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge)
526 expiryAge = Settings::maxArticleAge() *24*3600;
527 else // otherwise check if this feed has limitArticleAge set
528 if ( d->archiveMode == limitArticleAge)
529 expiryAge = d->maxArticleAge *24*3600;
530
531 return ( expiryAge != -1 && a.pubDate().secsTo(now) > expiryAge);
532}
533
534void Feed::appendArticle(const Article& a)
535{
536 if ( (a.keep() && Settings::doNotExpireImportantArticles()) || ( !usesExpiryByAge() || !isExpired(a) ) ) // if not expired
537 {
538 if (!d->articles.contains(a.guid()))
539 {
540 d->articles[a.guid()] = a;
541 if (!a.isDeleted() && a.status() != Article::Read)
542 setUnread(unread()+1);
543 }
544 }
545}
546
547
548void Feed::fetch(bool followDiscovery)
549{
550 d->followDiscovery = followDiscovery;
551 d->fetchTries = 0;
552
553 // mark all new as unread
554 TQValueList<Article> articles = d->articles.values();
555 TQValueList<Article>::Iterator it;
556 TQValueList<Article>::Iterator en = articles.end();
557 for (it = articles.begin(); it != en; ++it)
558 {
559 if ((*it).status() == Article::New)
560 {
561 (*it).setStatus(Article::Unread);
562 }
563 }
564
565 emit fetchStarted(this);
566
567 tryFetch();
568}
569
570void Feed::slotAbortFetch()
571{
572 if (d->loader)
573 {
574 d->loader->abort();
575 }
576}
577
578void Feed::tryFetch()
579{
580 d->fetchError = false;
581
582 d->loader = RSS::Loader::create( this, TQ_SLOT(fetchCompleted(Loader *, Document, Status)) );
583 //connect(d->loader, TQ_SIGNAL(progress(unsigned long)), this, TQ_SLOT(slotSetProgress(unsigned long)));
584 d->loader->loadFrom( d->xmlUrl, new RSS::FileRetriever );
585}
586
587void Feed::slotImageFetched(const TQPixmap& image)
588{
589 if (image.isNull())
590 return;
591 d->imagePixmap=image;
592 d->imagePixmap.save(TDEGlobal::dirs()->saveLocation("cache", "akregator/Media/")
593 + Utils::fileNameForUrl(d->xmlUrl) +
594".png","PNG");
595 nodeModified();
596}
597
598void Feed::fetchCompleted(RSS::Loader *l, RSS::Document doc, RSS::Status status)
599{
600 // Note that loader instances delete themselves
601 d->loader = 0;
602
603 // fetching wasn't successful:
604 if (status != RSS::Success)
605 {
606 if (status == RSS::Aborted)
607 {
608 d->fetchError = false;
609 emit fetchAborted(this);
610 }
611 else if (d->followDiscovery && (status == RSS::ParseError) && (d->fetchTries < 3) && (l->discoveredFeedURL().isValid()))
612 {
613 d->fetchTries++;
614 d->xmlUrl = l->discoveredFeedURL().url();
615 emit fetchDiscovery(this);
616 tryFetch();
617 }
618 else
619 {
620 d->fetchError = true;
621 d->lastErrorFetch = TQDateTime::currentDateTime().toTime_t();
622 emit fetchError(this);
623 }
624 return;
625 }
626
627 loadArticles(); // TODO: make me fly: make this delayed
628
629 // Restore favicon.
630 if (d->favicon.isNull())
631 loadFavicon();
632
633 d->fetchError = false;
634
635 if (doc.image() && d->imagePixmap.isNull())
636 {
637 d->image = *doc.image();
638 connect(&d->image, TQ_SIGNAL(gotPixmap(const TQPixmap&)), this, TQ_SLOT(slotImageFetched(const TQPixmap&)));
639 d->image.getPixmap();
640 }
641
642 if (title().isEmpty())
643 setTitle( doc.title() );
644
645 d->description = doc.description();
646 d->htmlUrl = doc.link().url();
647
648 appendArticles(doc);
649
650 d->archive->setLastFetch( TQDateTime::currentDateTime().toTime_t());
651 emit fetched(this);
652}
653
655{
656 FeedIconManager::self()->fetchIcon(this);
657}
658
660{
661 if ( !usesExpiryByAge() )
662 return;
663
664 TQValueList<Article> articles = d->articles.values();
665
666 TQValueList<Article>::Iterator en = articles.end();
667
668 setNotificationMode(false);
669
670 // check keep flag only if it should be respected for expiry
671 // the code could be more compact, but we better check
672 // doNotExpiredArticles once instead of in every iteration
673 if (Settings::doNotExpireImportantArticles())
674 {
675 for (TQValueList<Article>::Iterator it = articles.begin(); it != en; ++it)
676 {
677 if (!(*it).keep() && isExpired(*it))
678 {
679 (*it).setDeleted();
680 }
681 }
682 }
683 else
684 {
685 for (TQValueList<Article>::Iterator it = articles.begin(); it != en; ++it)
686 {
687 if (isExpired(*it))
688 {
689 (*it).setDeleted();
690 }
691 }
692 }
694}
695
696void Feed::setFavicon(const TQPixmap &p)
697{
698 d->favicon = p;
699 nodeModified();
700}
701
703{
704 return d->archiveMode;
705}
706
708{
709 d->archiveMode = archiveMode;
710}
711
712int Feed::unread() const
713{
714 return d->archive ? d->archive->unread() : 0;
715}
716
717void Feed::setUnread(int unread)
718{
719 if (d->archive && unread != d->archive->unread())
720 {
721 d->archive->setUnread(unread);
722 nodeModified();
723 }
724}
725
726
727void Feed::setArticleDeleted(Article& a)
728{
729 if (!d->deletedArticles.contains(a))
730 d->deletedArticles.append(a);
731
732 if (!d->removedArticlesNotify.contains(a))
733 d->removedArticlesNotify.append(a);
734
736}
737
738void Feed::setArticleChanged(Article& a, int oldStatus)
739{
740 if (oldStatus != -1)
741 {
742 int newStatus = a.status();
743 if (oldStatus == Article::Read && newStatus != Article::Read)
744 setUnread(unread()+1);
745 else if (oldStatus != Article::Read && newStatus == Article::Read)
746 setUnread(unread()-1);
747 }
748 d->updatedArticlesNotify.append(a);
750}
751
753{
754 return d->articles.count();
755}
756
758{
759 if ( nextSibling() )
760 return nextSibling();
761
762 Folder* p = parent();
763 while (p)
764 {
765 if ( p->nextSibling() )
766 return p->nextSibling();
767 else
768 p = p->parent();
769 }
770 return 0;
771}
772
774{
775 if (!d->addedArticlesNotify.isEmpty())
776 {
777 // copy list, otherwise the refcounting in Article::Private breaks for
778 // some reason (causing segfaults)
779 TQValueList<Article> l = d->addedArticlesNotify;
780 emit signalArticlesAdded(this, l);
781 d->addedArticlesNotify.clear();
782 }
783 if (!d->updatedArticlesNotify.isEmpty())
784 {
785 // copy list, otherwise the refcounting in Article::Private breaks for
786 // some reason (causing segfaults)
787 TQValueList<Article> l = d->updatedArticlesNotify;
788 emit signalArticlesUpdated(this, l);
789 d->updatedArticlesNotify.clear();
790 }
791 if (!d->removedArticlesNotify.isEmpty())
792 {
793 // copy list, otherwise the refcounting in Article::Private breaks for
794 // some reason (causing segfaults)
795 TQValueList<Article> l = d->removedArticlesNotify;
796 emit signalArticlesRemoved(this, l);
797 d->removedArticlesNotify.clear();
798 }
800}
801
802void Feed::enforceLimitArticleNumber()
803{
804 int limit = -1;
805 if (d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleNumber)
806 limit = Settings::maxArticleNumber();
807 else if (d->archiveMode == limitArticleNumber)
808 limit = maxArticleNumber();
809
810 if (limit == -1 || limit >= d->articles.count() - d->deletedArticles.count())
811 return;
812
813 setNotificationMode(false);
814 TQValueList<Article> articles = d->articles.values();
815 qHeapSort(articles);
816 TQValueList<Article>::Iterator it = articles.begin();
817 TQValueList<Article>::Iterator tmp;
818 TQValueList<Article>::Iterator en = articles.end();
819
820 int c = 0;
821
822 if (Settings::doNotExpireImportantArticles())
823 {
824 while (it != en)
825 {
826 tmp = it;
827 ++it;
828 if (c < limit)
829 {
830 if (!(*tmp).isDeleted() && !(*tmp).keep())
831 c++;
832 }
833 else if (!(*tmp).keep())
834 (*tmp).setDeleted();
835 }
836 }
837 else
838 {
839 while (it != en)
840 {
841 tmp = it;
842 ++it;
843 if (c < limit && !(*tmp).isDeleted())
844 {
845 c++;
846 }
847 else
848 {
849 (*tmp).setDeleted();
850 }
851 }
852 }
854}
855
856} // namespace Akregator
857#include "feed.moc"
A proxy class for RSS::Article with some additional methods to assist sorting.
Definition: article.h:58
represents a feed
Definition: feed.h:63
virtual int unread() const
returns the unread count for this feed
Definition: feed.cpp:712
void loadImage()
load the image from the cache if it is in there
Definition: feed.cpp:209
void setArchiveMode(ArchiveMode archiveMode)
sets the archiving mode for this feed
Definition: feed.cpp:707
virtual TQValueList< Article > articles(const TQString &tag=TQString())
Returns a sequence of the articles this node contains.
Definition: feed.cpp:192
void fetchError(Feed *)
emitted when a fetch error occurred
ArchiveMode
the archiving modes:
Definition: feed.h:76
bool markImmediatelyAsRead() const
if true, new articles are marked immediately as read instead of new/unread.
Definition: feed.cpp:318
void setDescription(const TQString &s)
sets the description of this feed
Definition: feed.cpp:361
bool fetchErrorOccurred()
returns whether a fetch error has occurred
Definition: feed.cpp:363
virtual TreeNode * next()
returns the next node in the tree.
Definition: feed.cpp:757
void loadFavicon()
downloads the favicon
Definition: feed.cpp:654
const TQPixmap & favicon() const
returns the favicon
Definition: feed.cpp:347
void loadArticles()
loads articles from archive
Definition: feed.cpp:217
int fetchInterval() const
Returns custom auto fetch interval of this feed.
Definition: feed.cpp:306
virtual int totalCount() const
returns the number of total articles in this feed
Definition: feed.cpp:752
void setLoadLinkedWebsite(bool enabled)
if true, the linked URL is loaded directly in the article viewer instead of showing the description
Definition: feed.cpp:337
void fetchStarted(Feed *)
emitted when fetching started
void fetchDiscovery(Feed *)
emitted when a feed URL was found by auto discovery
void setMaxArticleAge(int maxArticleAge)
sets the maximum age of articles used for expiration by age (used in limitArticleAge archive mode)
Definition: feed.cpp:312
const TQString & description() const
returns the description of this feed
Definition: feed.cpp:359
virtual void doArticleNotification()
reimplement this in subclasses to do the actual notification called by articlesModified
Definition: feed.cpp:773
virtual void slotAddToFetchQueue(FetchQueue *queue, bool intervalFetchOnly=false)
add this feed to the fetch queue queue
Definition: feed.cpp:409
void setMaxArticleNumber(int maxArticleNumber)
sets the article count limit used in limitArticleNumber archive mode
Definition: feed.cpp:316
static TQString archiveModeToString(ArchiveMode mode)
converts ArchiveMode values to corresponding strings
Definition: feed.cpp:104
int maxArticleAge() const
returns the maximum age of articles used for expiration by age (used in limitArticleAge archive mode)
Definition: feed.cpp:310
const TQPixmap & image() const
returns the feed image
Definition: feed.cpp:349
bool isArticlesLoaded() const
returns if the article archive of this feed is loaded
Definition: feed.cpp:365
Feed()
default constructor
Definition: feed.cpp:276
static Feed * fromOPML(TQDomElement e)
creates a Feed object from a description in OPML format
Definition: feed.cpp:125
const TQString & xmlUrl() const
returns the url of the actual feed source (rss/rdf/atom file)
Definition: feed.cpp:351
void setXmlUrl(const TQString &s)
sets the url of the actual feed source (rss/rdf/atom file)
Definition: feed.cpp:353
void setUnread(int unread)
sets the unread count for this feed
Definition: feed.cpp:717
bool useCustomFetchInterval() const
returns whether this feed uses its own fetch interval or the global setting
Definition: feed.cpp:302
virtual TQDomElement toOPML(TQDomElement parent, TQDomDocument document) const
exports the feed settings to OPML
Definition: feed.cpp:368
int maxArticleNumber() const
returns the article count limit used in limitArticleNumber archive mode
Definition: feed.cpp:314
virtual TQStringList tags() const
returns a list of all tags occurring in this node (sub tree for folders)
Definition: feed.cpp:182
void setFetchInterval(int interval)
Sets custom auto fetch interval.
Definition: feed.cpp:308
void fetchAborted(Feed *)
emitted when a fetch is aborted
void fetched(Feed *)
emitted when feed finished fetching
ArchiveMode archiveMode() const
returns the archiving mode which is used for this feed
Definition: feed.cpp:702
virtual void slotDeleteExpiredArticles()
deletes expired articles
Definition: feed.cpp:659
void fetch(bool followDiscovery=false)
starts fetching
Definition: feed.cpp:548
virtual void slotMarkAllArticlesAsRead()
mark all articles in this feed as read
Definition: feed.cpp:395
static ArchiveMode stringToArchiveMode(const TQString &str)
converts strings to ArchiveMode value if parsing fails, it returns ArchiveMode::globalDefault
Definition: feed.cpp:260
const TQString & htmlUrl() const
returns the URL of the HTML page of this feed
Definition: feed.cpp:355
void setFavicon(const TQPixmap &p)
sets the favicon (used in the tree view)
Definition: feed.cpp:696
void setHtmlUrl(const TQString &s)
sets the URL of the HTML page of this feed
Definition: feed.cpp:357
virtual Article findArticle(const TQString &guid) const
returns the article with the given guid, or a null article if it not exists
Definition: feed.cpp:187
void setCustomFetchIntervalEnabled(bool enabled)
set if the feed has its custom fetch interval or uses the global setting
Definition: feed.cpp:304
Represents a folder (containing feeds and/or other folders)
Definition: folder.h:45
virtual void appendChild(TreeNode *node)
inserts node as last child
Definition: folder.cpp:168
Abstract base class for all kind of elements in the feed tree, like feeds and feed groups (and search...
Definition: treenode.h:52
virtual TreeNode * nextSibling() const
Get the next sibling.
Definition: treenode.cpp:92
virtual void setTitle(const TQString &title)
Sets the title of the node.
Definition: treenode.cpp:82
void signalArticlesAdded(TreeNode *node, const TQValueList< Article > &guids)
emitted when new articles were added to this node or any node in the subtree (for folders).
virtual void setNotificationMode(bool doNotify, bool notifyOccurredChanges=true)
Definition: treenode.cpp:125
void signalArticlesUpdated(TreeNode *, const TQValueList< Article > &guids)
emitted when articles were updated
virtual void doArticleNotification()
reimplement this in subclasses to do the actual notification called by articlesModified
Definition: treenode.cpp:171
virtual void articlesModified()
call this if the articles in the node were changed.
Definition: treenode.cpp:163
void signalArticlesRemoved(TreeNode *, const TQValueList< Article > &guids)
emitted when articles were removed from this subtree.
virtual void setId(uint id)
sets the ID
Definition: treenode.cpp:150
virtual const TQString & title() const
Get title of node.
Definition: treenode.cpp:77
virtual void nodeModified()
call this if you modified the actual node (title, unread count).
Definition: treenode.cpp:155
virtual Folder * parent() const
Returns the parent node.
Definition: treenode.cpp:115