// kmfoldertree.cpp #ifdef HAVE_CONFIG_H #include <config.h> #endif #include "kmfoldertree.h" #include "kmfoldermgr.h" #include "kmfolder.h" #include "kmfolderimap.h" #include "kmfoldercachedimap.h" #include "kmfolderdia.h" #include "kmheaders.h" #include "kmmainwidget.h" #include "kmailicalifaceimpl.h" #include "accountmanager.h" using KMail::AccountManager; #include "globalsettings.h" #include "kmcommands.h" #include "foldershortcutdialog.h" #include "expirypropertiesdialog.h" #include "newfolderdialog.h" #include "acljobs.h" #include "messagecopyhelper.h" using KMail::MessageCopyHelper; #include "favoritefolderview.h" #include "folderviewtooltip.h" using KMail::FolderViewToolTip; #include <maillistdrag.h> using namespace KPIM; #include <tdeapplication.h> #include <tdeglobalsettings.h> #include <kiconloader.h> #include <tdemessagebox.h> #include <tdeconfig.h> #include <tdepopupmenu.h> #include <kdebug.h> #include <tqpainter.h> #include <tqcursor.h> #include <tqregexp.h> #include <tqpopupmenu.h> #include <unistd.h> #include <assert.h> #include <X11/Xlib.h> #include <fixx11h.h> //============================================================================= KMFolderTreeItem::KMFolderTreeItem( KFolderTree *parent, const TQString & name, KFolderTreeItem::Protocol protocol ) : TQObject( parent, name.latin1() ), KFolderTreeItem( parent, name, protocol, Root ), mFolder( 0 ), mNeedsRepaint( true ) { init(); setPixmap( 0, normalIcon( iconSize() ) ); } //----------------------------------------------------------------------------- KMFolderTreeItem::KMFolderTreeItem( KFolderTree *parent, const TQString & name, KMFolder* folder ) : TQObject( parent, name.latin1() ), KFolderTreeItem( parent, name ), mFolder( folder ), mNeedsRepaint( true ) { init(); setPixmap( 0, normalIcon( iconSize() ) ); } //----------------------------------------------------------------------------- KMFolderTreeItem::KMFolderTreeItem( KFolderTreeItem *parent, const TQString & name, KMFolder* folder ) : TQObject( 0, name.latin1() ), KFolderTreeItem( parent, name ), mFolder( folder ), mNeedsRepaint( true ) { init(); setPixmap( 0, normalIcon( iconSize() ) ); } KMFolderTreeItem::~KMFolderTreeItem() { } static KFolderTreeItem::Protocol protocolFor( KMFolderType t ) { switch ( t ) { case KMFolderTypeImap: return KFolderTreeItem::Imap; case KMFolderTypeCachedImap: return KFolderTreeItem::CachedImap; case KMFolderTypeMbox: case KMFolderTypeMaildir: return KFolderTreeItem::Local; case KMFolderTypeSearch: return KFolderTreeItem::Search; default: return KFolderTreeItem::NONE; } } TQPixmap KMFolderTreeItem::normalIcon(int size) const { TQString icon; if ( (!mFolder && type() == Root) || useTopLevelIcon() ) { switch ( protocol() ) { case KFolderTreeItem::Imap: case KFolderTreeItem::CachedImap: case KFolderTreeItem::News: icon = "server"; break; case KFolderTreeItem::Search: icon = "viewmag";break; default: icon = "folder";break; } } else { // special folders switch ( type() ) { case Inbox: icon = "folder_inbox"; break; case Outbox: icon = "folder_outbox"; break; case SentMail: icon = "folder_sent_mail"; break; case Trash: icon = "trashcan_empty"; break; case Drafts: icon = "edit"; break; case Templates: icon = "document-new"; break; default: { //If not a resource folder don't try to use icalIface folder pixmap if(kmkernel->iCalIface().isResourceFolder( mFolder )) icon = kmkernel->iCalIface().folderPixmap( type() ); break; } } // non-root search folders if ( protocol() == KMFolderTreeItem::Search ) { icon = "mail_find"; } if ( mFolder && mFolder->noContent() ) { icon = "folder_grey"; } } if ( icon.isEmpty() ) icon = "folder"; if (mFolder && mFolder->useCustomIcons() ) { icon = mFolder->normalIconPath(); } TDEIconLoader * il = TDEGlobal::instance()->iconLoader(); TQPixmap pm = il->loadIcon( icon, TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); if ( mFolder && pm.isNull() ) { pm = il->loadIcon( mFolder->normalIconPath(), TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); } return pm; } TQPixmap KMFolderTreeItem::unreadIcon(int size) const { TQPixmap pm; if ( !mFolder || useTopLevelIcon() || mFolder->isSystemFolder() || kmkernel->folderIsTrash( mFolder ) || kmkernel->folderIsTemplates( mFolder ) || kmkernel->folderIsDraftOrOutbox( mFolder ) ) pm = normalIcon( size ); TDEIconLoader * il = TDEGlobal::instance()->iconLoader(); if ( mFolder && mFolder->useCustomIcons() ) { pm = il->loadIcon( mFolder->unreadIconPath(), TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); if ( pm.isNull() ) pm = il->loadIcon( mFolder->normalIconPath(), TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); } if ( pm.isNull() ) { if ( mFolder && mFolder->noContent() ) { pm = il->loadIcon( "folder_grey_open", TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); } else { if( kmkernel->iCalIface().isResourceFolder( mFolder ) ) pm = il->loadIcon( kmkernel->iCalIface().folderPixmap( type() ), TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); if ( pm.isNull() ) pm = il->loadIcon( "folder_open", TDEIcon::Small, size, TDEIcon::DefaultState, 0, true ); } } return pm; } void KMFolderTreeItem::init() { if ( !mFolder ) return; setProtocol( protocolFor( mFolder->folderType() ) ); if ( useTopLevelIcon() ) setType(Root); else { if ( mFolder == kmkernel->inboxFolder() ) setType( Inbox ); else if ( kmkernel->folderIsDraftOrOutbox( mFolder ) ) { if ( mFolder == kmkernel->outboxFolder() ) setType( Outbox ); else setType( Drafts ); } else if ( kmkernel->folderIsSentMailFolder( mFolder ) ) setType( SentMail ); else if ( kmkernel->folderIsTrash( mFolder ) ) setType( Trash ); else if ( kmkernel->folderIsTemplates( mFolder ) ) setType( Templates ); else if( kmkernel->iCalIface().isResourceFolder(mFolder) ) setType( kmkernel->iCalIface().folderType(mFolder) ); // System folders on dimap or imap which are not resource folders are // inboxes. Urgs. if ( mFolder->isSystemFolder() && !kmkernel->iCalIface().isResourceFolder( mFolder) && ( mFolder->folderType() == KMFolderTypeImap || mFolder->folderType() == KMFolderTypeCachedImap ) ) setType( Inbox ); } if ( !mFolder->isSystemFolder() ) setRenameEnabled( 0, false ); KMFolderTree* tree = dynamic_cast<KMFolderTree*>( listView() ); if ( tree ) tree->insertIntoFolderToItemMap( mFolder, this ); } void KMFolderTreeItem::adjustUnreadCount( int newUnreadCount ) { // adjust the icons if the folder is now newly unread or // now newly not-unread if ( newUnreadCount != 0 && unreadCount() == 0 ) setPixmap( 0, unreadIcon( iconSize() ) ); if ( unreadCount() != 0 && newUnreadCount == 0 ) setPixmap( 0, normalIcon( iconSize() ) ); setUnreadCount( newUnreadCount ); } void KMFolderTreeItem::slotIconsChanged() { kdDebug(5006) << k_funcinfo << endl; // this is prone to change, so better check KFolderTreeItem::Type newType = type(); if( kmkernel->iCalIface().isResourceFolder( mFolder ) ) newType = kmkernel->iCalIface().folderType(mFolder); // reload the folder tree if the type changed, needed because of the // various type-dependent folder hiding options if ( type() != newType ) static_cast<KMFolderTree*>( listView() )->delayedReload(); setType( newType ); if ( unreadCount() > 0 ) setPixmap( 0, unreadIcon( iconSize() ) ); else setPixmap( 0, normalIcon( iconSize() ) ); emit iconChanged( this ); repaint(); } void KMFolderTreeItem::slotNameChanged() { setText( 0, mFolder->label() ); emit nameChanged( this ); repaint(); } void KMFolderTreeItem::slotNoContentChanged() { // reload the folder tree if the no content state changed, needed because // we hide no-content folders if their child nodes are hidden TQTimer::singleShot( 0, static_cast<KMFolderTree*>( listView() ), TQT_SLOT(reload()) ); } //----------------------------------------------------------------------------- bool KMFolderTreeItem::acceptDrag(TQDropEvent* e) const { // Do not allow drags from the favorite folder view, as they don't really // make sense and do not work. KMMainWidget *mainWidget = static_cast<KMFolderTree*>( listView() )->mainWidget(); assert( mainWidget ); if ( mainWidget->favoriteFolderView() && e->source() == mainWidget->favoriteFolderView()->viewport() ) return false; if ( protocol() == KFolderTreeItem::Search ) return false; // nothing can be dragged into search folders if ( e->provides( KPIM::MailListDrag::format() ) ) { if ( !mFolder || mFolder->moveInProgress() || mFolder->isReadOnly() || (mFolder->noContent() && childCount() == 0) || (mFolder->noContent() && isOpen()) ) { return false; } else { return true; } } else if ( e->provides("application/x-qlistviewitem") ) { // wtf: protocol() is NONE instead of Local for the local root folder if ( !mFolder && protocol() == KFolderTreeItem::NONE && type() == KFolderTreeItem::Root ) return true; // local top-level folder if ( !mFolder || mFolder->isReadOnly() || mFolder->noContent() ) return false; return true; } return false; } //----------------------------------------------------------------------------- void KMFolderTreeItem::slotShowExpiryProperties() { if ( !mFolder ) return; KMFolderTree* tree = static_cast<KMFolderTree*>( listView() ); KMail::ExpiryPropertiesDialog *dlg = new KMail::ExpiryPropertiesDialog( tree, mFolder ); dlg->show(); } //----------------------------------------------------------------------------- void KMFolderTreeItem::properties() { if ( !mFolder ) return; KMail::FolderTreeBase* tree = static_cast<KMail::FolderTreeBase*>( listView() ); tree->mainWidget()->modifyFolder( this ); //Nothing here the above may actually delete this KMFolderTreeItem } //----------------------------------------------------------------------------- void KMFolderTreeItem::assignShortcut() { if ( !mFolder ) return; KMail::FolderShortcutDialog *shorty = new KMail::FolderShortcutDialog( mFolder, kmkernel->getKMMainWidget(), listView() ); shorty->exec(); delete shorty; } //----------------------------------------------------------------------------- void KMFolderTreeItem::updateCount() { if ( !folder() ) { setTotalCount( -1 ); return; } KMail::FolderTreeBase* tree = dynamic_cast<KMail::FolderTreeBase*>( listView() ); if ( !tree ) return; tree->slotUpdateCounts( folder(), true /* force update */ ); } //============================================================================= KMFolderTree::KMFolderTree( KMMainWidget *mainWidget, TQWidget *parent, const char *name ) : KMail::FolderTreeBase( mainWidget, parent, name ) , mUpdateTimer( 0, "mUpdateTimer" ) , autoopen_timer( 0, "autoopen_timer" ) { oldSelected = 0; oldCurrent = 0; mLastItem = 0; dropItem = 0; mMainWidget = mainWidget; mReloading = false; mCutFolder = false; mUpdateCountTimer= new TQTimer( this, "mUpdateCountTimer" ); setDragEnabled( true ); addAcceptableDropMimetype( "application/x-qlistviewitem", false ); setSelectionModeExt( Extended ); int namecol = addColumn( i18n("Folder"), 250 ); header()->setStretchEnabled( true, namecol ); setResizeMode( TQListView::NoColumn ); // connect connectSignals(); // popup to switch columns header()->setClickEnabled(true); header()->installEventFilter(this); mPopup = new TDEPopupMenu(this); mPopup->insertTitle(i18n("View Columns")); mPopup->setCheckable(true); mUnreadPop = mPopup->insertItem(i18n("Unread Column"), this, TQT_SLOT(slotToggleUnreadColumn())); mTotalPop = mPopup->insertItem(i18n("Total Column"), this, TQT_SLOT(slotToggleTotalColumn())); mSizePop = mPopup->insertItem(i18n("Size Column"), this, TQT_SLOT(slotToggleSizeColumn())); connect( this, TQT_SIGNAL( triggerRefresh() ), this, TQT_SLOT( refresh() ) ); new FolderViewToolTip( this ); } //----------------------------------------------------------------------------- // connects all needed signals to their slots void KMFolderTree::connectSignals() { connect( mUpdateCountTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotUpdateCountTimeout()) ); connect(&mUpdateTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(delayedUpdate())); connect(kmkernel->folderMgr(), TQT_SIGNAL(changed()), this, TQT_SLOT(doFolderListChanged())); connect(kmkernel->folderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), this, TQT_SLOT(slotFolderRemoved(KMFolder*))); connect(kmkernel->folderMgr(), TQT_SIGNAL(folderMoveOrCopyOperationFinished()), this, TQT_SLOT(slotFolderMoveOrCopyOperationFinished())); connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(changed()), this, TQT_SLOT(doFolderListChanged())); connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), this, TQT_SLOT(slotFolderRemoved(KMFolder*))); connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(changed()), this, TQT_SLOT(doFolderListChanged())); connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), this, TQT_SLOT(slotFolderRemoved(KMFolder*))); connect(kmkernel->searchFolderMgr(), TQT_SIGNAL(changed()), this, TQT_SLOT(doFolderListChanged())); connect(kmkernel->acctMgr(), TQT_SIGNAL(accountRemoved(KMAccount*)), this, TQT_SLOT(slotAccountRemoved(KMAccount*))); connect(kmkernel->acctMgr(), TQT_SIGNAL(accountAdded(KMAccount*)), this, TQT_SLOT(slotUnhideLocalInbox())); connect(kmkernel->searchFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), this, TQT_SLOT(slotFolderRemoved(KMFolder*))); connect( &autoopen_timer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( openFolder() ) ); connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint &, int ) ), this, TQT_SLOT( slotContextMenuRequested( TQListViewItem*, const TQPoint & ) ) ); connect( this, TQT_SIGNAL( expanded( TQListViewItem* ) ), this, TQT_SLOT( slotFolderExpanded( TQListViewItem* ) ) ); connect( this, TQT_SIGNAL( collapsed( TQListViewItem* ) ), this, TQT_SLOT( slotFolderCollapsed( TQListViewItem* ) ) ); connect( this, TQT_SIGNAL( itemRenamed( TQListViewItem*, int, const TQString &)), this, TQT_SLOT( slotRenameFolder( TQListViewItem*, int, const TQString &))); connect( this, TQT_SIGNAL(folderSelected(KMFolder*)), TQT_SLOT(updateCopyActions()) ); } //----------------------------------------------------------------------------- void KMFolderTree::readConfig (void) { TDEConfig* conf = KMKernel::config(); readColorConfig(); // Custom/Ssystem font support { TDEConfigGroupSaver saver(conf, "Fonts"); if (!conf->readBoolEntry("defaultFonts",true)) { TQFont folderFont( TDEGlobalSettings::generalFont() ); setFont(conf->readFontEntry("folder-font", &folderFont)); } else setFont(TDEGlobalSettings::generalFont()); } // restore the layout restoreLayout(conf, "Geometry"); } //----------------------------------------------------------------------------- // Save the configuration file void KMFolderTree::writeConfig() { // save the current state of the folders for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) { KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (fti) writeIsListViewItemOpen(fti); } // save the current layout saveLayout(KMKernel::config(), "Geometry"); } //----------------------------------------------------------------------------- // Updates the count of unread messages (count of unread messages // is now cached in KMails config file) void KMFolderTree::updateUnreadAll() { bool upd = isUpdatesEnabled(); setUpdatesEnabled(false); KMFolderDir* fdir; KMFolderNode* folderNode; KMFolder* folder; fdir = &kmkernel->folderMgr()->dir(); for (folderNode = fdir->first(); folderNode != 0; folderNode =fdir->next()) { if (!folderNode->isDir()) { folder = static_cast<KMFolder*>(folderNode); folder->open("updateunread"); folder->countUnread(); folder->close("updateunread"); } } setUpdatesEnabled(upd); } //----------------------------------------------------------------------------- // Reload the tree of items in the list view void KMFolderTree::reload(bool openFolders) { if ( mReloading ) { // no parallel reloads are allowed kdDebug(5006) << "KMFolderTree::reload - already reloading" << endl; return; } mReloading = true; int top = contentsY(); mLastItem = 0; // invalidate selected drop item oldSelected = 0; // remember last KMFolder* last = currentFolder(); KMFolder* selected = 0; KMFolder* oldCurrentFolder = ( oldCurrent ? static_cast<KMFolderTreeItem*>(oldCurrent)->folder(): 0 ); for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) { KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current()); writeIsListViewItemOpen( fti ); if ( fti->isSelected() ) selected = fti->folder(); } mFolderToItem.clear(); clear(); // construct the root of the local folders KMFolderTreeItem * root = new KMFolderTreeItem( this, i18n("Local Folders") ); root->setOpen( readIsListViewItemOpen(root) ); KMFolderDir * fdir = &kmkernel->folderMgr()->dir(); addDirectory(fdir, root); fdir = &kmkernel->imapFolderMgr()->dir(); // each imap-account creates it's own root addDirectory(fdir, 0); fdir = &kmkernel->dimapFolderMgr()->dir(); // each dimap-account creates it's own root addDirectory(fdir, 0); // construct the root of the search folder hierarchy: root = new KMFolderTreeItem( this, i18n("Searches"), KFolderTreeItem::Search ); root->setOpen( readIsListViewItemOpen( root ) ); fdir = &kmkernel->searchFolderMgr()->dir(); addDirectory(fdir, root); if (openFolders) { // we open all folders to update the count mUpdateIterator = TQListViewItemIterator (this); TQTimer::singleShot( 0, this, TQT_SLOT(slotUpdateOneCount()) ); } for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) { KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current()); if ( !fti || !fti->folder() ) continue; disconnect(fti->folder(),TQT_SIGNAL(iconsChanged()), fti,TQT_SLOT(slotIconsChanged())); connect(fti->folder(),TQT_SIGNAL(iconsChanged()), fti,TQT_SLOT(slotIconsChanged())); disconnect(fti->folder(),TQT_SIGNAL(nameChanged()), fti,TQT_SLOT(slotNameChanged())); connect(fti->folder(),TQT_SIGNAL(nameChanged()), fti,TQT_SLOT(slotNameChanged())); disconnect( fti->folder(), TQT_SIGNAL(noContentChanged()), fti, TQT_SLOT(slotNoContentChanged()) ); connect( fti->folder(), TQT_SIGNAL(noContentChanged()), fti, TQT_SLOT(slotNoContentChanged()) ); disconnect( fti->folder(), TQT_SIGNAL(syncStateChanged()), this, TQT_SLOT(slotSyncStateChanged()) ); connect( fti->folder(), TQT_SIGNAL(syncStateChanged()), this, TQT_SLOT(slotSyncStateChanged()) ); // we want to be noticed of changes to update the unread/total columns disconnect(fti->folder(), TQT_SIGNAL(msgAdded(KMFolder*,TQ_UINT32)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); connect(fti->folder(), TQT_SIGNAL(msgAdded(KMFolder*,TQ_UINT32)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); //} disconnect(fti->folder(), TQT_SIGNAL(numUnreadMsgsChanged(KMFolder*)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); connect(fti->folder(), TQT_SIGNAL(numUnreadMsgsChanged(KMFolder*)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); disconnect(fti->folder(), TQT_SIGNAL(msgRemoved(KMFolder*)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); connect(fti->folder(), TQT_SIGNAL(msgRemoved(KMFolder*)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); disconnect(fti->folder(), TQT_SIGNAL(folderSizeChanged( KMFolder* )), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); connect(fti->folder(), TQT_SIGNAL(folderSizeChanged( KMFolder* )), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); disconnect(fti->folder(), TQT_SIGNAL(shortcutChanged(KMFolder*)), mMainWidget, TQT_SLOT( slotShortcutChanged(KMFolder*))); connect(fti->folder(), TQT_SIGNAL(shortcutChanged(KMFolder*)), mMainWidget, TQT_SLOT( slotShortcutChanged(KMFolder*))); if (!openFolders) slotUpdateCounts(fti->folder()); // populate the size column fti->setFolderSize( 0 ); fti->setFolderIsCloseToQuota( fti->folder()->storage()->isCloseToQuota() ); } ensureVisible(0, top + visibleHeight(), 0, 0); // if current and selected folder did not change set it again for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) { if ( last && static_cast<KMFolderTreeItem*>( it.current() )->folder() == last ) { mLastItem = static_cast<KMFolderTreeItem*>( it.current() ); setCurrentItem( it.current() ); } if ( selected && static_cast<KMFolderTreeItem*>( it.current() )->folder() == selected ) { setSelected( it.current(), true ); } if ( oldCurrentFolder && static_cast<KMFolderTreeItem*>( it.current() )->folder() == oldCurrentFolder ) { oldCurrent = it.current(); } } refresh(); mReloading = false; } //----------------------------------------------------------------------------- void KMFolderTree::slotUpdateOneCount() { if ( !mUpdateIterator.current() ) return; KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(mUpdateIterator.current()); ++mUpdateIterator; if ( !fti->folder() ) { // next one please TQTimer::singleShot( 0, this, TQT_SLOT(slotUpdateOneCount()) ); return; } // open the folder and update the count bool open = fti->folder()->isOpened(); if (!open) fti->folder()->open("updatecount"); slotUpdateCounts(fti->folder()); // restore previous state if (!open) fti->folder()->close("updatecount"); TQTimer::singleShot( 0, this, TQT_SLOT(slotUpdateOneCount()) ); } //----------------------------------------------------------------------------- // Recursively add a directory of folders to the tree of folders void KMFolderTree::addDirectory( KMFolderDir *fdir, KMFolderTreeItem* parent ) { for ( KMFolderNode * node = fdir->first() ; node ; node = fdir->next() ) { if ( node->isDir() ) continue; KMFolder * folder = static_cast<KMFolder*>(node); KMFolderTreeItem * fti = 0; if (!parent) { // create new root-item, but only if this is not the root of a // "groupware folders only" account if ( kmkernel->iCalIface().hideResourceAccountRoot( folder ) ) continue; // it needs a folder e.g. to save it's state (open/close) fti = new KMFolderTreeItem( this, folder->label(), folder ); fti->setExpandable( true ); // add child-folders if (folder && folder->child()) { addDirectory( folder->child(), fti ); } } else { // hide local inbox if unused if ( kmkernel->inboxFolder() == folder && hideLocalInbox() ) { connect( kmkernel->inboxFolder(), TQT_SIGNAL(msgAdded(KMFolder*,TQ_UINT32)), TQT_SLOT(slotUnhideLocalInbox()) ); continue; } // create new child fti = new KMFolderTreeItem( parent, folder->label(), folder ); // set folders explicitely to exandable when they have children // this way we can do a listing for IMAP folders when the user expands them // even when the child folders are not created yet if ( folder->storage()->hasChildren() == FolderStorage::HasChildren ) { fti->setExpandable( true ); } else { fti->setExpandable( false ); } // add child-folders if (folder && folder->child()) { addDirectory( folder->child(), fti ); } // Check if this is an IMAP resource folder or a no-content parent only // containing groupware folders if ( (kmkernel->iCalIface().hideResourceFolder( folder ) || folder->noContent()) && fti->childCount() == 0 ) { // It is removeFromFolderToItemMap( folder ); delete fti; // still, it might change in the future, so we better check the change signals connect ( folder, TQT_SIGNAL(noContentChanged()), TQT_SLOT(delayedReload()) ); continue; } connect (fti, TQT_SIGNAL(iconChanged(KMFolderTreeItem*)), this, TQT_SIGNAL(iconChanged(KMFolderTreeItem*))); connect (fti, TQT_SIGNAL(nameChanged(KMFolderTreeItem*)), this, TQT_SIGNAL(nameChanged(KMFolderTreeItem*))); } // restore last open-state fti->setOpen( readIsListViewItemOpen(fti) ); } // for-end } //----------------------------------------------------------------------------- // Initiate a delayed refresh of the tree void KMFolderTree::refresh() { mUpdateTimer.changeInterval(200); } //----------------------------------------------------------------------------- // Updates the pixmap and extendedLabel information for items void KMFolderTree::delayedUpdate() { bool upd = isUpdatesEnabled(); if ( upd ) { setUpdatesEnabled(false); for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) { KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (!fti || !fti->folder()) continue; if ( fti->needsRepaint() ) { fti->repaint(); fti->setNeedsRepaint( false ); } } setUpdatesEnabled(upd); } mUpdateTimer.stop(); } //----------------------------------------------------------------------------- // Folders have been added/deleted update the tree of folders void KMFolderTree::doFolderListChanged() { reload(); } //----------------------------------------------------------------------------- void KMFolderTree::slotAccountRemoved(KMAccount *) { doFolderSelected( firstChild() ); } //----------------------------------------------------------------------------- void KMFolderTree::slotFolderMoveOrCopyOperationFinished() { setDragEnabled( true ); } //----------------------------------------------------------------------------- void KMFolderTree::slotFolderRemoved(KMFolder *aFolder) { TQListViewItem *item = indexOfFolder(aFolder); if (!item) return; KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*> ( item ); if ( oldCurrent == fti ) oldCurrent = 0; if ( oldSelected == fti ) oldSelected = 0; if (!fti || !fti->folder()) return; if (fti == currentItem()) { TQListViewItem *qlvi = fti->itemAbove(); if (!qlvi) qlvi = fti->itemBelow(); doFolderSelected( qlvi ); } removeFromFolderToItemMap( aFolder ); if ( dropItem == fti ) { // The removed item is the dropItem dropItem = 0; // it becomes invalid } delete fti; updateCopyActions(); } //----------------------------------------------------------------------------- // Methods for navigating folders with the keyboard void KMFolderTree::prepareItem( KMFolderTreeItem* fti ) { for ( TQListViewItem * parent = fti->parent() ; parent ; parent = parent->parent() ) parent->setOpen( true ); ensureItemVisible( fti ); } //----------------------------------------------------------------------------- void KMFolderTree::nextUnreadFolder() { nextUnreadFolder( false ); } //----------------------------------------------------------------------------- void KMFolderTree::nextUnreadFolder(bool confirm) { TQListViewItemIterator it( currentItem() ? currentItem() : firstChild() ); if ( currentItem() ) ++it; // don't find current item for ( ; it.current() ; ++it ) { //check if folder is one to stop on KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (checkUnreadFolder(fti,confirm)) return; } //Now if confirm is true we are doing "ReadOn" //we have got to the bottom of the folder list //so we have to start at the top if (confirm) { for ( it = firstChild() ; it.current() ; ++it ) { //check if folder is one to stop on KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (checkUnreadFolder(fti,confirm)) return; } } } //----------------------------------------------------------------------------- bool KMFolderTree::checkUnreadFolder (KMFolderTreeItem* fti, bool confirm) { if ( fti && fti->folder() && !fti->folder()->ignoreNewMail() && ( fti->folder()->countUnread() > 0 ) ) { // Don't change into the trash or outbox folders. if (fti->type() == KFolderTreeItem::Trash || fti->type() == KFolderTreeItem::Outbox ) return false; if (confirm) { // Skip drafts, sent mail and templates as well, when reading mail with // the space bar but not when changing into the next folder with unread // mail via ctrl+ or ctrl- so we do this only if (confirm == true), // which means we are doing readOn. if ( fti->type() == KFolderTreeItem::Drafts || fti->type() == KFolderTreeItem::Templates || fti->type() == KFolderTreeItem::SentMail ) return false; // warn user that going to next folder - but keep track of // whether he wishes to be notified again in "AskNextFolder" // parameter (kept in the config file for kmail) if ( KMessageBox::questionYesNo( this, i18n( "<qt>Go to the next unread message in folder <b>%1</b>?</qt>" ) .arg( fti->folder()->label() ), i18n( "Go to Next Unread Message" ), i18n("Go To"), i18n("Do Not Go To"), // defaults "AskNextFolder", false) == KMessageBox::No ) return true; } prepareItem( fti ); blockSignals( true ); doFolderSelected( fti ); blockSignals( false ); emit folderSelectedUnread( fti->folder() ); return true; } return false; } //----------------------------------------------------------------------------- void KMFolderTree::prevUnreadFolder() { TQListViewItemIterator it( currentItem() ? currentItem() : lastItem() ); if ( currentItem() ) --it; // don't find current item for ( ; it.current() ; --it ) { KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (checkUnreadFolder(fti,false)) return; } } //----------------------------------------------------------------------------- void KMFolderTree::incCurrentFolder() { TQListViewItemIterator it( currentItem() ); ++it; KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (fti) { prepareItem( fti ); setFocus(); setCurrentItem( fti ); } } //----------------------------------------------------------------------------- void KMFolderTree::decCurrentFolder() { TQListViewItemIterator it( currentItem() ); --it; KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(it.current()); if (fti) { prepareItem( fti ); setFocus(); setCurrentItem( fti ); } } //----------------------------------------------------------------------------- void KMFolderTree::selectCurrentFolder() { KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( currentItem() ); if (fti) { prepareItem( fti ); doFolderSelected( fti ); } } //----------------------------------------------------------------------------- KMFolder *KMFolderTree::currentFolder() const { KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( currentItem() ); if (fti ) return fti->folder(); else return 0; } TQValueList<TQGuardedPtr<KMFolder> > KMFolderTree::selectedFolders() { TQValueList<TQGuardedPtr<KMFolder> > rv; for ( TQListViewItemIterator it( this ); it.current(); ++it ) { if ( it.current()->isSelected() ) { KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>( it.current() ); rv.append( fti->folder() ); } } return rv; } //----------------------------------------------------------------------------- // When not dragging and dropping a change in the selected item // indicates the user has changed the active folder emit a signal // so that the header list and reader window can be udpated. void KMFolderTree::doFolderSelected( TQListViewItem* qlvi, bool keepSelection ) { if (!qlvi) return; if ( mLastItem && mLastItem == qlvi && (keepSelection || selectedFolders().count() == 1) ) return; KMFolderTreeItem* fti = static_cast< KMFolderTreeItem* >(qlvi); KMFolder* folder = 0; if (fti) folder = fti->folder(); if (mLastItem && mLastItem != fti && mLastItem->folder() && (mLastItem->folder()->folderType() == KMFolderTypeImap)) { KMFolderImap *imapFolder = static_cast<KMFolderImap*>(mLastItem->folder()->storage()); imapFolder->setSelected(false); } mLastItem = fti; if ( !keepSelection ) clearSelection(); setCurrentItem( qlvi ); if ( !keepSelection ) setSelected( qlvi, true ); ensureItemVisible( qlvi ); if (!folder) { emit folderSelected(0); // Root has been selected } else { emit folderSelected(folder); slotUpdateCounts(folder); } } //----------------------------------------------------------------------------- void KMFolderTree::resizeEvent(TQResizeEvent* e) { TDEConfig* conf = KMKernel::config(); TDEConfigGroupSaver saver(conf, "Geometry"); conf->writeEntry(name(), size().width()); TDEListView::resizeEvent(e); } //----------------------------------------------------------------------------- // show context menu void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi, const TQPoint &p ) { if (!lvi) return; setCurrentItem( lvi ); if (!mMainWidget) return; // safe bet KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(lvi); if ( !isSelected( fti ) ) doFolderSelected( fti ); else if ( fti != mLastItem ) doFolderSelected( fti, true ); if (!fti ) return; TDEPopupMenu *folderMenu = new TDEPopupMenu; bool multiFolder = selectedFolders().count() > 1; if (fti->folder()) folderMenu->insertTitle(fti->folder()->label()); // outbox specific, but there it's the most used action if ( (fti->folder() == kmkernel->outboxFolder()) && fti->folder()->count() ) mMainWidget->action("send_queued")->plug( folderMenu ); // Mark all as read is supposedly used often, therefor it is first if ( fti->folder() && !fti->folder()->noContent() ) mMainWidget->action("mark_all_as_read")->plug( folderMenu ); /* Treat the special case of the root and account folders */ if ((!fti->folder() || (fti->folder()->noContent() && !fti->parent()))) { TQString createChild = i18n("&New Subfolder..."); if (!fti->folder()) createChild = i18n("&New Folder..."); if ( ( fti->folder() || (fti->text(0) != i18n("Searches")) ) && !multiFolder) folderMenu->insertItem(SmallIconSet("folder-new"), createChild, this, TQT_SLOT(addChildFolder())); if (!fti->folder()) { mMainWidget->action("compact_all_folders")->plug(folderMenu); mMainWidget->action("expire_all_folders")->plug(folderMenu); } else if (fti->folder()->folderType() == KMFolderTypeImap) { folderMenu->insertItem(SmallIconSet("mail_get"), i18n("Check &Mail"), this, TQT_SLOT(slotCheckMail())); } } else { // regular folders folderMenu->insertSeparator(); if ( !fti->folder()->noChildren() && !multiFolder ) { folderMenu->insertItem(SmallIconSet("folder-new"), i18n("&New Subfolder..."), this, TQT_SLOT(addChildFolder())); } // copy folder TQPopupMenu *copyMenu = new TQPopupMenu( folderMenu ); folderToPopupMenu( CopyFolder, TQT_TQOBJECT(this), &mMenuToFolder, copyMenu ); folderMenu->insertItem( i18n("&Copy Folder To"), copyMenu ); if ( fti->folder()->isMoveable() && fti->folder()->canDeleteMessages() ) { TQPopupMenu *moveMenu = new TQPopupMenu( folderMenu ); folderToPopupMenu( MoveFolder, TQT_TQOBJECT(this), &mMenuToFolder, moveMenu ); folderMenu->insertItem( i18n("&Move Folder To"), moveMenu ); } // Want to be able to display properties for ALL folders, // so we can edit expiry properties. // -- smp. if (!fti->folder()->noContent()) { if ( !multiFolder ) mMainWidget->action("search_messages")->plug(folderMenu); mMainWidget->action( "archive_folder" )->plug( folderMenu ); mMainWidget->action("compact")->plug(folderMenu); if ( GlobalSettings::self()->enableFavoriteFolderView() ) { folderMenu->insertItem( SmallIconSet("bookmark_add"), i18n("Add to Favorite Folders"), this, TQT_SLOT(slotAddToFavorites()) ); } folderMenu->insertSeparator(); mMainWidget->action("empty")->plug(folderMenu); if ( !fti->folder()->isSystemFolder() ) { mMainWidget->action("delete_folder")->plug(folderMenu); } folderMenu->insertSeparator(); } } /* plug in IMAP and DIMAP specific things */ if (fti->folder() && (fti->folder()->folderType() == KMFolderTypeImap || fti->folder()->folderType() == KMFolderTypeCachedImap )) { folderMenu->insertItem(SmallIconSet("bookmark_folder"), i18n("Serverside Subscription..."), mMainWidget, TQT_SLOT(slotSubscriptionDialog())); folderMenu->insertItem(SmallIcon("bookmark_folder"), i18n("Local Subscription..."), mMainWidget, TQT_SLOT(slotLocalSubscriptionDialog())); if (!fti->folder()->noContent()) { mMainWidget->action("refresh_folder")->plug(folderMenu); if ( fti->folder()->folderType() == KMFolderTypeImap && !multiFolder ) { folderMenu->insertItem(SmallIconSet("reload"), i18n("Refresh Folder List"), this, TQT_SLOT(slotResetFolderList())); } } if ( fti->folder()->folderType() == KMFolderTypeCachedImap && !multiFolder ) { KMFolderCachedImap * folder = static_cast<KMFolderCachedImap*>( fti->folder()->storage() ); folderMenu->insertItem( SmallIconSet("wizard"), i18n("&Troubleshoot IMAP Cache..."), folder, TQT_SLOT(slotTroubleshoot()) ); } folderMenu->insertSeparator(); } if ( fti->folder() && fti->folder()->isMailingListEnabled() && !multiFolder ) { mMainWidget->action("post_message")->plug(folderMenu); } if (fti->folder() && fti->parent() && !multiFolder) { folderMenu->insertItem(SmallIconSet("configure_shortcuts"), i18n("&Assign Shortcut..."), fti, TQT_SLOT(assignShortcut())); if ( !fti->folder()->noContent() && fti->folder()->canDeleteMessages() ) { folderMenu->insertItem( i18n("Expire..."), fti, TQT_SLOT( slotShowExpiryProperties() ) ); } mMainWidget->action("modify")->plug(folderMenu); } kmkernel->setContextMenuShown( true ); folderMenu->exec (p, 0); kmkernel->setContextMenuShown( false ); triggerUpdate(); delete folderMenu; folderMenu = 0; } //----------------------------------------------------------------------------- void KMFolderTree::contentsMousePressEvent(TQMouseEvent * e) { // KFolderTree messes around with the selection mode TDEListView::contentsMousePressEvent( e ); } // If middle button and folder holds mailing-list, create a message to that list void KMFolderTree::contentsMouseReleaseEvent(TQMouseEvent* me) { TQListViewItem *lvi = currentItem(); // Needed for when branches are clicked on ButtonState btn = me->button(); doFolderSelected(lvi, true); // get underlying folder KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>(lvi); if (!fti || !fti->folder()) { KFolderTree::contentsMouseReleaseEvent(me); return; } // react on middle-button only if (btn != Qt::MidButton) { KFolderTree::contentsMouseReleaseEvent(me); return; } if ( fti->folder()->isMailingListEnabled() ) { KMCommand *command = new KMMailingListPostCommand( this, fti->folder() ); command->start(); } KFolderTree::contentsMouseReleaseEvent(me); } // little static helper static bool folderHasCreateRights( const KMFolder *folder ) { bool createRights = true; // we don't have acls for local folders yet if ( folder && folder->folderType() == KMFolderTypeImap ) { const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() ); createRights = imapFolder->userRightsState() != KMail::ACLJobs::Ok || // hack, we should get the acls ( imapFolder->userRightsState() == KMail::ACLJobs::Ok && ( imapFolder->userRights() & KMail::ACLJobs::Create ) ); } else if ( folder && folder->folderType() == KMFolderTypeCachedImap ) { const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() ); createRights = dimapFolder->userRightsState() != KMail::ACLJobs::Ok || ( dimapFolder->userRightsState() == KMail::ACLJobs::Ok && ( dimapFolder->userRights() & KMail::ACLJobs::Create ) ); } return createRights; } //----------------------------------------------------------------------------- // Create a subfolder. // Requires creating the appropriate subdirectory and show a dialog void KMFolderTree::addChildFolder( KMFolder *folder, TQWidget * parent ) { KMFolder *aFolder = folder; if ( !aFolder ) { KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(currentItem()); if (!fti) return; aFolder = fti->folder(); } if (aFolder) { if (!aFolder->createChildFolder()) return; if ( !folderHasCreateRights( aFolder ) ) { const TQString message = i18n( "<qt>Cannot create folder under <b>%1</b> because of insufficient " "permissions on the server. If you think you should be able to create " "subfolders here, ask your administrator to grant you rights to do so." "</qt> " ).arg(aFolder->label()); KMessageBox::error( this, message ); return; } } if ( parent ) ( new KMail::NewFolderDialog( parent, aFolder ) )->exec(); else ( new KMail::NewFolderDialog( this, aFolder ) )->show(); return; /* KMFolderDir *dir = &(kmkernel->folderMgr()->dir()); if (aFolder) dir = aFolder->child(); KMFolderDialog *d = new KMFolderDialog(0, dir, this, i18n("Create Subfolder") ); if (d->exec()) { // fti may be deleted here TQListViewItem *qlvi = indexOfFolder( aFolder ); if (qlvi) { qlvi->setOpen(true); blockSignals( true ); setCurrentItem( qlvi ); blockSignals( false ); } } delete d; // update if added to root Folder if (!aFolder || aFolder->noContent()) { doFolderListChanged(); } */ } //----------------------------------------------------------------------------- // Returns whether a folder directory should be open as specified in the // config file. bool KMFolderTree::readIsListViewItemOpen(KMFolderTreeItem *fti) { TDEConfig* config = KMKernel::config(); KMFolder *folder = fti->folder(); TQString name; if (folder) { name = "Folder-" + folder->idString(); } else if (fti->type() == KFolderTreeItem::Root) { if (fti->protocol() == KFolderTreeItem::NONE) // local root name = "Folder_local_root"; else if (fti->protocol() == KFolderTreeItem::Search) name = "Folder_search"; else return false; } else { return false; } TDEConfigGroupSaver saver(config, name); return config->readBoolEntry("isOpen", false); } //----------------------------------------------------------------------------- // Saves open/closed state of a folder directory into the config file void KMFolderTree::writeIsListViewItemOpen(KMFolderTreeItem *fti) { TDEConfig* config = KMKernel::config(); KMFolder *folder = fti->folder(); TQString name; if (folder && !folder->idString().isEmpty()) { name = "Folder-" + folder->idString(); } else if (fti->type() == KFolderTreeItem::Root) { if (fti->protocol() == KFolderTreeItem::NONE) // local root name = "Folder_local_root"; else if (fti->protocol() == KFolderTreeItem::Search) name = "Folder_search"; else return; } else { return; } TDEConfigGroupSaver saver(config, name); config->writeEntry("isOpen", fti->isOpen() ); } //----------------------------------------------------------------------------- void KMFolderTree::cleanupConfigFile() { if ( childCount() == 0 ) return; // just in case reload wasn't called before TDEConfig* config = KMKernel::config(); TQStringList existingFolders; TQListViewItemIterator fldIt(this); TQMap<TQString,bool> folderMap; KMFolderTreeItem *fti; for (TQListViewItemIterator fldIt(this); fldIt.current(); fldIt++) { fti = static_cast<KMFolderTreeItem*>(fldIt.current()); if (fti && fti->folder()) folderMap.insert(fti->folder()->idString(), true); } TQStringList groupList = config->groupList(); TQString name; for (TQStringList::Iterator grpIt = groupList.begin(); grpIt != groupList.end(); grpIt++) { if ((*grpIt).left(7) != "Folder-") continue; name = (*grpIt).mid(7); if (folderMap.find(name) == folderMap.end()) { KMFolder* folder = kmkernel->findFolderById( name ); if ( folder ) { if ( kmkernel->iCalIface().hideResourceFolder( folder ) || kmkernel->iCalIface().hideResourceAccountRoot( folder ) ) continue; // hidden IMAP resource folder, don't delete info if ( folder->noContent() ) continue; // we hide nocontent folders if they have no child folders if ( folder == kmkernel->inboxFolder() ) continue; // local inbox can be hidden as well } //KMessageBox::error( 0, "cleanupConfigFile: Deleting group " + *grpIt ); config->deleteGroup(*grpIt, true); kdDebug(5006) << "Deleting information about folder " << name << endl; } } } //----------------------------------------------------------------------------- void KMFolderTree::openFolder() { autoopen_timer.stop(); if ( dropItem && !dropItem->isOpen() ) { dropItem->setOpen( true ); dropItem->repaint(); } } static const int autoopenTime = 750; //----------------------------------------------------------------------------- void KMFolderTree::contentsDragEnterEvent( TQDragEnterEvent *e ) { oldCurrent = 0; oldSelected = 0; oldCurrent = currentItem(); for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) if ( it.current()->isSelected() ) oldSelected = it.current(); setFocus(); TQListViewItem *i = itemAt( contentsToViewport(e->pos()) ); if ( i ) { dropItem = i; autoopen_timer.start( autoopenTime ); } else dropItem = 0; e->accept( acceptDrag(e) ); } //----------------------------------------------------------------------------- void KMFolderTree::contentsDragMoveEvent( TQDragMoveEvent *e ) { TQPoint vp = contentsToViewport(e->pos()); TQListViewItem *i = itemAt( vp ); if ( i ) { bool dragAccepted = acceptDrag( e ); if ( dragAccepted ) { setCurrentItem( i ); } if ( i != dropItem ) { autoopen_timer.stop(); dropItem = i; autoopen_timer.start( autoopenTime ); } if ( dragAccepted ) { e->accept( itemRect(i) ); switch ( e->action() ) { case TQDropEvent::Copy: break; case TQDropEvent::Move: e->acceptAction(); break; case TQDropEvent::Link: e->acceptAction(); break; default: ; } } else { e->accept( false ); } } else { e->accept( false ); autoopen_timer.stop(); dropItem = 0; } } //----------------------------------------------------------------------------- void KMFolderTree::contentsDragLeaveEvent( TQDragLeaveEvent * ) { if (!oldCurrent) return; autoopen_timer.stop(); dropItem = 0; setCurrentItem( oldCurrent ); if ( oldSelected ) setSelected( oldSelected, true ); } //----------------------------------------------------------------------------- void KMFolderTree::contentsDropEvent( TQDropEvent *e ) { autoopen_timer.stop(); TQListViewItem *item = itemAt( contentsToViewport(e->pos()) ); KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item); // Check that each pointer is not null for ( TQValueList<TQGuardedPtr<KMFolder> >::ConstIterator it = mCopySourceFolders.constBegin(); it != mCopySourceFolders.constEnd(); ++it ) { if ( ! (*it) ) { fti = 0; break; } } if (fti && mCopySourceFolders.count() == 1) { KMFolder *source = mCopySourceFolders.first(); // if we are dragging to ourselves or to our parent, set fti to 0 so nothing is done if (source == fti->folder() || source->parent()->owner() == fti->folder()) fti = 0; } if (fti && acceptDrag(e) && ( fti != oldSelected || e->source() != mMainWidget->headers()->viewport() ) ) { if ( e->provides("application/x-qlistviewitem") ) { int action = dndMode( true /* always ask */ ); if ( (action == DRAG_COPY || action == DRAG_MOVE) && !mCopySourceFolders.isEmpty() ) { for ( TQValueList<TQGuardedPtr<KMFolder> >::ConstIterator it = mCopySourceFolders.constBegin(); it != mCopySourceFolders.constEnd(); ++it ) { if ( ! (*it)->isMoveable() ) action = DRAG_COPY; } moveOrCopyFolder( mCopySourceFolders, fti->folder(), (action == DRAG_MOVE) ); } } else { if ( e->source() == mMainWidget->headers()->viewport() ) { int action; if ( mMainWidget->headers()->folder() && mMainWidget->headers()->folder()->isReadOnly() ) action = DRAG_COPY; else action = dndMode(); // KMHeaders does copy/move itself if ( action == DRAG_MOVE && fti->folder() ) emit folderDrop( fti->folder() ); else if ( action == DRAG_COPY && fti->folder() ) emit folderDropCopy( fti->folder() ); } else { handleMailListDrop( e, fti->folder() ); } } e->accept( true ); } else e->accept( false ); dropItem = 0; setCurrentItem( oldCurrent ); if ( oldCurrent) mLastItem = static_cast<KMFolderTreeItem*>(oldCurrent); if ( oldSelected ) { clearSelection(); setSelected( oldSelected, true ); } mCopySourceFolders.clear(); } //----------------------------------------------------------------------------- void KMFolderTree::slotFolderExpanded( TQListViewItem * item ) { KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item); if ( !fti || !fti->folder() || !fti->folder()->storage() ) return; fti->setFolderSize( fti->folder()->storage()->folderSize() ); if( fti->folder()->folderType() == KMFolderTypeImap ) { KMFolderImap *folder = static_cast<KMFolderImap*>( fti->folder()->storage() ); // if we should list all folders we limit this to the root folder if ( !folder->account() || ( !folder->account()->listOnlyOpenFolders() && fti->parent() ) ) return; if ( folder->getSubfolderState() == KMFolderImap::imapNoInformation ) { // check if all parents are expanded TQListViewItem *parent = item->parent(); while ( parent ) { if ( !parent->isOpen() ) return; parent = parent->parent(); } // the tree will be reloaded after that bool success = folder->listDirectory(); if (!success) fti->setOpen( false ); if ( fti->childCount() == 0 && fti->parent() ) fti->setExpandable( false ); } } } //----------------------------------------------------------------------------- void KMFolderTree::slotFolderCollapsed( TQListViewItem * item ) { slotResetFolderList( item, false ); KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item); if ( !fti || !fti->folder() || !fti->folder()->storage() ) return; fti->setFolderSize( fti->folder()->storage()->folderSize() ); } //----------------------------------------------------------------------------- void KMFolderTree::slotRenameFolder(TQListViewItem *item, int col, const TQString &text) { KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>(item); if ((!fti) || (fti && fti->folder() && col != 0 && !currentFolder()->child())) return; TQString fldName, oldFldName; oldFldName = fti->name(0); if (!text.isEmpty()) fldName = text; else fldName = oldFldName; fldName.replace("/", ""); fldName.replace(TQRegExp("^\\."), ""); if (fldName.isEmpty()) fldName = i18n("unnamed"); fti->setText(0, fldName); fti->folder()->rename(fldName, &(kmkernel->folderMgr()->dir())); } //----------------------------------------------------------------------------- void KMFolderTree::slotUpdateCountsDelayed(KMFolder * folder) { // kdDebug(5006) << "KMFolderTree::slotUpdateCountsDelayed()" << endl; if ( !mFolderToUpdateCount.contains( folder->idString() ) ) { // kdDebug( 5006 )<< "adding " << folder->idString() << " to updateCountList " << endl; mFolderToUpdateCount.insert( folder->idString(),folder ); } if ( !mUpdateCountTimer->isActive() ) mUpdateCountTimer->start( 500 ); } void KMFolderTree::slotUpdateCountTimeout() { // kdDebug(5006) << "KMFolderTree::slotUpdateCountTimeout()" << endl; TQMap<TQString,KMFolder*>::iterator it; for ( it= mFolderToUpdateCount.begin(); it!=mFolderToUpdateCount.end(); ++it ) { slotUpdateCounts( it.data() ); } mFolderToUpdateCount.clear(); mUpdateCountTimer->stop(); } void KMFolderTree::updatePopup() const { mPopup->setItemChecked( mUnreadPop, isUnreadActive() ); mPopup->setItemChecked( mTotalPop, isTotalActive() ); mPopup->setItemChecked( mSizePop, isSizeActive() ); } //----------------------------------------------------------------------------- void KMFolderTree::toggleColumn(int column, bool openFolders) { if (column == unread) { // switch unread if ( isUnreadActive() ) { removeUnreadColumn(); reload(); } else { addUnreadColumn( i18n("Unread"), 70 ); reload(); } // toggle TDEPopupMenu mPopup->setItemChecked( mUnreadPop, isUnreadActive() ); } else if (column == total) { // switch total if ( isTotalActive() ) { removeTotalColumn(); reload(); } else { addTotalColumn( i18n("Total"), 70 ); reload(openFolders); } mPopup->setItemChecked( mTotalPop, isTotalActive() ); } else if (column == foldersize) { // switch total if ( isSizeActive() ) { removeSizeColumn(); reload(); } else { addSizeColumn( i18n("Size"), 70 ); reload( openFolders ); } // toggle TDEPopupMenu mPopup->setItemChecked( mSizePop, isSizeActive() ); } else kdDebug(5006) << "unknown column:" << column << endl; // toggles the switches of the mainwin emit columnsChanged(); } //----------------------------------------------------------------------------- void KMFolderTree::slotToggleUnreadColumn() { toggleColumn(unread); } //----------------------------------------------------------------------------- void KMFolderTree::slotToggleTotalColumn() { // activate the total-column and force the folders to be opened toggleColumn(total, true); } //----------------------------------------------------------------------------- void KMFolderTree::slotToggleSizeColumn() { // activate the size-column and force the folders to be opened toggleColumn(foldersize, true); } //----------------------------------------------------------------------------- bool KMFolderTree::eventFilter( TQObject *o, TQEvent *e ) { if ( e->type() == TQEvent::MouseButtonPress && TQT_TQMOUSEEVENT(e)->button() == Qt::RightButton && o->isA(TQHEADER_OBJECT_NAME_STRING) ) { mPopup->popup( TQT_TQMOUSEEVENT(e)->globalPos() ); return true; } return KFolderTree::eventFilter(o, e); } //----------------------------------------------------------------------------- void KMFolderTree::slotCheckMail() { if (!currentItem()) return; KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>(currentItem()); KMFolder* folder = fti->folder(); if (folder && folder->storage() ) { if ( KMAccount* acct = folder->storage()->account() ) { kmkernel->acctMgr()->singleCheckMail(acct, true); } } } //----------------------------------------------------------------------------- void KMFolderTree::slotNewMessageToMailingList() { KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>( currentItem() ); if ( !fti || !fti->folder() ) return; KMCommand *command = new KMMailingListPostCommand( this, fti->folder() ); command->start(); } //----------------------------------------------------------------------------- void KMFolderTree::createFolderList( TQStringList *str, TQValueList<TQGuardedPtr<KMFolder> > *folders, bool localFolders, bool imapFolders, bool dimapFolders, bool searchFolders, bool includeNoContent, bool includeNoChildren ) { for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) { KMFolderTreeItem * fti = static_cast<KMFolderTreeItem*>(it.current()); if (!fti || !fti->folder()) continue; // type checks KMFolder* folder = fti->folder(); if (!imapFolders && folder->folderType() == KMFolderTypeImap) continue; if (!dimapFolders && folder->folderType() == KMFolderTypeCachedImap) continue; if (!localFolders && (folder->folderType() == KMFolderTypeMbox || folder->folderType() == KMFolderTypeMaildir)) continue; if (!searchFolders && folder->folderType() == KMFolderTypeSearch) continue; if (!includeNoContent && folder->noContent()) continue; if (!includeNoChildren && folder->noChildren()) continue; TQString prefix; prefix.fill( ' ', 2 * fti->depth() ); str->append(prefix + fti->text(0)); folders->append(fti->folder()); } } //----------------------------------------------------------------------------- void KMFolderTree::slotResetFolderList( TQListViewItem* item, bool startList ) { if ( !item ) item = currentItem(); KMFolderTreeItem* fti = dynamic_cast<KMFolderTreeItem*>( item ); if ( fti && fti->folder() && fti->folder()->folderType() == KMFolderTypeImap ) { KMFolderImap *folder = static_cast<KMFolderImap*>( fti->folder()->storage() ); folder->setSubfolderState( KMFolderImap::imapNoInformation ); if ( startList ) folder->listDirectory(); } } //----------------------------------------------------------------------------- void KMFolderTree::showFolder( KMFolder* folder ) { if ( !folder ) return; TQListViewItem* item = indexOfFolder( folder ); if ( item ) { doFolderSelected( item ); ensureItemVisible( item ); } } //----------------------------------------------------------------------------- void KMFolderTree::folderToPopupMenu( MenuAction action, TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu, TQListViewItem *item ) { while ( menu->count() ) { TQPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup(); if ( popup ) delete popup; else menu->removeItemAt( 0 ); } // connect the signals if ( action == MoveMessage || action == MoveFolder ) { disconnect( menu, TQT_SIGNAL(activated(int)), receiver, TQT_SLOT(moveSelectedToFolder(int)) ); connect( menu, TQT_SIGNAL(activated(int)), receiver, TQT_SLOT(moveSelectedToFolder(int)) ); } else { disconnect( menu, TQT_SIGNAL(activated(int)), receiver, TQT_SLOT(copySelectedToFolder(int)) ); connect( menu, TQT_SIGNAL(activated(int)), receiver, TQT_SLOT(copySelectedToFolder(int)) ); } if ( !item ) { item = firstChild(); // avoid a popup menu with the single entry 'Local Folders' if there // are no IMAP accounts if ( childCount() == 2 && action != MoveFolder ) { // only 'Local Folders' and 'Searches' KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>( item ); if ( fti->protocol() == KFolderTreeItem::Search ) { // skip 'Searches' item = item->nextSibling(); fti = static_cast<KMFolderTreeItem*>( item ); } folderToPopupMenu( action, receiver, aMenuToFolder, menu, fti->firstChild() ); return; } } while ( item ) { KMFolderTreeItem* fti = static_cast<KMFolderTreeItem*>( item ); if ( fti->protocol() == KFolderTreeItem::Search ) { // skip search folders item = item->nextSibling(); continue; } TQString label = fti->text( 0 ); label.replace( "&","&&" ); if ( fti->firstChild() ) { // new level TQPopupMenu* popup = new TQPopupMenu( menu, "subMenu" ); folderToPopupMenu( action, receiver, aMenuToFolder, popup, fti->firstChild() ); bool subMenu = false; if ( ( action == MoveMessage || action == CopyMessage ) && fti->folder() && !fti->folder()->noContent() ) subMenu = true; if ( ( action == MoveFolder || action == CopyFolder ) && ( !fti->folder() || ( fti->folder() && !fti->folder()->noChildren() ) ) ) subMenu = true; TQString sourceFolderName; KMFolderTreeItem* srcItem = dynamic_cast<KMFolderTreeItem*>( currentItem() ); if ( srcItem ) sourceFolderName = srcItem->text( 0 ); if ( (action == MoveFolder || action == CopyFolder) && fti->folder() && fti->folder()->child() && fti->folder()->child()->hasNamedFolder( sourceFolderName ) ) { subMenu = false; } if ( subMenu ) { int menuId; if ( action == MoveMessage || action == MoveFolder ) menuId = popup->insertItem( i18n("Move to This Folder"), -1, 0 ); else menuId = popup->insertItem( i18n("Copy to This Folder"), -1, 0 ); popup->insertSeparator( 1 ); aMenuToFolder->insert( menuId, fti->folder() ); } menu->insertItem( label, popup ); } else { // insert an item int menuId = menu->insertItem( label ); if ( fti->folder() ) aMenuToFolder->insert( menuId, fti->folder() ); bool enabled = (fti->folder() ? true : false); if ( fti->folder() && ( fti->folder()->isReadOnly() || fti->folder()->noContent() ) ) enabled = false; menu->setItemEnabled( menuId, enabled ); } item = item->nextSibling(); } } //----------------------------------------------------------------------------- void KMFolderTree::moveSelectedToFolder( int menuId ) { moveOrCopyFolder( selectedFolders(), mMenuToFolder[ menuId ], true /*move*/ ); } //----------------------------------------------------------------------------- void KMFolderTree::copySelectedToFolder( int menuId ) { moveOrCopyFolder( selectedFolders(), mMenuToFolder[ menuId ], false /*copy, don't move*/ ); } //----------------------------------------------------------------------------- void KMFolderTree::moveOrCopyFolder( TQValueList<TQGuardedPtr<KMFolder> > sources, KMFolder* destination, bool move ) { kdDebug(5006) << k_funcinfo << "source: " << sources << " destination: " << destination << " move: " << move << endl; // Disable drag during copy operation since it prevents from many crashes setDragEnabled( false ); KMFolderDir* parent = &(kmkernel->folderMgr()->dir()); if ( destination ) parent = destination->createChildFolder(); TQStringList sourceFolderNames; // check if move/copy is possible at all for ( TQValueList<TQGuardedPtr<KMFolder> >::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it ) { KMFolder* source = *it; // check if folder with same name already exits TQString sourceFolderName; if ( source ) sourceFolderName = source->label(); if ( parent->hasNamedFolder( sourceFolderName ) || sourceFolderNames.contains( sourceFolderName ) ) { KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> here because a folder with the same name already exists.</qt>") .arg( sourceFolderName ) ); setDragEnabled( true ); return; } sourceFolderNames.append( sourceFolderName ); // don't move/copy a folder that's still not completely moved/copied KMFolder *f = source; while ( f ) { if ( f->moveInProgress() ) { KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> because it is not completely copied itself.</qt>") .arg( sourceFolderName ) ); setDragEnabled( true ); return; } if ( f->parent() ) f = f->parent()->owner(); } TQString message = i18n( "<qt>Cannot move or copy folder <b>%1</b> into a subfolder below itself.</qt>" ). arg( sourceFolderName ); KMFolderDir* folderDir = parent; // check that the folder can be moved if ( source && source->child() ) { while ( folderDir && ( folderDir != &kmkernel->folderMgr()->dir() ) && ( folderDir != source->parent() ) ) { if ( folderDir->findRef( source ) != -1 ) { KMessageBox::error( this, message ); setDragEnabled( true ); return; } folderDir = folderDir->parent(); } } if( source && source->child() && parent && ( parent->path().find( source->child()->path() + "/" ) == 0 ) ) { KMessageBox::error( this, message ); setDragEnabled( true ); return; } if( source && source->child() && ( parent == source->child() ) ) { KMessageBox::error( this, message ); setDragEnabled( true ); return; } } // check if the source folders are independent of each other for ( TQValueList<TQGuardedPtr<KMFolder> >::ConstIterator it = sources.constBegin(); move && it != sources.constEnd(); ++it ) { KMFolderDir *parentDir = (*it)->child(); if ( !parentDir ) continue; for ( TQValueList<TQGuardedPtr<KMFolder> >::ConstIterator it2 = sources.constBegin(); it2 != sources.constEnd(); ++it2 ) { if ( *it == *it2 ) continue; KMFolderDir *childDir = (*it2)->parent(); do { if ( parentDir == childDir || parentDir->findRef( childDir->owner() ) != -1 ) { KMessageBox::error( this, i18n("Moving the selected folders is not possible") ); setDragEnabled( true ); return; } childDir = childDir->parent(); } while ( childDir && childDir != &kmkernel->folderMgr()->dir() ); } } // de-select moved source folders (can cause crash due to unGetMsg() in KMHeaders) if ( move ) { doFolderSelected( indexOfFolder( destination ), false ); oldCurrent = currentItem(); } // do the actual move/copy for ( TQValueList<TQGuardedPtr<KMFolder> >::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it ) { KMFolder* source = *it; if ( move ) { kdDebug(5006) << "move folder " << (source ? source->label(): "Unknown") << " to " << ( destination ? destination->label() : "Local Folders" ) << endl; kmkernel->folderMgr()->moveFolder( source, parent ); } else { kmkernel->folderMgr()->copyFolder( source, parent ); } } } TQDragObject * KMFolderTree::dragObject() { KMFolderTreeItem *item = static_cast<KMFolderTreeItem*> (itemAt(viewport()->mapFromGlobal(TQCursor::pos()))); if ( !item || !item->parent() || !item->folder() ) // top-level items or something invalid return 0; mCopySourceFolders = selectedFolders(); TQDragObject *drag = KFolderTree::dragObject(); if ( drag ) drag->setPixmap( SmallIcon("folder") ); return drag; } void KMFolderTree::copyFolder() { KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() ); if ( item ) { mCopySourceFolders = selectedFolders(); mCutFolder = false; } updateCopyActions(); } void KMFolderTree::cutFolder() { KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() ); if ( item ) { mCopySourceFolders = selectedFolders(); mCutFolder = true; } updateCopyActions(); } void KMFolderTree::pasteFolder() { KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() ); if ( !mCopySourceFolders.isEmpty() && item && !mCopySourceFolders.contains( item->folder() ) ) { moveOrCopyFolder( mCopySourceFolders, item->folder(), mCutFolder ); if ( mCutFolder ) mCopySourceFolders.clear(); } updateCopyActions(); } void KMFolderTree::updateCopyActions() { TDEAction *copy = mMainWidget->action("copy_folder"); TDEAction *cut = mMainWidget->action("cut_folder"); TDEAction *paste = mMainWidget->action("paste_folder"); KMFolderTreeItem *item = static_cast<KMFolderTreeItem*>( currentItem() ); if ( !item || !item->folder() ) { copy->setEnabled( false ); cut->setEnabled( false ); } else { copy->setEnabled( true ); cut->setEnabled( item->folder()->isMoveable() ); } if ( mCopySourceFolders.isEmpty() ) paste->setEnabled( false ); else paste->setEnabled( true ); } void KMFolderTree::slotSyncStateChanged() { // Only emit the signal when a selected folder changes, otherwise the folder menu is updated // too often TQValueList< TQGuardedPtr<KMFolder> > folders = selectedFolders(); TQValueList< TQGuardedPtr<KMFolder> >::const_iterator it = folders.constBegin(); TQValueList< TQGuardedPtr<KMFolder> >::const_iterator end = folders.constEnd(); while ( it != end ) { TQGuardedPtr<KMFolder> folder = *it; if ( folder == sender() ) { emit syncStateChanged(); break; } ++it; } } void KMFolderTree::slotAddToFavorites() { KMail::FavoriteFolderView *favView = mMainWidget->favoriteFolderView(); assert( favView ); for ( TQListViewItemIterator it( this ); it.current(); ++it ) { if ( it.current()->isSelected() ) favView->addFolder( static_cast<KMFolderTreeItem*>( it.current() ) ); } } void KMFolderTree::slotUnhideLocalInbox() { disconnect( kmkernel->inboxFolder(), TQT_SIGNAL(msgAdded(KMFolder*,TQ_UINT32)), this, TQT_SLOT(slotUnhideLocalInbox()) ); reload(); } void KMFolderTree::delayedReload() { TQTimer::singleShot( 0, this, TQT_SLOT(reload()) ); } #include "kmfoldertree.moc"