diff options
Diffstat (limited to 'knode/kngroup.cpp')
-rw-r--r-- | knode/kngroup.cpp | 1112 |
1 files changed, 1112 insertions, 0 deletions
diff --git a/knode/kngroup.cpp b/knode/kngroup.cpp new file mode 100644 index 000000000..1c8bdfbf1 --- /dev/null +++ b/knode/kngroup.cpp @@ -0,0 +1,1112 @@ +/* + KNode, the KDE newsreader + Copyright (c) 1999-2005 the KNode authors. + See file AUTHORS for details + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + + +#include <ksimpleconfig.h> +#include <klocale.h> +#include <kdebug.h> + +#include <kqcstringsplitter.h> + +#include "knprotocolclient.h" +#include "knglobals.h" +#include "kncollectionviewitem.h" +#include "kngrouppropdlg.h" +#include "utilities.h" +#include "knconfigmanager.h" +#include "knmainwidget.h" +#include "knscoring.h" +#include "knarticlemanager.h" +#include "kngroupmanager.h" +#include "knnntpaccount.h" +#include "headerview.h" + + +#define SORT_DEPTH 5 + +KNGroup::KNGroup(KNCollection *p) + : KNArticleCollection(p), n_ewCount(0), l_astFetchCount(0), r_eadCount(0), i_gnoreCount(0), + l_astNr(0), m_axFetch(0), d_ynDataFormat(1), f_irstNew(-1), l_ocked(false), + u_seCharset(false), s_tatus(unknown), i_dentity(0) +{ + mCleanupConf = new KNConfig::Cleanup( false ); +} + + +KNGroup::~KNGroup() +{ + delete i_dentity; + delete mCleanupConf; +} + + +QString KNGroup::path() +{ + return p_arent->path(); +} + + +const QString& KNGroup::name() +{ + static QString ret; + if(n_ame.isEmpty()) ret=g_roupname; + else ret=n_ame; + return ret; +} + + +void KNGroup::updateListItem() +{ + if(!l_istItem) return; + l_istItem->setTotalCount( c_ount ); + l_istItem->setUnreadCount( c_ount - r_eadCount - i_gnoreCount ); + l_istItem->repaint(); +} + + +bool KNGroup::readInfo(const QString &confPath) +{ + KSimpleConfig info(confPath); + + g_roupname = info.readEntry("groupname"); + d_escription = info.readEntry("description"); + n_ame = info.readEntry("name"); + c_ount = info.readNumEntry("count",0); + r_eadCount = info.readNumEntry("read",0); + if (r_eadCount > c_ount) r_eadCount = c_ount; + f_irstNr = info.readNumEntry("firstMsg",0); + l_astNr = info.readNumEntry("lastMsg",0); + d_ynDataFormat = info.readNumEntry("dynDataFormat",0); + u_seCharset = info.readBoolEntry("useCharset", false); + d_efaultChSet = info.readEntry("defaultChSet").latin1(); + QString s = info.readEntry("status","unknown"); + if (s=="readOnly") + s_tatus = readOnly; + else if (s=="postingAllowed") + s_tatus = postingAllowed; + else if (s=="moderated") + s_tatus = moderated; + else + s_tatus = unknown; + c_rosspostIDBuffer = info.readListEntry("crosspostIDBuffer"); + + i_dentity=new KNConfig::Identity(false); + i_dentity->loadConfig(&info); + if(!i_dentity->isEmpty()) { + kdDebug(5003) << "KNGroup::readInfo(const QString &confPath) : using alternative user for " << g_roupname << endl; + } + else { + delete i_dentity; + i_dentity=0; + } + + mCleanupConf->loadConfig( &info ); + + return (!g_roupname.isEmpty()); +} + + +void KNGroup::saveInfo() +{ + QString dir(path()); + + if (!dir.isNull()) { + KSimpleConfig info(dir+g_roupname+".grpinfo"); + + info.writeEntry("groupname", g_roupname); + info.writeEntry("description", d_escription); + info.writeEntry("firstMsg", f_irstNr); + info.writeEntry("lastMsg", l_astNr); + info.writeEntry("count", c_ount); + info.writeEntry("read", r_eadCount); + info.writeEntry("dynDataFormat", d_ynDataFormat); + info.writeEntry("name", n_ame); + info.writeEntry("useCharset", u_seCharset); + info.writeEntry("defaultChSet", QString::fromLatin1(d_efaultChSet)); + switch (s_tatus) { + case unknown: info.writeEntry("status","unknown"); + break; + case readOnly: info.writeEntry("status","readOnly"); + break; + case postingAllowed: info.writeEntry("status","postingAllowed"); + break; + case moderated: info.writeEntry("status","moderated"); + break; + } + info.writeEntry("crosspostIDBuffer", c_rosspostIDBuffer); + + if(i_dentity) + i_dentity->saveConfig(&info); + else if(info.hasKey("Email")) { + info.deleteEntry("Name", false); + info.deleteEntry("Email", false); + info.deleteEntry("Reply-To", false); + info.deleteEntry("Mail-Copies-To", false); + info.deleteEntry("Org", false); + info.deleteEntry("UseSigFile", false); + info.deleteEntry("UseSigGenerator", false); + info.deleteEntry("sigFile", false); + info.deleteEntry("sigText", false); + } + + mCleanupConf->saveConfig( &info ); + } +} + + +KNNntpAccount* KNGroup::account() +{ + KNCollection *p=parent(); + while(p->type()!=KNCollection::CTnntpAccount) p=p->parent(); + + return (KNNntpAccount*)p_arent; +} + + +bool KNGroup::loadHdrs() +{ + if(isLoaded()) { + kdDebug(5003) << "KNGroup::loadHdrs() : nothing to load" << endl; + return true; + } + + kdDebug(5003) << "KNGroup::loadHdrs() : loading headers" << endl; + QCString buff, hdrValue; + KNFile f; + KQCStringSplitter split; + int cnt=0, id, lines, fileFormatVersion, artNumber; + unsigned int timeT; + KNRemoteArticle *art; + + QString dir(path()); + if (dir.isNull()) + return false; + + f.setName(dir+g_roupname+".static"); + + if(f.open(IO_ReadOnly)) { + + if(!resize(c_ount)) { + f.close(); + return false; + } + + while(!f.atEnd()) { + buff=f.readLine(); + if(buff.isEmpty()){ + if (f.status() == IO_Ok) { + kdWarning(5003) << "Found broken line in static-file: Ignored!" << endl; + continue; + } else { + kdError(5003) << "Corrupted static file, IO-error!" << endl; + clear(); + return false; + } + } + + split.init(buff, "\t"); + + art=new KNRemoteArticle(this); + + split.first(); + art->messageID()->from7BitString(split.string()); + + split.next(); + art->subject()->from7BitString(split.string()); + + split.next(); + art->from()->setEmail(split.string()); + split.next(); + if(split.string()!="0") + art->from()->setNameFrom7Bit(split.string()); + + buff=f.readLine(); + if(buff!="0") art->references()->from7BitString(buff.copy()); + + buff=f.readLine(); + if (sscanf(buff,"%d %d %u %d", &id, &lines, &timeT, &fileFormatVersion) < 4) + fileFormatVersion = 0; // KNode <= 0.4 had no version number + art->setId(id); + art->lines()->setNumberOfLines(lines); + art->date()->setUnixTime(timeT); + + if (fileFormatVersion > 0) { + buff=f.readLine(); + sscanf(buff,"%d", &artNumber); + art->setArticleNumber(artNumber); + } + + // optional headers + if (fileFormatVersion > 1) { + // first line is the number of addiotion headers + buff = f.readLine(); + // following lines contain one header per line + for (uint i = buff.toUInt(); i > 0; --i) { + buff = f.readLine(); + int pos = buff.find(':'); + QCString hdrName = buff.left( pos ); + // skip headers we already set above and which we actually never should + // find here, but however it still happens... (eg. #101355) + if ( hdrName == "Subject" || hdrName == "From" || hdrName == "Date" + || hdrName == "Message-ID" || hdrName == "References" + || hdrName == "Bytes" || hdrName == "Lines" ) + continue; + hdrValue = buff.right( buff.length() - (pos + 2) ); + if (hdrValue.length() > 0) + art->setHeader( new KMime::Headers::Generic( hdrName, art, hdrValue ) ); + } + } + + if(append(art)) cnt++; + else { + f.close(); + clear(); + return false; + } + } + + setLastID(); + f.close(); + } + else { + clear(); + return false; + } + + + f.setName(dir+g_roupname+".dynamic"); + + if (f.open(IO_ReadOnly)) { + + dynDataVer0 data0; + dynDataVer1 data1; + int readCnt=0,byteCount,dataSize; + if (d_ynDataFormat==0) + dataSize = sizeof(data0); + else + dataSize = sizeof(data1); + + while(!f.atEnd()) { + + if (d_ynDataFormat==0) + byteCount = f.readBlock((char*)(&data0), dataSize); + else + byteCount = f.readBlock((char*)(&data1), dataSize); + if ((byteCount == -1)||(byteCount!=dataSize)) + if (f.status() == IO_Ok) { + kdWarning(5003) << "Found broken entry in dynamic-file: Ignored!" << endl; + continue; + } else { + kdError(5003) << "Corrupted dynamic file, IO-error!" << endl; + clear(); + return false; + } + + if (d_ynDataFormat==0) + art=byId(data0.id); + else + art=byId(data1.id); + + if(art) { + if (d_ynDataFormat==0) + data0.getData(art); + else + data1.getData(art); + + if (art->isRead()) readCnt++; + } + + } + + f.close(); + + r_eadCount=readCnt; + + } + + else { + clear(); + return false; + } + + kdDebug(5003) << cnt << " articles read from file" << endl; + c_ount=length(); + + // convert old data files into current format: + if (d_ynDataFormat!=1) { + saveDynamicData(length(), true); + d_ynDataFormat=1; + } + + // restore "New" - flags + if( f_irstNew > -1 ) { + for( int i = f_irstNew; i < length(); i++ ) { + at(i)->setNew(true); + } + } + + updateThreadInfo(); + processXPostBuffer(false); + return true; +} + + +bool KNGroup::unloadHdrs(bool force) +{ + if(l_ockedArticles>0) + return false; + + if (!force && isNotUnloadable()) + return false; + + KNRemoteArticle *a; + for(int idx=0; idx<length(); idx++) { + a=at(idx); + if (a->hasContent() && !knGlobals.articleManager()->unloadArticle(a, force)) + return false; + } + syncDynamicData(); + clear(); + + return true; +} + + +// Attention: this method is called from the network thread! +void KNGroup::insortNewHeaders(QStrList *hdrs, QStrList *hdrfmt, KNProtocolClient *client) +{ + KNRemoteArticle *art=0, *art2=0; + QCString data, hdr, hdrName; + KQCStringSplitter split; + split.setIncludeSep(false); + int new_cnt=0, added_cnt=0, todo=hdrs->count(); + QTime timer; + + l_astFetchCount=0; + + if(!hdrs || hdrs->count()==0) + return; + + timer.start(); + + //resize the list + if(!resize(size()+hdrs->count())) return; + + // recreate msg-ID index + syncSearchIndex(); + + // remember index of first new + if(f_irstNew == -1) + f_irstNew = length(); // index of last + 1 + + for(char *line=hdrs->first(); line; line=hdrs->next()) { + split.init(line, "\t"); + + //new Header-Object + art=new KNRemoteArticle(this); + art->setNew(true); + + //Article Number + split.first(); + art->setArticleNumber(split.string().toInt()); + + //Subject + split.next(); + art->subject()->from7BitString(split.string()); + if(art->subject()->isEmpty()) + art->subject()->fromUnicodeString(i18n("no subject"), art->defaultCharset()); + + //From and Email + split.next(); + art->from()->from7BitString(split.string()); + + //Date + split.next(); + art->date()->from7BitString(split.string()); + + //Message-ID + split.next(); + art->messageID()->from7BitString(split.string().simplifyWhiteSpace()); + + //References + split.next(); + if(!split.string().isEmpty()) + art->references()->from7BitString(split.string()); //use QCString::copy() ? + + // Bytes + split.next(); + + //Lines + split.next(); + art->lines()->setNumberOfLines(split.string().toInt()); + + // optinal additional headers + mOptionalHeaders = *hdrfmt; + for (hdr = hdrfmt->first(); hdr; hdr = hdrfmt->next()) { + if (!split.next()) + break; + data = split.string(); + int pos = hdr.find(':'); + hdrName = hdr.left( pos ); + // if the header format is 'full' we have to strip the header name + if (hdr.findRev("full") == (int)(hdr.length() - 4)) + data = data.right( data.length() - (hdrName.length() + 2) ); + + // add header + art->setHeader( new KMime::Headers::Generic( hdrName, art, data ) ); + } + + // check if we have this article already in this group, + // if so mark it as new (useful with leafnodes delay-body function) + art2=byMessageId(art->messageID()->as7BitString(false)); + if(art2) { // ok, we already have this article + art2->setNew(true); + art2->setArticleNumber(art->articleNumber()); + delete art; + new_cnt++; + } + else if (append(art)) { + added_cnt++; + new_cnt++; + } + else { + delete art; + return; + } + + if (timer.elapsed() > 200) { // don't flicker + timer.restart(); + if (client) client->updatePercentage((new_cnt*30)/todo); + } + } + + // now we build the threads + syncSearchIndex(); // recreate the msgId-index so it contains the appended headers + buildThreads(added_cnt, client); + updateThreadInfo(); + + // save the new headers + saveStaticData(added_cnt); + saveDynamicData(added_cnt); + + // update group-info + c_ount=length(); + n_ewCount+=new_cnt; + l_astFetchCount=new_cnt; + updateListItem(); + saveInfo(); +} + + +int KNGroup::saveStaticData(int cnt,bool ovr) +{ + int idx, savedCnt=0, mode; + KNRemoteArticle *art; + + QString dir(path()); + if (dir.isNull()) + return 0; + + QFile f(dir+g_roupname+".static"); + + if(ovr) mode=IO_WriteOnly; + else mode=IO_WriteOnly | IO_Append; + + if(f.open(mode)) { + + QTextStream ts(&f); + ts.setEncoding(QTextStream::Latin1); + + for(idx=length()-cnt; idx<length(); idx++) { + + art=at(idx); + + if(art->isExpired()) continue; + + ts << art->messageID()->as7BitString(false) << '\t'; + ts << art->subject()->as7BitString(false) << '\t'; + ts << art->from()->email() << '\t'; + + if(art->from()->hasName()) + ts << art->from()->nameAs7Bit() << '\n'; + else + ts << "0\n"; + + if(!art->references()->isEmpty()) + ts << art->references()->as7BitString(false) << "\n"; + else + ts << "0\n"; + + ts << art->id() << ' '; + ts << art->lines()->numberOfLines() << ' '; + ts << art->date()->unixTime() << ' '; + ts << "2\n"; // version number to achieve backward compatibility easily + + ts << art->articleNumber() << '\n'; + + // optional headers + ts << mOptionalHeaders.count() << '\n'; + for (QCString hdrName = mOptionalHeaders.first(); hdrName; hdrName = mOptionalHeaders.next()) { + hdrName = hdrName.left( hdrName.find(':') ); + KMime::Headers::Base *hdr = art->getHeaderByType( hdrName ); + if ( hdr ) + ts << hdrName << ": " << hdr->asUnicodeString() << '\n'; + else + ts << hdrName << ": \n"; + } + + savedCnt++; + } + + f.close(); + } + + return savedCnt; +} + + +void KNGroup::saveDynamicData(int cnt,bool ovr) +{ + dynDataVer1 data; + int mode; + KNRemoteArticle *art; + + if(length()>0) { + QString dir(path()); + if (dir.isNull()) + return; + + QFile f(dir+g_roupname+".dynamic"); + + if(ovr) mode=IO_WriteOnly; + else mode=IO_WriteOnly | IO_Append; + + if(f.open(mode)) { + + for(int idx=length()-cnt; idx<length(); idx++) { + art=at(idx); + if(art->isExpired()) continue; + data.setData(art); + f.writeBlock((char*)(&data), sizeof(data)); + art->setChanged(false); + } + f.close(); + } + else KNHelper::displayInternalFileError(); + } +} + + +void KNGroup::syncDynamicData() +{ + dynDataVer1 data; + int cnt=0, readCnt=0, sOfData; + KNRemoteArticle *art; + + if(length()>0) { + + QString dir(path()); + if (dir.isNull()) + return; + + QFile f(dir+g_roupname+".dynamic"); + + if(f.open(IO_ReadWrite)) { + + sOfData=sizeof(data); + + for(int i=0; i<length(); i++) { + art=at(i); + + if(art->hasChanged() && !art->isExpired()) { + + data.setData(art); + f.at(i*sOfData); + f.writeBlock((char*) &data, sOfData); + cnt++; + art->setChanged(false); + } + + if(art->isRead() && !art->isExpired()) readCnt++; + } + + f.close(); + + kdDebug(5003) << g_roupname << " => updated " << cnt << " entries of dynamic data" << endl; + + r_eadCount=readCnt; + } + else KNHelper::displayInternalFileError(); + } +} + + +void KNGroup::appendXPostID(const QString &id) +{ + c_rosspostIDBuffer.append(id); +} + + +void KNGroup::processXPostBuffer(bool deleteAfterwards) +{ + QStringList remainder; + KNRemoteArticle *xp; + KNRemoteArticle::List al; + + for (QStringList::Iterator it = c_rosspostIDBuffer.begin(); it != c_rosspostIDBuffer.end(); ++it) { + if ((xp=byMessageId((*it).local8Bit()))) + al.append(xp); + else + remainder.append(*it); + } + knGlobals.articleManager()->setRead(al, true, false); + + if (!deleteAfterwards) + c_rosspostIDBuffer = remainder; + else + c_rosspostIDBuffer.clear(); +} + + +void KNGroup::buildThreads(int cnt, KNProtocolClient *client) +{ + int end=length(), + start=end-cnt, + foundCnt=0, bySubCnt=0, refCnt=0, + resortCnt=0, idx, oldRef; // idRef; + KNRemoteArticle *art, *ref; + QTime timer; + + timer.start(); + + // this method is called from the nntp-thread!!! +#ifndef NDEBUG + qDebug("knode: KNGroup::buildThreads() : start = %d end = %d",start,end); +#endif + + //resort old hdrs + if(start>0) + for(idx=0; idx<start; idx++) { + art=at(idx); + if(art->threadingLevel()>1) { + oldRef=art->idRef(); + ref=findReference(art); + if(ref) { + // this method is called from the nntp-thread!!! + #ifndef NDEBUG + qDebug("knode: %d: old %d new %d",art->id(), oldRef, art->idRef()); + #endif + resortCnt++; + art->setChanged(true); + } + } + } + + + for(idx=start; idx<end; idx++) { + + art=at(idx); + + if(art->idRef()==-1 && !art->references()->isEmpty() ){ //hdr has references + refCnt++; + if(findReference(art)) + foundCnt++; + } + else { + if(art->subject()->isReply()) { + art->setIdRef(0); //hdr has no references + art->setThreadingLevel(0); + } + else if(art->idRef()==-1) + refCnt++; + } + + if (timer.elapsed() > 200) { // don't flicker + timer.restart(); + if(client) + client->updatePercentage(30+((foundCnt)*70)/cnt); + } + } + + + if(foundCnt<refCnt) { // some references could not been found + + //try to sort by subject + KNRemoteArticle *oldest; + KNRemoteArticle::List list; + + for(idx=start; idx<end; idx++) { + + art=at(idx); + + if(art->idRef()==-1) { //for all not sorted headers + + list.clear(); + list.append(art); + + //find all headers with same subject + for(int idx2=0; idx2<length(); idx2++) + if(at(idx2)==art) continue; + else if(at(idx2)->subject()==art->subject()) + list.append(at(idx2)); + + if(list.count()==1) { + art->setIdRef(0); + art->setThreadingLevel(6); + bySubCnt++; + } + else { + + //find oldest + oldest=list.first(); + for ( KNRemoteArticle::List::Iterator it = list.begin(); it != list.end(); ++it ) + if ( (*it)->date()->unixTime() < oldest->date()->unixTime() ) + oldest = (*it); + + //oldest gets idRef 0 + if(oldest->idRef()==-1) bySubCnt++; + oldest->setIdRef(0); + oldest->setThreadingLevel(6); + + for ( KNRemoteArticle::List::Iterator it = list.begin(); it != list.end(); ++it ) { + if ( (*it) == oldest ) + continue; + if ( (*it)->idRef() == -1 || ( (*it)->idRef() != -1 && (*it)->threadingLevel() == 6 ) ) { + (*it)->setIdRef(oldest->id()); + (*it)->setThreadingLevel(6); + if ( (*it)->id() >= at(start)->id() ) + bySubCnt++; + } + } + } + } + + if (timer.elapsed() > 200) { // don't flicker + timer.restart(); + if (client) client->updatePercentage(30+((bySubCnt+foundCnt)*70)/cnt); + } + } + } + + //all not found items get refID 0 + for (int idx=start; idx<end; idx++){ + art=at(idx); + if(art->idRef()==-1) { + art->setIdRef(0); + art->setThreadingLevel(6); //was 0 !!! + } + } + + //check for loops in threads + int startId; + bool isLoop; + int iterationCount; + for (int idx=start; idx<end; idx++){ + art=at(idx); + startId=art->id(); + isLoop=false; + iterationCount=0; + while(art->idRef()!=0 && !isLoop && (iterationCount < end)) { + art=byId(art->idRef()); + isLoop=(art->id()==startId); + iterationCount++; + } + + if(isLoop) { + // this method is called from the nntp-thread!!! + #ifndef NDEBUG + qDebug("knode: Sorting : loop in %d",startId); + #endif + art=at(idx); + art->setIdRef(0); + art->setThreadingLevel(0); + } + } + + // propagate ignored/watched flags to new headers + for(int idx=start; idx<end; idx++) { + art=at(idx); + int idRef=art->idRef(); + int tmpIdRef; + + if(idRef!=0) { + while(idRef!=0) { + art=byId(idRef); + tmpIdRef=art->idRef(); + idRef = (idRef!=tmpIdRef)? tmpIdRef : 0; + + } + if (art) { + if (art->isIgnored()) { + at(idx)->setIgnored(true); + ++i_gnoreCount; + } + at(idx)->setWatched(art->isWatched()); + } + } + } + + // this method is called from the nntp-thread!!! +#ifndef NDEBUG + qDebug("knode: Sorting : %d headers resorted", resortCnt); + qDebug("knode: Sorting : %d references of %d found", foundCnt, refCnt); + qDebug("knode: Sorting : %d references of %d sorted by subject", bySubCnt, refCnt); +#endif +} + + +KNRemoteArticle* KNGroup::findReference(KNRemoteArticle *a) +{ + int found=false; + QCString ref_mid; + int ref_nr=0; + KNRemoteArticle *ref_art=0; + + ref_mid=a->references()->first(); + + while(!found && !ref_mid.isNull() && ref_nr < SORT_DEPTH) { + ref_art=byMessageId(ref_mid); + if(ref_art) { + found=true; + a->setThreadingLevel(ref_nr+1); + a->setIdRef(ref_art->id()); + } + ref_nr++; + ref_mid=a->references()->next(); + } + + return ref_art; +} + + +void KNGroup::scoreArticles(bool onlynew) +{ + kdDebug(5003) << "KNGroup::scoreArticles()" << endl; + int len=length(), + todo=(onlynew)? lastFetchCount():length(); + + if (todo) { + // reset the notify collection + delete KNScorableArticle::notifyC; + KNScorableArticle::notifyC = 0; + + kdDebug(5003) << "scoring " << newCount() << " articles" << endl; + kdDebug(5003) << "(total " << length() << " article in group)" << endl; + knGlobals.top->setCursorBusy(true); + knGlobals.setStatusMsg(i18n(" Scoring...")); + + int defScore; + KScoringManager *sm = knGlobals.scoringManager(); + sm->initCache(groupname()); + for(int idx=0; idx<todo; idx++) { + KNRemoteArticle *a = at(len-idx-1); + if ( !a ) { + kdWarning( 5003 ) << "found no article at " << len-idx-1 << endl; + continue; + } + + defScore = 0; + if (a->isIgnored()) + defScore = knGlobals.configManager()->scoring()->ignoredThreshold(); + else if (a->isWatched()) + defScore = knGlobals.configManager()->scoring()->watchedThreshold(); + + if (a->score() != defScore) { + a->setScore(defScore); + a->setChanged(true); + } + + bool read = a->isRead(); + + KNScorableArticle sa(a); + sm->applyRules(sa); + + if ( a->isRead() != read && !read ) + incReadCount(); + } + + knGlobals.setStatusMsg(QString::null); + knGlobals.top->setCursorBusy(false); + + //kdDebug(5003) << KNScorableArticle::notifyC->collection() << endl; + if (KNScorableArticle::notifyC) + KNScorableArticle::notifyC->displayCollection(knGlobals.topWidget); + } +} + + +void KNGroup::reorganize() +{ + kdDebug(5003) << "KNGroup::reorganize()" << endl; + + knGlobals.top->setCursorBusy(true); + knGlobals.setStatusMsg(i18n(" Reorganizing headers...")); + + for(int idx=0; idx<length(); idx++) { + KNRemoteArticle *a = at(idx); + Q_ASSERT( a ); + a->setId(idx+1); //new ids + a->setIdRef(-1); + a->setThreadingLevel(0); + } + + buildThreads(length()); + saveStaticData(length(), true); + saveDynamicData(length(), true); + knGlobals.top->headerView()->repaint(); + knGlobals.setStatusMsg(QString::null); + knGlobals.top->setCursorBusy(false); +} + + +void KNGroup::updateThreadInfo() +{ + KNRemoteArticle *ref; + bool brokenThread=false; + + for(int idx=0; idx<length(); idx++) { + at(idx)->setUnreadFollowUps(0); + at(idx)->setNewFollowUps(0); + } + + for(int idx=0; idx<length(); idx++) { + int idRef=at(idx)->idRef(); + int tmpIdRef; + int iterCount=1; // control iteration count to avoid infinite loops + while((idRef!=0) && (iterCount <= length())) { + ref=byId(idRef); + if(!ref) { + brokenThread=true; + break; + } + + if(!at(idx)->isRead()) { + ref->incUnreadFollowUps(); + if(at(idx)->isNew()) ref->incNewFollowUps(); + } + tmpIdRef=ref->idRef(); + idRef= (idRef!=tmpIdRef) ? ref->idRef() : 0; + iterCount++; + } + if(iterCount > length()) + brokenThread=true; + if(brokenThread) break; + } + + if(brokenThread) { + kdWarning(5003) << "KNGroup::updateThreadInfo() : Found broken threading infos! Restoring ..." << endl; + reorganize(); + updateThreadInfo(); + } +} + + +void KNGroup::showProperties() +{ + if(!i_dentity) i_dentity=new KNConfig::Identity(false); + KNGroupPropDlg *d=new KNGroupPropDlg(this, knGlobals.topWidget); + + if(d->exec()) + if(d->nickHasChanged()) + l_istItem->setText(0, name()); + + if(i_dentity->isEmpty()) { + delete i_dentity; + i_dentity=0; + } + + delete d; +} + + +int KNGroup::statThrWithNew() +{ + int cnt=0; + for(int i=0; i<length(); i++) + if( (at(i)->idRef()==0) && (at(i)->hasNewFollowUps()) ) cnt++; + return cnt; +} + + +int KNGroup::statThrWithUnread() +{ + int cnt=0; + for(int i=0; i<length(); i++) + if( (at(i)->idRef()==0) && (at(i)->hasUnreadFollowUps()) ) cnt++; + return cnt; +} + +QString KNGroup::prepareForExecution() +{ + if (knGlobals.groupManager()->loadHeaders(this)) + return QString::null; + else + return i18n("Cannot load saved headers: %1").arg(groupname()); +} + +//*************************************************************************** + +void KNGroup::dynDataVer0::setData(KNRemoteArticle *a) +{ + id=a->id(); + idRef=a->idRef(); + thrLevel=a->threadingLevel(); + read=a->getReadFlag(); + score=a->score(); +} + + +void KNGroup::dynDataVer0::getData(KNRemoteArticle *a) +{ + a->setId(id); + a->setIdRef(idRef); + a->setRead(read); + a->setThreadingLevel(thrLevel); + a->setScore(score); +} + + +void KNGroup::dynDataVer1::setData(KNRemoteArticle *a) +{ + id=a->id(); + idRef=a->idRef(); + thrLevel=a->threadingLevel(); + read=a->getReadFlag(); + score=a->score(); + ignoredWatched = 0; + if (a->isWatched()) + ignoredWatched = 1; + else if (a->isIgnored()) + ignoredWatched = 2; +} + + +void KNGroup::dynDataVer1::getData(KNRemoteArticle *a) +{ + a->setId(id); + a->setIdRef(idRef); + a->setRead(read); + a->setThreadingLevel(thrLevel); + a->setScore(score); + a->setWatched(ignoredWatched==1); + a->setIgnored(ignoredWatched==2); +} + + +KNConfig::Cleanup * KNGroup::activeCleanupConfig() +{ + if (!cleanupConfig()->useDefault()) + return cleanupConfig(); + return account()->activeCleanupConfig(); +} |