• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

knode

knarticlemanager.cpp

Go to the documentation of this file.
00001 /*
00002     KNode, the KDE newsreader
00003     Copyright (c) 1999-2006 the KNode authors.
00004     See file AUTHORS for details
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010     You should have received a copy of the GNU General Public License
00011     along with this program; if not, write to the Free Software Foundation,
00012     Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
00013 */
00014 
00015 #include <QByteArray>
00016 #include <QList>
00017 #include <krun.h>
00018 #include <kmessagebox.h>
00019 #include <kmimetypetrader.h>
00020 #include <klocale.h>
00021 #include <kdebug.h>
00022 #include <kwindowsystem.h>
00023 #include <ktemporaryfile.h>
00024 
00025 #include "articlewidget.h"
00026 #include "knmainwidget.h"
00027 #include "knglobals.h"
00028 #include "utilities.h"
00029 #include "knarticlemanager.h"
00030 #include "kngroupmanager.h"
00031 #include "knsearchdialog.h"
00032 #include "knfiltermanager.h"
00033 #include "knfolder.h"
00034 #include "knarticlefilter.h"
00035 #include "knhdrviewitem.h"
00036 #include "scheduler.h"
00037 #include "knnntpaccount.h"
00038 #include "knscoring.h"
00039 #include "knmemorymanager.h"
00040 #include "knarticlefactory.h"
00041 #include "knarticlewindow.h"
00042 #include "knfoldermanager.h"
00043 #include "headerview.h"
00044 #include "nntpjobs.h"
00045 #include "settings.h"
00046 
00047 using namespace KNode;
00048 
00049 
00050 KNArticleManager::KNArticleManager() : QObject(0)
00051 {
00052   g_roup=0;
00053   f_older=0;
00054   f_ilterMgr = knGlobals.filterManager();
00055   f_ilter = f_ilterMgr->currentFilter();
00056   s_earchDlg=0;
00057   d_isableExpander=false;
00058 
00059   connect(f_ilterMgr, SIGNAL(filterChanged(KNArticleFilter*)), this,
00060     SLOT(slotFilterChanged(KNArticleFilter*)));
00061 }
00062 
00063 
00064 KNArticleManager::~KNArticleManager()
00065 {
00066   delete s_earchDlg;
00067 }
00068 
00069 
00070 void KNArticleManager::deleteTempFiles()
00071 {
00072   for ( QList<KTemporaryFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
00073     delete (*it);
00074   }
00075   mTempFiles.clear();
00076 }
00077 
00078 
00079 void KNArticleManager::saveContentToFile(KMime::Content *c, QWidget *parent)
00080 {
00081   KNSaveHelper helper(c->contentType()->name(),parent);
00082 
00083   QFile *file = helper.getFile(i18n("Save Attachment"));
00084 
00085   if (file) {
00086     QByteArray data=c->decodedContent();
00087     if (file->write(data.data(), data.size()) == -1 )
00088       KNHelper::displayExternalFileError( parent );
00089   }
00090 }
00091 
00092 
00093 void KNArticleManager::saveArticleToFile(KNArticle *a, QWidget *parent)
00094 {
00095   QString fName = a->subject()->asUnicodeString();
00096   QString s = "";
00097 
00098   for ( int i = 0; i < fName.length(); ++i )
00099     if (fName[i].isLetterOrNumber())
00100       s.append(fName[i]);
00101     else
00102       s.append(' ');
00103   fName = s.simplified();
00104   fName.replace(QRegExp("[\\s]"),"_");
00105 
00106   KNSaveHelper helper(fName,parent);
00107   QFile *file = helper.getFile(i18n("Save Article"));
00108 
00109   if (file) {
00110     QByteArray tmp=a->encodedContent(false);
00111     if ( file->write(tmp.data(), tmp.size()) == -1 )
00112       KNHelper::displayExternalFileError( parent );
00113   }
00114 }
00115 
00116 
00117 QString KNArticleManager::saveContentToTemp(KMime::Content *c)
00118 {
00119   QString path;
00120   KTemporaryFile* tmpFile;
00121   KMime::Headers::Base *pathHdr=c->getHeaderByType("X-KNode-Tempfile");  // check for existing temp file
00122 
00123   if(pathHdr) {
00124     path = pathHdr->asUnicodeString();
00125     bool found=false;
00126 
00127     // lets see if the tempfile-path is still valid...
00128     for ( QList<KTemporaryFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
00129       if ( (*it)->fileName() == path ) {
00130         found = true;
00131         break;
00132       }
00133     }
00134 
00135     if (found)
00136       return path;
00137     else
00138       c->removeHeader("X-KNode-Tempfile");
00139   }
00140 
00141   tmpFile=new KTemporaryFile();
00142   if (!tmpFile->open()) {
00143     KNHelper::displayTempFileError();
00144     delete tmpFile;
00145     return QString();
00146   }
00147 
00148   mTempFiles.append(tmpFile);
00149   QByteArray data=c->decodedContent();
00150   tmpFile->write(data.data(), data.size());
00151   tmpFile->flush();
00152   path=tmpFile->fileName();
00153   pathHdr=new KMime::Headers::Generic("X-KNode-Tempfile", c, path, "UTF-8");
00154   c->setHeader(pathHdr);
00155 
00156   return path;
00157 }
00158 
00159 
00160 void KNArticleManager::openContent(KMime::Content *c)
00161 {
00162   QString path=saveContentToTemp(c);
00163   if(path.isNull()) return;
00164 
00165   KService::Ptr offer = KMimeTypeTrader::self()->preferredService(c->contentType()->mimeType(), "Application");
00166   KUrl::List lst;
00167   KUrl url;
00168   url.setPath(path);
00169   lst.append(url);
00170 
00171   if (offer)
00172     KRun::run(*offer, lst, knGlobals.top);
00173   else
00174     KRun::displayOpenWithDialog(lst, knGlobals.top);
00175 }
00176 
00177 
00178 void KNArticleManager::showHdrs(bool clear)
00179 {
00180   if(!g_roup && !f_older) return;
00181 
00182   bool setFirstChild=true;
00183   bool showThreads=knGlobals.settings()->showThreads();
00184   bool expandThreads=knGlobals.settings()->defaultToExpandedThreads();
00185 
00186   if(clear)
00187     v_iew->clear();
00188 
00189   knGlobals.top->setCursorBusy(true);
00190   knGlobals.setStatusMsg(i18n(" Creating list..."));
00191   knGlobals.top->secureProcessEvents();
00192 
00193   if(g_roup) {
00194     KNRemoteArticle *art, *ref, *current;
00195 
00196     current = static_cast<KNRemoteArticle*>( knGlobals.top->articleViewer()->article() );
00197 
00198     if(current && (current->collection() != g_roup)) {
00199       current=0;
00200       knGlobals.top->articleViewer()->setArticle( 0 );
00201     }
00202 
00203     if(g_roup->isLocked())
00204       knGlobals.scheduler()->nntpMutex().lock();
00205 
00206     if(f_ilter)
00207       f_ilter->doFilter(g_roup);
00208     else
00209       for(int i=0; i<g_roup->length(); i++) {
00210         art=g_roup->at(i);
00211         art->setFilterResult(true);
00212         art->setFiltered(true);
00213         ref=(art->idRef()!=0) ? g_roup->byId(art->idRef()) : 0;
00214         art->setDisplayedReference(ref);
00215         if(ref)
00216           ref->setVisibleFollowUps(true);
00217       }
00218 
00219     d_isableExpander=true;
00220 
00221     for(int i=0; i<g_roup->length(); i++) {
00222 
00223       art=g_roup->at(i);
00224       art->setThreadMode(showThreads);
00225 
00226       if(showThreads) {
00227         art->propagateThreadChangedDate();
00228 
00229         if( !art->listItem() && art->filterResult() ) {
00230 
00231           // ### disable delayed header view item creation for now, it breaks
00232           // the quick search
00233           // since it doesn't seem to improve performance at all, it probably
00234           // could be removed entirely (see also slotItemExpanded(), etc.)
00235           /*if (!expandThreads) {
00236 
00237             if( (ref=art->displayedReference()) ) {
00238 
00239               if( ref->listItem() && ( ref->listItem()->isOpen() || ref->listItem()->childCount()>0 ) ) {
00240                 art->setListItem(new KNHdrViewItem(ref->listItem()));
00241                 art->initListItem();
00242               }
00243 
00244             }
00245             else {
00246               art->setListItem(new KNHdrViewItem(v_iew));
00247               art->initListItem();
00248             }
00249 
00250         } else {  // expandThreads == true */
00251             createThread(art);
00252             if ( expandThreads )
00253               art->listItem()->setOpen(true);
00254 //           }
00255 
00256         }
00257         else if(art->listItem()) {
00258           art->updateListItem();
00259           if (expandThreads)
00260             art->listItem()->setOpen(true);
00261         }
00262 
00263       }
00264       else {
00265 
00266         if(!art->listItem() && art->filterResult()) {
00267           art->setListItem(new KNHdrViewItem(v_iew));
00268           art->initListItem();
00269         } else if(art->listItem())
00270           art->updateListItem();
00271 
00272       }
00273 
00274     }
00275 
00276     if (current && !current->filterResult()) {   // try to find a parent that is visible
00277       int idRef;
00278       while (current && !current->filterResult()) {
00279         idRef=current->idRef();
00280         if (idRef == -1)
00281           break;
00282         current = g_roup->byId(idRef);
00283       }
00284     }
00285 
00286     if(current && current->filterResult()) {
00287       if(!current->listItem())
00288         createCompleteThread(current);
00289       v_iew->setActive( current->listItem() );
00290       setFirstChild=false;
00291     }
00292 
00293     d_isableExpander=false;
00294 
00295     if (g_roup->isLocked())
00296       knGlobals.scheduler()->nntpMutex().unlock();
00297   }
00298 
00299   else { //folder
00300 
00301     KNLocalArticle *art;
00302     if(f_ilter) {
00303       f_ilter->doFilter(f_older);
00304     } else {
00305       for(int i=0; i<f_older->length(); i++) {
00306         art=f_older->at(i);
00307         art->setFilterResult(true);
00308       }
00309     }
00310 
00311     for(int idx=0; idx<f_older->length(); idx++) {
00312       art=f_older->at(idx);
00313 
00314       if(!art->listItem() &&  art->filterResult()) {
00315         art->setListItem( new KNHdrViewItem(v_iew, art) );
00316         art->updateListItem();
00317       } else if(art->listItem())
00318         art->updateListItem();
00319     }
00320 
00321   }
00322 
00323   if(setFirstChild && v_iew->firstChild()) {
00324     v_iew->setCurrentItem(v_iew->firstChild());
00325     knGlobals.top->articleViewer()->setArticle( 0 );
00326   }
00327 
00328   knGlobals.setStatusMsg( QString() );
00329   updateStatusString();
00330   knGlobals.top->setCursorBusy(false);
00331 }
00332 
00333 
00334 void KNArticleManager::updateViewForCollection(KNArticleCollection *c)
00335 {
00336   if(g_roup==c || f_older==c)
00337     showHdrs(false);
00338 }
00339 
00340 
00341 void KNArticleManager::updateListViewItems()
00342 {
00343   if(!g_roup && !f_older) return;
00344 
00345   if(g_roup) {
00346     KNRemoteArticle *art;
00347 
00348     for(int i=0; i<g_roup->length(); i++) {
00349       art=g_roup->at(i);
00350       if(art->listItem())
00351         art->updateListItem();
00352     }
00353   } else { //folder
00354     KNLocalArticle *art;
00355 
00356     for(int idx=0; idx<f_older->length(); idx++) {
00357       art=f_older->at(idx);
00358       if(art->listItem())
00359         art->updateListItem();
00360     }
00361   }
00362 }
00363 
00364 
00365 void KNArticleManager::setAllThreadsOpen(bool b)
00366 {
00367   KNRemoteArticle *art;
00368   if(g_roup) {
00369     knGlobals.top->setCursorBusy(true);
00370     d_isableExpander = true;
00371     for(int idx=0; idx<g_roup->length(); idx++) {
00372       art = g_roup->at(idx);
00373       if (art->listItem())
00374         art->listItem()->setOpen(b);
00375       else
00376         if (b && art->filterResult()) {
00377           createThread(art);
00378           art->listItem()->setOpen(true);
00379         }
00380     }
00381     d_isableExpander = false;
00382     knGlobals.top->setCursorBusy(false);
00383   }
00384 }
00385 
00386 
00387 void KNArticleManager::search()
00388 {
00389   if(s_earchDlg) {
00390     s_earchDlg->show();
00391 #ifdef Q_OS_UNIX    
00392     KWindowSystem::activateWindow(s_earchDlg->winId());
00393 #endif    
00394   } else {
00395     s_earchDlg = new SearchDialog( SearchDialog::STgroupSearch, 0 );
00396     connect(s_earchDlg, SIGNAL(doSearch(KNArticleFilter*)), this,
00397       SLOT(slotFilterChanged(KNArticleFilter*)));
00398     connect(s_earchDlg, SIGNAL(dialogDone()), this,
00399       SLOT(slotSearchDialogDone()));
00400     s_earchDlg->show();
00401   }
00402 }
00403 
00404 
00405 void KNArticleManager::setGroup(KNGroup *g)
00406 {
00407   g_roup = g;
00408   if ( g )
00409     emit aboutToShowGroup();
00410 }
00411 
00412 
00413 void KNArticleManager::setFolder(KNFolder *f)
00414 {
00415   f_older = f;
00416   if ( f )
00417     emit aboutToShowFolder();
00418 }
00419 
00420 
00421 KNArticleCollection* KNArticleManager::collection()
00422 {
00423   if(g_roup)
00424     return g_roup;
00425   if(f_older)
00426    return f_older;
00427 
00428   return 0;
00429 }
00430 
00431 
00432 bool KNArticleManager::loadArticle(KNArticle *a)
00433 {
00434   if (!a)
00435     return false;
00436 
00437   if (a->hasContent())
00438     return true;
00439 
00440   if (a->isLocked()) {
00441     if ( a->type() == KNArticle::ATremote )
00442       return true;   // locked == we are already loading this article...
00443     else
00444       return false;
00445   }
00446 
00447   if ( a->type() == KNArticle::ATremote ) {
00448     KNGroup *g=static_cast<KNGroup*>(a->collection());
00449     if(g)
00450       emitJob( new ArticleFetchJob( this, g->account(), a ) );
00451     else
00452       return false;
00453   }
00454   else { // local article
00455     KNFolder *f=static_cast<KNFolder*>(a->collection());
00456    if( f && f->loadArticle( static_cast<KNLocalArticle*>(a) ) )
00457       knGlobals.memoryManager()->updateCacheEntry(a);
00458     else
00459       return false;
00460   }
00461   return true;
00462 }
00463 
00464 
00465 bool KNArticleManager::unloadArticle(KNArticle *a, bool force)
00466 {
00467   if(!a || a->isLocked() )
00468     return false;
00469   if(!a->hasContent())
00470     return true;
00471 
00472   if (!force && a->isNotUnloadable())
00473     return false;
00474 
00475   if ( !force && ( ArticleWidget::articleVisible( a ) ) )
00476     return false;
00477 
00478   if (!force && ( a->type()== KNArticle::ATlocal ) &&
00479       (knGlobals.artFactory->findComposer(static_cast<KNLocalArticle*>(a))!=0))
00480     return false;
00481 
00482   if ( !ArticleWindow::closeAllWindowsForArticle( a, force ) )
00483     if (!force)
00484       return false;
00485 
00486   ArticleWidget::articleRemoved( a );
00487   if ( a->type() != KNArticle::ATlocal )
00488     knGlobals.artFactory->deleteComposerForArticle(static_cast<KNLocalArticle*>(a));
00489   a->Content::clear();
00490   a->updateListItem();
00491   knGlobals.memoryManager()->removeCacheEntry(a);
00492 
00493   return true;
00494 }
00495 
00496 
00497 void KNArticleManager::copyIntoFolder(KNArticle::List &l, KNFolder *f)
00498 {
00499   if(!f) return;
00500 
00501   KNLocalArticle *loc;
00502   KNLocalArticle::List l2;
00503 
00504   for ( KNArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00505     if ( !(*it)->hasContent() )
00506       continue;
00507     loc=new KNLocalArticle(0);
00508     loc->setEditDisabled(true);
00509     loc->setContent( (*it)->encodedContent() );
00510     loc->parse();
00511     l2.append(loc);
00512   }
00513 
00514   if ( !l2.isEmpty() ) {
00515 
00516     f->setNotUnloadable(true);
00517 
00518     if ( !f->isLoaded() && !knGlobals.folderManager()->loadHeaders( f ) ) {
00519       for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
00520         delete (*it);
00521       l2.clear();
00522       f->setNotUnloadable(false);
00523       return;
00524     }
00525 
00526     if( !f->saveArticles( l2 ) ) {
00527       for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it ) {
00528         if ( (*it)->isOrphant() )
00529           delete (*it); // ok, this is ugly; we simply delete orphant articles
00530         else
00531           (*it)->Content::clear(); // no need to keep them in memory
00532       }
00533       KNHelper::displayInternalFileError();
00534     } else {
00535       for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
00536         (*it)->Content::clear(); // no need to keep them in memory
00537       knGlobals.memoryManager()->updateCacheEntry(f);
00538     }
00539 
00540     f->setNotUnloadable(false);
00541   }
00542 }
00543 
00544 
00545 void KNArticleManager::moveIntoFolder(KNLocalArticle::List &l, KNFolder *f)
00546 {
00547   if(!f) return;
00548   kDebug(5003) <<" Target folder:" << f->name();
00549 
00550   f->setNotUnloadable(true);
00551 
00552   if (!f->isLoaded() && !knGlobals.folderManager()->loadHeaders(f)) {
00553     f->setNotUnloadable(false);
00554     return;
00555   }
00556 
00557   if ( f->saveArticles( l ) ) {
00558     for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
00559       knGlobals.memoryManager()->updateCacheEntry( (*it) );
00560     knGlobals.memoryManager()->updateCacheEntry(f);
00561   } else {
00562     for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
00563       if ( (*it)->isOrphant() )
00564         delete (*it); // ok, this is ugly; we simply delete orphant articles
00565     KNHelper::displayInternalFileError();
00566   }
00567 
00568   f->setNotUnloadable(false);
00569 }
00570 
00571 
00572 bool KNArticleManager::deleteArticles(KNLocalArticle::List &l, bool ask)
00573 {
00574   if(ask) {
00575     QStringList lst;
00576     for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00577       if ( (*it)->isLocked() )
00578         continue;
00579       if ( (*it)->subject()->isEmpty() )
00580         lst << i18n("no subject");
00581       else
00582         lst << (*it)->subject()->asUnicodeString();
00583     }
00584     if( KMessageBox::Cancel == KMessageBox::warningContinueCancelList(
00585       knGlobals.topWidget, i18n("Do you really want to delete these articles?"), lst,
00586         i18n("Delete Articles"), KGuiItem(i18n("&Delete"),"edit-delete")) )
00587       return false;
00588   }
00589 
00590   for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
00591     knGlobals.memoryManager()->removeCacheEntry( (*it) );
00592 
00593   KNFolder *f=static_cast<KNFolder*>(l.first()->collection());
00594   if ( f ) {
00595     f->removeArticles( l, true );
00596     knGlobals.memoryManager()->updateCacheEntry( f );
00597     return false; // composers for those articles were already removed in removeArticles
00598   }
00599   else {
00600     for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
00601       delete (*it);
00602   }
00603 
00604   return true;
00605 }
00606 
00607 
00608 void KNArticleManager::setAllRead( bool read, int lastcount )
00609 {
00610   if ( !g_roup )
00611     return;
00612 
00613   int groupLength = g_roup->length();
00614   int newCount = g_roup->newCount();
00615   int readCount = g_roup->readCount();
00616   int offset = lastcount;
00617 
00618   if ( lastcount > groupLength || lastcount < 0 )
00619     offset = groupLength;
00620 
00621   KNRemoteArticle *a;
00622   for ( int i = groupLength - offset; i < groupLength; i++ ) {
00623     a = g_roup->at( i );
00624     if ( a->getReadFlag() != read && !a->isIgnored() ) {
00625       a->setRead( read );
00626       a->setChanged( true );
00627       if ( !read ) {
00628         readCount--;
00629         if ( a->isNew() )
00630           newCount++;
00631       } else {
00632         readCount++;
00633         if ( a->isNew() )
00634           newCount--;
00635       }
00636     }
00637   }
00638 
00639   g_roup->updateThreadInfo();
00640   if ( lastcount < 0 && read ) {
00641     // HACK: try to hide the effects of the ignore/filter new/unread count bug
00642     g_roup->setReadCount( groupLength );
00643     g_roup->setNewCount( 0 );
00644   } else {
00645     g_roup->setReadCount( readCount );
00646     g_roup->setNewCount( newCount );
00647   }
00648 
00649   g_roup->updateListItem();
00650   showHdrs( true );
00651 }
00652 
00653 
00654 void KNArticleManager::setRead(KNRemoteArticle::List &l, bool r, bool handleXPosts)
00655 {
00656   if ( l.isEmpty() )
00657     return;
00658 
00659   KNRemoteArticle *ref = 0;
00660   KNGroup *g=static_cast<KNGroup*>( l.first()->collection() );
00661   int changeCnt=0, idRef=0;
00662 
00663   for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00664     if( r && knGlobals.settings()->markCrossposts() &&
00665         handleXPosts && (*it)->newsgroups()->isCrossposted() ) {
00666 
00667       QList<QByteArray> groups = (*it)->newsgroups()->groups();
00668       KNGroup *targetGroup=0;
00669       KNRemoteArticle *xp=0;
00670       KNRemoteArticle::List al;
00671       QByteArray mid = (*it)->messageID()->as7BitString( false );
00672 
00673       for ( QList<QByteArray>::Iterator it2 = groups.begin(); it2 != groups.end(); ++it2 ) {
00674         targetGroup = knGlobals.groupManager()->group(*it2, g->account());
00675         if (targetGroup) {
00676           if (targetGroup->isLoaded() && (xp=targetGroup->byMessageId(mid)) ) {
00677             al.clear();
00678             al.append(xp);
00679             setRead(al, r, false);
00680           } else {
00681             targetGroup->appendXPostID(mid);
00682           }
00683         }
00684       }
00685     }
00686 
00687     else if ( (*it)->getReadFlag() != r ) {
00688       (*it)->setRead( r );
00689       (*it)->setChanged( true );
00690       (*it)->updateListItem();
00691 
00692       if ( !(*it)->isIgnored() ) {
00693         changeCnt++;
00694         idRef = (*it)->idRef();
00695 
00696         while ( idRef != 0 ) {
00697           ref=g->byId(idRef);
00698           if(r) {
00699             ref->decUnreadFollowUps();
00700             if ( (*it)->isNew() )
00701               ref->decNewFollowUps();
00702           }
00703           else {
00704             ref->incUnreadFollowUps();
00705             if ( (*it)->isNew() )
00706               ref->incNewFollowUps();
00707           }
00708 
00709           if(ref->listItem() &&
00710              ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
00711               (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
00712             ref->updateListItem();
00713 
00714           idRef=ref->idRef();
00715         }
00716 
00717         if(r) {
00718           g->incReadCount();
00719           if ( (*it)->isNew() )
00720             g->decNewCount();
00721         }
00722         else {
00723           g->decReadCount();
00724           if ( (*it)->isNew() )
00725             g->incNewCount();
00726         }
00727       }
00728     }
00729   }
00730 
00731   if(changeCnt>0) {
00732     g->updateListItem();
00733     if(g==g_roup)
00734       updateStatusString();
00735   }
00736 }
00737 
00738 
00739 void KNArticleManager::setAllNotNew()
00740 {
00741   if ( !g_roup )
00742     return;
00743   KNRemoteArticle *a;
00744   for ( int i = 0; i < g_roup->length(); ++i) {
00745     a = g_roup->at(i);
00746     if ( a->isNew() ) {
00747       a->setNew( false );
00748       a->setChanged( true );
00749     }
00750   }
00751   g_roup->setFirstNewIndex( -1 );
00752   g_roup->setNewCount( 0 );
00753   g_roup->updateThreadInfo();
00754 }
00755 
00756 
00757 bool KNArticleManager::toggleWatched(KNRemoteArticle::List &l)
00758 {
00759   if(l.isEmpty())
00760     return true;
00761 
00762   KNRemoteArticle *a=l.first(), *ref=0;
00763   bool watch = (!a->isWatched());
00764   KNGroup *g=static_cast<KNGroup*>(a->collection() );
00765   int changeCnt=0, idRef=0;
00766 
00767   for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00768     if ( (*it)->isIgnored() ) {
00769       (*it)->setIgnored(false);
00770 
00771       if ( !(*it)->getReadFlag() ) {
00772         changeCnt++;
00773         idRef = (*it)->idRef();
00774 
00775         while ( idRef != 0 ) {
00776           ref=g->byId(idRef);
00777 
00778           ref->incUnreadFollowUps();
00779           if ( (*it)->isNew() )
00780             ref->incNewFollowUps();
00781 
00782           if(ref->listItem() &&
00783              ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
00784               (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
00785             ref->updateListItem();
00786 
00787           idRef=ref->idRef();
00788         }
00789         g->decReadCount();
00790         if ( (*it)->isNew() )
00791           g->incNewCount();
00792       }
00793     }
00794 
00795     (*it)->setWatched( watch );
00796     (*it)->updateListItem();
00797     (*it)->setChanged( true );
00798   }
00799 
00800   if(changeCnt>0) {
00801     g->updateListItem();
00802     if(g==g_roup)
00803       updateStatusString();
00804   }
00805 
00806   return watch;
00807 }
00808 
00809 
00810 bool KNArticleManager::toggleIgnored(KNRemoteArticle::List &l)
00811 {
00812   if(l.isEmpty())
00813     return true;
00814 
00815   KNRemoteArticle *ref = 0;
00816   bool ignore = !l.first()->isIgnored();
00817   KNGroup *g = static_cast<KNGroup*>( l.first()->collection() );
00818   int changeCnt = 0, idRef = 0;
00819 
00820   for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00821     (*it)->setWatched(false);
00822     if ( (*it)->isIgnored() != ignore ) {
00823       (*it)->setIgnored( ignore );
00824 
00825       if ( !(*it)->getReadFlag() ) {
00826         changeCnt++;
00827         idRef = (*it)->idRef();
00828 
00829         while ( idRef != 0 ) {
00830           ref = g->byId( idRef );
00831 
00832           if ( ignore ) {
00833             ref->decUnreadFollowUps();
00834             if ( (*it)->isNew() )
00835               ref->decNewFollowUps();
00836           } else {
00837             ref->incUnreadFollowUps();
00838             if ( (*it)->isNew() )
00839               ref->incNewFollowUps();
00840           }
00841 
00842           if(ref->listItem() &&
00843              ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
00844               (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
00845             ref->updateListItem();
00846 
00847           idRef=ref->idRef();
00848         }
00849 
00850         if ( ignore ) {
00851           g->incReadCount();
00852           if ( (*it)->isNew() )
00853             g->decNewCount();
00854         } else {
00855           g->decReadCount();
00856           if ( (*it)->isNew() )
00857             g->incNewCount();
00858         }
00859 
00860       }
00861     }
00862     (*it)->updateListItem();
00863     (*it)->setChanged(true);
00864   }
00865 
00866   if(changeCnt>0) {
00867     g->updateListItem();
00868     if(g==g_roup)
00869       updateStatusString();
00870   }
00871 
00872   return ignore;
00873 }
00874 
00875 
00876 void  KNArticleManager::rescoreArticles(KNRemoteArticle::List &l)
00877 {
00878   if ( l.isEmpty() )
00879     return;
00880 
00881   KNGroup *g = static_cast<KNGroup*>( l.first()->collection() );
00882   KScoringManager *sm = knGlobals.scoringManager();
00883   sm->initCache(g->groupname());
00884 
00885   for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00886     int defScore = 0;
00887     if ( (*it)->isIgnored())
00888       defScore = knGlobals.settings()->ignoredThreshold();
00889     else if ( (*it)->isWatched() )
00890       defScore = knGlobals.settings()->watchedThreshold();
00891     (*it)->setScore(defScore);
00892 
00893     bool read = (*it)->isRead();
00894 
00895     KNScorableArticle sa( (*it) );
00896     sm->applyRules(sa);
00897     (*it)->updateListItem();
00898     (*it)->setChanged( true );
00899 
00900     if ( !read && (*it)->isRead() != read )
00901       g_roup->incReadCount();
00902   }
00903 }
00904 
00905 
00906 void KNArticleManager::processJob(KNJobData *j)
00907 {
00908   if(j->type()==KNJobData::JTfetchArticle && !j->canceled()) {
00909     KNRemoteArticle *a = static_cast<KNRemoteArticle*>( j->data() );
00910     if(j->success()) {
00911       ArticleWidget::articleChanged( a );
00912       if(!a->isOrphant()) //orphant articles are deleted by the displaying widget
00913         knGlobals.memoryManager()->updateCacheEntry(a);
00914       if(a->listItem())
00915         a->updateListItem();
00916     } else {
00917       if ( j->error() == KIO::ERR_DOES_NOT_EXIST ) {
00918         // article is not available at the server anymore
00919         QString msgId = a->messageID()->as7BitString( false );
00920         // strip of '<' and '>'
00921         msgId = msgId.mid( 1, msgId.length() - 2 );
00922         ArticleWidget::articleLoadError( a,
00923             i18n("The article you requested is not available on your news server."
00924             "<br />You could try to get it from <a href=\"http://groups.google.com/groups?selm=%1\">groups.google.com</a>.",
00925               msgId ) );
00926         // mark article as read
00927         if ( knGlobals.settings()->autoMark() && !a->isOrphant() ) {
00928           KNRemoteArticle::List l;
00929           l.append( a );
00930           setRead( l, true );
00931         }
00932       } else
00933         ArticleWidget::articleLoadError( a, j->errorString() );
00934     }
00935   }
00936 
00937   delete j;
00938 }
00939 
00940 
00941 void KNArticleManager::createThread(KNRemoteArticle *a)
00942 {
00943   KNRemoteArticle *ref=a->displayedReference();
00944 
00945   if(ref) {
00946     if(!ref->listItem())
00947       createThread(ref);
00948     a->setListItem(new KNHdrViewItem(ref->listItem()));
00949   }
00950   else
00951     a->setListItem(new KNHdrViewItem(v_iew));
00952 
00953   a->setThreadMode( knGlobals.settings()->showThreads() );
00954   a->initListItem();
00955 }
00956 
00957 
00958 void KNArticleManager::createCompleteThread(KNRemoteArticle *a)
00959 {
00960   KNRemoteArticle *ref=a->displayedReference(), *art, *top;
00961   bool inThread=false;
00962   bool showThreads = knGlobals.settings()->showThreads();
00963 
00964   while (ref->displayedReference() != 0)
00965     ref=ref->displayedReference();
00966 
00967   top = ref;
00968 
00969   if (!top->listItem())  // shouldn't happen
00970     return;
00971 
00972   for(int i=0; i<g_roup->count(); i++) {
00973     art=g_roup