/*
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 <stdio.h>
# include <stdlib.h>
# include <tqdir.h>
# include <tdelocale.h>
# include <tdemessagebox.h>
# include <kiconloader.h>
# include <kdebug.h>
# include <kcharsets.h>
# include "articlewidget.h"
# include "knmainwidget.h"
# include "knarticlemanager.h"
# include "kngroupdialog.h"
# include "knnntpaccount.h"
# include "knprotocolclient.h"
# include "kncleanup.h"
# include "knnetaccess.h"
# include "knglobals.h"
# include "knconfigmanager.h"
# include "resource.h"
# include "utilities.h"
# include "knarticlewindow.h"
# include "knmemorymanager.h"
using namespace KNode ;
//=================================================================================
// helper classes for the group selection dialog (getting the server's grouplist,
// getting recently created groups)
KNGroupInfo : : KNGroupInfo ( )
{
}
KNGroupInfo : : KNGroupInfo ( const TQString & n_ame , const TQString & d_escription , bool n_ewGroup , bool s_ubscribed , KNGroup : : Status s_tatus )
: name ( n_ame ) , description ( d_escription ) , newGroup ( n_ewGroup ) , subscribed ( s_ubscribed ) ,
status ( s_tatus )
{
}
KNGroupInfo : : ~ KNGroupInfo ( )
{
}
bool KNGroupInfo : : operator = = ( const KNGroupInfo & gi2 )
{
return ( name = = gi2 . name ) ;
}
bool KNGroupInfo : : operator < ( const KNGroupInfo & gi2 )
{
return ( name < gi2 . name ) ;
}
//===============================================================================
KNGroupListData : : KNGroupListData ( )
: codecForDescriptions ( 0 )
{
groups = new TQSortedList < KNGroupInfo > ;
groups - > setAutoDelete ( true ) ;
}
KNGroupListData : : ~ KNGroupListData ( )
{
delete groups ;
}
bool KNGroupListData : : readIn ( KNProtocolClient * client )
{
KNFile f ( path + " groups " ) ;
TQCString line ;
int sepPos1 , sepPos2 ;
TQString name , description ;
bool sub ;
KNGroup : : Status status = KNGroup : : unknown ;
TQTime timer ;
uint size = f . size ( ) + 2 ;
timer . start ( ) ;
if ( client ) client - > updatePercentage ( 0 ) ;
if ( f . open ( IO_ReadOnly ) ) {
while ( ! f . atEnd ( ) ) {
line = f . readLine ( ) ;
sepPos1 = line . find ( ' ' ) ;
if ( sepPos1 = = - 1 ) { // no description
name = TQString : : fromUtf8 ( line ) ;
description = TQString ( ) ;
status = KNGroup : : unknown ;
} else {
name = TQString : : fromUtf8 ( line . left ( sepPos1 ) ) ;
sepPos2 = line . find ( ' ' , sepPos1 + 1 ) ;
if ( sepPos2 = = - 1 ) { // no status
description = TQString : : fromUtf8 ( line . right ( line . length ( ) - sepPos1 - 1 ) ) ;
status = KNGroup : : unknown ;
} else {
description = TQString : : fromUtf8 ( line . right ( line . length ( ) - sepPos2 - 1 ) ) ;
switch ( line [ sepPos1 + 1 ] ) {
case ' u ' : status = KNGroup : : unknown ;
break ;
case ' n ' : status = KNGroup : : readOnly ;
break ;
case ' y ' : status = KNGroup : : postingAllowed ;
break ;
case ' m ' : status = KNGroup : : moderated ;
break ;
}
}
}
if ( subscribed . contains ( name ) ) {
subscribed . remove ( name ) ; // group names are unique, we wont find it again anyway...
sub = true ;
} else
sub = false ;
groups - > append ( new KNGroupInfo ( name , description , false , sub , status ) ) ;
if ( timer . elapsed ( ) > 200 ) { // don't flicker
timer . restart ( ) ;
if ( client ) client - > updatePercentage ( ( f . at ( ) * 100 ) / size ) ;
}
}
f . close ( ) ;
return true ;
} else {
kdWarning ( 5003 ) < < " unable to open " < < f . name ( ) < < " reason " < < f . status ( ) < < endl ;
return false ;
}
}
bool KNGroupListData : : writeOut ( )
{
TQFile f ( path + " groups " ) ;
TQCString temp ;
if ( f . open ( IO_WriteOnly ) ) {
for ( KNGroupInfo * i = groups - > first ( ) ; i ; i = groups - > next ( ) ) {
temp = i - > name . utf8 ( ) ;
switch ( i - > status ) {
case KNGroup : : unknown : temp + = " u " ;
break ;
case KNGroup : : readOnly : temp + = " n " ;
break ;
case KNGroup : : postingAllowed : temp + = " y " ;
break ;
case KNGroup : : moderated : temp + = " m " ;
break ;
}
temp + = i - > description . utf8 ( ) + " \n " ;
f . writeBlock ( temp . data ( ) , temp . length ( ) ) ;
}
f . close ( ) ;
return true ;
} else {
kdWarning ( 5003 ) < < " unable to open " < < f . name ( ) < < " reason " < < f . status ( ) < < endl ;
return false ;
}
}
// merge in new groups, we want to preserve the "subscribed"-flag
// of the loaded groups and the "new"-flag of the new groups.
void KNGroupListData : : merge ( TQSortedList < KNGroupInfo > * newGroups )
{
bool subscribed ;
for ( KNGroupInfo * i = newGroups - > first ( ) ; i ; i = newGroups - > next ( ) ) {
if ( groups - > find ( i ) > = 0 ) {
subscribed = groups - > current ( ) - > subscribed ;
groups - > remove ( ) ; // avoid duplicates
} else
subscribed = false ;
groups - > append ( new KNGroupInfo ( i - > name , i - > description , true , subscribed , i - > status ) ) ;
}
groups - > sort ( ) ;
}
TQSortedList < KNGroupInfo > * KNGroupListData : : extractList ( )
{
TQSortedList < KNGroupInfo > * temp = groups ;
groups = 0 ;
return temp ;
}
//===============================================================================
KNGroupManager : : KNGroupManager ( TQObject * parent , const char * name )
: TQObject ( parent , name )
{
c_urrentGroup = 0 ;
a_rticleMgr = knGlobals . articleManager ( ) ;
}
KNGroupManager : : ~ KNGroupManager ( )
{
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it )
delete ( * it ) ;
}
void KNGroupManager : : syncGroups ( )
{
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it ) {
( * it ) - > syncDynamicData ( ) ;
( * it ) - > saveInfo ( ) ;
}
}
void KNGroupManager : : loadGroups ( KNNntpAccount * a )
{
KNGroup * group ;
TQString dir ( a - > path ( ) ) ;
if ( dir . isNull ( ) )
return ;
TQDir d ( dir ) ;
TQStringList entries ( d . entryList ( " *.grpinfo " ) ) ;
for ( TQStringList : : Iterator it = entries . begin ( ) ; it ! = entries . end ( ) ; + + it ) {
group = new KNGroup ( a ) ;
if ( group - > readInfo ( dir + ( * it ) ) ) {
mGroupList . append ( group ) ;
emit groupAdded ( group ) ;
} else {
delete group ;
kdError ( 5003 ) < < " Unable to load " < < ( * it ) < < " ! " < < endl ;
}
}
}
void KNGroupManager : : getSubscribed ( KNNntpAccount * a , TQStringList & l )
{
l . clear ( ) ;
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it )
if ( ( * it ) - > account ( ) = = a )
l . append ( ( * it ) - > groupname ( ) ) ;
}
TQValueList < KNGroup * > KNGroupManager : : groupsOfAccount ( KNNntpAccount * a )
{
TQValueList < KNGroup * > ret ;
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it )
if ( ( * it ) - > account ( ) = = a )
ret . append ( ( * it ) ) ;
return ret ;
}
bool KNGroupManager : : loadHeaders ( KNGroup * g )
{
if ( ! g )
return false ;
if ( g - > isLoaded ( ) )
return true ;
// we want to delete old stuff first => reduce vm fragmentation
knGlobals . memoryManager ( ) - > prepareLoad ( g ) ;
if ( g - > loadHdrs ( ) ) {
knGlobals . memoryManager ( ) - > updateCacheEntry ( g ) ;
return true ;
}
return false ;
}
bool KNGroupManager : : unloadHeaders ( KNGroup * g , bool force )
{
if ( ! g | | g - > isLocked ( ) )
return false ;
if ( ! g - > isLoaded ( ) )
return true ;
if ( ! force & & ( c_urrentGroup = = g ) )
return false ;
if ( g - > unloadHdrs ( force ) )
knGlobals . memoryManager ( ) - > removeCacheEntry ( g ) ;
else
return false ;
return true ;
}
KNGroup * KNGroupManager : : group ( const TQString & gName , const KNServerInfo * s )
{
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it )
if ( ( * it ) - > account ( ) = = s & & ( * it ) - > groupname ( ) = = gName )
return ( * it ) ;
return 0 ;
}
KNGroup * KNGroupManager : : firstGroupOfAccount ( const KNServerInfo * s )
{
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it )
if ( ( * it ) - > account ( ) = = s )
return ( * it ) ;
return 0 ;
}
void KNGroupManager : : expireAll ( KNCleanUp * cup )
{
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it ) {
if ( ( * it ) - > isLocked ( ) | | ( * it ) - > lockedArticles ( ) > 0 )
continue ;
if ( ! ( * it ) - > activeCleanupConfig ( ) - > expireToday ( ) )
continue ;
cup - > appendCollection ( * ( it ) ) ;
}
}
void KNGroupManager : : expireAll ( KNNntpAccount * a )
{
KNCleanUp * cup = new KNCleanUp ( ) ;
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it ) {
if ( ( * it ) - > account ( ) ! = a | | ( * it ) - > isLocked ( ) | | ( * it ) - > lockedArticles ( ) > 0 )
continue ;
KNArticleWindow : : closeAllWindowsForCollection ( ( * it ) ) ;
cup - > appendCollection ( ( * it ) ) ;
}
cup - > start ( ) ;
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it ) {
if ( ( * it ) - > account ( ) ! = a | | ( * it ) - > isLocked ( ) | | ( * it ) - > lockedArticles ( ) > 0 )
continue ;
emit groupUpdated ( ( * it ) ) ;
if ( ( * it ) = = c_urrentGroup ) {
if ( loadHeaders ( ( * it ) ) )
a_rticleMgr - > showHdrs ( ) ;
else
a_rticleMgr - > setGroup ( 0 ) ;
}
}
delete cup ;
}
void KNGroupManager : : showGroupDialog ( KNNntpAccount * a , TQWidget * parent )
{
KNGroupDialog * gDialog = new KNGroupDialog ( ( parent ! = 0 ) ? parent : knGlobals . topWidget , a ) ;
connect ( gDialog , TQ_SIGNAL ( loadList ( KNNntpAccount * ) ) , this , TQ_SLOT ( slotLoadGroupList ( KNNntpAccount * ) ) ) ;
connect ( gDialog , TQ_SIGNAL ( fetchList ( KNNntpAccount * ) ) , this , TQ_SLOT ( slotFetchGroupList ( KNNntpAccount * ) ) ) ;
connect ( gDialog , TQ_SIGNAL ( checkNew ( KNNntpAccount * , TQDate ) ) , this , TQ_SLOT ( slotCheckForNewGroups ( KNNntpAccount * , TQDate ) ) ) ;
connect ( this , TQ_SIGNAL ( newListReady ( KNGroupListData * ) ) , gDialog , TQ_SLOT ( slotReceiveList ( KNGroupListData * ) ) ) ;
if ( gDialog - > exec ( ) ) {
KNGroup * g = 0 ;
TQStringList lst ;
gDialog - > toUnsubscribe ( & lst ) ;
if ( lst . count ( ) > 0 ) {
if ( KMessageBox : : Yes = = KMessageBox : : questionYesNoList ( ( parent ! = 0 ) ? parent : knGlobals . topWidget , i18n ( " Do you really want to unsubscribe \n from these groups? " ) ,
lst , TQString ( ) , i18n ( " Unsubscribe " ) , KStdGuiItem : : cancel ( ) ) ) {
for ( TQStringList : : Iterator it = lst . begin ( ) ; it ! = lst . end ( ) ; + + it ) {
if ( ( g = group ( * it , a ) ) )
unsubscribeGroup ( g ) ;
}
}
}
TQSortedList < KNGroupInfo > lst2 ;
gDialog - > toSubscribe ( & lst2 ) ;
for ( KNGroupInfo * var = lst2 . first ( ) ; var ; var = lst2 . next ( ) ) {
subscribeGroup ( var , a ) ;
}
}
delete gDialog ;
}
void KNGroupManager : : subscribeGroup ( const KNGroupInfo * gi , KNNntpAccount * a )
{
KNGroup * grp ;
grp = new KNGroup ( a ) ;
grp - > setGroupname ( gi - > name ) ;
grp - > setDescription ( gi - > description ) ;
grp - > setStatus ( gi - > status ) ;
grp - > saveInfo ( ) ;
mGroupList . append ( grp ) ;
emit groupAdded ( grp ) ;
}
bool KNGroupManager : : unsubscribeGroup ( KNGroup * g )
{
KNNntpAccount * acc ;
if ( ! g ) g = c_urrentGroup ;
if ( ! g ) return false ;
if ( ( g - > isLocked ( ) ) | | ( g - > lockedArticles ( ) > 0 ) ) {
KMessageBox : : sorry ( knGlobals . topWidget , i18n ( " The group \" %1 \" is being updated currently. \n It is not possible to unsubscribe from it at the moment. " ) . arg ( g - > groupname ( ) ) ) ;
return false ;
}
KNArticleWindow : : closeAllWindowsForCollection ( g ) ;
ArticleWidget : : collectionRemoved ( g ) ;
acc = g - > account ( ) ;
TQDir dir ( acc - > path ( ) , g - > groupname ( ) + " * " ) ;
if ( dir . exists ( ) ) {
if ( unloadHeaders ( g , true ) ) {
if ( c_urrentGroup = = g ) {
setCurrentGroup ( 0 ) ;
a_rticleMgr - > updateStatusString ( ) ;
}
const TQFileInfoList * list = dir . entryInfoList ( ) ; // get list of matching files and delete all
if ( list ) {
TQFileInfoListIterator it ( * list ) ;
while ( it . current ( ) ) {
if ( it . current ( ) - > fileName ( ) = = g - > groupname ( ) + " .dynamic " | |
it . current ( ) - > fileName ( ) = = g - > groupname ( ) + " .static " | |
it . current ( ) - > fileName ( ) = = g - > groupname ( ) + " .grpinfo " )
dir . remove ( it . current ( ) - > fileName ( ) ) ;
+ + it ;
}
}
kdDebug ( 5003 ) < < " Files deleted! " < < endl ;
emit groupRemoved ( g ) ;
mGroupList . remove ( g ) ;
delete g ;
return true ;
}
}
return false ;
}
void KNGroupManager : : showGroupProperties ( KNGroup * g )
{
if ( ! g ) g = c_urrentGroup ;
if ( ! g ) return ;
g - > showProperties ( ) ;
}
void KNGroupManager : : checkGroupForNewHeaders ( KNGroup * g )
{
if ( ! g ) g = c_urrentGroup ;
if ( ! g ) return ;
if ( g - > isLocked ( ) ) {
kdDebug ( 5003 ) < < " KNGroupManager::checkGroupForNewHeaders() : group locked - returning " < < endl ;
return ;
}
g - > setMaxFetch ( knGlobals . configManager ( ) - > readNewsGeneral ( ) - > maxToFetch ( ) ) ;
emitJob ( new KNJobData ( KNJobData : : JTfetchNewHeaders , this , g - > account ( ) , g ) ) ;
}
void KNGroupManager : : expireGroupNow ( KNGroup * g )
{
if ( ! g ) return ;
if ( ( g - > isLocked ( ) ) | | ( g - > lockedArticles ( ) > 0 ) ) {
KMessageBox : : sorry ( knGlobals . topWidget ,
i18n ( " This group cannot be expired because it is currently being updated. \n Please try again later. " ) ) ;
return ;
}
KNArticleWindow : : closeAllWindowsForCollection ( g ) ;
KNCleanUp cup ;
cup . expireGroup ( g , true ) ;
emit groupUpdated ( g ) ;
if ( g = = c_urrentGroup ) {
if ( loadHeaders ( g ) )
a_rticleMgr - > showHdrs ( ) ;
else
a_rticleMgr - > setGroup ( 0 ) ;
}
}
void KNGroupManager : : reorganizeGroup ( KNGroup * g )
{
if ( ! g ) g = c_urrentGroup ;
if ( ! g ) return ;
g - > reorganize ( ) ;
if ( g = = c_urrentGroup )
a_rticleMgr - > showHdrs ( ) ;
}
void KNGroupManager : : setCurrentGroup ( KNGroup * g )
{
c_urrentGroup = g ;
a_rticleMgr - > setGroup ( g ) ;
kdDebug ( 5003 ) < < " KNGroupManager::setCurrentGroup() : group changed " < < endl ;
if ( g ) {
if ( ! loadHeaders ( g ) ) {
//KMessageBox::error(knGlobals.topWidget, i18n("Cannot load saved headers"));
return ;
}
a_rticleMgr - > showHdrs ( ) ;
if ( knGlobals . configManager ( ) - > readNewsGeneral ( ) - > autoCheckGroups ( ) )
checkGroupForNewHeaders ( g ) ;
}
}
void KNGroupManager : : checkAll ( KNNntpAccount * a , bool silent )
{
if ( ! a ) return ;
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it ) {
if ( ( * it ) - > account ( ) = = a ) {
( * it ) - > setMaxFetch ( knGlobals . configManager ( ) - > readNewsGeneral ( ) - > maxToFetch ( ) ) ;
if ( silent )
emitJob ( new KNJobData ( KNJobData : : JTsilentFetchNewHeaders , this , ( * it ) - > account ( ) , ( * it ) ) ) ;
else
emitJob ( new KNJobData ( KNJobData : : JTfetchNewHeaders , this , ( * it ) - > account ( ) , ( * it ) ) ) ;
}
}
}
void KNGroupManager : : processJob ( KNJobData * j )
{
if ( ( j - > type ( ) = = KNJobData : : JTLoadGroups ) | | ( j - > type ( ) = = KNJobData : : JTFetchGroups ) | | ( j - > type ( ) = = KNJobData : : JTCheckNewGroups ) ) {
KNGroupListData * d = static_cast < KNGroupListData * > ( j - > data ( ) ) ;
if ( ! j - > canceled ( ) ) {
if ( j - > success ( ) ) {
if ( ( j - > type ( ) = = KNJobData : : JTFetchGroups ) | | ( j - > type ( ) = = KNJobData : : JTCheckNewGroups ) ) {
// update the descriptions of the subscribed groups
for ( TQValueList < KNGroup * > : : Iterator it = mGroupList . begin ( ) ; it ! = mGroupList . end ( ) ; + + it ) {
if ( ( * it ) - > account ( ) = = j - > account ( ) ) {
for ( KNGroupInfo * inf = d - > groups - > first ( ) ; inf ; inf = d - > groups - > next ( ) )
if ( inf - > name = = ( * it ) - > groupname ( ) ) {
( * it ) - > setDescription ( inf - > description ) ;
( * it ) - > setStatus ( inf - > status ) ;
break ;
}
}
}
}
emit ( newListReady ( d ) ) ;
} else {
KMessageBox : : error ( knGlobals . topWidget , j - > errorString ( ) ) ;
emit ( newListReady ( 0 ) ) ;
}
} else
emit ( newListReady ( 0 ) ) ;
delete j ;
delete d ;
} else { //KNJobData::JTfetchNewHeaders or KNJobData::JTsilentFetchNewHeaders
KNGroup * group = static_cast < KNGroup * > ( j - > data ( ) ) ;
if ( ! j - > canceled ( ) ) {
if ( j - > success ( ) ) {
if ( group - > lastFetchCount ( ) > 0 ) {
group - > scoreArticles ( ) ;
group - > processXPostBuffer ( true ) ;
emit groupUpdated ( group ) ;
group - > saveInfo ( ) ;
knGlobals . memoryManager ( ) - > updateCacheEntry ( group ) ;
}
} else {
// ok, hack (?):
// stop all other active fetch jobs, this prevents that
// we show multiple error dialogs if a server is unavailable
knGlobals . netAccess ( ) - > stopJobsNntp ( KNJobData : : JTfetchNewHeaders ) ;
knGlobals . netAccess ( ) - > stopJobsNntp ( KNJobData : : JTsilentFetchNewHeaders ) ;
if ( ! ( j - > type ( ) = = KNJobData : : JTsilentFetchNewHeaders ) ) {
KMessageBox : : error ( knGlobals . topWidget , j - > errorString ( ) ) ;
}
}
}
if ( group = = c_urrentGroup )
a_rticleMgr - > showHdrs ( false ) ;
delete j ;
}
}
// load group list from disk (if this fails: ask user if we should fetch the list)
void KNGroupManager : : slotLoadGroupList ( KNNntpAccount * a )
{
KNGroupListData * d = new KNGroupListData ( ) ;
d - > path = a - > path ( ) ;
if ( ! TQFileInfo ( d - > path + " groups " ) . exists ( ) ) {
if ( KMessageBox : : Yes = = KMessageBox : : questionYesNo ( knGlobals . topWidget , i18n ( " You do not have any groups for this account; \n do you want to fetch a current list? " ) , TQString ( ) , i18n ( " Fetch List " ) , i18n ( " Do Not Fetch " ) ) ) {
delete d ;
slotFetchGroupList ( a ) ;
return ;
} else {
emit ( newListReady ( d ) ) ;
delete d ;
return ;
}
}
getSubscribed ( a , d - > subscribed ) ;
d - > getDescriptions = a - > fetchDescriptions ( ) ;
emitJob ( new KNJobData ( KNJobData : : JTLoadGroups , this , a , d ) ) ;
}
// fetch group list from server
void KNGroupManager : : slotFetchGroupList ( KNNntpAccount * a )
{
KNGroupListData * d = new KNGroupListData ( ) ;
d - > path = a - > path ( ) ;
getSubscribed ( a , d - > subscribed ) ;
d - > getDescriptions = a - > fetchDescriptions ( ) ;
d - > codecForDescriptions = TDEGlobal : : charsets ( ) - > codecForName ( knGlobals . configManager ( ) - > postNewsTechnical ( ) - > charset ( ) ) ;
emitJob ( new KNJobData ( KNJobData : : JTFetchGroups , this , a , d ) ) ;
}
// check for new groups (created after the given date)
void KNGroupManager : : slotCheckForNewGroups ( KNNntpAccount * a , TQDate date )
{
KNGroupListData * d = new KNGroupListData ( ) ;
d - > path = a - > path ( ) ;
getSubscribed ( a , d - > subscribed ) ;
d - > getDescriptions = a - > fetchDescriptions ( ) ;
d - > fetchSince = date ;
d - > codecForDescriptions = TDEGlobal : : charsets ( ) - > codecForName ( knGlobals . configManager ( ) - > postNewsTechnical ( ) - > charset ( ) ) ;
emitJob ( new KNJobData ( KNJobData : : JTCheckNewGroups , this , a , d ) ) ;
}
//--------------------------------
# include "kngroupmanager.moc"