kmail

kmfoldertree.cpp

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