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( "archive_folder" )->plug( folderMenu );
01132 
01133       mMainWidget->action("compact")->plug(folderMenu);
01134 
01135       if ( GlobalSettings::self()->enableFavoriteFolderView() ) {
01136         folderMenu->insertItem( SmallIconSet("bookmark_add"), i18n("Add to Favorite Folders"),
01137                                 this, SLOT(slotAddToFavorites()) );
01138       }
01139 
01140       folderMenu->insertSeparator();
01141       mMainWidget->action("empty")->plug(folderMenu);
01142       if ( !fti->folder()->isSystemFolder() ) {
01143         mMainWidget->action("delete_folder")->plug(folderMenu);
01144       }
01145       folderMenu->insertSeparator();
01146     }
01147   }
01148 
01149   /* plug in IMAP and DIMAP specific things */
01150   if (fti->folder() &&
01151       (fti->folder()->folderType() == KMFolderTypeImap ||
01152        fti->folder()->folderType() == KMFolderTypeCachedImap ))
01153   {
01154     folderMenu->insertItem(SmallIconSet("bookmark_folder"),
01155         i18n("Serverside Subscription..."), mMainWidget,
01156         SLOT(slotSubscriptionDialog()));
01157     folderMenu->insertItem(SmallIcon("bookmark_folder"),
01158         i18n("Local Subscription..."), mMainWidget,
01159         SLOT(slotLocalSubscriptionDialog()));
01160 
01161     if (!fti->folder()->noContent())
01162     {
01163       mMainWidget->action("refresh_folder")->plug(folderMenu);
01164       if ( fti->folder()->folderType() == KMFolderTypeImap && !multiFolder ) {
01165         folderMenu->insertItem(SmallIconSet("reload"), i18n("Refresh Folder List"), this,
01166             SLOT(slotResetFolderList()));
01167       }
01168     }
01169     if ( fti->folder()->folderType() == KMFolderTypeCachedImap && !multiFolder ) {
01170       KMFolderCachedImap * folder = static_cast<KMFolderCachedImap*>( fti->folder()->storage() );
01171       folderMenu->insertItem( SmallIconSet("wizard"),
01172                               i18n("&Troubleshoot IMAP Cache..."),
01173                               folder, SLOT(slotTroubleshoot()) );
01174     }
01175     folderMenu->insertSeparator();
01176   }
01177 
01178   if ( fti->folder() && fti->folder()->isMailingListEnabled() && !multiFolder ) {
01179     mMainWidget->action("post_message")->plug(folderMenu);
01180   }
01181 
01182   if (fti->folder() && fti->parent() && !multiFolder)
01183   {
01184     folderMenu->insertItem(SmallIconSet("configure_shortcuts"),
01185         i18n("&Assign Shortcut..."),
01186         fti,
01187         SLOT(assignShortcut()));
01188 
01189     if ( !fti->folder()->noContent() ) {
01190       folderMenu->insertItem( i18n("Expire..."), fti,
01191                               SLOT( slotShowExpiryProperties() ) );
01192     }
01193     mMainWidget->action("modify")->plug(folderMenu);
01194   }
01195 
01196 
01197   kmkernel->setContextMenuShown( true );
01198   folderMenu->exec (p, 0);
01199   kmkernel->setContextMenuShown( false );
01200   triggerUpdate();
01201   delete folderMenu;
01202   folderMenu = 0;
01203 }
01204 
01205 //-----------------------------------------------------------------------------
01206 void KMFolderTree::contentsMousePressEvent(QMouseEvent * e)
01207 {
01208   // KFolderTree messes around with the selection mode
01209   KListView::contentsMousePressEvent( e );
01210 }
01211 
01212 // If middle button and folder holds mailing-list, create a message to that list
01213 void KMFolderTree::contentsMouseReleaseEvent(QMouseEvent* me)
01214 {
01215   QListViewItem *lvi = currentItem(); // Needed for when branches are clicked on
01216   ButtonState btn = me->button();
01217   doFolderSelected(lvi, true);
01218 
01219   // get underlying folder
01220   KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>(lvi);
01221 
01222   if (!fti || !fti->folder()) {
01223     KFolderTree::contentsMouseReleaseEvent(me);
01224     return;
01225   }
01226 
01227   // react on middle-button only
01228   if (btn != Qt::MidButton) {
01229     KFolderTree::contentsMouseReleaseEvent(me);
01230     return;
01231   }
01232 
01233   if ( fti->folder()->isMailingListEnabled() ) {
01234     KMCommand *command = new KMMailingListPostCommand( this, fti->folder() );
01235     command->start();
01236   }
01237 
01238   KFolderTree::contentsMouseReleaseEvent(me);
01239 }
01240 
01241 // little static helper
01242 static bool folderHasCreateRights( const KMFolder *folder )
01243 {
01244   bool createRights = true; // we don't have acls for local folders yet
01245   if ( folder && folder->folderType() == KMFolderTypeImap ) {
01246     const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
01247     createRights = imapFolder->userRights() == 0 || // hack, we should get the acls
01248       ( imapFolder->userRights() > 0 && ( imapFolder->userRights() & KMail::ACLJobs::Create ) );
01249   } else if ( folder && folder->folderType() == KMFolderTypeCachedImap ) {
01250     const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
01251     createRights = dimapFolder->userRights() == 0 ||
01252       ( dimapFolder->userRights() > 0 && ( dimapFolder->userRights() & KMail::ACLJobs::Create ) );
01253   }
01254   return createRights;
01255 }
01256 
01257 //-----------------------------------------------------------------------------
01258 // Create a subfolder.
01259 // Requires creating the appropriate subdirectory and show a dialog
01260 void KMFolderTree::addChildFolder( KMFolder *folder, QWidget * parent )
01261 {
01262   KMFolder *aFolder = folder;
01263   if ( !aFolder ) {
01264     KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(currentItem());
01265     if (!fti)
01266       return;
01267     aFolder = fti->folder();
01268   }
01269   if (aFolder) {
01270     if (!aFolder->createChildFolder())
01271       return;
01272     if ( !folderHasCreateRights( aFolder ) ) {
01273       // FIXME: change this message to "Cannot create folder under ..." or similar
01274       const QString message = i18n( "<qt>Cannot create folder <b>%1</b> because of insufficient "
01275                                     "permissions on the server. If you think you should be able to create "
01276                                     "subfolders here, ask your administrator to grant you rights to do so."
01277                                     "</qt> " ).arg(aFolder->label());
01278       KMessageBox::error( this, message );
01279       return;
01280     }
01281   }
01282 
01283   if ( parent )
01284     ( new KMail::NewFolderDialog( parent, aFolder ) )->exec();
01285   else
01286     ( new KMail::NewFolderDialog( this, aFolder ) )->show();
01287   return;
01288 /*
01289   KMFolderDir *dir = &(kmkernel->folderMgr()->dir());
01290   if (aFolder)
01291     dir = aFolder->child();
01292 
01293   KMFolderDialog *d =
01294     new KMFolderDialog(0, dir, this, i18n("Create Subfolder") );
01295 
01296   if (d->exec()) { // fti may be deleted here
01297     QListViewItem *qlvi = indexOfFolder( aFolder );
01298     if (qlvi) {
01299       qlvi->setOpen(true);
01300       blockSignals( true );
01301       setCurrentItem( qlvi );
01302       blockSignals( false );
01303     }
01304   }
01305   delete d;
01306   // update if added to root Folder
01307   if (!aFolder || aFolder->noContent()) {
01308      doFolderListChanged();
01309   }
01310   */
01311 }
01312 
01313 //-----------------------------------------------------------------------------
01314 // Returns whether a folder directory should be open as specified in the
01315 // config file.
01316 bool KMFolderTree::readIsListViewItemOpen(KMFolderTreeItem *fti)
01317 {
01318   KConfig* config = KMKernel::config();
01319   KMFolder *folder = fti->folder();
01320   QString name;
01321   if (folder)
01322   {
01323     name = "Folder-" + folder->idString();
01324   } else if (fti->type() == KFolderTreeItem::Root)
01325   {
01326     if (fti->protocol() == KFolderTreeItem::NONE) // local root
01327       name = "Folder_local_root";
01328     else if (fti->protocol() == KFolderTreeItem::Search)
01329       name = "Folder_search";
01330     else
01331       return false;
01332   } else {
01333     return false;
01334   }
01335   KConfigGroupSaver saver(config, name);
01336 
01337   return config->readBoolEntry("isOpen", false);
01338 }
01339 
01340 //-----------------------------------------------------------------------------
01341 // Saves open/closed state of a folder directory into the config file
01342 void KMFolderTree::writeIsListViewItemOpen(KMFolderTreeItem *fti)
01343 {
01344   KConfig* config = KMKernel::config();
01345   KMFolder *folder = fti->folder();
01346   QString name;
01347   if (folder && !folder->idString().isEmpty())
01348   {
01349     name = "Folder-" + folder->idString();
01350   } else if (fti->type() == KFolderTreeItem::Root)
01351   {
01352     if (fti->protocol() == KFolderTreeItem::NONE) // local root
01353       name = "Folder_local_root";
01354     else if (fti->protocol() == KFolderTreeItem::Search)
01355       name = "Folder_search";
01356     else
01357       return;
01358   } else {
01359     return;
01360   }
01361   KConfigGroupSaver saver(config, name);
01362   config->writeEntry("isOpen", fti->isOpen() );
01363 }
01364 
01365 
01366 //-----------------------------------------------------------------------------
01367 void KMFolderTree::cleanupConfigFile()
01368 {
01369   if ( childCount() == 0 )
01370     return; // just in case reload wasn't called before
01371   KConfig* config = KMKernel::config();
01372   QStringList existingFolders;
01373   QListViewItemIterator fldIt(this);
01374   QMap<QString,bool> folderMap;
01375   KMFolderTreeItem *fti;
01376   for (QListViewItemIterator fldIt(this); fldIt.current(); fldIt++)
01377   {
01378     fti = static_cast<KMFolderTreeItem*>(fldIt.current());
01379     if (fti && fti->folder())
01380       folderMap.insert(fti->folder()->idString(), true);
01381   }
01382   QStringList groupList = config->groupList();
01383   QString name;
01384   for (QStringList::Iterator grpIt = groupList.begin();
01385     grpIt != groupList.end(); grpIt++)
01386   {
01387     if ((*grpIt).left(7) != "Folder-") continue;
01388     name = (*grpIt).mid(7);
01389     if (folderMap.find(name) == folderMap.end())
01390     {
01391       KMFolder* folder = kmkernel->findFolderById( name );
01392       if ( folder ) {
01393         if ( kmkernel->iCalIface().hideResourceFolder( folder )
01394            ||  kmkernel->iCalIface().hideResourceAccountRoot( folder ) )
01395           continue; // hidden IMAP resource folder, don't delete info
01396         if ( folder->noContent() )
01397           continue; // we hide nocontent folders if they have no child folders
01398         if ( folder == kmkernel->inboxFolder() )
01399           continue; // local inbox can be hidden as well
01400       }
01401 
01402       //KMessageBox::error( 0, "cleanupConfigFile: Deleting group " + *grpIt );
01403       config->deleteGroup(*grpIt, true);
01404       kdDebug(5006) << "Deleting information about folder " << name << endl;
01405     }
01406   }
01407 }
01408 
01409 
01410 //-----------------------------------------------------------------------------
01411 void KMFolderTree::openFolder()
01412 {
01413     autoopen_timer.stop();
01414     if ( dropItem && !dropItem->isOpen() ) {
01415         dropItem->setOpen( true );
01416         dropItem->repaint();
01417     }
01418 }
01419 
01420 static const int autoopenTime = 750;
01421 
01422 //-----------------------------------------------------------------------------
01423 void KMFolderTree::contentsDragEnterEvent( QDragEnterEvent *e )
01424 {
01425   oldCurrent = 0;
01426   oldSelected = 0;
01427 
01428   oldCurrent = currentItem();
01429   for ( QListViewItemIterator it( this ) ; it.current() ; ++it )
01430     if ( it.current()->isSelected() )
01431       oldSelected = it.current();
01432 
01433   setFocus();
01434 
01435   QListViewItem *i = itemAt( contentsToViewport(e->pos()) );
01436   if ( i ) {
01437     dropItem = i;
01438     autoopen_timer.start( autoopenTime );
01439   }
01440   else
01441     dropItem = 0;
01442 
01443   e->accept( acceptDrag(e) );
01444 }
01445 
01446 //-----------------------------------------------------------------------------
01447 void KMFolderTree::contentsDragMoveEvent( QDragMoveEvent *e )
01448 {
01449     QPoint vp = contentsToViewport(e->pos());
01450     QListViewItem *i = itemAt( vp );
01451     if ( i ) {
01452         bool dragAccepted = acceptDrag( e );
01453         if ( dragAccepted ) {
01454             setCurrentItem( i );
01455         }
01456 
01457         if ( i != dropItem ) {
01458             autoopen_timer.stop();
01459             dropItem = i;
01460             autoopen_timer.start( autoopenTime );
01461         }
01462 
01463         if ( dragAccepted ) {
01464             e->accept( itemRect(i) );
01465 
01466             switch ( e->action() ) {
01467                 case QDropEvent::Copy:
01468                 break;
01469                 case QDropEvent::Move:
01470                 e->acceptAction();
01471                 break;
01472                 case QDropEvent::Link:
01473                 e->acceptAction();
01474                 break;
01475                 default:
01476                 ;
01477             }
01478         } else {
01479             e->accept( false );
01480         }
01481     } else {
01482         e->accept( false );
01483         autoopen_timer.stop();
01484         dropItem = 0;
01485     }
01486 }
01487 
01488 //-----------------------------------------------------------------------------
01489 void KMFolderTree::contentsDragLeaveEvent( QDragLeaveEvent * )
01490 {
01491     if (!oldCurrent) return;
01492 
01493     autoopen_timer.stop();
01494     dropItem = 0;
01495 
01496     setCurrentItem( oldCurrent );
01497     if ( oldSelected )
01498       setSelected( oldSelected, true );
01499 }
01500 
01501 //-----------------------------------------------------------------------------
01502 void KMFolderTree::contentsDropEvent( QDropEvent *e )
01503 {
01504     autoopen_timer.stop();
01505 
01506     QListViewItem *item = itemAt( contentsToViewport(e->pos()) );
01507     KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01508     // Check that each pointer is not null
01509     for ( QValueList<QGuardedPtr<KMFolder> >::ConstIterator it = mCopySourceFolders.constBegin();
01510       it != mCopySourceFolders.constEnd(); ++it ) {
01511       if ( ! (*it) ) {
01512     fti = 0;
01513     break;
01514       }
01515     }
01516     if (fti && mCopySourceFolders.count() == 1)
01517     {
01518       KMFolder *source = mCopySourceFolders.first();
01519       // if we are dragging to ourselves or to our parent, set fti to 0 so nothing is done
01520       if (source == fti->folder() || source->parent()->owner() == fti->folder()) fti = 0;
01521     }
01522     if (fti && acceptDrag(e) && ( fti != oldSelected || e->source() != mMainWidget->headers()->viewport() ) )
01523     {
01524       if ( e->provides("application/x-qlistviewitem") ) {
01525         int action = dndMode( true /* always ask */ );
01526         if ( (action == DRAG_COPY || action == DRAG_MOVE) && !mCopySourceFolders.isEmpty() ) {
01527           for ( QValueList<QGuardedPtr<KMFolder> >::ConstIterator it = mCopySourceFolders.constBegin();
01528                 it != mCopySourceFolders.constEnd(); ++it ) {
01529             if ( ! (*it)->isMoveable() )
01530               action = DRAG_COPY;
01531           }
01532           moveOrCopyFolder( mCopySourceFolders, fti->folder(), (action == DRAG_MOVE) );
01533         }
01534       } else {
01535         if ( e->source() == mMainWidget->headers()->viewport() ) {
01536           int action;
01537           if ( mMainWidget->headers()->folder() && mMainWidget->headers()->folder()->isReadOnly() )
01538             action = DRAG_COPY;
01539           else
01540             action = dndMode();
01541           // KMHeaders does copy/move itself
01542           if ( action == DRAG_MOVE && fti->folder() )
01543             emit folderDrop( fti->folder() );
01544           else if ( action == DRAG_COPY && fti->folder() )
01545             emit folderDropCopy( fti->folder() );
01546         } else {
01547           handleMailListDrop( e, fti->folder() );
01548         }
01549       }
01550       e->accept( true );
01551     } else
01552       e->accept( false );
01553 
01554     dropItem = 0;
01555 
01556     setCurrentItem( oldCurrent );
01557     if ( oldCurrent) mLastItem = static_cast<KMFolderTreeItem*>(oldCurrent);
01558     if ( oldSelected )
01559     {
01560       clearSelection();
01561       setSelected( oldSelected, true );
01562     }
01563 
01564     mCopySourceFolders.clear();
01565 }
01566 
01567 //-----------------------------------------------------------------------------
01568 void KMFolderTree::slotFolderExpanded( QListViewItem * item )
01569 {
01570   KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01571   if ( !fti || !fti->folder() || !fti->folder()->storage() ) return;
01572 
01573   fti->setFolderSize( fti->folder()->storage()->folderSize() );
01574 
01575   if( fti->folder()->folderType() == KMFolderTypeImap )
01576   {
01577     KMFolderImap *folder = static_cast<KMFolderImap*>( fti->folder()->storage() );
01578     // if we should list all folders we limit this to the root folder
01579     if ( !folder->account() || ( !folder->account()->listOnlyOpenFolders() &&
01580          fti->parent() ) )
01581       return;
01582     if ( folder->getSubfolderState() == KMFolderImap::imapNoInformation )
01583     {
01584       // check if all parents are expanded
01585       QListViewItem *parent = item->parent();
01586       while ( parent )
01587       {
01588         if ( !parent->isOpen() )
01589           return;
01590         parent = parent->parent();
01591       }
01592       // the tree will be reloaded after that
01593       bool success = folder->listDirectory();
01594       if (!success) fti->setOpen( false );
01595       if ( fti->childCount() == 0 && fti->parent() )
01596         fti->setExpandable( false );
01597     }
01598   }
01599 }
01600 
01601 
01602 //-----------------------------------------------------------------------------
01603 void KMFolderTree::slotFolderCollapsed( QListViewItem * item )
01604 {
01605   slotResetFolderList( item, false );
01606   KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01607   if ( !fti || !fti->folder() || !fti->folder()->storage() ) return;
01608 
01609   fti->setFolderSize( fti->folder()->storage()->folderSize() );
01610 }
01611 
01612 //-----------------------------------------------------------------------------
01613 void KMFolderTree::slotRenameFolder(QListViewItem *item, int col,
01614                 const QString &text)
01615 {
01616 
01617   KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item);
01618 
01619   if ((!fti) || (fti && fti->folder() && col != 0 && !currentFolder()->child()))
01620           return;
01621 
01622   QString fldName, oldFldName;
01623 
01624   oldFldName = fti->name(0);
01625 
01626   if (!text.isEmpty())
01627           fldName = text;
01628   else
01629           fldName = oldFldName;
01630 
01631   fldName.replace("/", "");
01632   fldName.replace(QRegExp("^\\."), "");
01633 
01634   if (fldName.isEmpty())
01635           fldName = i18n("unnamed");
01636 
01637   fti->setText(0, fldName);
01638   fti->folder()->rename(fldName, &(kmkernel->folderMgr()->dir()));
01639 }
01640 
01641 //-----------------------------------------------------------------------------
01642 void KMFolderTree::slotUpdateCountsDelayed(KMFolder * folder)
01643 {
01644 //  kdDebug(5006) << "KMFolderTree::slotUpdateCountsDelayed()" << endl;
01645   if ( !mFolderToUpdateCount.contains( folder->idString() ) )
01646   {
01647 //    kdDebug( 5006 )<< "adding " << folder->idString() << " to updateCountList " << endl;
01648     mFolderToUpdateCount.insert( folder->idString(),folder );
01649   }
01650   if ( !mUpdateCountTimer->isActive() )
01651     mUpdateCountTimer->start( 500 );
01652 }
01653 
01654 
01655 void KMFolderTree::slotUpdateCountTimeout()
01656 {
01657 //  kdDebug(5006) << "KMFolderTree::slotUpdateCountTimeout()" << endl;
01658 
01659   QMap<QString,KMFolder*>::iterator it;
01660   for ( it= mFolderToUpdateCount.begin();
01661       it!=mFolderToUpdateCount.end();
01662       ++it )
01663   {
01664     slotUpdateCounts( it.data() );
01665   }
01666   mFolderToUpdateCount.clear();
01667   mUpdateCountTimer->stop();
01668 
01669 }
01670 
01671 void KMFolderTree::updatePopup() const
01672 {
01673    mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
01674    mPopup->setItemChecked( mTotalPop, isTotalActive() );
01675    mPopup->setItemChecked( mSizePop, isSizeActive() );
01676 }
01677 
01678 //-----------------------------------------------------------------------------
01679 void KMFolderTree::toggleColumn(int column, bool openFolders)
01680 {
01681   if (column == unread)
01682   {
01683     // switch unread
01684     if ( isUnreadActive() )
01685     {
01686       removeUnreadColumn();
01687       reload();
01688     } else {
01689       addUnreadColumn( i18n("Unread"), 70 );
01690       reload();
01691     }
01692     // toggle KPopupMenu
01693     mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
01694 
01695   } else if (column == total) {
01696     // switch total
01697     if ( isTotalActive() )
01698     {
01699       removeTotalColumn();
01700       reload();
01701     } else {
01702       addTotalColumn( i18n("Total"), 70 );
01703       reload(openFolders);
01704     }
01705     mPopup->setItemChecked( mTotalPop, isTotalActive() );
01706   } else if (column == foldersize) {
01707     // switch total
01708     if ( isSizeActive() )
01709     {
01710       removeSizeColumn();
01711       reload();
01712     } else {
01713       addSizeColumn( i18n("Size"), 70 );
01714       reload( openFolders );
01715     }
01716     // toggle KPopupMenu
01717     mPopup->setItemChecked( mSizePop, isSizeActive() );
01718 
01719   } else kdDebug(5006) << "unknown column:" << column << endl;
01720 
01721   // toggles the switches of the mainwin
01722   emit columnsChanged();
01723 }
01724 
01725 //-----------------------------------------------------------------------------
01726 void KMFolderTree::slotToggleUnreadColumn()
01727 {
01728   toggleColumn(unread);
01729 }
01730 
01731 //-----------------------------------------------------------------------------
01732 void KMFolderTree::slotToggleTotalColumn()
01733 {
01734   // activate the total-column and force the folders to be opened
01735   toggleColumn(total, true);
01736 }
01737 
01738 //-----------------------------------------------------------------------------
01739 void KMFolderTree::slotToggleSizeColumn()
01740 {
01741   // activate the size-column and force the folders to be opened
01742   toggleColumn(foldersize, true);
01743 }
01744 
01745 
01746 //-----------------------------------------------------------------------------
01747 bool KMFolderTree::eventFilter( QObject *o, QEvent *e )
01748 {
01749   if ( e->type() == QEvent::MouseButtonPress &&
01750       static_cast<QMouseEvent*>(e)->button() == RightButton &&
01751       o->isA("QHeader") )
01752   {
01753     mPopup->popup( static_cast<QMouseEvent*>(e)->globalPos() );
01754     return true;
01755   }
01756   return KFolderTree::eventFilter(o, e);
01757 }
01758 
01759 //-----------------------------------------------------------------------------
01760 void KMFolderTree::slotCheckMail()
01761 {
01762   if (!currentItem())
01763     return;
01764   KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(currentItem());
01765   KMFolder* folder = fti->folder();
01766   if (folder && folder->storage() ) {
01767       if ( KMAccount* acct = folder->storage()->account() ) {
01768          kmkernel->acctMgr()->singleCheckMail(acct, true);
01769       }
01770   }
01771 }
01772 
01773 //-----------------------------------------------------------------------------
01774 void KMFolderTree::slotNewMessageToMailingList()
01775 {
01776   KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>( currentItem() );
01777   if ( !fti || !fti->folder() )
01778     return;
01779   KMCommand *command = new KMMailingListPostCommand( this, fti->folder() );
01780   command->start();
01781 }
01782 
01783 //-----------------------------------------------------------------------------
01784 void KMFolderTree::createFolderList( QStringList *str,
01785                                      QValueList<QGuardedPtr<KMFolder> > *folders,
01786                                      bool localFolders,
01787                                      bool imapFolders,
01788                                      bool dimapFolders,
01789                                      bool searchFolders,
01790                                      bool includeNoContent,
01791                                      bool includeNoChildren )
01792 {
01793   for ( QListViewItemIterator it( this ) ; it.current() ; ++it )
01794   {
01795     KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current());
01796     if (!fti || !fti->folder()) continue;
01797     // type checks
01798     KMFolder* folder = fti->folder();
01799     if (!imapFolders && folder->folderType() == KMFolderTypeImap) continue;
01800     if (!dimapFolders && folder->folderType() == KMFolderTypeCachedImap) continue;
01801     if (!localFolders && (folder->folderType() == KMFolderTypeMbox ||
01802                           folder->folderType() == KMFolderTypeMaildir)) continue;
01803     if (!searchFolders && folder->folderType() == KMFolderTypeSearch) continue;
01804     if (!includeNoContent && folder->noContent()) continue;
01805     if (!includeNoChildren && folder->noChildren()) continue;
01806     QString prefix;
01807     prefix.fill( ' ', 2 * fti->depth() );
01808     str->append(prefix + fti->text(0));
01809     folders->append(fti->folder());
01810   }
01811 }
01812 
01813 //-----------------------------------------------------------------------------
01814 void KMFolderTree::slotResetFolderList( QListViewItem* item, bool startList )
01815 {
01816   if ( !item )
01817     item = currentItem();
01818 
01819   KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>( item );
01820   if ( fti && fti->folder() &&
01821        fti->folder()->folderType() == KMFolderTypeImap )
01822   {
01823     KMFolderImap *folder = static_cast<KMFolderImap*>( fti->folder()->storage() );
01824     folder->setSubfolderState( KMFolderImap::imapNoInformation );
01825     if ( startList )
01826       folder->listDirectory();
01827   }
01828 }
01829 
01830 //-----------------------------------------------------------------------------
01831 void KMFolderTree::showFolder( KMFolder* folder )
01832 {
01833   if ( !folder ) return;
01834   QListViewItem* item = indexOfFolder( folder );
01835   if ( item )
01836   {
01837     doFolderSelected( item );
01838     ensureItemVisible( item );
01839   }
01840 }
01841 
01842 //-----------------------------------------------------------------------------
01843 void KMFolderTree::folderToPopupMenu( MenuAction action, QObject *receiver,
01844     KMMenuToFolder *aMenuToFolder, QPopupMenu *menu, QListViewItem *item )
01845 {
01846   while ( menu->count() )
01847   {
01848     QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
01849     if ( popup )
01850       delete popup;
01851     else
01852       menu->removeItemAt( 0 );
01853   }
01854   // connect the signals
01855   if ( action == MoveMessage || action == MoveFolder )
01856   {
01857     disconnect( menu, SIGNAL(activated(int)), receiver,
01858         SLOT(moveSelectedToFolder(int)) );
01859     connect( menu, SIGNAL(activated(int)), receiver,
01860         SLOT(moveSelectedToFolder(int)) );
01861   } else {
01862     disconnect( menu, SIGNAL(activated(int)), receiver,
01863         SLOT(copySelectedToFolder(int)) );
01864     connect( menu, SIGNAL(activated(int)), receiver,
01865         SLOT(copySelectedToFolder(int)) );
01866   }
01867   if ( !item ) {
01868     item = firstChild();
01869 
01870     // avoid a popup menu with the single entry 'Local Folders' if there
01871     // are no IMAP accounts
01872     if ( childCount() == 2 && action != MoveFolder ) { // only 'Local Folders' and 'Searches'
01873       KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>( item );
01874       if ( fti->protocol() == KFolderTreeItem::Search ) {
01875         // skip 'Searches'
01876         item = item->nextSibling();
01877         fti = static_cast<KMFolderTreeItem*>( item );
01878       }
01879       folderToPopupMenu( action, receiver, aMenuToFolder, menu, fti->firstChild() );
01880       return;
01881     }
01882   }
01883 
01884   while ( item )
01885   {
01886     KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( item );
01887     if ( fti->protocol() == KFolderTreeItem::Search )
01888     {
01889       // skip search folders
01890       item = item->nextSibling();
01891       continue;
01892     }
01893     QString label = fti->text( 0 );
01894     label.replace( "&","&&" );
01895     if ( fti->firstChild() )
01896     {
01897       // new level
01898       QPopupMenu* popup = new QPopupMenu( menu, "subMenu" );
01899       folderToPopupMenu( action, receiver, aMenuToFolder, popup, fti->firstChild() );
01900       bool subMenu = false;
01901       if ( ( action == MoveMessage || action == CopyMessage ) &&
01902            fti->folder() && !fti->folder()->noContent() )
01903         subMenu = true;
01904       if ( ( action == MoveFolder || action == CopyFolder )
01905           && ( !fti->folder() || ( fti->folder() && !fti->folder()->noChildren() ) ) )
01906         subMenu = true;
01907 
01908       QString sourceFolderName;
01909       KMFolderTreeItem* srcItem = dynamic_cast<KMFolderTreeItem*>( currentItem() );
01910       if ( srcItem )
01911         sourceFolderName = srcItem->text( 0 );
01912 
01913       if ( (action == MoveFolder || action == CopyFolder)
01914               && fti->folder() && fti->folder()->child()
01915               && fti->folder()->child()->hasNamedFolder( sourceFolderName ) ) {
01916         subMenu = false;
01917       }
01918 
01919       if ( subMenu )
01920       {
01921         int menuId;
01922         if ( action == MoveMessage || action == MoveFolder )
01923           menuId = popup->insertItem( i18n("Move to This Folder"), -1, 0 );
01924         else
01925           menuId = popup->insertItem( i18n("Copy to This Folder"), -1, 0 );
01926         popup->insertSeparator( 1 );
01927         aMenuToFolder->insert( menuId, fti->folder() );
01928       }
01929       menu->insertItem( label, popup );
01930     } else
01931     {
01932       // insert an item
01933       int menuId = menu->insertItem( label );
01934       if ( fti->folder() )
01935         aMenuToFolder->insert( menuId, fti->folder() );
01936       bool enabled = (fti->folder() ? true : false);
01937       if ( fti->folder() &&
01938            ( fti->folder()->isReadOnly() || fti->folder()->noContent() ) )
01939         enabled = false;
01940       menu->setItemEnabled( menuId, enabled );
01941     }
01942 
01943     item = item->nextSibling();
01944   }
01945 }
01946 
01947 //-----------------------------------------------------------------------------
01948 void KMFolderTree::moveSelectedToFolder( int menuId )
01949 {
01950   moveOrCopyFolder( selectedFolders(), mMenuToFolder[ menuId ], true /*move*/ );
01951 }
01952 
01953 //-----------------------------------------------------------------------------
01954 void KMFolderTree::copySelectedToFolder( int menuId )
01955 {
01956   moveOrCopyFolder( selectedFolders(), mMenuToFolder[ menuId ], false /*copy, don't move*/ );
01957 }
01958 
01959 //-----------------------------------------------------------------------------
01960 void KMFolderTree::moveOrCopyFolder( QValueList<QGuardedPtr<KMFolder> > sources, KMFolder* destination, bool move )
01961 {
01962   kdDebug(5006) << k_funcinfo << "source: " << sources << " destination: " << destination << " move: " << move << endl;
01963 
01964   // Disable drag during copy operation since it prevents from many crashes
01965   setDragEnabled( false );
01966 
01967   KMFolderDir* parent = &(kmkernel->folderMgr()->dir());
01968   if ( destination )
01969     parent = destination->createChildFolder();
01970 
01971   QStringList sourceFolderNames;
01972 
01973   // check if move/copy is possible at all
01974   for ( QValueList<QGuardedPtr<KMFolder> >::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it ) {
01975     KMFolder* source = *it;
01976 
01977     // check if folder with same name already exits
01978     QString sourceFolderName;
01979     if ( source )
01980       sourceFolderName = source->label();
01981 
01982     if ( parent->hasNamedFolder( sourceFolderName ) || sourceFolderNames.contains( sourceFolderName ) ) {
01983       KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> here because a folder with the same name already exists.</qt>")
01984           .arg( sourceFolderName ) );
01985       setDragEnabled( true );
01986       return;
01987     }
01988     sourceFolderNames.append( sourceFolderName );
01989 
01990     // don't move/copy a folder that's still not completely moved/copied
01991     KMFolder *f = source;
01992     while ( f ) {
01993       if ( f->moveInProgress() ) {
01994         KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> because it is not completely copied itself.</qt>")
01995             .arg( sourceFolderName ) );
01996         setDragEnabled( true );
01997         return;
01998       }
01999       if ( f->parent() )
02000         f = f->parent()->owner();
02001     }
02002 
02003     QString message =
02004       i18n( "<qt>Cannot move or copy folder <b>%1</b> into a subfolder below itself.</qt>" ).
02005           arg( sourceFolderName );
02006     KMFolderDir* folderDir = parent;
02007     // check that the folder can be moved
02008     if ( source && source->child() )
02009     {
02010       while ( folderDir && ( folderDir != &kmkernel->folderMgr()->dir() ) &&
02011           ( folderDir != source->parent() ) )
02012       {
02013         if ( folderDir->findRef( source ) != -1 )
02014         {
02015           KMessageBox::error( this, message );
02016           setDragEnabled( true );
02017           return;
02018         }
02019         folderDir = folderDir->parent();
02020       }
02021     }
02022 
02023     if( source && source->child() && parent &&
02024         ( parent->path().find( source->child()->path() + "/" ) == 0 ) ) {
02025       KMessageBox::error( this, message );
02026       setDragEnabled( true );
02027       return;
02028     }
02029 
02030     if( source && source->child()
02031         && ( parent == source->child() ) ) {
02032       KMessageBox::error( this, message );
02033       setDragEnabled( true );
02034       return;
02035     }
02036   }
02037 
02038   // check if the source folders are independent of each other
02039   for ( QValueList<QGuardedPtr<KMFolder> >::ConstIterator it = sources.constBegin(); move && it != sources.constEnd(); ++it ) {
02040     KMFolderDir *parentDir = (*it)->child();
02041     if ( !parentDir )
02042       continue;
02043     for ( QValueList<QGuardedPtr<KMFolder> >::ConstIterator it2 = sources.constBegin(); it2 != sources.constEnd(); ++it2 ) {
02044       if ( *it == *it2 )
02045         continue;
02046       KMFolderDir *childDir = (*it2)->parent();
02047       do {
02048         if ( parentDir == childDir || parentDir->findRef( childDir->owner() ) != -1 ) {
02049           KMessageBox::error( this, i18n("Moving the selected folders is not possible") );
02050           setDragEnabled( true );
02051           return;
02052         }
02053         childDir = childDir->parent();
02054       }
02055       while ( childDir && childDir != &kmkernel->folderMgr()->dir() );
02056     }
02057   }
02058 
02059   // de-select moved source folders (can cause crash due to unGetMsg() in KMHeaders)
02060   if ( move ) {
02061     doFolderSelected( indexOfFolder( destination ), false );
02062     oldCurrent = currentItem();
02063   }
02064 
02065   // do the actual move/copy
02066   for ( QValueList<QGuardedPtr<KMFolder> >::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it ) {
02067     KMFolder* source = *it;
02068     if ( move ) {
02069       kdDebug(5006) << "move folder " << (source ? source->label(): "Unknown") << " to "
02070         << ( destination ? destination->label() : "Local Folders" ) << endl;
02071       kmkernel->folderMgr()->moveFolder( source, parent );
02072     } else {
02073       kmkernel->folderMgr()->copyFolder( source, parent );
02074     }
02075   }
02076 }
02077 
02078 QDragObject * KMFolderTree::dragObject()
02079 {
02080   KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>
02081       (itemAt(viewport()->mapFromGlobal(QCursor::pos())));
02082   if ( !item || !item->parent() || !item->folder() ) // top-level items or something invalid
02083     return 0;
02084   mCopySourceFolders = selectedFolders();
02085 
02086   QDragObject *drag = KFolderTree::dragObject();
02087   if ( drag )
02088     drag->setPixmap( SmallIcon("folder") );
02089   return drag;
02090 }
02091 
02092 void KMFolderTree::copyFolder()
02093 {
02094   KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() );
02095   if ( item ) {
02096     mCopySourceFolders = selectedFolders();
02097     mCutFolder = false;
02098   }
02099   updateCopyActions();
02100 }
02101 
02102 void KMFolderTree::cutFolder()
02103 {
02104   KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() );
02105   if ( item ) {
02106     mCopySourceFolders = selectedFolders();
02107     mCutFolder = true;
02108   }
02109   updateCopyActions();
02110 }
02111 
02112 void KMFolderTree::pasteFolder()
02113 {
02114   KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() );
02115   if ( !mCopySourceFolders.isEmpty() && item && !mCopySourceFolders.contains( item->folder() ) ) {
02116     moveOrCopyFolder( mCopySourceFolders, item->folder(), mCutFolder );
02117     if ( mCutFolder )
02118       mCopySourceFolders.clear();
02119   }
02120   updateCopyActions();
02121 }
02122 
02123 void KMFolderTree::updateCopyActions()
02124 {
02125   KAction *copy = mMainWidget->action("copy_folder");
02126   KAction *cut = mMainWidget->action("cut_folder");
02127   KAction *paste = mMainWidget->action("paste_folder");
02128   KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() );
02129 
02130   if ( !item ||  !item->folder() ) {
02131     copy->setEnabled( false );
02132     cut->setEnabled( false );
02133   } else {
02134     copy->setEnabled( true );
02135     cut->setEnabled( item->folder()->isMoveable() );
02136   }
02137 
02138   if ( mCopySourceFolders.isEmpty() )
02139     paste->setEnabled( false );
02140   else
02141     paste->setEnabled( true );
02142 }
02143 
02144 void KMFolderTree::slotAddToFavorites()
02145 {
02146   KMail::FavoriteFolderView *favView = mMainWidget->favoriteFolderView();
02147   assert( favView );
02148   for ( QListViewItemIterator it( this ); it.current(); ++it ) {
02149     if ( it.current()->isSelected() )
02150       favView->addFolder( static_cast<KMFolderTreeItem*>( it.current() ) );
02151   }
02152 }
02153 
02154 void KMFolderTree::slotUnhideLocalInbox()
02155 {
02156   disconnect( kmkernel->inboxFolder(), SIGNAL(msgAdded(KMFolder*,Q_UINT32)),
02157               this, SLOT(slotUnhideLocalInbox()) );
02158   reload();
02159 }
02160 
02161 void KMFolderTree::delayedReload()
02162 {
02163   QTimer::singleShot( 0, this, SLOT(reload()) );
02164 }
02165 
02166 #include "kmfoldertree.moc"