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