Fix typo found by Yuri Chornoivan
[kdepim.git] / knode / knarticlemanager.cpp
blob59cbeeeb7cee43849d757c8c195afcc032edd1fc
1 /*
2 KNode, the KDE newsreader
3 Copyright (c) 1999-2006 the KNode authors.
4 See file AUTHORS for details
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 You should have received a copy of the GNU General Public License
11 along with this program; if not, write to the Free Software Foundation,
12 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
15 #include <QByteArray>
16 #include <QList>
17 #include <krun.h>
18 #include <kmessagebox.h>
19 #include <kmimetypetrader.h>
20 #include <klocale.h>
21 #include <kdebug.h>
22 #include <kwindowsystem.h>
23 #include <ktemporaryfile.h>
25 #include "articlewidget.h"
26 #include "knmainwidget.h"
27 #include "knglobals.h"
28 #include "utilities.h"
29 #include "knarticlemanager.h"
30 #include "kngroupmanager.h"
31 #include "knsearchdialog.h"
32 #include "knfiltermanager.h"
33 #include "knfolder.h"
34 #include "knarticlefilter.h"
35 #include "knhdrviewitem.h"
36 #include "scheduler.h"
37 #include "knnntpaccount.h"
38 #include "knscoring.h"
39 #include "knmemorymanager.h"
40 #include "knarticlefactory.h"
41 #include "knarticlewindow.h"
42 #include "knfoldermanager.h"
43 #include "headerview.h"
44 #include "nntpjobs.h"
45 #include "settings.h"
47 using namespace KNode;
50 KNArticleManager::KNArticleManager() : QObject(0)
52 g_roup=0;
53 f_older=0;
54 f_ilterMgr = knGlobals.filterManager();
55 f_ilter = f_ilterMgr->currentFilter();
56 s_earchDlg=0;
57 d_isableExpander=false;
59 connect(f_ilterMgr, SIGNAL(filterChanged(KNArticleFilter*)), this,
60 SLOT(slotFilterChanged(KNArticleFilter*)));
64 KNArticleManager::~KNArticleManager()
66 delete s_earchDlg;
70 void KNArticleManager::deleteTempFiles()
72 for ( QList<KTemporaryFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
73 delete (*it);
75 mTempFiles.clear();
79 void KNArticleManager::saveContentToFile(KMime::Content *c, QWidget *parent)
81 KNSaveHelper helper(c->contentType()->name(),parent);
83 QFile *file = helper.getFile(i18n("Save Attachment"));
85 if (file) {
86 QByteArray data=c->decodedContent();
87 if (file->write(data.data(), data.size()) == -1 )
88 KNHelper::displayExternalFileError( parent );
93 void KNArticleManager::saveArticleToFile(KNArticle *a, QWidget *parent)
95 QString fName = a->subject()->asUnicodeString();
96 QString s = "";
98 for ( int i = 0; i < fName.length(); ++i )
99 if (fName[i].isLetterOrNumber())
100 s.append(fName[i]);
101 else
102 s.append(' ');
103 fName = s.simplified();
104 fName.replace(QRegExp("[\\s]"),"_");
106 KNSaveHelper helper(fName,parent);
107 QFile *file = helper.getFile(i18n("Save Article"));
109 if (file) {
110 QByteArray tmp=a->encodedContent(false);
111 if ( file->write(tmp.data(), tmp.size()) == -1 )
112 KNHelper::displayExternalFileError( parent );
117 QString KNArticleManager::saveContentToTemp(KMime::Content *c)
119 QString path;
120 KTemporaryFile* tmpFile;
121 KMime::Headers::Base *pathHdr=c->headerByType("X-KNode-Tempfile"); // check for existing temp file
123 if(pathHdr) {
124 path = pathHdr->asUnicodeString();
125 bool found=false;
127 // lets see if the tempfile-path is still valid...
128 for ( QList<KTemporaryFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
129 if ( (*it)->fileName() == path ) {
130 found = true;
131 break;
135 if (found)
136 return path;
137 else
138 c->removeHeader("X-KNode-Tempfile");
141 tmpFile=new KTemporaryFile();
142 if (!tmpFile->open()) {
143 KNHelper::displayTempFileError();
144 delete tmpFile;
145 return QString();
148 mTempFiles.append(tmpFile);
149 QByteArray data=c->decodedContent();
150 tmpFile->write(data.data(), data.size());
151 tmpFile->flush();
152 path=tmpFile->fileName();
153 pathHdr=new KMime::Headers::Generic("X-KNode-Tempfile", c, path, "UTF-8");
154 c->setHeader(pathHdr);
156 return path;
160 void KNArticleManager::openContent(KMime::Content *c)
162 QString path=saveContentToTemp(c);
163 if(path.isNull()) return;
165 KService::Ptr offer = KMimeTypeTrader::self()->preferredService(c->contentType()->mimeType(), "Application");
166 KUrl::List lst;
167 KUrl url;
168 url.setPath(path);
169 lst.append(url);
171 if (offer)
172 KRun::run(*offer, lst, knGlobals.top);
173 else
174 KRun::displayOpenWithDialog(lst, knGlobals.top);
178 void KNArticleManager::showHdrs(bool clear)
180 if(!g_roup && !f_older) return;
182 bool setFirstChild=true;
183 bool showThreads=knGlobals.settings()->showThreads();
184 bool expandThreads=knGlobals.settings()->defaultToExpandedThreads();
186 if(clear)
187 v_iew->clear();
189 knGlobals.top->setCursorBusy(true);
190 knGlobals.setStatusMsg(i18n(" Creating list..."));
191 knGlobals.top->secureProcessEvents();
193 if(g_roup) {
194 KNRemoteArticle *art, *ref, *current;
196 current = static_cast<KNRemoteArticle*>( knGlobals.top->articleViewer()->article() );
198 if(current && (current->collection() != g_roup)) {
199 current=0;
200 knGlobals.top->articleViewer()->setArticle( 0 );
203 if(g_roup->isLocked())
204 knGlobals.scheduler()->nntpMutex().lock();
206 if(f_ilter)
207 f_ilter->doFilter(g_roup);
208 else
209 for(int i=0; i<g_roup->length(); i++) {
210 art=g_roup->at(i);
211 art->setFilterResult(true);
212 art->setFiltered(true);
213 ref=(art->idRef()!=0) ? g_roup->byId(art->idRef()) : 0;
214 art->setDisplayedReference(ref);
215 if(ref)
216 ref->setVisibleFollowUps(true);
219 d_isableExpander=true;
221 for(int i=0; i<g_roup->length(); i++) {
223 art=g_roup->at(i);
224 art->setThreadMode(showThreads);
226 if(showThreads) {
227 art->propagateThreadChangedDate();
229 if( !art->listItem() && art->filterResult() ) {
231 // ### disable delayed header view item creation for now, it breaks
232 // the quick search
233 // since it doesn't seem to improve performance at all, it probably
234 // could be removed entirely (see also slotItemExpanded(), etc.)
235 /*if (!expandThreads) {
237 if( (ref=art->displayedReference()) ) {
239 if( ref->listItem() && ( ref->listItem()->isOpen() || ref->listItem()->childCount()>0 ) ) {
240 art->setListItem(new KNHdrViewItem(ref->listItem()));
241 art->initListItem();
245 else {
246 art->setListItem(new KNHdrViewItem(v_iew));
247 art->initListItem();
250 } else { // expandThreads == true */
251 createThread(art);
252 if ( expandThreads )
253 art->listItem()->setOpen(true);
254 // }
257 else if(art->listItem()) {
258 art->updateListItem();
259 if (expandThreads)
260 art->listItem()->setOpen(true);
264 else {
266 if(!art->listItem() && art->filterResult()) {
267 art->setListItem(new KNHdrViewItem(v_iew));
268 art->initListItem();
269 } else if(art->listItem())
270 art->updateListItem();
276 if (current && !current->filterResult()) { // try to find a parent that is visible
277 int idRef;
278 while (current && !current->filterResult()) {
279 idRef=current->idRef();
280 if (idRef == -1)
281 break;
282 current = g_roup->byId(idRef);
286 if(current && current->filterResult()) {
287 if(!current->listItem())
288 createCompleteThread(current);
289 v_iew->setActive( current->listItem() );
290 setFirstChild=false;
293 d_isableExpander=false;
295 if (g_roup->isLocked())
296 knGlobals.scheduler()->nntpMutex().unlock();
299 else { //folder
301 KNLocalArticle *art;
302 if(f_ilter) {
303 f_ilter->doFilter(f_older);
304 } else {
305 for(int i=0; i<f_older->length(); i++) {
306 art=f_older->at(i);
307 art->setFilterResult(true);
311 for(int idx=0; idx<f_older->length(); idx++) {
312 art=f_older->at(idx);
314 if(!art->listItem() && art->filterResult()) {
315 art->setListItem( new KNHdrViewItem(v_iew, art) );
316 art->updateListItem();
317 } else if(art->listItem())
318 art->updateListItem();
323 if(setFirstChild && v_iew->firstChild()) {
324 v_iew->setCurrentItem(v_iew->firstChild());
325 knGlobals.top->articleViewer()->setArticle( 0 );
328 knGlobals.setStatusMsg( QString() );
329 updateStatusString();
330 knGlobals.top->setCursorBusy(false);
334 void KNArticleManager::updateViewForCollection(KNArticleCollection *c)
336 if(g_roup==c || f_older==c)
337 showHdrs(false);
341 void KNArticleManager::updateListViewItems()
343 if(!g_roup && !f_older) return;
345 if(g_roup) {
346 KNRemoteArticle *art;
348 for(int i=0; i<g_roup->length(); i++) {
349 art=g_roup->at(i);
350 if(art->listItem())
351 art->updateListItem();
353 } else { //folder
354 KNLocalArticle *art;
356 for(int idx=0; idx<f_older->length(); idx++) {
357 art=f_older->at(idx);
358 if(art->listItem())
359 art->updateListItem();
365 void KNArticleManager::setAllThreadsOpen(bool b)
367 KNRemoteArticle *art;
368 if(g_roup) {
369 knGlobals.top->setCursorBusy(true);
370 d_isableExpander = true;
371 for(int idx=0; idx<g_roup->length(); idx++) {
372 art = g_roup->at(idx);
373 if (art->listItem())
374 art->listItem()->setOpen(b);
375 else
376 if (b && art->filterResult()) {
377 createThread(art);
378 art->listItem()->setOpen(true);
381 d_isableExpander = false;
382 knGlobals.top->setCursorBusy(false);
387 void KNArticleManager::search()
389 if(s_earchDlg) {
390 s_earchDlg->show();
391 #ifdef Q_OS_UNIX
392 KWindowSystem::activateWindow(s_earchDlg->winId());
393 #endif
394 } else {
395 s_earchDlg = new SearchDialog( SearchDialog::STgroupSearch, 0 );
396 connect(s_earchDlg, SIGNAL(doSearch(KNArticleFilter*)), this,
397 SLOT(slotFilterChanged(KNArticleFilter*)));
398 connect(s_earchDlg, SIGNAL(dialogDone()), this,
399 SLOT(slotSearchDialogDone()));
400 s_earchDlg->show();
405 void KNArticleManager::setGroup(KNGroup *g)
407 g_roup = g;
408 if ( g )
409 emit aboutToShowGroup();
413 void KNArticleManager::setFolder(KNFolder *f)
415 f_older = f;
416 if ( f )
417 emit aboutToShowFolder();
421 KNArticleCollection* KNArticleManager::collection()
423 if(g_roup)
424 return g_roup;
425 if(f_older)
426 return f_older;
428 return 0;
432 bool KNArticleManager::loadArticle(KNArticle *a)
434 if (!a)
435 return false;
437 if (a->hasContent())
438 return true;
440 if (a->isLocked()) {
441 if ( a->type() == KNArticle::ATremote )
442 return true; // locked == we are already loading this article...
443 else
444 return false;
447 if ( a->type() == KNArticle::ATremote ) {
448 KNGroup *g=static_cast<KNGroup*>(a->collection());
449 if(g)
450 emitJob( new ArticleFetchJob( this, g->account(), a ) );
451 else
452 return false;
454 else { // local article
455 KNFolder *f=static_cast<KNFolder*>(a->collection());
456 if( f && f->loadArticle( static_cast<KNLocalArticle*>(a) ) )
457 knGlobals.memoryManager()->updateCacheEntry(a);
458 else
459 return false;
461 return true;
465 bool KNArticleManager::unloadArticle(KNArticle *a, bool force)
467 if(!a || a->isLocked() )
468 return false;
469 if(!a->hasContent())
470 return true;
472 if (!force && a->isNotUnloadable())
473 return false;
475 if ( !force && ( ArticleWidget::articleVisible( a ) ) )
476 return false;
478 if (!force && ( a->type()== KNArticle::ATlocal ) &&
479 (knGlobals.artFactory->findComposer(static_cast<KNLocalArticle*>(a))!=0))
480 return false;
482 if ( !ArticleWindow::closeAllWindowsForArticle( a, force ) )
483 if (!force)
484 return false;
486 ArticleWidget::articleRemoved( a );
487 if ( a->type() != KNArticle::ATlocal )
488 knGlobals.artFactory->deleteComposerForArticle(static_cast<KNLocalArticle*>(a));
489 a->Content::clear();
490 a->updateListItem();
491 knGlobals.memoryManager()->removeCacheEntry(a);
493 return true;
497 void KNArticleManager::copyIntoFolder(KNArticle::List &l, KNFolder *f)
499 if(!f) return;
501 KNLocalArticle *loc;
502 KNLocalArticle::List l2;
504 for ( KNArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
505 if ( !(*it)->hasContent() )
506 continue;
507 loc=new KNLocalArticle(0);
508 loc->setEditDisabled(true);
509 loc->setContent( (*it)->encodedContent() );
510 loc->parse();
511 l2.append(loc);
514 if ( !l2.isEmpty() ) {
516 f->setNotUnloadable(true);
518 if ( !f->isLoaded() && !knGlobals.folderManager()->loadHeaders( f ) ) {
519 for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
520 delete (*it);
521 l2.clear();
522 f->setNotUnloadable(false);
523 return;
526 if( !f->saveArticles( l2 ) ) {
527 for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it ) {
528 if ( (*it)->isOrphant() )
529 delete (*it); // ok, this is ugly; we simply delete orphant articles
530 else
531 (*it)->Content::clear(); // no need to keep them in memory
533 KNHelper::displayInternalFileError();
534 } else {
535 for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
536 (*it)->Content::clear(); // no need to keep them in memory
537 knGlobals.memoryManager()->updateCacheEntry(f);
540 f->setNotUnloadable(false);
545 void KNArticleManager::moveIntoFolder(KNLocalArticle::List &l, KNFolder *f)
547 if(!f) return;
548 kDebug(5003) <<" Target folder:" << f->name();
550 f->setNotUnloadable(true);
552 if (!f->isLoaded() && !knGlobals.folderManager()->loadHeaders(f)) {
553 f->setNotUnloadable(false);
554 return;
557 if ( f->saveArticles( l ) ) {
558 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
559 knGlobals.memoryManager()->updateCacheEntry( (*it) );
560 knGlobals.memoryManager()->updateCacheEntry(f);
561 } else {
562 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
563 if ( (*it)->isOrphant() )
564 delete (*it); // ok, this is ugly; we simply delete orphant articles
565 KNHelper::displayInternalFileError();
568 f->setNotUnloadable(false);
572 bool KNArticleManager::deleteArticles(KNLocalArticle::List &l, bool ask)
574 if(ask) {
575 QStringList lst;
576 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
577 if ( (*it)->isLocked() )
578 continue;
579 if ( (*it)->subject()->isEmpty() )
580 lst << i18n("no subject");
581 else
582 lst << (*it)->subject()->asUnicodeString();
584 if( KMessageBox::Cancel == KMessageBox::warningContinueCancelList(
585 knGlobals.topWidget, i18n("Do you really want to delete these articles?"), lst,
586 i18n("Delete Articles"), KGuiItem(i18n("&Delete"),"edit-delete")) )
587 return false;
590 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
591 knGlobals.memoryManager()->removeCacheEntry( (*it) );
593 KNFolder *f=static_cast<KNFolder*>(l.first()->collection());
594 if ( f ) {
595 f->removeArticles( l, true );
596 knGlobals.memoryManager()->updateCacheEntry( f );
597 return false; // composers for those articles were already removed in removeArticles
599 else {
600 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
601 delete (*it);
604 return true;
608 void KNArticleManager::setAllRead( bool read, int lastcount )
610 if ( !g_roup )
611 return;
613 int groupLength = g_roup->length();
614 int newCount = g_roup->newCount();
615 int readCount = g_roup->readCount();
616 int offset = lastcount;
618 if ( lastcount > groupLength || lastcount < 0 )
619 offset = groupLength;
621 KNRemoteArticle *a;
622 for ( int i = groupLength - offset; i < groupLength; i++ ) {
623 a = g_roup->at( i );
624 if ( a->getReadFlag() != read && !a->isIgnored() ) {
625 a->setRead( read );
626 a->setChanged( true );
627 if ( !read ) {
628 readCount--;
629 if ( a->isNew() )
630 newCount++;
631 } else {
632 readCount++;
633 if ( a->isNew() )
634 newCount--;
639 g_roup->updateThreadInfo();
640 if ( lastcount < 0 && read ) {
641 // HACK: try to hide the effects of the ignore/filter new/unread count bug
642 g_roup->setReadCount( groupLength );
643 g_roup->setNewCount( 0 );
644 } else {
645 g_roup->setReadCount( readCount );
646 g_roup->setNewCount( newCount );
649 g_roup->updateListItem();
650 showHdrs( true );
654 void KNArticleManager::setRead(KNRemoteArticle::List &l, bool r, bool handleXPosts)
656 if ( l.isEmpty() )
657 return;
659 KNRemoteArticle *ref = 0;
660 KNGroup *g=static_cast<KNGroup*>( l.first()->collection() );
661 int changeCnt=0, idRef=0;
663 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
664 if( r && knGlobals.settings()->markCrossposts() &&
665 handleXPosts && (*it)->newsgroups()->isCrossposted() ) {
667 QList<QByteArray> groups = (*it)->newsgroups()->groups();
668 KNGroup *targetGroup=0;
669 KNRemoteArticle *xp=0;
670 KNRemoteArticle::List al;
671 QByteArray mid = (*it)->messageID()->as7BitString( false );
673 for ( QList<QByteArray>::Iterator it2 = groups.begin(); it2 != groups.end(); ++it2 ) {
674 targetGroup = knGlobals.groupManager()->group(*it2, g->account());
675 if (targetGroup) {
676 if (targetGroup->isLoaded() && (xp=targetGroup->byMessageId(mid)) ) {
677 al.clear();
678 al.append(xp);
679 setRead(al, r, false);
680 } else {
681 targetGroup->appendXPostID(mid);
687 else if ( (*it)->getReadFlag() != r ) {
688 (*it)->setRead( r );
689 (*it)->setChanged( true );
690 (*it)->updateListItem();
692 if ( !(*it)->isIgnored() ) {
693 changeCnt++;
694 idRef = (*it)->idRef();
696 while ( idRef != 0 ) {
697 ref=g->byId(idRef);
698 if(r) {
699 ref->decUnreadFollowUps();
700 if ( (*it)->isNew() )
701 ref->decNewFollowUps();
703 else {
704 ref->incUnreadFollowUps();
705 if ( (*it)->isNew() )
706 ref->incNewFollowUps();
709 if(ref->listItem() &&
710 ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
711 (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
712 ref->updateListItem();
714 idRef=ref->idRef();
717 if(r) {
718 g->incReadCount();
719 if ( (*it)->isNew() )
720 g->decNewCount();
722 else {
723 g->decReadCount();
724 if ( (*it)->isNew() )
725 g->incNewCount();
731 if(changeCnt>0) {
732 g->updateListItem();
733 if(g==g_roup)
734 updateStatusString();
739 void KNArticleManager::setAllNotNew()
741 if ( !g_roup )
742 return;
743 KNRemoteArticle *a;
744 for ( int i = 0; i < g_roup->length(); ++i) {
745 a = g_roup->at(i);
746 if ( a->isNew() ) {
747 a->setNew( false );
748 a->setChanged( true );
751 g_roup->setFirstNewIndex( -1 );
752 g_roup->setNewCount( 0 );
753 g_roup->updateThreadInfo();
757 bool KNArticleManager::toggleWatched(KNRemoteArticle::List &l)
759 if(l.isEmpty())
760 return true;
762 KNRemoteArticle *a=l.first(), *ref=0;
763 bool watch = (!a->isWatched());
764 KNGroup *g=static_cast<KNGroup*>(a->collection() );
765 int changeCnt=0, idRef=0;
767 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
768 if ( (*it)->isIgnored() ) {
769 (*it)->setIgnored(false);
771 if ( !(*it)->getReadFlag() ) {
772 changeCnt++;
773 idRef = (*it)->idRef();
775 while ( idRef != 0 ) {
776 ref=g->byId(idRef);
778 ref->incUnreadFollowUps();
779 if ( (*it)->isNew() )
780 ref->incNewFollowUps();
782 if(ref->listItem() &&
783 ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
784 (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
785 ref->updateListItem();
787 idRef=ref->idRef();
789 g->decReadCount();
790 if ( (*it)->isNew() )
791 g->incNewCount();
795 (*it)->setWatched( watch );
796 (*it)->updateListItem();
797 (*it)->setChanged( true );
800 if(changeCnt>0) {
801 g->updateListItem();
802 if(g==g_roup)
803 updateStatusString();
806 return watch;
810 bool KNArticleManager::toggleIgnored(KNRemoteArticle::List &l)
812 if(l.isEmpty())
813 return true;
815 KNRemoteArticle *ref = 0;
816 bool ignore = !l.first()->isIgnored();
817 KNGroup *g = static_cast<KNGroup*>( l.first()->collection() );
818 int changeCnt = 0, idRef = 0;
820 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
821 (*it)->setWatched(false);
822 if ( (*it)->isIgnored() != ignore ) {
823 (*it)->setIgnored( ignore );
825 if ( !(*it)->getReadFlag() ) {
826 changeCnt++;
827 idRef = (*it)->idRef();
829 while ( idRef != 0 ) {
830 ref = g->byId( idRef );
832 if ( ignore ) {
833 ref->decUnreadFollowUps();
834 if ( (*it)->isNew() )
835 ref->decNewFollowUps();
836 } else {
837 ref->incUnreadFollowUps();
838 if ( (*it)->isNew() )
839 ref->incNewFollowUps();
842 if(ref->listItem() &&
843 ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
844 (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
845 ref->updateListItem();
847 idRef=ref->idRef();
850 if ( ignore ) {
851 g->incReadCount();
852 if ( (*it)->isNew() )
853 g->decNewCount();
854 } else {
855 g->decReadCount();
856 if ( (*it)->isNew() )
857 g->incNewCount();
862 (*it)->updateListItem();
863 (*it)->setChanged(true);
866 if(changeCnt>0) {
867 g->updateListItem();
868 if(g==g_roup)
869 updateStatusString();
872 return ignore;
876 void KNArticleManager::rescoreArticles(KNRemoteArticle::List &l)
878 if ( l.isEmpty() )
879 return;
881 KNGroup *g = static_cast<KNGroup*>( l.first()->collection() );
882 KScoringManager *sm = knGlobals.scoringManager();
883 sm->initCache(g->groupname());
885 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
886 int defScore = 0;
887 if ( (*it)->isIgnored())
888 defScore = knGlobals.settings()->ignoredThreshold();
889 else if ( (*it)->isWatched() )
890 defScore = knGlobals.settings()->watchedThreshold();
891 (*it)->setScore(defScore);
893 bool read = (*it)->isRead();
895 KNScorableArticle sa( (*it) );
896 sm->applyRules(sa);
897 (*it)->updateListItem();
898 (*it)->setChanged( true );
900 if ( !read && (*it)->isRead() != read )
901 g_roup->incReadCount();
906 void KNArticleManager::processJob(KNJobData *j)
908 if(j->type()==KNJobData::JTfetchArticle && !j->canceled()) {
909 KNRemoteArticle *a = static_cast<KNRemoteArticle*>( j->data() );
910 if(j->success()) {
911 ArticleWidget::articleChanged( a );
912 if(!a->isOrphant()) //orphant articles are deleted by the displaying widget
913 knGlobals.memoryManager()->updateCacheEntry(a);
914 if(a->listItem())
915 a->updateListItem();
916 } else {
917 if ( j->error() == KIO::ERR_DOES_NOT_EXIST ) {
918 // article is not available at the server anymore
919 QString msgId = a->messageID()->as7BitString( false );
920 // strip of '<' and '>'
921 msgId = msgId.mid( 1, msgId.length() - 2 );
922 ArticleWidget::articleLoadError( a,
923 i18n("The article you requested is not available on your news server."
924 "<br />You could try to get it from <a href=\"http://groups.google.com/groups?selm=%1\">groups.google.com</a>.",
925 msgId ) );
926 // mark article as read
927 if ( knGlobals.settings()->autoMark() && !a->isOrphant() ) {
928 KNRemoteArticle::List l;
929 l.append( a );
930 setRead( l, true );
932 } else
933 ArticleWidget::articleLoadError( a, j->errorString() );
937 delete j;
941 void KNArticleManager::createThread(KNRemoteArticle *a)
943 KNRemoteArticle *ref=a->displayedReference();
945 if(ref) {
946 if(!ref->listItem())
947 createThread(ref);
948 a->setListItem(new KNHdrViewItem(ref->listItem()));
950 else
951 a->setListItem(new KNHdrViewItem(v_iew));
953 a->setThreadMode( knGlobals.settings()->showThreads() );
954 a->initListItem();
958 void KNArticleManager::createCompleteThread(KNRemoteArticle *a)
960 KNRemoteArticle *ref=a->displayedReference(), *art, *top;
961 bool inThread=false;
962 bool showThreads = knGlobals.settings()->showThreads();
964 while (ref->displayedReference() != 0)
965 ref=ref->displayedReference();
967 top = ref;
969 if (!top->listItem()) // shouldn't happen
970 return;
972 for(int i=0; i<g_roup->count(); i++) {
973 art=g_roup->at(i);
974 if(art->filterResult() && !art->listItem()) {
976 if(art->displayedReference()==top) {
977 art->setListItem(new KNHdrViewItem(top->listItem()));
978 art->setThreadMode(showThreads);
979 art->initListItem();
981 else {
982 ref=art->displayedReference();
983 inThread=false;
984 while(ref && !inThread) {
985 inThread=(ref==top);
986 ref=ref->displayedReference();
988 if(inThread)
989 createThread(art);
994 if ( knGlobals.settings()->totalExpandThreads() )
995 top->listItem()->expandChildren();
999 void KNArticleManager::updateStatusString()
1001 int displCnt=0;
1003 if(g_roup) {
1004 if(f_ilter)
1005 displCnt=f_ilter->count();
1006 else
1007 displCnt=g_roup->count();
1009 QString name = g_roup->name();
1010 if (g_roup->status()==KNGroup::moderated)
1011 name += i18n(" (moderated)");
1013 knGlobals.setStatusMsg(i18n(" %1: %2 new , %3 displayed",
1014 name, g_roup->newCount(), displCnt),SB_GROUP);
1016 if(f_ilter)
1017 knGlobals.setStatusMsg(i18n(" Filter: %1", f_ilter->translatedName()), SB_FILTER);
1018 else
1019 knGlobals.setStatusMsg( QString(), SB_FILTER );
1021 else if(f_older) {
1022 if(f_ilter)
1023 displCnt=f_ilter->count();
1024 else
1025 displCnt=f_older->count();
1026 knGlobals.setStatusMsg(i18n(" %1: %2 displayed",
1027 f_older->name(), displCnt), SB_GROUP);
1028 knGlobals.setStatusMsg( QString(), SB_FILTER );
1029 } else {
1030 knGlobals.setStatusMsg( QString(), SB_GROUP );
1031 knGlobals.setStatusMsg( QString(), SB_FILTER );
1036 void KNArticleManager::slotFilterChanged(KNArticleFilter *f)
1038 f_ilter=f;
1039 showHdrs();
1043 void KNArticleManager::slotSearchDialogDone()
1045 s_earchDlg->hide();
1046 slotFilterChanged(f_ilterMgr->currentFilter());
1050 void KNArticleManager::slotItemExpanded(Q3ListViewItem *p)
1052 if (d_isableExpander) // we don't want to call this method recursively
1053 return;
1054 d_isableExpander = true;
1056 KNRemoteArticle *top, *art, *ref;
1057 KNHdrViewItem *hdrItem;
1058 bool inThread=false;
1059 bool showThreads = knGlobals.settings()->showThreads();
1060 hdrItem=static_cast<KNHdrViewItem*>(p);
1061 top=static_cast<KNRemoteArticle*>(hdrItem->art);
1063 if (p->childCount() == 0) {
1065 knGlobals.top->setCursorBusy(true);
1067 for(int i=0; i<g_roup->count(); i++) {
1068 art=g_roup->at(i);
1069 if(art->filterResult() && !art->listItem()) {
1071 if(art->displayedReference()==top) {
1072 art->setListItem(new KNHdrViewItem(hdrItem));
1073 art->setThreadMode(showThreads);
1074 art->initListItem();
1076 else if( knGlobals.settings()->totalExpandThreads() ) { //totalExpand
1077 ref=art->displayedReference();
1078 inThread=false;
1079 while(ref && !inThread) {
1080 inThread=(ref==top);
1081 ref=ref->displayedReference();
1083 if(inThread)
1084 createThread(art);
1089 knGlobals.top->setCursorBusy(false);
1092 if ( knGlobals.settings()->totalExpandThreads() )
1093 hdrItem->expandChildren();
1095 d_isableExpander = false;
1099 void KNArticleManager::setView(KNHeaderView* v) {
1100 v_iew = v;
1101 if(v) {
1102 connect(v, SIGNAL(expanded(Q3ListViewItem*)), this,
1103 SLOT(slotItemExpanded(Q3ListViewItem*)));
1107 //-----------------------------
1108 #include "knarticlemanager.moc"