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