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