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