00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <tqtimer.h>
00027 #include <tqdatetime.h>
00028 #include <tqlistview.h>
00029 #include <tqdom.h>
00030 #include <tqmap.h>
00031 #include <tqpixmap.h>
00032 #include <tqvaluelist.h>
00033
00034 #include <kurl.h>
00035 #include <kdebug.h>
00036 #include <tdeglobal.h>
00037 #include <kstandarddirs.h>
00038
00039 #include "akregatorconfig.h"
00040 #include "article.h"
00041 #include "articleinterceptor.h"
00042 #include "feed.h"
00043 #include "folder.h"
00044 #include "fetchqueue.h"
00045 #include "feediconmanager.h"
00046 #include "feedstorage.h"
00047 #include "storage.h"
00048 #include "treenodevisitor.h"
00049 #include "utils.h"
00050
00051 #include "librss/librss.h"
00052
00053 namespace Akregator {
00054
00055 class Feed::FeedPrivate
00056 {
00057 public:
00058 bool autoFetch;
00059 int fetchInterval;
00060 ArchiveMode archiveMode;
00061 int maxArticleAge;
00062 int maxArticleNumber;
00063 bool markImmediatelyAsRead;
00064 bool useNotification;
00065 bool loadLinkedWebsite;
00066
00067 bool fetchError;
00068
00069 int lastErrorFetch;
00070
00071
00072
00073
00074 int fetchTries;
00075 bool followDiscovery;
00076 RSS::Loader* loader;
00077 bool articlesLoaded;
00078 Backend::FeedStorage* archive;
00079
00080 TQString xmlUrl;
00081 TQString htmlUrl;
00082 TQString description;
00083
00085 TQMap<TQString, Article> articles;
00086
00088 TQMap<TQString, TQStringList> taggedArticles;
00089
00091 TQValueList<Article> deletedArticles;
00092
00095 TQValueList<Article> addedArticlesNotify;
00096 TQValueList<Article> removedArticlesNotify;
00097 TQValueList<Article> updatedArticlesNotify;
00098
00099 TQPixmap imagePixmap;
00100 RSS::Image image;
00101 TQPixmap favicon;
00102 };
00103
00104 TQString Feed::archiveModeToString(ArchiveMode mode)
00105 {
00106 switch (mode)
00107 {
00108 case keepAllArticles:
00109 return "keepAllArticles";
00110 case disableArchiving:
00111 return "disableArchiving";
00112 case limitArticleNumber:
00113 return "limitArticleNumber";
00114 case limitArticleAge:
00115 return "limitArticleAge";
00116 default:
00117 return "globalDefault";
00118 }
00119
00120
00121
00122 return "globalDefault";
00123 }
00124
00125 Feed* Feed::fromOPML(TQDomElement e)
00126 {
00127
00128 Feed* feed = 0;
00129
00130 if( e.hasAttribute("xmlUrl") || e.hasAttribute("xmlurl") || e.hasAttribute("xmlURL") )
00131 {
00132 TQString title = e.hasAttribute("text") ? e.attribute("text") : e.attribute("title");
00133
00134 TQString xmlUrl = e.hasAttribute("xmlUrl") ? e.attribute("xmlUrl") : e.attribute("xmlurl");
00135 if (xmlUrl.isEmpty())
00136 xmlUrl = e.attribute("xmlURL");
00137
00138 bool useCustomFetchInterval = e.attribute("useCustomFetchInterval") == "true" || e.attribute("autoFetch") == "true";
00139
00140
00141
00142 TQString htmlUrl = e.attribute("htmlUrl");
00143 TQString description = e.attribute("description");
00144 int fetchInterval = e.attribute("fetchInterval").toInt();
00145 ArchiveMode archiveMode = stringToArchiveMode(e.attribute("archiveMode"));
00146 int maxArticleAge = e.attribute("maxArticleAge").toUInt();
00147 int maxArticleNumber = e.attribute("maxArticleNumber").toUInt();
00148 bool markImmediatelyAsRead = e.attribute("markImmediatelyAsRead") == "true";
00149 bool useNotification = e.attribute("useNotification") == "true";
00150 bool loadLinkedWebsite = e.attribute("loadLinkedWebsite") == "true";
00151 uint id = e.attribute("id").toUInt();
00152
00153 feed = new Feed();
00154 feed->setTitle(title);
00155 feed->setXmlUrl(xmlUrl);
00156 feed->setCustomFetchIntervalEnabled(useCustomFetchInterval);
00157 feed->setHtmlUrl(htmlUrl);
00158 feed->setId(id);
00159 feed->setDescription(description);
00160 feed->setArchiveMode(archiveMode);
00161 feed->setUseNotification(useNotification);
00162 feed->setFetchInterval(fetchInterval);
00163 feed->setMaxArticleAge(maxArticleAge);
00164 feed->setMaxArticleNumber(maxArticleNumber);
00165 feed->setMarkImmediatelyAsRead(markImmediatelyAsRead);
00166 feed->setLoadLinkedWebsite(loadLinkedWebsite);
00167 feed->loadArticles();
00168 feed->loadImage();
00169 }
00170
00171 return feed;
00172 }
00173
00174 bool Feed::accept(TreeNodeVisitor* visitor)
00175 {
00176 if (visitor->visitFeed(this))
00177 return true;
00178 else
00179 return visitor->visitTreeNode(this);
00180 }
00181
00182 TQStringList Feed::tags() const
00183 {
00184 return d->archive->tags();
00185 }
00186
00187 Article Feed::findArticle(const TQString& guid) const
00188 {
00189 return d->articles[guid];
00190 }
00191
00192 TQValueList<Article> Feed::articles(const TQString& tag)
00193 {
00194 if (!d->articlesLoaded)
00195 loadArticles();
00196 if (tag.isNull())
00197 return d->articles.values();
00198 else
00199 {
00200 TQValueList<Article> tagged;
00201 TQStringList guids = d->archive->articles(tag);
00202 for (TQStringList::ConstIterator it = guids.begin(); it != guids.end(); ++it)
00203 tagged += d->articles[*it];
00204 return tagged;
00205
00206 }
00207 }
00208
00209 void Feed::loadImage()
00210 {
00211 TQString imageFileName = TDEGlobal::dirs()->saveLocation("cache", "akregator/Media/")
00212 + Utils::fileNameForUrl(d->xmlUrl) +
00213 ".png";
00214 d->imagePixmap.load(imageFileName, "PNG");
00215 }
00216
00217 void Feed::loadArticles()
00218 {
00219 if (d->articlesLoaded)
00220 return;
00221
00222 if (!d->archive)
00223 d->archive = Backend::Storage::getInstance()->archiveFor(xmlUrl());
00224
00225 TQStringList list = d->archive->articles();
00226 for ( TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
00227 {
00228 Article mya(*it, this);
00229 d->articles[mya.guid()] = mya;
00230 if (mya.isDeleted())
00231 d->deletedArticles.append(mya);
00232 }
00233
00234 d->articlesLoaded = true;
00235 enforceLimitArticleNumber();
00236 recalcUnreadCount();
00237 }
00238
00239 void Feed::recalcUnreadCount()
00240 {
00241 TQValueList<Article> tarticles = articles();
00242 TQValueList<Article>::Iterator it;
00243 TQValueList<Article>::Iterator en = tarticles.end();
00244
00245 int oldUnread = d->archive->unread();
00246
00247 int unread = 0;
00248
00249 for (it = tarticles.begin(); it != en; ++it)
00250 if (!(*it).isDeleted() && (*it).status() != Article::Read)
00251 ++unread;
00252
00253 if (unread != oldUnread)
00254 {
00255 d->archive->setUnread(unread);
00256 nodeModified();
00257 }
00258 }
00259
00260 Feed::ArchiveMode Feed::stringToArchiveMode(const TQString& str)
00261 {
00262 if (str == "globalDefault")
00263 return globalDefault;
00264 if (str == "keepAllArticles")
00265 return keepAllArticles;
00266 if (str == "disableArchiving")
00267 return disableArchiving;
00268 if (str == "limitArticleNumber")
00269 return limitArticleNumber;
00270 if (str == "limitArticleAge")
00271 return limitArticleAge;
00272
00273 return globalDefault;
00274 }
00275
00276 Feed::Feed() : TreeNode(), d(new FeedPrivate)
00277 {
00278 d->autoFetch = false;
00279 d->fetchInterval = 30;
00280 d->archiveMode = globalDefault;
00281 d->maxArticleAge = 60;
00282 d->maxArticleNumber = 1000;
00283 d->markImmediatelyAsRead = false;
00284 d->useNotification = false;
00285 d->fetchError = false;
00286 d->lastErrorFetch = 0;
00287 d->fetchTries = 0;
00288 d->loader = 0;
00289 d->articlesLoaded = false;
00290 d->archive = 0;
00291 d->loadLinkedWebsite = false;
00292 }
00293
00294 Feed::~Feed()
00295 {
00296 slotAbortFetch();
00297 emitSignalDestroyed();
00298 delete d;
00299 d = 0;
00300 }
00301
00302 bool Feed::useCustomFetchInterval() const { return d->autoFetch; }
00303
00304 void Feed::setCustomFetchIntervalEnabled(bool enabled) { d->autoFetch = enabled; }
00305
00306 int Feed::fetchInterval() const { return d->fetchInterval; }
00307
00308 void Feed::setFetchInterval(int interval) { d->fetchInterval = interval; }
00309
00310 int Feed::maxArticleAge() const { return d->maxArticleAge; }
00311
00312 void Feed::setMaxArticleAge(int maxArticleAge) { d->maxArticleAge = maxArticleAge; }
00313
00314 int Feed::maxArticleNumber() const { return d->maxArticleNumber; }
00315
00316 void Feed::setMaxArticleNumber(int maxArticleNumber) { d->maxArticleNumber = maxArticleNumber; }
00317
00318 bool Feed::markImmediatelyAsRead() const { return d->markImmediatelyAsRead; }
00319
00320 void Feed::setMarkImmediatelyAsRead(bool enabled)
00321 {
00322 d->markImmediatelyAsRead = enabled;
00323 if (enabled)
00324 slotMarkAllArticlesAsRead();
00325 }
00326
00327 void Feed::setUseNotification(bool enabled)
00328 {
00329 d->useNotification = enabled;
00330 }
00331
00332 bool Feed::useNotification() const
00333 {
00334 return d->useNotification;
00335 }
00336
00337 void Feed::setLoadLinkedWebsite(bool enabled)
00338 {
00339 d->loadLinkedWebsite = enabled;
00340 }
00341
00342 bool Feed::loadLinkedWebsite() const
00343 {
00344 return d->loadLinkedWebsite;
00345 }
00346
00347 const TQPixmap& Feed::favicon() const { return d->favicon; }
00348
00349 const TQPixmap& Feed::image() const { return d->imagePixmap; }
00350
00351 const TQString& Feed::xmlUrl() const { return d->xmlUrl; }
00352
00353 void Feed::setXmlUrl(const TQString& s) { d->xmlUrl = s; }
00354
00355 const TQString& Feed::htmlUrl() const { return d->htmlUrl; }
00356
00357 void Feed::setHtmlUrl(const TQString& s) { d->htmlUrl = s; }
00358
00359 const TQString& Feed::description() const { return d->description; }
00360
00361 void Feed::setDescription(const TQString& s) { d->description = s; }
00362
00363 bool Feed::fetchErrorOccurred() { return d->fetchError; }
00364
00365 bool Feed::isArticlesLoaded() const { return d->articlesLoaded; }
00366
00367
00368 TQDomElement Feed::toOPML( TQDomElement parent, TQDomDocument document ) const
00369 {
00370 TQDomElement el = document.createElement( "outline" );
00371 el.setAttribute( "text", title() );
00372 el.setAttribute( "title", title() );
00373 el.setAttribute( "xmlUrl", d->xmlUrl );
00374 el.setAttribute( "htmlUrl", d->htmlUrl );
00375 el.setAttribute( "id", TQString::number(id()) );
00376 el.setAttribute( "description", d->description );
00377 el.setAttribute( "useCustomFetchInterval", (useCustomFetchInterval() ? "true" : "false") );
00378 el.setAttribute( "fetchInterval", TQString::number(fetchInterval()) );
00379 el.setAttribute( "archiveMode", archiveModeToString(d->archiveMode) );
00380 el.setAttribute( "maxArticleAge", d->maxArticleAge );
00381 el.setAttribute( "maxArticleNumber", d->maxArticleNumber );
00382 if (d->markImmediatelyAsRead)
00383 el.setAttribute( "markImmediatelyAsRead", "true" );
00384 if (d->useNotification)
00385 el.setAttribute( "useNotification", "true" );
00386 if (d->loadLinkedWebsite)
00387 el.setAttribute( "loadLinkedWebsite", "true" );
00388 el.setAttribute( "maxArticleNumber", d->maxArticleNumber );
00389 el.setAttribute( "type", "rss" );
00390 el.setAttribute( "version", "RSS" );
00391 parent.appendChild( el );
00392 return el;
00393 }
00394
00395 void Feed::slotMarkAllArticlesAsRead()
00396 {
00397 if (unread() > 0)
00398 {
00399 setNotificationMode(false, true);
00400 TQValueList<Article> tarticles = articles();
00401 TQValueList<Article>::Iterator it;
00402 TQValueList<Article>::Iterator en = tarticles.end();
00403
00404 for (it = tarticles.begin(); it != en; ++it)
00405 (*it).setStatus(Article::Read);
00406 setNotificationMode(true, true);
00407 }
00408 }
00409 void Feed::slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchOnly)
00410 {
00411 if (!intervalFetchOnly)
00412 queue->addFeed(this);
00413 else
00414 {
00415 uint now = TQDateTime::currentDateTime().toTime_t();
00416
00417
00418
00419
00420 if ( fetchErrorOccurred() && now - d->lastErrorFetch <= 30*60 )
00421 return;
00422
00423 int interval = -1;
00424
00425 if (useCustomFetchInterval() )
00426 interval = fetchInterval() * 60;
00427 else
00428 if ( Settings::useIntervalFetch() )
00429 interval = Settings::autoFetchInterval() * 60;
00430
00431 uint lastFetch = d->archive->lastFetch();
00432
00433 if ( interval > 0 && now - lastFetch >= (uint)interval )
00434 queue->addFeed(this);
00435 }
00436 }
00437
00438
00439 void Feed::appendArticles(const RSS::Document &doc)
00440 {
00441 bool changed = false;
00442
00443 RSS::Article::List d_articles = doc.articles();
00444 RSS::Article::List::ConstIterator it;
00445 RSS::Article::List::ConstIterator en = d_articles.end();
00446
00447 int nudge=0;
00448
00449 TQValueList<Article> deletedArticles = d->deletedArticles;
00450
00451 for (it = d_articles.begin(); it != en; ++it)
00452 {
00453 if ( !d->articles.contains((*it).guid()) )
00454 {
00455 Article mya(*it, this);
00456 mya.offsetPubDate(nudge);
00457 nudge--;
00458 appendArticle(mya);
00459
00460 TQValueList<ArticleInterceptor*> interceptors = ArticleInterceptorManager::self()->interceptors();
00461 for (TQValueList<ArticleInterceptor*>::ConstIterator it = interceptors.begin(); it != interceptors.end(); ++it)
00462 (*it)->processArticle(mya);
00463
00464 d->addedArticlesNotify.append(mya);
00465
00466 if (!mya.isDeleted() && !markImmediatelyAsRead())
00467 mya.setStatus(Article::New);
00468 else
00469 mya.setStatus(Article::Read);
00470
00471 changed = true;
00472 }
00473 else
00474 {
00475
00476 Article old = d->articles[(*it).guid()];
00477 Article mya(*it, this);
00478 if (!mya.guidIsHash() && mya.hash() != old.hash() && !old.isDeleted())
00479 {
00480 mya.setKeep(old.keep());
00481 int oldstatus = old.status();
00482 old.setStatus(Article::Read);
00483
00484 d->articles.remove(old.guid());
00485 appendArticle(mya);
00486
00487 mya.setStatus(oldstatus);
00488
00489 d->updatedArticlesNotify.append(mya);
00490 changed = true;
00491 }
00492 else if (old.isDeleted())
00493 deletedArticles.remove(mya);
00494 }
00495 }
00496
00497 TQValueList<Article>::ConstIterator dit = deletedArticles.begin();
00498 TQValueList<Article>::ConstIterator dtmp;
00499 TQValueList<Article>::ConstIterator den = deletedArticles.end();
00500
00501
00502 while (dit != den)
00503 {
00504 dtmp = dit;
00505 ++dit;
00506 d->articles.remove((*dtmp).guid());
00507 d->archive->deleteArticle((*dtmp).guid());
00508 d->deletedArticles.remove(*dtmp);
00509 }
00510
00511 if (changed)
00512 articlesModified();
00513 }
00514
00515 bool Feed::usesExpiryByAge() const
00516 {
00517 return ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge) || d->archiveMode == limitArticleAge;
00518 }
00519
00520 bool Feed::isExpired(const Article& a) const
00521 {
00522 TQDateTime now = TQDateTime::currentDateTime();
00523 int expiryAge = -1;
00524
00525 if ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge)
00526 expiryAge = Settings::maxArticleAge() *24*3600;
00527 else
00528 if ( d->archiveMode == limitArticleAge)
00529 expiryAge = d->maxArticleAge *24*3600;
00530
00531 return ( expiryAge != -1 && a.pubDate().secsTo(now) > expiryAge);
00532 }
00533
00534 void Feed::appendArticle(const Article& a)
00535 {
00536 if ( (a.keep() && Settings::doNotExpireImportantArticles()) || ( !usesExpiryByAge() || !isExpired(a) ) )
00537 {
00538 if (!d->articles.contains(a.guid()))
00539 {
00540 d->articles[a.guid()] = a;
00541 if (!a.isDeleted() && a.status() != Article::Read)
00542 setUnread(unread()+1);
00543 }
00544 }
00545 }
00546
00547
00548 void Feed::fetch(bool followDiscovery)
00549 {
00550 d->followDiscovery = followDiscovery;
00551 d->fetchTries = 0;
00552
00553
00554 TQValueList<Article> articles = d->articles.values();
00555 TQValueList<Article>::Iterator it;
00556 TQValueList<Article>::Iterator en = articles.end();
00557 for (it = articles.begin(); it != en; ++it)
00558 {
00559 if ((*it).status() == Article::New)
00560 {
00561 (*it).setStatus(Article::Unread);
00562 }
00563 }
00564
00565 emit fetchStarted(this);
00566
00567 tryFetch();
00568 }
00569
00570 void Feed::slotAbortFetch()
00571 {
00572 if (d->loader)
00573 {
00574 d->loader->abort();
00575 }
00576 }
00577
00578 void Feed::tryFetch()
00579 {
00580 d->fetchError = false;
00581
00582 d->loader = RSS::Loader::create( this, TQT_SLOT(fetchCompleted(Loader *, Document, Status)) );
00583
00584 d->loader->loadFrom( d->xmlUrl, new RSS::FileRetriever );
00585 }
00586
00587 void Feed::slotImageFetched(const TQPixmap& image)
00588 {
00589 if (image.isNull())
00590 return;
00591 d->imagePixmap=image;
00592 d->imagePixmap.save(TDEGlobal::dirs()->saveLocation("cache", "akregator/Media/")
00593 + Utils::fileNameForUrl(d->xmlUrl) +
00594 ".png","PNG");
00595 nodeModified();
00596 }
00597
00598 void Feed::fetchCompleted(RSS::Loader *l, RSS::Document doc, RSS::Status status)
00599 {
00600
00601 d->loader = 0;
00602
00603
00604 if (status != RSS::Success)
00605 {
00606 if (status == RSS::Aborted)
00607 {
00608 d->fetchError = false;
00609 emit fetchAborted(this);
00610 }
00611 else if (d->followDiscovery && (status == RSS::ParseError) && (d->fetchTries < 3) && (l->discoveredFeedURL().isValid()))
00612 {
00613 d->fetchTries++;
00614 d->xmlUrl = l->discoveredFeedURL().url();
00615 emit fetchDiscovery(this);
00616 tryFetch();
00617 }
00618 else
00619 {
00620 d->fetchError = true;
00621 d->lastErrorFetch = TQDateTime::currentDateTime().toTime_t();
00622 emit fetchError(this);
00623 }
00624 return;
00625 }
00626
00627 loadArticles();
00628
00629
00630 if (d->favicon.isNull())
00631 loadFavicon();
00632
00633 d->fetchError = false;
00634
00635 if (doc.image() && d->imagePixmap.isNull())
00636 {
00637 d->image = *doc.image();
00638 connect(&d->image, TQT_SIGNAL(gotPixmap(const TQPixmap&)), this, TQT_SLOT(slotImageFetched(const TQPixmap&)));
00639 d->image.getPixmap();
00640 }
00641
00642 if (title().isEmpty())
00643 setTitle( doc.title() );
00644
00645 d->description = doc.description();
00646 d->htmlUrl = doc.link().url();
00647
00648 appendArticles(doc);
00649
00650 d->archive->setLastFetch( TQDateTime::currentDateTime().toTime_t());
00651 emit fetched(this);
00652 }
00653
00654 void Feed::loadFavicon()
00655 {
00656 FeedIconManager::self()->fetchIcon(this);
00657 }
00658
00659 void Feed::slotDeleteExpiredArticles()
00660 {
00661 if ( !usesExpiryByAge() )
00662 return;
00663
00664 TQValueList<Article> articles = d->articles.values();
00665
00666 TQValueList<Article>::Iterator en = articles.end();
00667
00668 setNotificationMode(false);
00669
00670
00671
00672
00673 if (Settings::doNotExpireImportantArticles())
00674 {
00675 for (TQValueList<Article>::Iterator it = articles.begin(); it != en; ++it)
00676 {
00677 if (!(*it).keep() && isExpired(*it))
00678 {
00679 (*it).setDeleted();
00680 }
00681 }
00682 }
00683 else
00684 {
00685 for (TQValueList<Article>::Iterator it = articles.begin(); it != en; ++it)
00686 {
00687 if (isExpired(*it))
00688 {
00689 (*it).setDeleted();
00690 }
00691 }
00692 }
00693 setNotificationMode(true);
00694 }
00695
00696 void Feed::setFavicon(const TQPixmap &p)
00697 {
00698 d->favicon = p;
00699 nodeModified();
00700 }
00701
00702 Feed::ArchiveMode Feed::archiveMode() const
00703 {
00704 return d->archiveMode;
00705 }
00706
00707 void Feed::setArchiveMode(ArchiveMode archiveMode)
00708 {
00709 d->archiveMode = archiveMode;
00710 }
00711
00712 int Feed::unread() const
00713 {
00714 return d->archive ? d->archive->unread() : 0;
00715 }
00716
00717 void Feed::setUnread(int unread)
00718 {
00719 if (d->archive && unread != d->archive->unread())
00720 {
00721 d->archive->setUnread(unread);
00722 nodeModified();
00723 }
00724 }
00725
00726
00727 void Feed::setArticleDeleted(Article& a)
00728 {
00729 if (!d->deletedArticles.contains(a))
00730 d->deletedArticles.append(a);
00731
00732 if (!d->removedArticlesNotify.contains(a))
00733 d->removedArticlesNotify.append(a);
00734
00735 articlesModified();
00736 }
00737
00738 void Feed::setArticleChanged(Article& a, int oldStatus)
00739 {
00740 if (oldStatus != -1)
00741 {
00742 int newStatus = a.status();
00743 if (oldStatus == Article::Read && newStatus != Article::Read)
00744 setUnread(unread()+1);
00745 else if (oldStatus != Article::Read && newStatus == Article::Read)
00746 setUnread(unread()-1);
00747 }
00748 d->updatedArticlesNotify.append(a);
00749 articlesModified();
00750 }
00751
00752 int Feed::totalCount() const
00753 {
00754 return d->articles.count();
00755 }
00756
00757 TreeNode* Feed::next()
00758 {
00759 if ( nextSibling() )
00760 return nextSibling();
00761
00762 Folder* p = parent();
00763 while (p)
00764 {
00765 if ( p->nextSibling() )
00766 return p->nextSibling();
00767 else
00768 p = p->parent();
00769 }
00770 return 0;
00771 }
00772
00773 void Feed::doArticleNotification()
00774 {
00775 if (!d->addedArticlesNotify.isEmpty())
00776 {
00777
00778
00779 TQValueList<Article> l = d->addedArticlesNotify;
00780 emit signalArticlesAdded(this, l);
00781 d->addedArticlesNotify.clear();
00782 }
00783 if (!d->updatedArticlesNotify.isEmpty())
00784 {
00785
00786
00787 TQValueList<Article> l = d->updatedArticlesNotify;
00788 emit signalArticlesUpdated(this, l);
00789 d->updatedArticlesNotify.clear();
00790 }
00791 if (!d->removedArticlesNotify.isEmpty())
00792 {
00793
00794
00795 TQValueList<Article> l = d->removedArticlesNotify;
00796 emit signalArticlesRemoved(this, l);
00797 d->removedArticlesNotify.clear();
00798 }
00799 TreeNode::doArticleNotification();
00800 }
00801
00802 void Feed::enforceLimitArticleNumber()
00803 {
00804 int limit = -1;
00805 if (d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleNumber)
00806 limit = Settings::maxArticleNumber();
00807 else if (d->archiveMode == limitArticleNumber)
00808 limit = maxArticleNumber();
00809
00810 if (limit == -1 || limit >= d->articles.count() - d->deletedArticles.count())
00811 return;
00812
00813 setNotificationMode(false);
00814 TQValueList<Article> articles = d->articles.values();
00815 qHeapSort(articles);
00816 TQValueList<Article>::Iterator it = articles.begin();
00817 TQValueList<Article>::Iterator tmp;
00818 TQValueList<Article>::Iterator en = articles.end();
00819
00820 int c = 0;
00821
00822 if (Settings::doNotExpireImportantArticles())
00823 {
00824 while (it != en)
00825 {
00826 tmp = it;
00827 ++it;
00828 if (c < limit)
00829 {
00830 if (!(*tmp).isDeleted() && !(*tmp).keep())
00831 c++;
00832 }
00833 else if (!(*tmp).keep())
00834 (*tmp).setDeleted();
00835 }
00836 }
00837 else
00838 {
00839 while (it != en)
00840 {
00841 tmp = it;
00842 ++it;
00843 if (c < limit && !(*tmp).isDeleted())
00844 {
00845 c++;
00846 }
00847 else
00848 {
00849 (*tmp).setDeleted();
00850 }
00851 }
00852 }
00853 setNotificationMode(true);
00854 }
00855
00856 }
00857 #include "feed.moc"