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