split message into several paras and fix file extension markup
[kdepim.git] / knode / knarticlemanager.cpp
blobdc2b70b9ff775885a4fee61cc246bbcec500f862
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 "knarticlemanager.h"
17 #include "utils/scoped_cursor_override.h"
19 #include <QByteArray>
20 #include <QList>
21 #include <krun.h>
22 #include <kmessagebox.h>
23 #include <kmimetypetrader.h>
24 #include <klocale.h>
25 #include <kdebug.h>
26 #include <kwindowsystem.h>
27 #include <ktemporaryfile.h>
29 #include "articlewidget.h"
30 #include "knmainwidget.h"
31 #include "knglobals.h"
32 #include "utilities.h"
33 #include "knarticlemanager.h"
34 #include "kngroupmanager.h"
35 #include "knsearchdialog.h"
36 #include "knfiltermanager.h"
37 #include "knfolder.h"
38 #include "knarticlefilter.h"
39 #include "knhdrviewitem.h"
40 #include "scheduler.h"
41 #include "knnntpaccount.h"
42 #include "knscoring.h"
43 #include "knmemorymanager.h"
44 #include "knarticlefactory.h"
45 #include "knarticlewindow.h"
46 #include "knfoldermanager.h"
47 #include "headerview.h"
48 #include "nntpjobs.h"
49 #include "settings.h"
51 using namespace KNode;
52 using namespace KNode::Utilities;
55 KNArticleManager::KNArticleManager() : QObject(0)
57 f_ilterMgr = knGlobals.filterManager();
58 f_ilter = f_ilterMgr->currentFilter();
59 s_earchDlg=0;
60 d_isableExpander=false;
62 connect(f_ilterMgr, SIGNAL(filterChanged(KNArticleFilter*)), this,
63 SLOT(slotFilterChanged(KNArticleFilter*)));
67 KNArticleManager::~KNArticleManager()
69 delete s_earchDlg;
73 void KNArticleManager::deleteTempFiles()
75 for ( QList<KTemporaryFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
76 delete (*it);
78 mTempFiles.clear();
82 void KNArticleManager::saveContentToFile(KMime::Content *c, QWidget *parent)
84 KNSaveHelper helper(c->contentType()->name(),parent);
86 QFile *file = helper.getFile(i18n("Save Attachment"));
88 if (file) {
89 QByteArray data=c->decodedContent();
90 if (file->write(data.data(), data.size()) == -1 )
91 KNHelper::displayExternalFileError( parent );
96 void KNArticleManager::saveArticleToFile( KNArticle::Ptr a, QWidget *parent )
98 QString fName = a->subject()->asUnicodeString();
99 QString s = "";
101 for ( int i = 0; i < fName.length(); ++i )
102 if (fName[i].isLetterOrNumber())
103 s.append(fName[i]);
104 else
105 s.append(' ');
106 fName = s.simplified();
107 fName.replace(QRegExp("[\\s]"),"_");
109 KNSaveHelper helper(fName,parent);
110 QFile *file = helper.getFile(i18n("Save Article"));
112 if (file) {
113 QByteArray tmp=a->encodedContent(false);
114 if ( file->write(tmp.data(), tmp.size()) == -1 )
115 KNHelper::displayExternalFileError( parent );
120 QString KNArticleManager::saveContentToTemp(KMime::Content *c)
122 QString path;
123 KTemporaryFile* tmpFile;
124 KMime::Headers::Base *pathHdr=c->headerByType("X-KNode-Tempfile"); // check for existing temp file
126 if(pathHdr) {
127 path = pathHdr->asUnicodeString();
128 bool found=false;
130 // lets see if the tempfile-path is still valid...
131 for ( QList<KTemporaryFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
132 if ( (*it)->fileName() == path ) {
133 found = true;
134 break;
138 if (found)
139 return path;
140 else
141 c->removeHeader("X-KNode-Tempfile");
144 tmpFile=new KTemporaryFile();
145 if (!tmpFile->open()) {
146 KNHelper::displayTempFileError();
147 delete tmpFile;
148 return QString();
151 mTempFiles.append(tmpFile);
152 QByteArray data=c->decodedContent();
153 tmpFile->write(data.data(), data.size());
154 tmpFile->flush();
155 path=tmpFile->fileName();
156 pathHdr=new KMime::Headers::Generic("X-KNode-Tempfile", c, path, "UTF-8");
157 c->setHeader(pathHdr);
159 return path;
163 void KNArticleManager::openContent(KMime::Content *c)
165 QString path=saveContentToTemp(c);
166 if(path.isNull()) return;
168 KService::Ptr offer = KMimeTypeTrader::self()->preferredService(c->contentType()->mimeType(), "Application");
169 KUrl::List lst;
170 KUrl url;
171 url.setPath(path);
172 lst.append(url);
174 if (offer)
175 KRun::run(*offer, lst, knGlobals.top);
176 else
177 KRun::displayOpenWithDialog(lst, knGlobals.top);
181 void KNArticleManager::showHdrs(bool clear)
183 if(!g_roup && !f_older) return;
185 bool setFirstChild=true;
186 bool showThreads=knGlobals.settings()->showThreads();
187 bool expandThreads=knGlobals.settings()->defaultToExpandedThreads();
189 if(clear)
190 v_iew->clear();
192 ScopedCursorOverride cursor( Qt::WaitCursor );
193 knGlobals.setStatusMsg(i18n(" Creating list..."));
194 knGlobals.top->secureProcessEvents();
196 if(g_roup) {
197 KNRemoteArticle::Ptr art, ref, current;
199 current = boost::static_pointer_cast<KNRemoteArticle>( knGlobals.top->articleViewer()->article() );
201 if(current && (current->collection() != g_roup)) {
202 current.reset();
203 knGlobals.top->articleViewer()->setArticle( KNRemoteArticle::Ptr() );
206 if(g_roup->isLocked())
207 knGlobals.scheduler()->nntpMutex().lock();
209 if(f_ilter)
210 f_ilter->doFilter(g_roup);
211 else
212 for(int i=0; i<g_roup->length(); ++i) {
213 art=g_roup->at(i);
214 art->setFilterResult(true);
215 art->setFiltered(true);
216 ref = ( art->idRef() ? g_roup->byId( art->idRef() ) : KNRemoteArticle::Ptr() );
217 art->setDisplayedReference(ref);
218 if(ref)
219 ref->setVisibleFollowUps(true);
222 d_isableExpander=true;
224 for(int i=0; i<g_roup->length(); ++i) {
226 art=g_roup->at(i);
227 art->setThreadMode(showThreads);
229 if(showThreads) {
230 art->propagateThreadChangedDate();
232 if( !art->listItem() && art->filterResult() ) {
234 // ### disable delayed header view item creation for now, it breaks
235 // the quick search
236 // since it doesn't seem to improve performance at all, it probably
237 // could be removed entirely (see also slotItemExpanded(), etc.)
238 /*if (!expandThreads) {
240 if( (ref=art->displayedReference()) ) {
242 if( ref->listItem() && ( ref->listItem()->isOpen() || ref->listItem()->childCount()>0 ) ) {
243 art->setListItem(new KNHdrViewItem(ref->listItem()));
244 art->initListItem();
248 else {
249 art->setListItem(new KNHdrViewItem(v_iew));
250 art->initListItem();
253 } else { // expandThreads == true */
254 createThread(art);
255 if ( expandThreads )
256 art->listItem()->setOpen(true);
257 // }
260 else if(art->listItem()) {
261 art->updateListItem();
262 if (expandThreads)
263 art->listItem()->setOpen(true);
267 else {
269 if(!art->listItem() && art->filterResult()) {
270 art->setListItem( new KNHdrViewItem( v_iew ), art );
271 art->initListItem();
272 } else if(art->listItem())
273 art->updateListItem();
279 if (current && !current->filterResult()) { // try to find a parent that is visible
280 int idRef;
281 while (current && !current->filterResult()) {
282 idRef=current->idRef();
283 if (idRef == -1)
284 break;
285 current = g_roup->byId(idRef);
289 if(current && current->filterResult()) {
290 if(!current->listItem())
291 createCompleteThread(current);
292 v_iew->setActive( current->listItem() );
293 setFirstChild=false;
296 d_isableExpander=false;
298 if (g_roup->isLocked())
299 knGlobals.scheduler()->nntpMutex().unlock();
302 else if (f_older) {
304 KNLocalArticle::Ptr art;
305 if(f_ilter) {
306 f_ilter->doFilter(f_older);
307 } else {
308 for(int i=0; i<f_older->length(); ++i) {
309 art=f_older->at(i);
310 art->setFilterResult(true);
314 for(int idx=0; idx<f_older->length(); idx++) {
315 art=f_older->at(idx);
317 if(!art->listItem() && art->filterResult()) {
318 art->setListItem( new KNHdrViewItem( v_iew ), art );
319 art->updateListItem();
320 } else if(art->listItem())
321 art->updateListItem();
326 if(setFirstChild && v_iew->firstChild()) {
327 v_iew->setCurrentItem(v_iew->firstChild());
328 knGlobals.top->articleViewer()->setArticle( KNArticle::Ptr() );
331 knGlobals.setStatusMsg( QString() );
332 updateStatusString();
336 void KNArticleManager::updateViewForCollection( KNArticleCollection::Ptr c )
338 if(g_roup==c || f_older==c)
339 showHdrs(false);
343 void KNArticleManager::updateListViewItems()
345 if(!g_roup && !f_older) return;
347 if(g_roup) {
348 KNRemoteArticle::Ptr art;
350 for(int i=0; i<g_roup->length(); ++i) {
351 art=g_roup->at(i);
352 if(art->listItem())
353 art->updateListItem();
355 } else { //folder
356 KNLocalArticle::Ptr art;
358 for(int idx=0; idx<f_older->length(); idx++) {
359 art=f_older->at(idx);
360 if(art->listItem())
361 art->updateListItem();
367 void KNArticleManager::setAllThreadsOpen(bool b)
369 KNRemoteArticle::Ptr art;
370 if(g_roup) {
371 ScopedCursorOverride cursor( Qt::WaitCursor );
372 d_isableExpander = true;
373 for(int idx=0; idx<g_roup->length(); idx++) {
374 art = g_roup->at(idx);
375 if (art->listItem())
376 art->listItem()->setOpen(b);
377 else
378 if (b && art->filterResult()) {
379 createThread(art);
380 art->listItem()->setOpen(true);
383 d_isableExpander = false;
388 void KNArticleManager::search()
390 if(s_earchDlg) {
391 s_earchDlg->show();
392 #ifdef Q_OS_UNIX
393 KWindowSystem::activateWindow(s_earchDlg->winId());
394 #endif
395 } else {
396 s_earchDlg = new SearchDialog( SearchDialog::STgroupSearch, 0 );
397 connect(s_earchDlg, SIGNAL(doSearch(KNArticleFilter*)), this,
398 SLOT(slotFilterChanged(KNArticleFilter*)));
399 connect(s_earchDlg, SIGNAL(dialogDone()), this,
400 SLOT(slotSearchDialogDone()));
401 s_earchDlg->show();
406 void KNArticleManager::setGroup( KNGroup::Ptr g )
408 g_roup = g;
409 if ( g )
410 emit aboutToShowGroup();
414 void KNArticleManager::setFolder( KNFolder::Ptr f )
416 f_older = f;
417 if ( f )
418 emit aboutToShowFolder();
422 KNArticleCollection::Ptr KNArticleManager::collection()
424 if(g_roup)
425 return g_roup;
426 if(f_older)
427 return f_older;
429 return KNArticleCollection::Ptr();
433 bool KNArticleManager::loadArticle( KNArticle::Ptr a )
435 if (!a)
436 return false;
438 if (a->hasContent())
439 return true;
441 if (a->isLocked()) {
442 if ( a->type() == KNArticle::ATremote )
443 return true; // locked == we are already loading this article...
444 else
445 return false;
448 if ( a->type() == KNArticle::ATremote ) {
449 KNGroup::Ptr g = boost::static_pointer_cast<KNGroup>( a->collection() );
450 if(g)
451 emitJob( new ArticleFetchJob( this, g->account(), a ) );
452 else
453 return false;
455 else { // local article
456 KNFolder::Ptr f = boost::static_pointer_cast<KNFolder>( a->collection() );
457 if( f && f->loadArticle( boost::static_pointer_cast<KNLocalArticle>( a ) ) )
458 knGlobals.memoryManager()->updateCacheEntry(a);
459 else
460 return false;
462 return true;
466 bool KNArticleManager::unloadArticle( KNArticle::Ptr a, bool force )
468 if(!a || a->isLocked() )
469 return false;
470 if(!a->hasContent())
471 return true;
473 if (!force && a->isNotUnloadable())
474 return false;
476 if ( !force && ( ArticleWidget::articleVisible( a ) ) )
477 return false;
479 if (!force && ( a->type()== KNArticle::ATlocal ) &&
480 ( KNGlobals::self()->articleFactory()->findComposer( boost::static_pointer_cast<KNLocalArticle>( a ) ) != 0 ) )
481 return false;
483 if ( !ArticleWindow::closeAllWindowsForArticle( a, force ) )
484 if (!force)
485 return false;
487 ArticleWidget::articleRemoved( a );
488 if ( a->type() != KNArticle::ATlocal )
489 KNGlobals::self()->articleFactory()->deleteComposerForArticle( boost::static_pointer_cast<KNLocalArticle>( a ) );
490 a->updateListItem();
491 knGlobals.memoryManager()->removeCacheEntry(a);
493 return true;
497 void KNArticleManager::copyIntoFolder( KNArticle::List &l, KNFolder::Ptr f )
499 if(!f) return;
501 KNLocalArticle::Ptr 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 = KNLocalArticle::Ptr( new KNLocalArticle( KNArticleCollection::Ptr() ) );
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 l2.clear();
520 f->setNotUnloadable(false);
521 return;
524 if( !f->saveArticles( l2 ) ) {
525 for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it ) {
526 if ( (*it)->isOrphant() )
527 (*it).reset(); // ok, this is ugly; we simply delete orphant articles
528 else
529 (*it)->KMime::Content::clear(); // no need to keep them in memory
531 KNHelper::displayInternalFileError();
532 } else {
533 for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
534 (*it)->KMime::Content::clear(); // no need to keep them in memory
535 knGlobals.memoryManager()->updateCacheEntry( boost::static_pointer_cast<KNArticleCollection>( f ) );
538 f->setNotUnloadable(false);
543 void KNArticleManager::moveIntoFolder( KNLocalArticle::List &l, KNFolder::Ptr f )
545 if(!f) return;
546 kDebug(5003) <<" Target folder:" << f->name();
548 f->setNotUnloadable(true);
550 if (!f->isLoaded() && !knGlobals.folderManager()->loadHeaders(f)) {
551 f->setNotUnloadable(false);
552 return;
555 if ( f->saveArticles( l ) ) {
556 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
557 knGlobals.memoryManager()->updateCacheEntry( boost::static_pointer_cast<KNArticle>(*it) );
558 knGlobals.memoryManager()->updateCacheEntry( boost::static_pointer_cast<KNArticleCollection>( f ) );
559 } else {
560 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
561 if ( (*it)->isOrphant() )
562 (*it).reset(); // ok, this is ugly; we simply delete orphant articles
563 KNHelper::displayInternalFileError();
566 f->setNotUnloadable(false);
570 bool KNArticleManager::deleteArticles(KNLocalArticle::List &l, bool ask)
572 if(ask) {
573 QStringList lst;
574 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
575 if ( (*it)->isLocked() )
576 continue;
577 if ( (*it)->subject()->isEmpty() )
578 lst << i18n("no subject");
579 else
580 lst << (*it)->subject()->asUnicodeString();
582 if( KMessageBox::Cancel == KMessageBox::warningContinueCancelList(
583 knGlobals.topWidget, i18n("Do you really want to delete these articles?"), lst,
584 i18n("Delete Articles"), KGuiItem(i18n("&Delete"),"edit-delete")) )
585 return false;
588 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
589 knGlobals.memoryManager()->removeCacheEntry( boost::static_pointer_cast<KNArticle>(*it) );
591 KNFolder::Ptr f = boost::static_pointer_cast<KNFolder>( l.first()->collection() );
592 if ( f ) {
593 f->removeArticles( l, true );
594 knGlobals.memoryManager()->updateCacheEntry( boost::static_pointer_cast<KNArticleCollection>( f ) );
595 return false; // composers for those articles were already removed in removeArticles
596 } else {
597 l.clear();
600 return true;
604 void KNArticleManager::setAllRead( bool read, int lastcount )
606 if ( !g_roup )
607 return;
609 int groupLength = g_roup->length();
610 int newCount = g_roup->newCount();
611 int readCount = g_roup->readCount();
612 int offset = lastcount;
614 if ( lastcount > groupLength || lastcount < 0 )
615 offset = groupLength;
617 KNRemoteArticle::Ptr a;
618 for ( int i = groupLength - offset; i < groupLength; ++i ) {
619 a = g_roup->at( i );
620 if ( a->getReadFlag() != read && !a->isIgnored() ) {
621 a->setRead( read );
622 a->setChanged( true );
623 if ( !read ) {
624 readCount--;
625 if ( a->isNew() )
626 newCount++;
627 } else {
628 readCount++;
629 if ( a->isNew() )
630 newCount--;
635 g_roup->updateThreadInfo();
636 if ( lastcount < 0 && read ) {
637 // HACK: try to hide the effects of the ignore/filter new/unread count bug
638 g_roup->setReadCount( groupLength );
639 g_roup->setNewCount( 0 );
640 } else {
641 g_roup->setReadCount( readCount );
642 g_roup->setNewCount( newCount );
645 g_roup->updateListItem();
646 showHdrs( true );
650 void KNArticleManager::setRead(KNRemoteArticle::List &l, bool r, bool handleXPosts)
652 if ( l.isEmpty() )
653 return;
655 KNRemoteArticle::Ptr ref;
656 KNGroup::Ptr g = boost::static_pointer_cast<KNGroup>( l.first()->collection() );
657 int changeCnt=0, idRef=0;
659 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
660 if( r && knGlobals.settings()->markCrossposts() &&
661 handleXPosts && (*it)->newsgroups()->isCrossposted() ) {
663 QList<QByteArray> groups = (*it)->newsgroups()->groups();
664 KNGroup::Ptr targetGroup;
665 KNRemoteArticle::Ptr xp;
666 KNRemoteArticle::List al;
667 QByteArray mid = (*it)->messageID()->as7BitString( false );
669 for ( QList<QByteArray>::Iterator it2 = groups.begin(); it2 != groups.end(); ++it2 ) {
670 targetGroup = knGlobals.groupManager()->group(*it2, g->account());
671 if (targetGroup) {
672 if (targetGroup->isLoaded() && (xp=targetGroup->byMessageId(mid)) ) {
673 al.clear();
674 al.append(xp);
675 setRead(al, r, false);
676 } else {
677 targetGroup->appendXPostID(mid);
683 else if ( (*it)->getReadFlag() != r ) {
684 (*it)->setRead( r );
685 (*it)->setChanged( true );
686 (*it)->updateListItem();
688 if ( !(*it)->isIgnored() ) {
689 changeCnt++;
690 idRef = (*it)->idRef();
692 while ( idRef != 0 ) {
693 ref=g->byId(idRef);
694 if(r) {
695 ref->decUnreadFollowUps();
696 if ( (*it)->isNew() )
697 ref->decNewFollowUps();
699 else {
700 ref->incUnreadFollowUps();
701 if ( (*it)->isNew() )
702 ref->incNewFollowUps();
705 if(ref->listItem() &&
706 ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
707 (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
708 ref->updateListItem();
710 idRef=ref->idRef();
713 if(r) {
714 g->incReadCount();
715 if ( (*it)->isNew() )
716 g->decNewCount();
718 else {
719 g->decReadCount();
720 if ( (*it)->isNew() )
721 g->incNewCount();
727 if(changeCnt>0) {
728 g->updateListItem();
729 if(g==g_roup)
730 updateStatusString();
735 void KNArticleManager::setAllNotNew()
737 if ( !g_roup )
738 return;
739 KNRemoteArticle::Ptr a;
740 for ( int i = 0; i < g_roup->length(); ++i) {
741 a = g_roup->at(i);
742 if ( a->isNew() ) {
743 a->setNew( false );
744 a->setChanged( true );
747 g_roup->setFirstNewIndex( -1 );
748 g_roup->setNewCount( 0 );
749 g_roup->updateThreadInfo();
753 bool KNArticleManager::toggleWatched(KNRemoteArticle::List &l)
755 if(l.isEmpty())
756 return true;
758 KNRemoteArticle::Ptr a = l.first();
759 KNRemoteArticle::Ptr ref;
760 bool watch = (!a->isWatched());
761 KNGroup::Ptr g = boost::static_pointer_cast<KNGroup>( a->collection() );
762 int changeCnt=0, idRef=0;
764 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
765 if ( (*it)->isIgnored() ) {
766 (*it)->setIgnored(false);
768 if ( !(*it)->getReadFlag() ) {
769 changeCnt++;
770 idRef = (*it)->idRef();
772 while ( idRef != 0 ) {
773 ref=g->byId(idRef);
775 ref->incUnreadFollowUps();
776 if ( (*it)->isNew() )
777 ref->incNewFollowUps();
779 if(ref->listItem() &&
780 ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
781 (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
782 ref->updateListItem();
784 idRef=ref->idRef();
786 g->decReadCount();
787 if ( (*it)->isNew() )
788 g->incNewCount();
792 (*it)->setWatched( watch );
793 (*it)->updateListItem();
794 (*it)->setChanged( true );
797 if(changeCnt>0) {
798 g->updateListItem();
799 if(g==g_roup)
800 updateStatusString();
803 return watch;
807 bool KNArticleManager::toggleIgnored(KNRemoteArticle::List &l)
809 if(l.isEmpty())
810 return true;
812 KNRemoteArticle::Ptr ref;
813 bool ignore = !l.first()->isIgnored();
814 KNGroup::Ptr g = boost::static_pointer_cast<KNGroup>( l.first()->collection() );
815 int changeCnt = 0, idRef = 0;
817 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
818 (*it)->setWatched(false);
819 if ( (*it)->isIgnored() != ignore ) {
820 (*it)->setIgnored( ignore );
822 if ( !(*it)->getReadFlag() ) {
823 changeCnt++;
824 idRef = (*it)->idRef();
826 while ( idRef != 0 ) {
827 ref = g->byId( idRef );
829 if ( ignore ) {
830 ref->decUnreadFollowUps();
831 if ( (*it)->isNew() )
832 ref->decNewFollowUps();
833 } else {
834 ref->incUnreadFollowUps();
835 if ( (*it)->isNew() )
836 ref->incNewFollowUps();
839 if(ref->listItem() &&
840 ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
841 (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
842 ref->updateListItem();
844 idRef=ref->idRef();
847 if ( ignore ) {
848 g->incReadCount();
849 if ( (*it)->isNew() )
850 g->decNewCount();
851 } else {
852 g->decReadCount();
853 if ( (*it)->isNew() )
854 g->incNewCount();
859 (*it)->updateListItem();
860 (*it)->setChanged(true);
863 if(changeCnt>0) {
864 g->updateListItem();
865 if(g==g_roup)
866 updateStatusString();
869 return ignore;
873 void KNArticleManager::rescoreArticles(KNRemoteArticle::List &l)
875 if ( l.isEmpty() )
876 return;
878 KNGroup::Ptr g = boost::static_pointer_cast<KNGroup>( l.first()->collection() );
879 KScoringManager *sm = knGlobals.scoringManager();
880 sm->initCache(g->groupname());
882 for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
883 int defScore = 0;
884 if ( (*it)->isIgnored())
885 defScore = knGlobals.settings()->ignoredThreshold();
886 else if ( (*it)->isWatched() )
887 defScore = knGlobals.settings()->watchedThreshold();
888 (*it)->setScore(defScore);
890 bool read = (*it)->isRead();
892 KNScorableArticle sa( (*it) );
893 sm->applyRules(sa);
894 (*it)->updateListItem();
895 (*it)->setChanged( true );
897 if ( !read && (*it)->isRead() != read )
898 g_roup->incReadCount();
903 void KNArticleManager::processJob(KNJobData *j)
905 if(j->type()==KNJobData::JTfetchArticle && !j->canceled()) {
906 KNRemoteArticle::Ptr a = boost::static_pointer_cast<KNRemoteArticle>( j->data() );
907 if(j->success()) {
908 ArticleWidget::articleChanged( a );
909 if(!a->isOrphant()) //orphant articles are deleted by the displaying widget
910 knGlobals.memoryManager()->updateCacheEntry( boost::static_pointer_cast<KNArticle>( a ) );
911 if(a->listItem())
912 a->updateListItem();
913 } else {
914 if ( j->error() == KIO::ERR_DOES_NOT_EXIST ) {
915 // article is not available at the server anymore
916 QString msgId = a->messageID()->as7BitString( false );
917 // strip of '<' and '>'
918 msgId = msgId.mid( 1, msgId.length() - 2 );
919 ArticleWidget::articleLoadError( a,
920 i18n("The article you requested is not available on your news server."
921 "<br />You could try to get it from <a href=\"http://groups.google.com/groups?selm=%1\">groups.google.com</a>.",
922 msgId ) );
923 // mark article as read
924 if ( knGlobals.settings()->autoMark() && !a->isOrphant() ) {
925 KNRemoteArticle::List l;
926 l.append( a );
927 setRead( l, true );
929 } else
930 ArticleWidget::articleLoadError( a, j->errorString() );
934 delete j;
938 void KNArticleManager::createThread( KNRemoteArticle::Ptr a )
940 KNRemoteArticle::Ptr ref = a->displayedReference();
942 if(ref) {
943 if(!ref->listItem())
944 createThread(ref);
945 a->setListItem( new KNHdrViewItem( ref->listItem() ), a );
947 else
948 a->setListItem( new KNHdrViewItem( v_iew ), a );
950 a->setThreadMode( knGlobals.settings()->showThreads() );
951 a->initListItem();
955 void KNArticleManager::createCompleteThread( KNRemoteArticle::Ptr a )
957 KNRemoteArticle::Ptr ref = a->displayedReference();
958 if ( !ref ) {
959 return;
962 KNRemoteArticle::Ptr art, top;
963 bool inThread=false;
964 bool showThreads = knGlobals.settings()->showThreads();
966 while (ref->displayedReference() != 0)
967 ref=ref->displayedReference();
969 top = ref;
971 if (!top->listItem()) // shouldn't happen
972 return;
974 for(int i=0; i<g_roup->count(); ++i) {
975 art=g_roup->at(i);
976 if(art->filterResult() && !art->listItem()) {
978 if(art->displayedReference()==top) {
979 art->setListItem( new KNHdrViewItem( top->listItem() ), art );
980 art->setThreadMode(showThreads);
981 art->initListItem();
983 else {
984 ref=art->displayedReference();
985 inThread=false;
986 while(ref && !inThread) {
987 inThread=(ref==top);
988 ref=ref->displayedReference();
990 if(inThread)
991 createThread(art);
996 if ( knGlobals.settings()->totalExpandThreads() )
997 top->listItem()->expandChildren();
1001 void KNArticleManager::updateStatusString()
1003 int displCnt=0;
1005 if(g_roup) {
1006 if(f_ilter)
1007 displCnt=f_ilter->count();
1008 else
1009 displCnt=g_roup->count();
1011 QString name = g_roup->name();
1012 if (g_roup->status()==KNGroup::moderated)
1013 name += i18n(" (moderated)");
1015 knGlobals.setStatusMsg(i18n(" %1: %2 new , %3 displayed",
1016 name, g_roup->newCount(), displCnt),SB_GROUP);
1018 if(f_ilter)
1019 knGlobals.setStatusMsg(i18n(" Filter: %1", f_ilter->translatedName()), SB_FILTER);
1020 else
1021 knGlobals.setStatusMsg( QString(), SB_FILTER );
1023 else if(f_older) {
1024 if(f_ilter)
1025 displCnt=f_ilter->count();
1026 else
1027 displCnt=f_older->count();
1028 knGlobals.setStatusMsg(i18n(" %1: %2 displayed",
1029 f_older->name(), displCnt), SB_GROUP);
1030 knGlobals.setStatusMsg( QString(), SB_FILTER );
1031 } else {
1032 knGlobals.setStatusMsg( QString(), SB_GROUP );
1033 knGlobals.setStatusMsg( QString(), SB_FILTER );
1038 void KNArticleManager::slotFilterChanged(KNArticleFilter *f)
1040 f_ilter=f;
1041 showHdrs();
1045 void KNArticleManager::slotSearchDialogDone()
1047 s_earchDlg->hide();
1048 slotFilterChanged(f_ilterMgr->currentFilter());
1052 void KNArticleManager::slotItemExpanded(Q3ListViewItem *p)
1054 if (d_isableExpander) // we don't want to call this method recursively
1055 return;
1056 d_isableExpander = true;
1058 KNRemoteArticle::Ptr top, art, ref;
1059 KNHdrViewItem *hdrItem;
1060 bool inThread=false;
1061 bool showThreads = knGlobals.settings()->showThreads();
1062 hdrItem=static_cast<KNHdrViewItem*>(p);
1063 top = boost::static_pointer_cast<KNRemoteArticle>( hdrItem->art );
1065 if (p->childCount() == 0) {
1066 ScopedCursorOverride cursor( Qt::WaitCursor );
1068 for(int i=0; i<g_roup->count(); ++i) {
1069 art=g_roup->at(i);
1070 if(art->filterResult() && !art->listItem()) {
1072 if(art->displayedReference()==top) {
1073 art->setListItem( new KNHdrViewItem( hdrItem ), art );
1074 art->setThreadMode(showThreads);
1075 art->initListItem();
1077 else if( knGlobals.settings()->totalExpandThreads() ) { //totalExpand
1078 ref=art->displayedReference();
1079 inThread=false;
1080 while(ref && !inThread) {
1081 inThread=(ref==top);
1082 ref=ref->displayedReference();
1084 if(inThread)
1085 createThread(art);
1090 cursor.restore();
1093 if ( knGlobals.settings()->totalExpandThreads() )
1094 hdrItem->expandChildren();
1096 d_isableExpander = false;
1100 void KNArticleManager::setView(KNHeaderView* v) {
1101 v_iew = v;
1102 if(v) {
1103 connect(v, SIGNAL(expanded(Q3ListViewItem*)), this,
1104 SLOT(slotItemExpanded(Q3ListViewItem*)));
1108 //-----------------------------
1109 #include "knarticlemanager.moc"