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