kmail

kmfoldertree.cpp

00001 // kmfoldertree.cpp
00002 #ifdef HAVE_CONFIG_H
00003 #include <config.h>
00004 #endif
00005 
00006 #include "kmfoldertree.h"
00007 
00008 #include "kmfoldermgr.h"
00009 #include "kmfolder.h"
00010 #include "kmfolderimap.h"
00011 #include "kmfoldercachedimap.h"
00012 #include "kmfolderdia.h"
00013 #include "kmmainwidget.h"
00014 #include "kmailicalifaceimpl.h"
00015 #include "accountmanager.h"
00016 using KMail::AccountManager;
00017 #include "globalsettings.h"
00018 #include "kmcommands.h"
00019 #include "foldershortcutdialog.h"
00020 #include "expirypropertiesdialog.h"
00021 #include "newfolderdialog.h"
00022 #include "acljobs.h"
00023 
00024 #include <maillistdrag.h>
00025 using namespace KPIM;
00026 
00027 #include <kapplication.h>
00028 #include <kglobalsettings.h>
00029 #include <kiconloader.h>
00030 #include <kmessagebox.h>
00031 #include <kconfig.h>
00032 #include <kpopupmenu.h>
00033 #include <kdebug.h>
00034 
00035 #include <qpainter.h>
00036 #include <qcursor.h>
00037 #include <qregexp.h>
00038 #include <qpopupmenu.h>
00039 
00040 #include <unistd.h>
00041 #include <assert.h>
00042 
00043 #include <X11/Xlib.h>
00044 #include <fixx11h.h>
00045 
00046 //=============================================================================
00047 
00048 KMFolderTreeItem::KMFolderTreeItem( KFolderTree *parent, const QString & name,
00049                                     KFolderTreeItem::Protocol protocol )
00050   : QObject( parent, name.latin1() ),
00051     KFolderTreeItem( parent, name, protocol, Root ),
00052     mFolder( 0 ), mNeedsRepaint( true )
00053 {
00054   init();
00055   setPixmap( 0, normalIcon() );
00056 }
00057 
00058 //-----------------------------------------------------------------------------
00059 KMFolderTreeItem::KMFolderTreeItem( KFolderTree *parent, const QString & name,
00060                     KMFolder* folder )
00061   : QObject( parent, name.latin1() ),
00062     KFolderTreeItem( parent, name ),
00063     mFolder( folder ), mNeedsRepaint( true )
00064 {
00065   init();
00066   setPixmap( 0, normalIcon() );
00067 }
00068 
00069 //-----------------------------------------------------------------------------
00070 KMFolderTreeItem::KMFolderTreeItem( KFolderTreeItem *parent, const QString & name,
00071                     KMFolder* folder )
00072   : QObject( 0, name.latin1() ),
00073     KFolderTreeItem( parent, name ),
00074     mFolder( folder ), mNeedsRepaint( true )
00075 {
00076   init();
00077   setPixmap( 0, normalIcon() );
00078 }
00079 
00080 KMFolderTreeItem::~KMFolderTreeItem()
00081 {
00082 }
00083 
00084 static KFolderTreeItem::Protocol protocolFor( KMFolderType t ) {
00085   switch ( t ) {
00086   case KMFolderTypeImap:
00087     return KFolderTreeItem::Imap;
00088   case KMFolderTypeCachedImap:
00089     return KFolderTreeItem::CachedImap;
00090   case KMFolderTypeMbox:
00091   case KMFolderTypeMaildir:
00092     return KFolderTreeItem::Local;
00093   case KMFolderTypeSearch:
00094     return KFolderTreeItem::Search;
00095   default:
00096     return KFolderTreeItem::NONE;
00097   }
00098 }
00099 
00100 QPixmap KMFolderTreeItem::normalIcon(int size) const
00101 {
00102   QString icon;
00103   if ( (!mFolder && type() == Root) || depth() == 0 ) {
00104     switch ( protocol() ) {
00105       case KFolderTreeItem::Imap:
00106       case KFolderTreeItem::CachedImap:
00107       case KFolderTreeItem::News:
00108         icon = "server"; break;
00109       case KFolderTreeItem::Search:
00110         icon = "viewmag";break;
00111       default:
00112         icon = "folder";break;
00113     }
00114   } else {
00115     // special folders
00116     switch ( type() ) {
00117       case Inbox: icon = "folder_inbox"; break;
00118       case Outbox: icon = "folder_outbox"; break;
00119       case SentMail: icon = "folder_sent_mail"; break;
00120       case Trash: icon = "trashcan_empty"; break;
00121       case Drafts: icon = "edit"; break;
00122       default: icon = kmkernel->iCalIface().folderPixmap( type() ); break;
00123     }
00124     // non-root search folders
00125     if ( protocol() == KMFolderTreeItem::Search ) {
00126       icon = "mail_find";
00127     }
00128     if ( mFolder && mFolder->noContent() ) {
00129       icon = "folder_grey";
00130     }
00131   }
00132 
00133   if ( icon.isEmpty() )
00134     icon = "folder";
00135 
00136   if (mFolder && mFolder->useCustomIcons() ) {
00137     icon = mFolder->normalIconPath();
00138   }
00139   KIconLoader * il = KGlobal::instance()->iconLoader();
00140   QPixmap pm = il->loadIcon( icon, KIcon::Small, size,
00141                              KIcon::DefaultState, 0, true );
00142   if ( pm.isNull() ) {
00143       pm = il->loadIcon( mFolder->normalIconPath(), KIcon::Small, size,
00144                          KIcon::DefaultState, 0, true );
00145   }
00146 
00147   return pm;
00148 }
00149 
00150 QPixmap KMFolderTreeItem::unreadIcon(int size) const
00151 {
00152   QPixmap pm;
00153 
00154   if ( !mFolder || depth() == 0 || mFolder->isSystemFolder()
00155     || kmkernel->folderIsTrash( mFolder )
00156     || kmkernel->folderIsDraftOrOutbox( mFolder ) )
00157     pm = normalIcon( size );
00158 
00159   KIconLoader * il = KGlobal::instance()->iconLoader();
00160   if ( mFolder->useCustomIcons() ) {
00161     pm = il->loadIcon( mFolder->unreadIconPath(), KIcon::Small, size,
00162                        KIcon::DefaultState, 0, true );
00163     if ( pm.isNull() )
00164       pm = il->loadIcon( mFolder->normalIconPath(), KIcon::Small, size,
00165                          KIcon::DefaultState, 0, true );
00166   }
00167   if ( pm.isNull() ) {
00168     if ( mFolder && mFolder->noContent() ) {
00169       pm = il->loadIcon( "folder_grey_open", KIcon::Small, size,
00170                          KIcon::DefaultState, 0, true );
00171     } else {
00172       pm = il->loadIcon( kmkernel->iCalIface().folderPixmap( type() ),
00173                          KIcon::Small, size, KIcon::DefaultState, 0, true );
00174       if ( pm.isNull() )
00175         pm = il->loadIcon( "folder_open", KIcon::Small, size,
00176                            KIcon::DefaultState, 0, true );
00177     }
00178   }
00179 
00180   return pm;
00181 }
00182 
00183 void KMFolderTreeItem::init()
00184 {
00185   if ( !mFolder )
00186     return;
00187 
00188   setProtocol( protocolFor( mFolder->folderType() ) );
00189 
00190   if ( depth() == 0 )
00191     setType(Root);
00192   else {
00193     if ( mFolder == kmkernel->inboxFolder() )
00194       setType( Inbox );
00195     else if ( kmkernel->folderIsDraftOrOutbox( mFolder ) ) {
00196       if ( mFolder == kmkernel->outboxFolder() )
00197         setType( Outbox );
00198       else
00199         setType( Drafts );
00200     }
00201     else if ( kmkernel->folderIsSentMailFolder( mFolder ) )
00202       setType( SentMail );
00203     else if ( kmkernel->folderIsTrash( mFolder ) )
00204       setType( Trash );
00205     else if( kmkernel->iCalIface().isResourceFolder(mFolder) )
00206       setType( kmkernel->iCalIface().folderType(mFolder) );
00207     // System folders on dimap or imap which are not resource folders are
00208     // inboxes. Urgs.
00209     if ( mFolder->isSystemFolder() &&
00210         !kmkernel->iCalIface().isResourceFolder( mFolder) &&
00211          ( mFolder->folderType() == KMFolderTypeImap
00212         || mFolder->folderType() == KMFolderTypeCachedImap ) )
00213       setType( Inbox );
00214   }
00215   if ( !mFolder->isSystemFolder() )
00216     setRenameEnabled( 0, false );
00217 
00218   KMFolderTree* tree = static_cast<KMFolderTree*>( listView() );
00219   tree->insertIntoFolderToItemMap( mFolder, this );
00220 }
00221 
00222 void KMFolderTreeItem::adjustUnreadCount( int newUnreadCount ) {
00223   // adjust the icons if the folder is now newly unread or
00224   // now newly not-unread
00225   if ( newUnreadCount != 0 && unreadCount() == 0 )
00226     setPixmap( 0, unreadIcon() );
00227   if ( unreadCount() != 0 && newUnreadCount == 0 )
00228     setPixmap( 0, normalIcon() );
00229 
00230   setUnreadCount( newUnreadCount );
00231 }
00232 
00233 void KMFolderTreeItem::slotIconsChanged()
00234 {
00235   kdDebug(5006) << k_funcinfo << endl;
00236   // this is prone to change, so better check
00237   if( kmkernel->iCalIface().isResourceFolder( mFolder ) )
00238       setType( kmkernel->iCalIface().folderType(mFolder) );
00239 
00240   if ( unreadCount() > 0 )
00241     setPixmap( 0, unreadIcon() );
00242   else
00243     setPixmap( 0, normalIcon() );
00244   emit iconChanged( this );
00245   repaint();
00246 }
00247 
00248 void KMFolderTreeItem::slotNameChanged()
00249 {
00250   setText( 0, mFolder->label() );
00251   emit nameChanged( this );
00252   repaint();
00253 }
00254 
00255 
00256 //-----------------------------------------------------------------------------
00257 bool KMFolderTreeItem::acceptDrag(QDropEvent*) const
00258 {
00259   if ( !mFolder || mFolder->isReadOnly() ||
00260       (mFolder->noContent() && childCount() == 0) ||
00261        (mFolder->noContent() && isOpen()) ) {
00262     return false;
00263   }
00264   else {
00265     return true;
00266   }
00267 }
00268 
00269 //-----------------------------------------------------------------------------
00270 void KMFolderTreeItem::slotShowExpiryProperties()
00271 {
00272   if ( !mFolder )
00273     return;
00274 
00275   KMFolderTree* tree = static_cast<KMFolderTree*>( listView() );
00276   KMail::ExpiryPropertiesDialog *dlg =
00277     new KMail::ExpiryPropertiesDialog( tree, mFolder );
00278   dlg->show();
00279 }
00280 
00281 
00282 //-----------------------------------------------------------------------------
00283 void KMFolderTreeItem::properties()
00284 {
00285   if ( !mFolder )
00286     return;
00287 
00288   KMFolderTree* tree = static_cast<KMFolderTree*>( listView() );
00289   tree->mainWidget()->modifyFolder( this );
00290   //Nothing here the above may actually delete this KMFolderTreeItem
00291 }
00292 
00293 //-----------------------------------------------------------------------------
00294 void KMFolderTreeItem::assignShortcut()
00295 {
00296   if ( !mFolder )
00297     return;
00298 
00299   KMail::FolderShortcutDialog *shorty =
00300     new KMail::FolderShortcutDialog( mFolder,
00301               static_cast<KMFolderTree *>( listView() )->mainWidget(),
00302               listView() );
00303   shorty->exec();
00304   return;
00305 }
00306 
00307 
00308 //=============================================================================
00309 
00310 
00311 KMFolderTree::KMFolderTree( KMMainWidget *mainWidget, QWidget *parent,
00312                             const char *name )
00313   : KFolderTree( parent, name )
00314 {
00315   oldSelected = 0;
00316   oldCurrent = 0;
00317   mLastItem = 0;
00318   mMainWidget = mainWidget;
00319   mReloading = false;
00320 
00321   mUpdateCountTimer= new QTimer( this );
00322 
00323   addAcceptableDropMimetype(MailListDrag::format(), false);
00324 
00325   int namecol = addColumn( i18n("Folder"), 250 );
00326   header()->setStretchEnabled( true, namecol );
00327 
00328   // connect
00329   connectSignals();
00330 
00331   // popup to switch columns
00332   header()->setClickEnabled(true);
00333   header()->installEventFilter(this);
00334   mPopup = new KPopupMenu(this);
00335   mPopup->insertTitle(i18n("View Columns"));
00336   mPopup->setCheckable(true);
00337   mUnreadPop = mPopup->insertItem(i18n("Unread Column"), this, SLOT(slotToggleUnreadColumn()));
00338   mTotalPop = mPopup->insertItem(i18n("Total Column"), this, SLOT(slotToggleTotalColumn()));
00339 }
00340 
00341 //-----------------------------------------------------------------------------
00342 // connects all needed signals to their slots
00343 void KMFolderTree::connectSignals()
00344 {
00345   connect( mUpdateCountTimer, SIGNAL(timeout()),
00346           this, SLOT(slotUpdateCountTimeout()) );
00347 
00348   connect(&mUpdateTimer, SIGNAL(timeout()),
00349           this, SLOT(delayedUpdate()));
00350 
00351   connect(kmkernel->folderMgr(), SIGNAL(changed()),
00352           this, SLOT(doFolderListChanged()));
00353 
00354   connect(kmkernel->folderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00355           this, SLOT(slotFolderRemoved(KMFolder*)));
00356 
00357   connect(kmkernel->imapFolderMgr(), SIGNAL(changed()),
00358           this, SLOT(doFolderListChanged()));
00359 
00360   connect(kmkernel->imapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00361           this, SLOT(slotFolderRemoved(KMFolder*)));
00362 
00363   connect(kmkernel->dimapFolderMgr(), SIGNAL(changed()),
00364           this, SLOT(doFolderListChanged()));
00365 
00366   connect(kmkernel->dimapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00367           this, SLOT(slotFolderRemoved(KMFolder*)));
00368 
00369   connect(kmkernel->searchFolderMgr(), SIGNAL(changed()),
00370           this, SLOT(doFolderListChanged()));
00371 
00372   connect(kmkernel->acctMgr(), SIGNAL(accountRemoved(KMAccount*)),
00373           this, SLOT(slotAccountRemoved(KMAccount*)));
00374 
00375   connect(kmkernel->searchFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00376           this, SLOT(slotFolderRemoved(KMFolder*)));
00377 
00378   connect( &autoopen_timer, SIGNAL( timeout() ),
00379            this, SLOT( openFolder() ) );
00380 
00381   connect( this, SIGNAL( contextMenuRequested( QListViewItem*, const QPoint &, int ) ),
00382            this, SLOT( slotContextMenuRequested( QListViewItem*, const QPoint & ) ) );
00383 
00384   connect( this, SIGNAL( expanded( QListViewItem* ) ),
00385            this, SLOT( slotFolderExpanded( QListViewItem* ) ) );
00386 
00387   connect( this, SIGNAL( collapsed( QListViewItem* ) ),
00388            this, SLOT( slotFolderCollapsed( QListViewItem* ) ) );
00389 
00390   connect( this, SIGNAL( itemRenamed( QListViewItem*, int, const QString &)),
00391            this, SLOT( slotRenameFolder( QListViewItem*, int, const QString &)));
00392 }
00393 
00394 //-----------------------------------------------------------------------------
00395 bool KMFolderTree::event(QEvent *e)
00396 {
00397   if (e->type() == QEvent::ApplicationPaletteChange)
00398   {
00399      readColorConfig();
00400      return true;
00401   }
00402   return KListView::event(e);
00403 }
00404 
00405 //-----------------------------------------------------------------------------
00406 void KMFolderTree::readColorConfig (void)
00407 {
00408   KConfig* conf = KMKernel::config();
00409   // Custom/System color support
00410   KConfigGroupSaver saver(conf, "Reader");
00411   QColor c1=QColor(kapp->palette().active().text());
00412   QColor c2=QColor("blue");
00413   QColor c4=QColor(kapp->palette().active().base());
00414 
00415   if (!conf->readBoolEntry("defaultColors",TRUE)) {
00416     mPaintInfo.colFore = conf->readColorEntry("ForegroundColor",&c1);
00417     mPaintInfo.colUnread = conf->readColorEntry("UnreadMessage",&c2);
00418     mPaintInfo.colBack = conf->readColorEntry("BackgroundColor",&c4);
00419   }
00420   else {
00421     mPaintInfo.colFore = c1;
00422     mPaintInfo.colUnread = c2;
00423     mPaintInfo.colBack = c4;
00424   }
00425   QPalette newPal = kapp->palette();
00426   newPal.setColor( QColorGroup::Base, mPaintInfo.colBack );
00427   newPal.setColor( QColorGroup::Text, mPaintInfo.colFore );
00428   setPalette( newPal );
00429 }
00430 
00431 //-----------------------------------------------------------------------------
00432 void KMFolderTree::readConfig (void)
00433 {
00434   KConfig* conf = KMKernel::config();
00435 
00436   readColorConfig();
00437 
00438   // Custom/Ssystem font support
00439   {
00440     KConfigGroupSaver saver(conf, "Fonts");
00441     if (!conf->readBoolEntry("defaultFonts",TRUE)) {
00442       QFont folderFont( KGlobalSettings::generalFont() );
00443       setFont(conf->readFontEntry("folder-font", &folderFont));
00444     }
00445     else
00446       setFont(KGlobalSettings::generalFont());
00447   }
00448 
00449   // restore the layout
00450   restoreLayout(conf, "Geometry");
00451 }
00452 
00453 //-----------------------------------------------------------------------------
00454 // Save the configuration file
00455 void KMFolderTree::writeConfig()
00456 {
00457   // save the current state of the folders
00458   for ( QListViewItemIterator it( this ) ; it.current() ; ++it ) {
00459     KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00460     if (fti)
00461       writeIsListViewItemOpen(fti);
00462   }
00463 
00464   // save the current layout
00465   saveLayout(KMKernel::config(), "Geometry");
00466 }
00467 
00468 //-----------------------------------------------------------------------------
00469 // Updates the count of unread messages (count of unread messages
00470 // is now cached in KMails config file)
00471 void KMFolderTree::updateUnreadAll()
00472 {
00473   bool upd = isUpdatesEnabled();
00474   setUpdatesEnabled(FALSE);
00475 
00476   KMFolderDir* fdir;
00477   KMFolderNode* folderNode;
00478   KMFolder* folder;
00479 
00480   fdir = &kmkernel->folderMgr()->dir();
00481   for (folderNode = fdir->first();
00482     folderNode != 0;
00483     folderNode =fdir->next())
00484   {
00485     if (!folderNode->isDir()) {
00486       folder = static_cast<KMFolder*>(folderNode);
00487 
00488       folder->open();
00489       folder->countUnread();
00490       folder->close();
00491     }
00492   }
00493 
00494   setUpdatesEnabled(upd);
00495 }
00496 
00497 //-----------------------------------------------------------------------------
00498 // Reload the tree of items in the list view
00499 void KMFolderTree::reload(bool openFolders)
00500 {
00501   if ( mReloading ) {
00502     // no parallel reloads are allowed
00503     kdDebug(5006) << "KMFolderTree::reload - already reloading" << endl;
00504     return;
00505   }
00506   mReloading = true;
00507 
00508   int top = contentsY();
00509   mLastItem = 0;
00510   // invalidate selected drop item
00511   oldSelected = 0;
00512   // remember last
00513   KMFolder* last = currentFolder();
00514   KMFolder* selected = 0;
00515   KMFolder* oldCurrentFolder =
00516     ( oldCurrent ? static_cast<KMFolderTreeItem*>(oldCurrent)->folder(): 0 );
00517   for ( QListViewItemIterator it( this ) ; it.current() ; ++it ) {
00518     KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current());
00519     writeIsListViewItemOpen( fti );
00520     if ( fti->isSelected() )
00521       selected = fti->folder();
00522   }
00523   mFolderToItem.clear();
00524   clear();
00525 
00526   // construct the root of the local folders
00527   KMFolderTreeItem * root = new KMFolderTreeItem( this, i18n("Local Folders") );
00528   root->setOpen( readIsListViewItemOpen(root) );
00529 
00530   KMFolderDir * fdir = &kmkernel->folderMgr()->dir();
00531   addDirectory(fdir, root);
00532 
00533   fdir = &kmkernel->imapFolderMgr()->dir();
00534   // each imap-account creates it's own root
00535   addDirectory(fdir, 0);
00536 
00537   fdir = &kmkernel->dimapFolderMgr()->dir();
00538   // each dimap-account creates it's own root
00539   addDirectory(fdir, 0);
00540 
00541   // construct the root of the search folder hierarchy:
00542   root = new KMFolderTreeItem( this, i18n("Searches"), KFolderTreeItem::Search );
00543   root->setOpen( readIsListViewItemOpen( root ) );
00544 
00545   fdir = &kmkernel->searchFolderMgr()->dir();
00546   addDirectory(fdir, root);
00547 
00548   if (openFolders)
00549   {
00550     // we open all folders to update the count
00551     mUpdateIterator = QListViewItemIterator (this);
00552     QTimer::singleShot( 0, this, SLOT(slotUpdateOneCount()) );
00553   }
00554 
00555   for ( QListViewItemIterator it( this ) ; it.current() ; ++it ) {
00556     KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current());
00557     if ( !fti || !fti->folder() )
00558       continue;
00559 
00560     disconnect(fti->folder(),SIGNAL(iconsChanged()),
00561                fti,SLOT(slotIconsChanged()));
00562     connect(fti->folder(),SIGNAL(iconsChanged()),
00563             fti,SLOT(slotIconsChanged()));
00564 
00565     disconnect(fti->folder(),SIGNAL(nameChanged()),
00566                fti,SLOT(slotNameChanged()));
00567     connect(fti->folder(),SIGNAL(nameChanged()),
00568             fti,SLOT(slotNameChanged()));
00569 
00570     // With the use of slotUpdateCountsDelayed is not necesary
00571     // a specific processing for Imap
00572 #if 0
00573     if (fti->folder()->folderType() == KMFolderTypeImap) {
00574       // imap-only
00575       KMFolderImap *imapFolder =
00576         dynamic_cast<KMFolderImap*> ( fti->folder()->storage() );
00577       disconnect( imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00578           this,SLOT(slotUpdateCounts(KMFolderImap*, bool)));
00579       connect( imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00580           this,SLOT(slotUpdateCounts(KMFolderImap*, bool)));
00581     } else {*/
00582 #endif
00583 
00584     // we want to be noticed of changes to update the unread/total columns
00585     disconnect(fti->folder(), SIGNAL(msgAdded(KMFolder*,Q_UINT32)),
00586         this,SLOT(slotUpdateCountsDelayed(KMFolder*)));
00587     connect(fti->folder(), SIGNAL(msgAdded(KMFolder*,Q_UINT32)),
00588         this,SLOT(slotUpdateCountsDelayed(KMFolder*)));
00589     //}
00590 
00591     disconnect(fti->folder(), SIGNAL(numUnreadMsgsChanged(KMFolder*)),
00592                this,SLOT(slotUpdateCountsDelayed(KMFolder*)));
00593     connect(fti->folder(), SIGNAL(numUnreadMsgsChanged(KMFolder*)),
00594             this,SLOT(slotUpdateCountsDelayed(KMFolder*)));
00595     disconnect(fti->folder(), SIGNAL(msgRemoved(KMFolder*)),
00596                this,SLOT(slotUpdateCountsDelayed(KMFolder*)));
00597     connect(fti->folder(), SIGNAL(msgRemoved(KMFolder*)),
00598             this,SLOT(slotUpdateCountsDelayed(KMFolder*)));
00599 
00600     disconnect(fti->folder(), SIGNAL(shortcutChanged(KMFolder*)),
00601                mMainWidget, SLOT( slotShortcutChanged(KMFolder*)));
00602     connect(fti->folder(), SIGNAL(shortcutChanged(KMFolder*)),
00603             mMainWidget, SLOT( slotShortcutChanged(KMFolder*)));
00604 
00605     if (!openFolders)
00606       slotUpdateCounts(fti->folder());
00607   }
00608   ensureVisible(0, top + visibleHeight(), 0, 0);
00609   // if current and selected folder did not change set it again
00610   for ( QListViewItemIterator it( this ) ; it.current() ; ++it )
00611   {
00612     if ( last &&
00613          static_cast<KMFolderTreeItem*>( it.current() )->folder() == last )
00614     {
00615       mLastItem = static_cast<KMFolderTreeItem*>( it.current() );
00616       setCurrentItem( it.current() );
00617     }
00618     if ( selected &&
00619          static_cast<KMFolderTreeItem*>( it.current() )->folder() == selected )
00620     {
00621       setSelected( it.current(), true );
00622     }
00623     if ( oldCurrentFolder &&
00624          static_cast<KMFolderTreeItem*>( it.current() )->folder() == oldCurrentFolder )
00625     {
00626       oldCurrent = it.current();
00627     }
00628   }
00629   refresh();
00630   mReloading = false;
00631 }
00632 
00633 //-----------------------------------------------------------------------------
00634 void KMFolderTree::slotUpdateOneCount()
00635 {
00636   if ( !mUpdateIterator.current() ) return;
00637   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(mUpdateIterator.current());
00638   ++mUpdateIterator;
00639   if ( !fti->folder() ) {
00640     // next one please
00641     QTimer::singleShot( 0, this, SLOT(slotUpdateOneCount()) );
00642     return;
00643   }
00644 
00645   // open the folder and update the count
00646   bool open = fti->folder()->isOpened();
00647   if (!open) fti->folder()->open();
00648   slotUpdateCounts(fti->folder());
00649   // restore previous state
00650   if (!open) fti->folder()->close();
00651 
00652   QTimer::singleShot( 0, this, SLOT(slotUpdateOneCount()) );
00653 }
00654 
00655 //-----------------------------------------------------------------------------
00656 // Recursively add a directory of folders to the tree of folders
00657 void KMFolderTree::addDirectory( KMFolderDir *fdir, KMFolderTreeItem* parent )
00658 {
00659   for ( KMFolderNode * node = fdir->first() ; node ; node = fdir->next() ) {
00660     if ( node->isDir() )
00661       continue;
00662 
00663     KMFolder * folder = static_cast<KMFolder*>(node);
00664     KMFolderTreeItem * fti = 0;
00665     if (!parent)
00666     {
00667       // create new root-item
00668       // it needs a folder e.g. to save it's state (open/close)
00669       fti = new KMFolderTreeItem( this, folder->label(), folder );
00670       fti->setExpandable( true );
00671     } else {
00672       // Check if this is an IMAP resource folder
00673       if ( kmkernel->iCalIface().hideResourceFolder( folder ) )
00674         // It is
00675         continue;
00676 
00677       // create new child
00678       fti = new KMFolderTreeItem( parent, folder->label(), folder );
00679       // set folders explicitely to exandable when they have children
00680       // this way we can do a listing for IMAP folders when the user expands them
00681       // even when the child folders are not created yet
00682       if ( folder->storage()->hasChildren() == FolderStorage::HasChildren ) {
00683         fti->setExpandable( true );
00684       } else {
00685         fti->setExpandable( false );
00686       }
00687 
00688       connect (fti, SIGNAL(iconChanged(KMFolderTreeItem*)),
00689           this, SIGNAL(iconChanged(KMFolderTreeItem*)));
00690       connect (fti, SIGNAL(nameChanged(KMFolderTreeItem*)),
00691           this, SIGNAL(nameChanged(KMFolderTreeItem*)));
00692 
00693     }
00694     // restore last open-state
00695     fti->setOpen( readIsListViewItemOpen(fti) );
00696 
00697     // add child-folders
00698     if (folder && folder->child()) {
00699       addDirectory( folder->child(), fti );
00700     }
00701    } // for-end
00702 }
00703 
00704 //-----------------------------------------------------------------------------
00705 // Initiate a delayed refresh of the tree
00706 void KMFolderTree::refresh()
00707 {
00708   mUpdateTimer.changeInterval(200);
00709 }
00710 
00711 //-----------------------------------------------------------------------------
00712 // Updates the pixmap and extendedLabel information for items
00713 void KMFolderTree::delayedUpdate()
00714 {
00715   bool upd = isUpdatesEnabled();
00716   if ( upd ) {
00717     setUpdatesEnabled(FALSE);
00718 
00719     for ( QListViewItemIterator it( this ) ; it.current() ; ++it ) {
00720       KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00721       if (!fti || !fti->folder())
00722         continue;
00723 
00724       if ( fti->needsRepaint() ) {
00725         fti->repaint();
00726         fti->setNeedsRepaint( false );
00727       }
00728     }
00729     setUpdatesEnabled(upd);
00730   }
00731   mUpdateTimer.stop();
00732 }
00733 
00734 //-----------------------------------------------------------------------------
00735 // Folders have been added/deleted update the tree of folders
00736 void KMFolderTree::doFolderListChanged()
00737 {
00738   reload();
00739 }
00740 
00741 //-----------------------------------------------------------------------------
00742 void KMFolderTree::slotAccountRemoved(KMAccount *)
00743 {
00744   doFolderSelected( firstChild() );
00745 }
00746 
00747 //-----------------------------------------------------------------------------
00748 void KMFolderTree::slotFolderRemoved(KMFolder *aFolder)
00749 {
00750   KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>
00751     (indexOfFolder(aFolder));
00752   if (!fti || !fti->folder()) return;
00753   if (fti == currentItem())
00754   {
00755     QListViewItem *qlvi = fti->itemAbove();
00756     if (!qlvi) qlvi = fti->itemBelow();
00757     doFolderSelected( qlvi );
00758   }
00759   removeFromFolderToItemMap( aFolder );
00760   delete fti;
00761 }
00762 
00763 //-----------------------------------------------------------------------------
00764 // Methods for navigating folders with the keyboard
00765 void KMFolderTree::prepareItem( KMFolderTreeItem* fti )
00766 {
00767   for ( QListViewItem * parent = fti->parent() ; parent ; parent = parent->parent() )
00768     parent->setOpen( TRUE );
00769   ensureItemVisible( fti );
00770 }
00771 
00772 //-----------------------------------------------------------------------------
00773 void KMFolderTree::nextUnreadFolder()
00774 {
00775     nextUnreadFolder( false );
00776 }
00777 
00778 //-----------------------------------------------------------------------------
00779 void KMFolderTree::nextUnreadFolder(bool confirm)
00780 {
00781   QListViewItemIterator it( currentItem() ? currentItem() : firstChild() );
00782   if ( currentItem() )
00783     ++it; // don't find current item
00784   for ( ; it.current() ; ++it ) {
00785     //check if folder is one to stop on
00786     KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00787     if (checkUnreadFolder(fti,confirm)) return;
00788   }
00789   //Now if confirm is true we are doing "ReadOn"
00790   //we have got to the bottom of the folder list
00791   //so we have to start at the top
00792   if (confirm) {
00793     for ( it = firstChild() ; it.current() ; ++it ) {
00794       //check if folder is one to stop on
00795       KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00796       if (checkUnreadFolder(fti,confirm)) return;
00797     }
00798   }
00799 }
00800 
00801 //-----------------------------------------------------------------------------
00802 bool KMFolderTree::checkUnreadFolder (KMFolderTreeItem* fti, bool confirm)
00803 {
00804   if ( fti && fti->folder() && !fti->folder()->ignoreNewMail() &&
00805        ( fti->folder()->countUnread() > 0 ) ) {
00806 
00807     // Don't change into the trash or outbox folders.
00808     if (fti->type() == KFolderTreeItem::Trash ||
00809         fti->type() == KFolderTreeItem::Outbox )
00810       return false;
00811 
00812     if (confirm) {
00813       // Skip drafts and sent mail as well, when reading mail with the space bar
00814       // but not when changing into the next folder with unread mail via ctrl+ or
00815       // ctrl- so we do this only if (confirm == true), which means we are doing
00816       // readOn.
00817       if ( fti->type() == KFolderTreeItem::Drafts ||
00818            fti->type() == KFolderTreeItem::SentMail )
00819         return false;
00820 
00821       //  warn user that going to next folder - but keep track of
00822       //  whether he wishes to be notified again in "AskNextFolder"
00823       //  parameter (kept in the config file for kmail)
00824       if ( KMessageBox::questionYesNo( this,
00825             i18n( "<qt>Go to the next unread message in folder <b>%1</b>?</qt>" )
00826             .arg( fti->folder()->label() ),
00827             i18n( "Go to Next Unread Message" ),
00828             i18n("Go To"), i18n("Do Not Go To"), // defaults
00829             "AskNextFolder",
00830             false)
00831           == KMessageBox::No ) return true;
00832     }
00833     prepareItem( fti );
00834     blockSignals( true );
00835     doFolderSelected( fti );
00836     blockSignals( false );
00837     emit folderSelectedUnread( fti->folder() );
00838     return true;
00839   }
00840   return false;
00841 }
00842 
00843 //-----------------------------------------------------------------------------
00844 void KMFolderTree::prevUnreadFolder()
00845 {
00846   QListViewItemIterator it( currentItem() ? currentItem() : lastItem() );
00847   if ( currentItem() )
00848     --it; // don't find current item
00849   for ( ; it.current() ; --it ) {
00850     KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00851     if (checkUnreadFolder(fti,false)) return;
00852   }
00853 }
00854 
00855 //-----------------------------------------------------------------------------
00856 void KMFolderTree::incCurrentFolder()
00857 {
00858   QListViewItemIterator it( currentItem() );
00859   ++it;
00860   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00861   if (fti) {
00862       prepareItem( fti );
00863       setFocus();
00864       setCurrentItem( fti );
00865   }
00866 }
00867 
00868 //-----------------------------------------------------------------------------
00869 void KMFolderTree::decCurrentFolder()
00870 {
00871   QListViewItemIterator it( currentItem() );
00872   --it;
00873   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current());
00874   if (fti) {
00875       prepareItem( fti );
00876       setFocus();
00877       setCurrentItem( fti );
00878   }
00879 }
00880 
00881 //-----------------------------------------------------------------------------
00882 void KMFolderTree::selectCurrentFolder()
00883 {
00884   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( currentItem() );
00885   if (fti) {
00886       prepareItem( fti );
00887       doFolderSelected( fti );
00888   }
00889 }
00890 
00891 //-----------------------------------------------------------------------------
00892 KMFolder *KMFolderTree::currentFolder() const
00893 {
00894     KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( currentItem() );
00895     if (fti )
00896         return fti->folder();
00897     else
00898         return 0;
00899 }
00900 
00901 //-----------------------------------------------------------------------------
00902 // When not dragging and dropping a change in the selected item
00903 // indicates the user has changed the active folder emit a signal
00904 // so that the header list and reader window can be udpated.
00905 void KMFolderTree::doFolderSelected( QListViewItem* qlvi )
00906 {
00907   if (!qlvi) return;
00908   if ( mLastItem && mLastItem == qlvi )
00909     return;
00910 
00911   KMFolderTreeItem* fti = static_cast< KMFolderTreeItem* >(qlvi);
00912   KMFolder* folder = 0;
00913   if (fti) folder = fti->folder();
00914 
00915 
00916   if (mLastItem && mLastItem != fti && mLastItem->folder()
00917      && (mLastItem->folder()->folderType() == KMFolderTypeImap))
00918   {
00919     KMFolderImap *imapFolder = static_cast<KMFolderImap*>(mLastItem->folder()->storage());
00920     imapFolder->setSelected(FALSE);
00921   }
00922   mLastItem = fti;
00923 
00924   clearSelection();
00925   setCurrentItem( qlvi );
00926   setSelected( qlvi, TRUE );
00927   ensureItemVisible( qlvi );
00928   if (!folder) {
00929     emit folderSelected(0); // Root has been selected
00930   }
00931   else {
00932     emit folderSelected(folder);
00933     slotUpdateCounts(folder);
00934   }
00935 }
00936 
00937 //-----------------------------------------------------------------------------
00938 void KMFolderTree::resizeEvent(QResizeEvent* e)
00939 {
00940   KConfig* conf = KMKernel::config();
00941 
00942   KConfigGroupSaver saver(conf, "Geometry");
00943   conf->writeEntry(name(), size().width());
00944 
00945   KListView::resizeEvent(e);
00946 }
00947 
00948 //-----------------------------------------------------------------------------
00949 // show context menu
00950 void KMFolderTree::slotContextMenuRequested( QListViewItem *lvi,
00951                                              const QPoint &p )
00952 {
00953   if (!lvi)
00954     return;
00955   setCurrentItem( lvi );
00956   setSelected( lvi, TRUE );
00957 
00958   if (!mMainWidget) return; // safe bet
00959 
00960   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(lvi);
00961   if ( fti != mLastItem )
00962     doFolderSelected( fti );
00963 
00964   if (!fti )
00965     return;
00966 
00967   KPopupMenu *folderMenu = new KPopupMenu;
00968   if (fti->folder()) folderMenu->insertTitle(fti->folder()->label());
00969 
00970   // outbox specific, but there it's the most used action
00971   if ( (fti->folder() == kmkernel->outboxFolder()) && fti->folder()->count() )
00972         mMainWidget->action("send_queued")->plug( folderMenu );
00973   // Mark all as read is supposedly used often, therefor it is first
00974   if ( fti->folder() && !fti->folder()->noContent() )
00975       mMainWidget->action("mark_all_as_read")->plug( folderMenu );
00976 
00977   /* Treat the special case of the root and account folders */
00978   if ((!fti->folder() || (fti->folder()->noContent()
00979     && !fti->parent())))
00980   {
00981     QString createChild = i18n("&New Subfolder...");
00982     if (!fti->folder()) createChild = i18n("&New Folder...");
00983 
00984     if (fti->folder() || (fti->text(0) != i18n("Searches")))
00985         folderMenu->insertItem(SmallIconSet("folder_new"),
00986                                createChild, this,
00987                                SLOT(addChildFolder()));
00988 
00989     if (!fti->folder()) {
00990       mMainWidget->action("compact_all_folders")->plug(folderMenu);
00991       mMainWidget->action("expire_all_folders")->plug(folderMenu);
00992     } else if (fti->folder()->folderType() == KMFolderTypeImap) {
00993       folderMenu->insertItem(SmallIconSet("mail_get"), i18n("Check &Mail"),
00994         this,
00995         SLOT(slotCheckMail()));
00996     }
00997   } else { // regular folders
00998 
00999     folderMenu->insertSeparator();
01000     if ( !fti->folder()->noChildren() ) {
01001       folderMenu->insertItem(SmallIconSet("folder_new"),
01002                              i18n("&New Subfolder..."), this,
01003                              SLOT(addChildFolder()));
01004     }
01005 
01006     if ( fti->folder()->isMoveable() )
01007     {
01008       QPopupMenu *moveMenu = new QPopupMenu( folderMenu );
01009       folderToPopupMenu( MoveFolder, this, &mMenuToFolder, moveMenu );
01010       folderMenu->insertItem( i18n("&Move Folder To"), moveMenu );
01011     }
01012 
01013     // Want to be able to display properties for ALL folders,
01014     // so we can edit expiry properties.
01015     // -- smp.
01016     if (!fti->folder()->noContent())
01017     {
01018       mMainWidget->action("search_messages")->plug(folderMenu);
01019 
01020       mMainWidget->action("compact")->plug(folderMenu);
01021 
01022       folderMenu->insertSeparator();
01023       mMainWidget->action("empty")->plug(folderMenu);
01024       folderMenu->insertSeparator();
01025     }
01026   }
01027 
01028   /* plug in IMAP and DIMAP specific things */
01029   if (fti->folder() &&
01030       (fti->folder()->folderType() == KMFolderTypeImap ||
01031        fti->folder()->folderType() == KMFolderTypeCachedImap ))
01032   {
01033     folderMenu->insertItem(SmallIconSet("bookmark_folder"),
01034         i18n("Subscription..."), mMainWidget,
01035         SLOT(slotSubscriptionDialog()));
01036 
01037     if (!fti->folder()->noContent())
01038     {
01039       mMainWidget->action("refresh_folder")->plug(folderMenu);
01040       if ( fti->folder()->folderType() == KMFolderTypeImap ) {
01041         folderMenu->insertItem(SmallIconSet("reload"), i18n("Refresh Folder List"), this,
01042             SLOT(slotResetFolderList()));
01043       }
01044     }
01045     if ( fti->folder()->folderType() == KMFolderTypeCachedImap ) {
01046       KMFolderCachedImap * folder = static_cast<KMFolderCachedImap*>( fti->folder()->storage() );
01047       folderMenu->insertItem( SmallIconSet("wizard"),
01048                               i18n("&Troubleshoot IMAP Cache..."),
01049                               folder, SLOT(slotTroubleshoot()) );
01050     }
01051     folderMenu->insertSeparator();
01052   }
01053 
01054   if ( fti->folder() && fti->folder()->isMailingListEnabled() ) {
01055     mMainWidget->action("post_message")->plug(folderMenu);
01056   }
01057 
01058   if (fti->folder() && fti->parent())
01059   {
01060     folderMenu->insertItem(SmallIconSet("configure_shortcuts"),
01061         i18n("&Assign Shortcut..."),
01062         fti,
01063         SLOT(assignShortcut()));
01064 
01065     if ( !fti->folder()->noContent() ) {
01066       folderMenu->insertItem( i18n("Expire..."), fti,
01067                               SLOT( slotShowExpiryProperties() ) );
01068     }
01069     mMainWidget->action("modify")->plug(folderMenu);
01070   }
01071 
01072 
01073   kmkernel->setContextMenuShown( true );
01074   folderMenu->exec (p, 0);
01075   kmkernel->setContextMenuShown( false );
01076   triggerUpdate();
01077   delete folderMenu;
01078   folderMenu = 0;
01079 }
01080 
01081 //-----------------------------------------------------------------------------
01082 // If middle button and folder holds mailing-list, create a message to that list
01083 void KMFolderTree::contentsMouseReleaseEvent(QMouseEvent* me)
01084 {
01085   QListViewItem *lvi = currentItem(); // Needed for when branches are clicked on
01086   ButtonState btn = me->button();
01087   doFolderSelected(lvi);
01088 
01089   // get underlying folder
01090   KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>(lvi);
01091 
01092   if (!fti || !fti->folder()) {
01093     KFolderTree::contentsMouseReleaseEvent(me);
01094     return;
01095   }
01096 
01097   // react on middle-button only
01098   if (btn != Qt::MidButton) {
01099     KFolderTree::contentsMouseReleaseEvent(me);
01100     return;
01101   }
01102 
01103   if ( fti->folder()->isMailingListEnabled() ) {
01104     KMCommand *command = new KMMailingListPostCommand( this, fti->folder() );
01105     command->start();
01106   }
01107 
01108   KFolderTree::contentsMouseReleaseEvent(me);
01109 }
01110 
01111 // little static helper
01112 static bool folderHasCreateRights( const KMFolder *folder )
01113 {
01114   bool createRights = true; // we don't have acls for local folders yet
01115   if ( folder && folder->folderType() == KMFolderTypeImap ) {
01116     const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
01117     createRights = imapFolder->userRights() == 0 || // hack, we should get the acls
01118       ( imapFolder->userRights() > 0 && ( imapFolder->userRights() & KMail::ACLJobs::Create ) );
01119   } else if ( folder && folder->folderType() == KMFolderTypeCachedImap ) {
01120     const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
01121     createRights = dimapFolder->userRights() == 0 ||
01122       ( dimapFolder->userRights() > 0 && ( dimapFolder->userRights() & KMail::ACLJobs::Create ) );
01123   }
01124   return createRights;
01125 }
01126 
01127 //-----------------------------------------------------------------------------
01128 // Create a subfolder.
01129 // Requires creating the appropriate subdirectory and show a dialog
01130 void KMFolderTree::addChildFolder( KMFolder *folder, QWidget * parent )
01131 {
01132   KMFolder *aFolder = folder;
01133   if ( !aFolder ) {
01134     KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(currentItem());
01135     if (!fti)
01136       return;
01137     aFolder = fti->folder();
01138   }
01139   if (aFolder) {
01140     if (!aFolder->createChildFolder())
01141       return;
01142     if ( !folderHasCreateRights( aFolder ) ) {
01143       // FIXME: change this message to "Cannot create folder under ..." or similar
01144       const QString message = i18n( "<qt>Cannot create folder <b>%1</b> because of insufficient "
01145                                     "permissions on the server. If you think you should be able to create "
01146                                     "subfolders here, ask your administrator to grant you rights to do so."
01147                                     "</qt> " ).arg(aFolder->label());
01148       KMessageBox::error( this, message );
01149       return;
01150     }
01151   }
01152 
01153   if ( parent )
01154     ( new KMail::NewFolderDialog( parent, aFolder ) )->exec();
01155   else
01156     ( new KMail::NewFolderDialog( this, aFolder ) )->show();
01157   return;
01158 /*
01159   KMFolderDir *dir = &(kmkernel->folderMgr()->dir());
01160   if (aFolder)
01161     dir = aFolder->child();
01162 
01163   KMFolderDialog *d =
01164     new KMFolderDialog(0, dir, this, i18n("Create Subfolder") );
01165 
01166   if (d->exec()) { // fti may be deleted here
01167     QListViewItem *qlvi = indexOfFolder( aFolder );
01168     if (qlvi) {
01169       qlvi->setOpen(TRUE);
01170       blockSignals( true );
01171       setCurrentItem( qlvi );
01172       blockSignals( false );
01173     }
01174   }
01175   delete d;
01176   // update if added to root Folder
01177   if (!aFolder || aFolder->noContent()) {
01178      doFolderListChanged();
01179   }
01180   */
01181 }
01182 
01183 //-----------------------------------------------------------------------------
01184 // Returns whether a folder directory should be open as specified in the
01185 // config file.
01186 bool KMFolderTree::readIsListViewItemOpen(KMFolderTreeItem *fti)
01187 {
01188   KConfig* config = KMKernel::config();
01189   KMFolder *folder = fti->folder();
01190   QString name;
01191   if (folder)
01192   {
01193     name = "Folder-" + folder->idString();
01194   } else if (fti->type() == KFolderTreeItem::Root)
01195   {
01196     if (fti->protocol() == KFolderTreeItem::NONE) // local root
01197       name = "Folder_local_root";
01198     else if (fti->protocol() == KFolderTreeItem::Search)
01199       name = "Folder_search";
01200     else
01201       return false;
01202   } else {
01203     return false;
01204   }
01205   KConfigGroupSaver saver(config, name);
01206 
01207   return config->readBoolEntry("isOpen", false);
01208 }
01209 
01210 //-----------------------------------------------------------------------------
01211 // Saves open/closed state of a folder directory into the config file
01212 void KMFolderTree::writeIsListViewItemOpen(KMFolderTreeItem *fti)
01213 {
01214   KConfig* config = KMKernel::config();
01215   KMFolder *folder = fti->folder();
01216   QString name;
01217   if (folder && !folder->idString().isEmpty())
01218   {
01219     name = "Folder-" + folder->idString();
01220   } else if (fti->type() == KFolderTreeItem::Root)
01221   {
01222     if (fti->protocol() == KFolderTreeItem::NONE) // local root
01223       name = "Folder_local_root";
01224     else if (fti->protocol() == KFolderTreeItem::Search)
01225       name = "Folder_search";
01226     else
01227       return;
01228   } else {
01229     return;
01230   }
01231   KConfigGroupSaver saver(config, name);
01232   config->writeEntry("isOpen", fti->isOpen() );
01233 }
01234 
01235 
01236 //-----------------------------------------------------------------------------
01237 void KMFolderTree::cleanupConfigFile()
01238 {
01239   if ( childCount() == 0 )
01240     return; // just in case reload wasn't called before
01241   KConfig* config = KMKernel::config();
01242   QStringList existingFolders;
01243   QListViewItemIterator fldIt(this);
01244   QMap<QString,bool> folderMap;
01245   KMFolderTreeItem *fti;
01246   for (QListViewItemIterator fldIt(this); fldIt.current(); fldIt++)
01247   {
01248     fti = static_cast<KMFolderTreeItem*>(fldIt.current());
01249     if (fti && fti->folder())
01250       folderMap.insert(fti->folder()->idString(), true);
01251   }
01252   QStringList groupList = config->groupList();
01253   QString name;
01254   for (QStringList::Iterator grpIt = groupList.begin();
01255     grpIt != groupList.end(); grpIt++)
01256   {
01257     if ((*grpIt).left(7) != "Folder-") continue;
01258     name = (*grpIt).mid(7);
01259     if (folderMap.find(name) == folderMap.end())
01260     {
01261       KMFolder* folder = kmkernel->findFolderById( name );
01262       if ( folder && kmkernel->iCalIface().hideResourceFolder( folder ) )
01263         continue; // hidden IMAP resource folder, don't delete info
01264 
01265       //KMessageBox::error( 0, "cleanupConfigFile: Deleting group " + *grpIt );
01266       config->deleteGroup(*grpIt, TRUE);
01267       kdDebug(5006) << "Deleting information about folder " << name << endl;
01268     }
01269   }
01270 }
01271 
01272 
01273 //-----------------------------------------------------------------------------
01274 // Drag and Drop handling -- based on the Troll Tech dirview example
01275 
01276 enum {
01277   DRAG_COPY = 0,
01278   DRAG_MOVE = 1,
01279   DRAG_CANCEL = 2
01280 };
01281 
01282 //-----------------------------------------------------------------------------
01283 void KMFolderTree::openFolder()
01284 {
01285     autoopen_timer.stop();
01286     if ( dropItem && !dropItem->isOpen() ) {
01287         dropItem->setOpen( TRUE );
01288         dropItem->repaint();
01289     }
01290 }
01291 
01292 static const int autoopenTime = 750;
01293 
01294 //-----------------------------------------------------------------------------
01295 void KMFolderTree::contentsDragEnterEvent( QDragEnterEvent *e )
01296 {
01297   oldCurrent = 0;
01298   oldSelected = 0;
01299 
01300   oldCurrent = currentItem();
01301   for ( QListViewItemIterator it( this ) ; it.current() ; ++it )
01302     if ( it.current()->isSelected() )
01303       oldSelected = it.current();
01304 
01305   setFocus();
01306 
01307   QListViewItem *i = itemAt( contentsToViewport(e->pos()) );
01308   if ( i ) {
01309     dropItem = i;
01310     autoopen_timer.start( autoopenTime );
01311   }
01312   e->accept( acceptDrag(e) );
01313 }
01314 
01315 //-----------------------------------------------------------------------------
01316 void KMFolderTree::contentsDragMoveEvent( QDragMoveEvent *e )
01317 {
01318     QPoint vp = contentsToViewport(e->pos());
01319     QListViewItem *i = itemAt( vp );
01320     if ( i ) {
01321         bool dragAccepted = acceptDrag( e );
01322         if ( dragAccepted ) {
01323             setCurrentItem( i );
01324         }
01325 
01326         if ( i != dropItem ) {
01327             autoopen_timer.stop();
01328             dropItem = i;
01329             autoopen_timer.start( autoopenTime );
01330         }
01331 
01332         if ( dragAccepted ) {
01333             e->accept( itemRect(i) );
01334 
01335             switch ( e->action() ) {
01336                 case QDropEvent::Copy:
01337                 break;
01338                 case QDropEvent::Move:
01339                 e->acceptAction();
01340                 break;
01341                 case QDropEvent::Link:
01342                 e->acceptAction();
01343                 break;
01344                 default:
01345                 ;
01346             }
01347         } else {
01348             e->accept( false );
01349         }
01350     } else {
01351         e->accept( false );
01352         autoopen_timer.stop();
01353         dropItem = 0;
01354     }
01355 }
01356 
01357 //-----------------------------------------------------------------------------
01358 void KMFolderTree::contentsDragLeaveEvent( QDragLeaveEvent * )
01359 {
01360     if (!oldCurrent) return;
01361 
01362     autoopen_timer.stop();
01363     dropItem = 0;
01364 
01365     setCurrentItem( oldCurrent );
01366     if ( oldSelected )
01367       setSelected( oldSelected, TRUE );
01368 }
01369 
01370 //-----------------------------------------------------------------------------
01371 void KMFolderTree::contentsDropEvent( QDropEvent *e )
01372 {
01373     autoopen_timer.stop();
01374 
01375     QListViewItem *item = itemAt( contentsToViewport(e->pos()) );
01376     KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01377     if (fti && (fti != oldSelected) && (fti->folder()) && acceptDrag(e))
01378     {
01379       int keybstate = kapp->keyboardModifiers();
01380       if ( keybstate & KApplication::ControlModifier ) {
01381         emit folderDropCopy(fti->folder());
01382       } else if ( keybstate & KApplication::ShiftModifier ) {
01383         emit folderDrop(fti->folder());
01384       } else {
01385         if ( GlobalSettings::self()->showPopupAfterDnD() ) {
01386           KPopupMenu *menu = new KPopupMenu( this );
01387           menu->insertItem( i18n("&Move Here"), DRAG_MOVE, 0 );
01388           menu->insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), DRAG_COPY, 1 );
01389           menu->insertSeparator();
01390           menu->insertItem( SmallIcon("cancel"), i18n("C&ancel"), DRAG_CANCEL, 3 );
01391           int id = menu->exec( QCursor::pos(), 0 );
01392           switch(id) {
01393             case DRAG_COPY:
01394               emit folderDropCopy(fti->folder());
01395               break;
01396             case DRAG_MOVE:
01397               emit folderDrop(fti->folder());
01398               break;
01399             case DRAG_CANCEL: // cancelled by menuitem
01400             case -1: // cancelled by Esc
01401               //just chill, doing nothing
01402               break;
01403             default:
01404               kdDebug(5006) << "Unknown dnd-type! " << id << endl;
01405           }
01406         }
01407         else
01408           emit folderDrop(fti->folder());
01409       }
01410       e->accept( true );
01411     } else
01412       e->accept( false );
01413 
01414     dropItem = 0;
01415 
01416     setCurrentItem( oldCurrent );
01417     if ( oldCurrent) mLastItem = static_cast<KMFolderTreeItem*>(oldCurrent);
01418     if ( oldSelected )
01419     {
01420       clearSelection();
01421       setSelected( oldSelected, TRUE );
01422     }
01423 }
01424 
01425 //-----------------------------------------------------------------------------
01426 void KMFolderTree::slotFolderExpanded( QListViewItem * item )
01427 {
01428   KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01429 
01430   if ( fti && fti->folder() &&
01431        fti->folder()->folderType() == KMFolderTypeImap )
01432   {
01433     KMFolderImap *folder = static_cast<KMFolderImap*>( fti->folder()->storage() );
01434     // if we should list all folders we limit this to the root folder
01435     if ( !folder->account()->listOnlyOpenFolders() &&
01436          fti->parent() )
01437       return;
01438     if ( folder->getSubfolderState() == KMFolderImap::imapNoInformation )
01439     {
01440       // check if all parents are expanded
01441       QListViewItem *parent = item->parent();
01442       while ( parent )
01443       {
01444         if ( !parent->isOpen() )
01445           return;
01446         parent = parent->parent();
01447       }
01448       // the tree will be reloaded after that
01449       bool success = folder->listDirectory();
01450       if (!success) fti->setOpen( false );
01451       if ( fti->childCount() == 0 && fti->parent() )
01452         fti->setExpandable( false );
01453     }
01454   }
01455 }
01456 
01457 
01458 //-----------------------------------------------------------------------------
01459 void KMFolderTree::slotFolderCollapsed( QListViewItem * item )
01460 {
01461   slotResetFolderList( item, false );
01462 }
01463 
01464 //-----------------------------------------------------------------------------
01465 void KMFolderTree::slotRenameFolder(QListViewItem *item, int col,
01466                 const QString &text)
01467 {
01468 
01469   KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01470 
01471   if (fti && fti->folder() && col != 0 && !currentFolder()->child())
01472           return;
01473 
01474   QString fldName, oldFldName;
01475 
01476   oldFldName = fti->name(0);
01477 
01478   if (!text.isEmpty())
01479           fldName = text;
01480   else
01481           fldName = oldFldName;
01482 
01483   fldName.replace("/", "");
01484   fldName.replace(QRegExp("^\\."), "");
01485 
01486   if (fldName.isEmpty())
01487           fldName = i18n("unnamed");
01488 
01489   fti->setText(0, fldName);
01490   fti->folder()->rename(fldName, &(kmkernel->folderMgr()->dir()));
01491 }
01492 
01493 //-----------------------------------------------------------------------------
01494 void KMFolderTree::slotUpdateCounts(KMFolderImap * folder, bool success)
01495 {
01496   if (success) slotUpdateCounts(folder->folder());
01497 }
01498 
01499 //-----------------------------------------------------------------------------
01500 void KMFolderTree::slotUpdateCountsDelayed(KMFolder * folder)
01501 {
01502 //  kdDebug(5006) << "KMFolderTree::slotUpdateCountsDelayed()" << endl;
01503   if ( !mFolderToUpdateCount.contains( folder->idString() ) )
01504   {
01505 //    kdDebug( 5006 )<< "adding " << folder->idString() << " to updateCountList " << endl;
01506     mFolderToUpdateCount.insert( folder->idString(),folder );
01507   }
01508   if ( !mUpdateCountTimer->isActive() )
01509     mUpdateCountTimer->start( 500 );
01510 }
01511 
01512 
01513 void KMFolderTree::slotUpdateCountTimeout()
01514 {
01515 //  kdDebug(5006) << "KMFolderTree::slotUpdateCountTimeout()" << endl;
01516 
01517   QMap<QString,KMFolder*>::iterator it;
01518   for ( it= mFolderToUpdateCount.begin();
01519       it!=mFolderToUpdateCount.end();
01520       ++it )
01521   {
01522     slotUpdateCounts( it.data() );
01523   }
01524   mFolderToUpdateCount.clear();
01525   mUpdateCountTimer->stop();
01526 
01527 }
01528 
01529 void KMFolderTree::slotUpdateCounts(KMFolder * folder)
01530 {
01531  // kdDebug(5006) << "KMFolderTree::slotUpdateCounts()" << endl;
01532   QListViewItem * current;
01533   if (folder)
01534     current = indexOfFolder(folder);
01535   else
01536     current = currentItem();
01537 
01538   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(current);
01539   // sanity check
01540   if (!fti) return;
01541   if (!fti->folder()) fti->setTotalCount(-1);
01542 
01543   // get the unread count
01544   int count = 0;
01545   if (folder->noContent()) // always empty
01546     count = -1;
01547   else
01548     count = fti->folder()->countUnread();
01549 
01550   // set it
01551   bool repaint = false;
01552   if (fti->unreadCount() != count) {
01553      fti->adjustUnreadCount( count );
01554      repaint = true;
01555   }
01556   if (isTotalActive())
01557   {
01558     // get the total-count
01559     if (fti->folder()->noContent())
01560       count = -1;
01561     else {
01562       // get the cached count if the folder is not open
01563       count = fti->folder()->count( !fti->folder()->isOpened() );
01564     }
01565     // set it
01566     if ( count != fti->totalCount() ) {
01567       fti->setTotalCount(count);
01568       repaint = true;
01569     }
01570   }
01571   if (fti->parent() && !fti->parent()->isOpen())
01572     repaint = false; // we're not visible
01573   if (repaint) {
01574     fti->setNeedsRepaint( true );
01575     refresh();
01576   }
01577   // tell the kernel that one of the counts has changed
01578   kmkernel->messageCountChanged();
01579 }
01580 
01581 void KMFolderTree::updatePopup() const
01582 {
01583    mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
01584    mPopup->setItemChecked( mTotalPop, isTotalActive() );
01585 }
01586 
01587 //-----------------------------------------------------------------------------
01588 void KMFolderTree::toggleColumn(int column, bool openFolders)
01589 {
01590   if (column == unread)
01591   {
01592     // switch unread
01593     if ( isUnreadActive() )
01594     {
01595       removeUnreadColumn();
01596       reload();
01597     } else {
01598       addUnreadColumn( i18n("Unread"), 70 );
01599       reload();
01600     }
01601     // toggle KPopupMenu
01602     mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
01603 
01604   } else if (column == total) {
01605     // switch total
01606     if ( isTotalActive() )
01607     {
01608       removeTotalColumn();
01609       reload();
01610     } else {
01611       addTotalColumn( i18n("Total"), 70 );
01612       reload(openFolders);
01613     }
01614     // toggle KPopupMenu
01615     mPopup->setItemChecked( mTotalPop, isTotalActive() );
01616 
01617   } else kdDebug(5006) << "unknown column:" << column << endl;
01618 
01619   // toggles the switches of the mainwin
01620   emit columnsChanged();
01621 }
01622 
01623 //-----------------------------------------------------------------------------
01624 void KMFolderTree::slotToggleUnreadColumn()
01625 {
01626   toggleColumn(unread);
01627 }
01628 
01629 //-----------------------------------------------------------------------------
01630 void KMFolderTree::slotToggleTotalColumn()
01631 {
01632   // activate the total-column and force the folders to be opened
01633   toggleColumn(total, true);
01634 }
01635 
01636 //-----------------------------------------------------------------------------
01637 bool KMFolderTree::eventFilter( QObject *o, QEvent *e )
01638 {
01639   if ( e->type() == QEvent::MouseButtonPress &&
01640       static_cast<QMouseEvent*>(e)->button() == RightButton &&
01641       o->isA("QHeader") )
01642   {
01643     mPopup->popup( static_cast<QMouseEvent*>(e)->globalPos() );
01644     return true;
01645   }
01646   return KFolderTree::eventFilter(o, e);
01647 }
01648 
01649 //-----------------------------------------------------------------------------
01650 void KMFolderTree::slotCheckMail()
01651 {
01652   if (!currentItem())
01653     return;
01654   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(currentItem());
01655   KMFolder* folder = fti->folder();
01656   if (folder && folder->folderType() == KMFolderTypeImap)
01657   {
01658     KMAccount* acct = static_cast<KMFolderImap*>(folder->storage())->account();
01659     kmkernel->acctMgr()->singleCheckMail(acct, true);
01660   }
01661 }
01662 
01663 //-----------------------------------------------------------------------------
01664 void KMFolderTree::slotNewMessageToMailingList()
01665 {
01666   KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>( currentItem() );
01667   if ( !fti || !fti->folder() )
01668     return;
01669   KMCommand *command = new KMMailingListPostCommand( this, fti->folder() );
01670   command->start();
01671 }
01672 
01673 //-----------------------------------------------------------------------------
01674 void KMFolderTree::createFolderList( QStringList *str,
01675                                      QValueList<QGuardedPtr<KMFolder> > *folders,
01676                                      bool localFolders,
01677                                      bool imapFolders,
01678                                      bool dimapFolders,
01679                                      bool searchFolders,
01680                                      bool includeNoContent,
01681                                      bool includeNoChildren )
01682 {
01683   for ( QListViewItemIterator it( this ) ; it.current() ; ++it )
01684   {
01685     KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current());
01686     if (!fti || !fti->folder()) continue;
01687     // type checks
01688     KMFolder* folder = fti->folder();
01689     if (!imapFolders && folder->folderType() == KMFolderTypeImap) continue;
01690     if (!dimapFolders && folder->folderType() == KMFolderTypeCachedImap) continue;
01691     if (!localFolders && (folder->folderType() == KMFolderTypeMbox ||
01692                           folder->folderType() == KMFolderTypeMaildir)) continue;
01693     if (!searchFolders && folder->folderType() == KMFolderTypeSearch) continue;
01694     if (!includeNoContent && folder->noContent()) continue;
01695     if (!includeNoChildren && folder->noChildren()) continue;
01696     QString prefix;
01697     prefix.fill( ' ', 2 * fti->depth() );
01698     str->append(prefix + fti->text(0));
01699     folders->append(fti->folder());
01700   }
01701 }
01702 
01703 //-----------------------------------------------------------------------------
01704 void KMFolderTree::slotResetFolderList( QListViewItem* item, bool startList )
01705 {
01706   if ( !item )
01707     item = currentItem();
01708 
01709   KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>( item );
01710   if ( fti && fti->folder() &&
01711        fti->folder()->folderType() == KMFolderTypeImap )
01712   {
01713     KMFolderImap *folder = static_cast<KMFolderImap*>( fti->folder()->storage() );
01714     folder->setSubfolderState( KMFolderImap::imapNoInformation );
01715     if ( startList )
01716       folder->listDirectory();
01717   }
01718 }
01719 
01720 //-----------------------------------------------------------------------------
01721 void KMFolderTree::showFolder( KMFolder* folder )
01722 {
01723   if ( !folder ) return;
01724   QListViewItem* item = indexOfFolder( folder );
01725   if ( item )
01726   {
01727     doFolderSelected( item );
01728     ensureItemVisible( item );
01729   }
01730 }
01731 
01732 //-----------------------------------------------------------------------------
01733 void KMFolderTree::folderToPopupMenu( MenuAction action, QObject *receiver,
01734     KMMenuToFolder *aMenuToFolder, QPopupMenu *menu, QListViewItem *item )
01735 {
01736   while ( menu->count() )
01737   {
01738     QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
01739     if ( popup )
01740       delete popup;
01741     else
01742       menu->removeItemAt( 0 );
01743   }
01744   // connect the signals
01745   if ( action == MoveMessage || action == MoveFolder )
01746   {
01747     disconnect( menu, SIGNAL(activated(int)), receiver,
01748         SLOT(moveSelectedToFolder(int)) );
01749     connect( menu, SIGNAL(activated(int)), receiver,
01750         SLOT(moveSelectedToFolder(int)) );
01751   } else {
01752     disconnect( menu, SIGNAL(activated(int)), receiver,
01753         SLOT(copySelectedToFolder(int)) );
01754     connect( menu, SIGNAL(activated(int)), receiver,
01755         SLOT(copySelectedToFolder(int)) );
01756   }
01757   if ( !item ) {
01758     item = firstChild();
01759 
01760     // avoid a popup menu with the single entry 'Local Folders' if there
01761     // are no IMAP accounts
01762     if ( childCount() == 2 && action != MoveFolder ) { // only 'Local Folders' and 'Searches'
01763       KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>( item );
01764       if ( fti->protocol() == KFolderTreeItem::Search ) {
01765         // skip 'Searches'
01766         item = item->nextSibling();
01767         fti = static_cast<KMFolderTreeItem*>( item );
01768       }
01769       folderToPopupMenu( action, receiver, aMenuToFolder, menu, fti->firstChild() );
01770       return;
01771     }
01772   }
01773 
01774   while ( item )
01775   {
01776     KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( item );
01777     if ( fti->protocol() == KFolderTreeItem::Search )
01778     {
01779       // skip search folders
01780       item = item->nextSibling();
01781       continue;
01782     }
01783     if ( action == MoveFolder ) {
01784       // FIXME remove in KDE 4
01785       // skip all but local folders if a folder is to be moved
01786       // because moving of nested folders to IMAP and DIMAP
01787       // looses all messages in the subfolders
01788       if ( fti->protocol() != KFolderTreeItem::Local
01789         && fti->protocol() != KFolderTreeItem::NONE )
01790       {
01791         item = item->nextSibling();
01792         continue;
01793       }
01794     }
01795     QString label = fti->text( 0 );
01796     label.replace( "&","&&" );
01797     if ( fti->firstChild() )
01798     {
01799       // new level
01800       QPopupMenu* popup = new QPopupMenu( menu, "subMenu" );
01801       folderToPopupMenu( action, receiver, aMenuToFolder, popup, fti->firstChild() );
01802       bool subMenu = false;
01803       if ( ( action == MoveMessage || action == CopyMessage ) &&
01804            fti->folder() && !fti->folder()->noContent() )
01805         subMenu = true;
01806       if ( action == MoveFolder && ( !fti->folder() ||
01807             ( fti->folder() && !fti->folder()->noChildren() ) ) )
01808         subMenu = true;
01809       if ( subMenu )
01810       {
01811         int menuId;
01812         if ( action == MoveMessage || action == MoveFolder )
01813           menuId = popup->insertItem( i18n("Move to This Folder"), -1, 0 );
01814         else
01815           menuId = popup->insertItem( i18n("Copy to This Folder"), -1, 0 );
01816         popup->insertSeparator( 1 );
01817         aMenuToFolder->insert( menuId, fti->folder() );
01818       }
01819       menu->insertItem( label, popup );
01820     } else
01821     {
01822       // insert an item
01823       int menuId = menu->insertItem( label );
01824       if ( fti->folder() )
01825         aMenuToFolder->insert( menuId, fti->folder() );
01826       bool enabled = (fti->folder() ? true : false);
01827       if ( fti->folder() &&
01828            ( fti->folder()->isReadOnly() || fti->folder()->noContent() ) )
01829         enabled = false;
01830       menu->setItemEnabled( menuId, enabled );
01831     }
01832 
01833     item = item->nextSibling();
01834   }
01835 }
01836 
01837 //-----------------------------------------------------------------------------
01838 void KMFolderTree::moveSelectedToFolder( int menuId )
01839 {
01840   moveFolder( mMenuToFolder[menuId] );
01841 }
01842 
01843 //-----------------------------------------------------------------------------
01844 void KMFolderTree::moveFolder( KMFolder* destination )
01845 {
01846   KMFolder* folder = currentFolder();
01847   KMFolderDir* parent = &(kmkernel->folderMgr()->dir());
01848   if ( destination )
01849     parent = destination->createChildFolder();
01850   QString message =
01851     i18n( "<qt>Cannot move folder <b>%1</b> into a subfolder below itself.</qt>" ).
01852         arg( folder->label() );
01853 
01854   KMFolderDir* folderDir = parent;
01855   // check that the folder can be moved
01856   if ( folder && folder->child() )
01857   {
01858     while ( folderDir && ( folderDir != &kmkernel->folderMgr()->dir() ) &&
01859         ( folderDir != folder->parent() ) )
01860     {
01861       if ( folderDir->findRef( folder ) != -1 )
01862       {
01863         KMessageBox::error( this, message );
01864         return;
01865       }
01866       folderDir = folderDir->parent();
01867     }
01868   }
01869 
01870   if( folder && folder->child() && parent &&
01871       ( parent->path().find( folder->child()->path() + "/" ) == 0 ) ) {
01872     KMessageBox::error( this, message );
01873     return;
01874   }
01875 
01876   if( folder && folder->child()
01877       && ( parent == folder->child() ) ) {
01878     KMessageBox::error( this, message );
01879     return;
01880   }
01881 
01882   kdDebug(5006) << "move folder " << currentFolder()->label() << " to "
01883     << ( destination ? destination->label() : "Local Folders" ) << endl;
01884   kmkernel->folderMgr()->moveFolder( folder, parent );
01885 }
01886 
01887 #include "kmfoldertree.moc"
01888 
KDE Home | KDE Accessibility Home | Description of Access Keys