From 4b0d2e5d3af69d249a9b41d205df43511c37379a Mon Sep 17 00:00:00 2001 From: Till Adam Date: Mon, 11 Apr 2005 21:51:57 +0000 Subject: [PATCH] Implement "Copy folder to" which allows one to copy a hierarchy of folders to somewhere else in the tree. Works for all source folder types but not for online imap or search target folders. Part of this is the result of a merge of the "Move folder to" functionality in HEAD since some of the infrastructure can be shared. The "move to" part is disabled, though, since it appears to be somewhat buggy and lose metainformation, such as folder types, for example. ContentsType (mail, event, etc) information is retained for the copies. A few const correctness fixes here and there. svn path=/branches/proko2/kdepim/; revision=404928 --- Makefile.am | 2 +- copyfolderjob.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++ copyfolderjob.h | 98 +++++++++++++++++++++ kmacctcachedimap.cpp | 7 +- kmacctcachedimap.h | 6 ++ kmfolder.cpp | 5 ++ kmfolder.h | 3 + kmfolderdir.h | 2 +- kmfolderimap.h | 2 +- kmfoldermgr.cpp | 35 ++++++++ kmfoldermgr.h | 12 +++ kmfoldertree.cpp | 169 +++++++++++++++++++++++++++++++++++ kmfoldertree.h | 23 +++++ renamejob.cpp | 36 ++++---- 14 files changed, 581 insertions(+), 22 deletions(-) create mode 100644 copyfolderjob.cpp create mode 100644 copyfolderjob.h diff --git a/Makefile.am b/Makefile.am index f904acd4f..c1e8f1e11 100644 --- a/Makefile.am +++ b/Makefile.am @@ -96,7 +96,7 @@ libkmailprivate_la_SOURCES = kmmessage.cpp kmmainwin.cpp configuredialog.cpp \ headerlistquicksearch.cpp acljobs.cpp folderdiaacltab.cpp \ partnodebodypart.cpp \ expirejob.cpp compactionjob.cpp jobscheduler.cpp callback.cpp \ - listjob.cpp \ + listjob.cpp renamejob.cpp copyfolderjob.cpp \ composercryptoconfiguration.ui \ warningconfiguration.ui smimeconfiguration.ui annotationjobs.cpp \ accountcombobox.cpp diff --git a/copyfolderjob.cpp b/copyfolderjob.cpp new file mode 100644 index 000000000..855de3f40 --- /dev/null +++ b/copyfolderjob.cpp @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2005 Till Adam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#include "copyfolderjob.h" +#include "folderstorage.h" +#include "kmfolder.h" +#include "kmfolderdir.h" +#include "kmfoldertype.h" +#include "kmfoldermgr.h" +#include "kmcommands.h" +#include "kmmsgbase.h" +#include "undostack.h" + +#include +#include +#include + +using namespace KMail; + +CopyFolderJob::CopyFolderJob( const FolderStorage* const storage, KMFolderDir* const newParent ) + : FolderJob( 0, tOther, (storage ? storage->folder() : 0) ), + mStorage( storage ), mNewParent( newParent ), + mNewFolder( 0 ), mChildFolderNodeIterator( *mStorage->folder()->createChildFolder() ), + mNextChildFolder( 0 ) +{ +} + +CopyFolderJob::~CopyFolderJob() +{ + kdDebug(5006) << k_funcinfo << endl; +} + +/* + * The basic strategy is to first create the target folder, then copy all the mail + * from the source to the target folder, then recurse for each of the folder's children + */ +void CopyFolderJob::execute() +{ + createTargetDir(); + copyMessagesToTargetDir(); +} + +void CopyFolderJob::copyMessagesToTargetDir() +{ + // Hmmmm. Tasty hack. Can I have fries with that? + const_cast( mStorage )->blockSignals( true ); + // move all messages to the new folder + QPtrList msgList; + for ( int i = 0; i < mStorage->count(); i++ ) + { + const KMMsgBase* msgBase = mStorage->getMsgBase( i ); + assert( msgBase ); + msgList.append( msgBase ); + } + if ( msgList.count() == 0 ) { + slotCopyNextChild(); // no contents, check subfolders + const_cast( mStorage )->blockSignals( false ); + } else { + KMCommand *command = new KMCopyCommand( mNewFolder, msgList ); + connect( command, SIGNAL( completed( KMCommand * ) ), + this, SLOT( slotCopyCompleted( KMCommand * ) ) ); + command->start(); + } +} + +void CopyFolderJob::slotCopyCompleted( KMCommand* command ) +{ + kdDebug(5006) << k_funcinfo << (command?command->result():0) << endl; + disconnect( command, SIGNAL( completed( KMCommand * ) ), + this, SLOT( slotCopyCompleted( KMCommand * ) ) ); + + const_cast( mStorage )->blockSignals( false ); + + if ( command && command->result() != KMCommand::OK ) { + rollback(); + } + // if we have children, recurse + if ( mStorage->folder()->child() ) { + slotCopyNextChild(); + } else { + emit folderCopyComplete( true ); + deleteLater(); + } +} + +void CopyFolderJob::slotCopyNextChild( bool success ) +{ + //kdDebug(5006) << k_funcinfo << endl; + if ( mNextChildFolder ) + mNextChildFolder->close(); // refcount + // previous sibling failed + if ( !success ) { + kdDebug(5006) << "Failed to copy one subfolder, let's not continue: " << mNewFolder->prettyURL() << endl; + rollback(); + emit folderCopyComplete( false ); + deleteLater(); + } + + KMFolderNode* node = mChildFolderNodeIterator.current(); + while ( node && node->isDir() ) { + ++mChildFolderNodeIterator; + node = mChildFolderNodeIterator.current(); + } + if ( node ) { + mNextChildFolder = static_cast(node); + ++mChildFolderNodeIterator; + } else { + // no more children, we are done + emit folderCopyComplete( true ); + deleteLater(); + return; + } + + KMFolderDir * const dir = mNewFolder->createChildFolder(); + if ( !dir ) { + kdDebug(5006) << "Failed to create subfolders of: " << mNewFolder->prettyURL() << endl; + emit folderCopyComplete( false ); + deleteLater(); + return; + } + // let it do its thing and report back when we are ready to do the next sibling + mNextChildFolder->open(); // refcount + FolderJob* job = new CopyFolderJob( mNextChildFolder->storage(), dir); + connect( job, SIGNAL( folderCopyComplete( bool ) ), + this, SLOT( slotCopyNextChild( bool ) ) ); + job->start(); +} + + +// FIXME factor into CreateFolderJob and make async, so it works with online imap +// FIXME this is the same in renamejob. Refactor RenameJob to use a copy job and then delete +void CopyFolderJob::createTargetDir() +{ + KMFolderMgr* folderMgr = kmkernel->folderMgr(); + if ( mNewParent->type() == KMImapDir ) { + folderMgr = kmkernel->imapFolderMgr(); + } else if ( mNewParent->type() == KMDImapDir ) { + folderMgr = kmkernel->dimapFolderMgr(); + } + + // get the default mailbox type + KConfig * const config = KMKernel::config(); + KConfigGroupSaver saver(config, "General"); + int deftype = config->readNumEntry("default-mailbox-format", 1); + if ( deftype < 0 || deftype > 1 ) deftype = 1; + + // the type of the new folder + KMFolderType typenew = + ( deftype == 0 ) ? KMFolderTypeMbox : KMFolderTypeMaildir; + if ( mNewParent->owner() ) + typenew = mNewParent->owner()->folderType(); + + mNewFolder = folderMgr->createFolder( mStorage->name(), false, typenew, mNewParent ); + if ( !mNewFolder ) + { + kdWarning(5006) << k_funcinfo << "could not create folder" << endl; + emit folderCopyComplete( false ); + deleteLater(); + return; + } + // inherit the folder type + // FIXME we should probably copy over most if not all settings + mNewFolder->storage()->setContentsType( mStorage->contentsType() ); + mNewFolder->storage()->writeConfig(); + kdDebug(5006)<< "CopyJob::createTargetDir - " << mStorage->folder()->idString() + << " |=> " << mNewFolder->idString() << endl; +} + + +void CopyFolderJob::rollback() +{ + // FIXME do something + KMFolderMgr * const folderMgr = mNewFolder->createChildFolder()->manager(); + folderMgr->remove( mNewFolder ); + emit folderCopyComplete( false ); + deleteLater(); +} +#include "copyfolderjob.moc" diff --git a/copyfolderjob.h b/copyfolderjob.h new file mode 100644 index 000000000..0a6479ee2 --- /dev/null +++ b/copyfolderjob.h @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2005 Till Adam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#ifndef COPYFOLDERJOB_H +#define COPYFOLDERJOB_H + +#include "folderjob.h" + +class FolderStorage; +class KMFolderDir; +class KMFolder; +class KMCommand; + +class KMFolderNode; + +namespace KMail { + +/** + * Copy a hierarchy of folders somewhere else in the folder tree. Currently + * online imap folders are not supported as target folders, and the same is + * true for search folders where it does not make much sense for them to be + * target folders. + */ +class CopyFolderJob : public FolderJob +{ + Q_OBJECT +public: + /** + * Create a new job + * @param storage of the folder that should be copied + * @param newParent the target parent folder + */ + CopyFolderJob( const FolderStorage* const storage, KMFolderDir* const newParent = 0 ); + + virtual ~CopyFolderJob(); + + virtual void execute(); + +protected slots: + + /** Create the target directory under the new parent. */ + void createTargetDir(); + + /** Copy all messages from the original folder to mNewFolder */ + void copyMessagesToTargetDir(); + + /** Called when the CopyCommand has either succesfully completed copying + * the contents of our folder to the new location or failed. */ + void slotCopyCompleted( KMCommand *command ); + + /** Called when the previous sibling's copy operation completed. + * @param success indicates whether the last copy was successful. */ + void slotCopyNextChild( bool success = true ); + + /** Called when one of the operations of the foldre itself or one of it's + * child folders failed and the already created target folder needs to be + * removed again. */ + void rollback(); + +signals: + /** Emitted when the job is done, check the success bool */ + void folderCopyComplete( bool success ); + +protected: + const FolderStorage* const mStorage; + KMFolderDir* const mNewParent; + KMFolder* mNewFolder; + QPtrListIterator mChildFolderNodeIterator; + KMFolder* mNextChildFolder; +}; + +} // namespace KMail + +#endif /* COPYFOLDERJOB_H */ diff --git a/kmacctcachedimap.cpp b/kmacctcachedimap.cpp index 00df169df..eadf10ac8 100644 --- a/kmacctcachedimap.cpp +++ b/kmacctcachedimap.cpp @@ -387,7 +387,7 @@ void KMAcctCachedImap::addDeletedFolder( KMFolder* folder ) if ( folder->folderType() != KMFolderTypeCachedImap ) return; KMFolderCachedImap* storage = static_cast(folder->storage()); - mDeletedFolders << storage->imapPath(); + addDeletedFolder( storage->imapPath() ); kdDebug(5006) << k_funcinfo << storage->imapPath() << endl; // Add all child folders too @@ -402,6 +402,11 @@ void KMAcctCachedImap::addDeletedFolder( KMFolder* folder ) } } +void KMAcctCachedImap::addDeletedFolder( const QString& imapPath ) +{ + mDeletedFolders << imapPath; +} + QStringList KMAcctCachedImap::deletedFolderPaths( const QString& subFolderPath ) const { QStringList lst; diff --git a/kmacctcachedimap.h b/kmacctcachedimap.h index 0e9155309..0ed484673 100644 --- a/kmacctcachedimap.h +++ b/kmacctcachedimap.h @@ -116,6 +116,12 @@ public: */ void addDeletedFolder( KMFolder* folder ); + /** + * Remember that a folder got explicitely deleted - NOT including all child folders + * This is used when renaming a folder. + */ + void addDeletedFolder( const QString& imapPath ); + /** * Ask if a folder was explicitely deleted in this session */ diff --git a/kmfolder.cpp b/kmfolder.cpp index 821e8e1b5..108466340 100644 --- a/kmfolder.cpp +++ b/kmfolder.cpp @@ -805,4 +805,9 @@ void KMFolder::reallyAddCopyOfMsg( KMMessage* aMsg ) mStorage->reallyAddCopyOfMsg( aMsg ); } +bool KMFolder::isMoveable() const +{ + return !isSystemFolder(); +} + #include "kmfolder.moc" diff --git a/kmfolder.h b/kmfolder.h index 5a0998caa..b7902826b 100644 --- a/kmfolder.h +++ b/kmfolder.h @@ -497,6 +497,9 @@ public: bool ignoreNewMail() const { return mIgnoreNewMail; } void setIgnoreNewMail( bool b ) { mIgnoreNewMail = b; } + /** Returns true if this folder can be moved */ + bool isMoveable() const; + signals: /** Emitted when the status, name, or associated accounts of this folder changed. */ diff --git a/kmfolderdir.h b/kmfolderdir.h index 15976fe95..c77d90724 100644 --- a/kmfolderdir.h +++ b/kmfolderdir.h @@ -52,7 +52,7 @@ public: /** Returns the folder whose children we are holding */ KMFolder* owner() const { return mOwner; } - virtual KMFolderDirType type() { return mDirType; } + virtual KMFolderDirType folderDirType() const { return mDirType; } protected: KMFolder * mOwner; diff --git a/kmfolderimap.h b/kmfolderimap.h index 75241b2f2..40965bb1d 100644 --- a/kmfolderimap.h +++ b/kmfolderimap.h @@ -98,7 +98,7 @@ public: virtual KMMessage* getMsg(int idx); /** The path to the imap folder on the server */ void setImapPath(const QString &path) { mImapPath = path; } - QString imapPath() { return mImapPath; } + QString imapPath() const { return mImapPath; } /** The highest UID in the folder */ ulong lastUid(); diff --git a/kmfoldermgr.cpp b/kmfoldermgr.cpp index 9f6c6ed50..452219c92 100644 --- a/kmfoldermgr.cpp +++ b/kmfoldermgr.cpp @@ -30,6 +30,11 @@ #include "undostack.h" #include "kmmsgdict.h" #include "folderstorage.h" +#include "renamejob.h" +#include "copyfolderjob.h" + +using KMail::RenameJob; +using KMail::CopyFolderJob; //----------------------------------------------------------------------------- KMFolderMgr::KMFolderMgr(const QString& aBasePath, KMFolderDirType dirType): @@ -577,4 +582,34 @@ uint KMFolderMgr::createId() return newId; } +//----------------------------------------------------------------------------- +void KMFolderMgr::moveFolder( KMFolder* folder, KMFolderDir *newParent ) +{ + renameFolder( folder, folder->name(), newParent ); +} + +//----------------------------------------------------------------------------- +void KMFolderMgr::renameFolder( KMFolder* folder, const QString& newName, + KMFolderDir *newParent ) +{ + RenameJob* job = new RenameJob( folder->storage(), newName, newParent ); + connect( job, SIGNAL( renameDone( QString, bool ) ), + this, SLOT( slotRenameDone( QString, bool ) ) ); + job->start(); +} + +//----------------------------------------------------------------------------- +void KMFolderMgr::copyFolder( KMFolder* folder, KMFolderDir *newParent ) +{ + kdDebug(5006) << "Copy folder: " << folder->prettyURL() << endl; + CopyFolderJob* job = new CopyFolderJob( folder->storage(), newParent ); + job->start(); +} + +//----------------------------------------------------------------------------- +void KMFolderMgr::slotRenameDone( QString, bool success ) +{ + kdDebug(5006) << k_funcinfo << success << endl; +} + #include "kmfoldermgr.moc" diff --git a/kmfoldermgr.h b/kmfoldermgr.h index f357b5c69..6493ab95a 100644 --- a/kmfoldermgr.h +++ b/kmfoldermgr.h @@ -124,6 +124,15 @@ public: /** Create a new unique ID */ uint createId(); + /** Move a folder */ + void moveFolder( KMFolder* folder, KMFolderDir* newParent ); + + /** Rename or move a folder */ + void renameFolder( KMFolder* folder, const QString& newName, + KMFolderDir* newParent = 0 ); + /** Copy a folder */ + void copyFolder( KMFolder* folder, KMFolderDir* newParent ); + public slots: /** GUI action: compact all folders that need to be compacted */ void compactAll() { compactAllFolders( true ); } @@ -134,6 +143,9 @@ public slots: /** Called from KMFolder::remove when the folderstorage was removed */ void removeFolderAux(KMFolder* obsoleteFolder, bool success); + /** Called when the renaming of a folder is done */ + void slotRenameDone( QString newName, bool success ); + signals: /** Emitted when the list of folders has changed. This signal is a hook where clients like the KMFolderTree tree-view can connect. The signal diff --git a/kmfoldertree.cpp b/kmfoldertree.cpp index 8eabaf177..0c8429a55 100644 --- a/kmfoldertree.cpp +++ b/kmfoldertree.cpp @@ -923,6 +923,22 @@ void KMFolderTree::slotContextMenuRequested( QListViewItem *lvi, i18n("&New Subfolder..."), this, SLOT(addChildFolder())); } +#ifdef DISABLED_BECAUSE_IT_IS_BUGGY + if ( fti->folder()->isMoveable() ) + { + QPopupMenu *moveMenu = new QPopupMenu( folderMenu ); + folderToPopupMenu( MoveFolder, this, &mMenuToFolder, moveMenu ); + folderMenu->insertItem( i18n("&Move Folder To"), moveMenu ); + } +#endif + if ( fti->folder() + && fti->folder()->folderType() != KMFolderTypeImap ) { +// && fti->folder()->folderType() != KMFolderTypeSearch ) { + // copy folder + QPopupMenu *copyMenu = new QPopupMenu( folderMenu ); + folderToPopupMenu( CopyFolder, this, &mMenuToFolder, copyMenu ); + folderMenu->insertItem( i18n("&Copy Folder To"), copyMenu ); + } // Want to be able to display properties for ALL folders, // so we can edit expiry properties. @@ -1623,5 +1639,158 @@ void KMFolderTree::showFolder( KMFolder* folder ) } } +//----------------------------------------------------------------------------- +void KMFolderTree::folderToPopupMenu( MenuAction action, QObject *receiver, + KMMenuToFolder *aMenuToFolder, QPopupMenu *menu, QListViewItem *item ) +{ + while ( menu->count() ) + { + QPopupMenu *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, SIGNAL(activated(int)), receiver, + SLOT(moveSelectedToFolder(int)) ); + connect( menu, SIGNAL(activated(int)), receiver, + SLOT(moveSelectedToFolder(int)) ); + } else { + disconnect( menu, SIGNAL(activated(int)), receiver, + SLOT(copySelectedToFolder(int)) ); + connect( menu, SIGNAL(activated(int)), receiver, + 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( item ); + if ( fti->protocol() == KFolderTreeItem::Search ) { + // skip 'Searches' + item = item->nextSibling(); + fti = static_cast( item ); + } + folderToPopupMenu( action, receiver, aMenuToFolder, menu, fti->firstChild() ); + return; + } + } + + while ( item ) + { + KMFolderTreeItem* fti = static_cast( item ); + if ( fti->protocol() == KFolderTreeItem::Search ) + { + // skip search folders + item = item->nextSibling(); + continue; + } + QString label = fti->text( 0 ); + label.replace( "&","&&" ); + if ( fti->firstChild() ) + { + // new level + QPopupMenu* popup = new QPopupMenu( 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; + 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 ) +{ + moveOrCopyCurrentFolder( mMenuToFolder[ menuId ], true /*move*/ ); +} + +//----------------------------------------------------------------------------- +void KMFolderTree::copySelectedToFolder( int menuId ) +{ + moveOrCopyCurrentFolder( mMenuToFolder[ menuId ], false /*copy, don't move*/ ); +} + +//----------------------------------------------------------------------------- +void KMFolderTree::moveOrCopyCurrentFolder( KMFolder* destination, bool move ) +{ + KMFolder* folder = currentFolder(); + KMFolderDir* parent = &(kmkernel->folderMgr()->dir()); + if ( destination ) + parent = destination->createChildFolder(); + QString message = + i18n( "Cannot move or copy folder %1 into a subfolder below itself." ). + arg( folder->label() ); + + KMFolderDir* folderDir = parent; + // check that the folder can be moved + if ( folder && folder->child() ) + { + while ( folderDir && ( folderDir != &kmkernel->folderMgr()->dir() ) && + ( folderDir != folder->parent() ) ) + { + if ( folderDir->findRef( folder ) != -1 ) + { + KMessageBox::error( this, message ); + return; + } + folderDir = folderDir->parent(); + } + } + + if( folder && folder->child() && parent && + ( parent->path().find( folder->child()->path() + "/" ) == 0 ) ) { + KMessageBox::error( this, message ); + return; + } + + if( folder && folder->child() + && ( parent == folder->child() ) ) { + KMessageBox::error( this, message ); + return; + } + + if ( move ) { + kdDebug(5006) << "move folder " << currentFolder()->label() << " to " + << ( destination ? destination->label() : "Local Folders" ) << endl; + kmkernel->folderMgr()->moveFolder( folder, parent ); + } else { + kmkernel->folderMgr()->copyFolder( folder, parent ); + } +} + #include "kmfoldertree.moc" diff --git a/kmfoldertree.h b/kmfoldertree.h index 446344892..137f40586 100644 --- a/kmfoldertree.h +++ b/kmfoldertree.h @@ -172,6 +172,18 @@ public: { mFolderToItem.remove( folder ); } + + /** Valid actions for the folderToPopup method */ + enum MenuAction { + CopyMessage, + MoveMessage, + CopyFolder, + MoveFolder + }; + + /** Generate a popup menu that contains all folders that can have content */ + void folderToPopupMenu( MenuAction action, QObject *receiver, KMMenuToFolder *, + QPopupMenu *menu, QListViewItem *start = 0 ); signals: /** The selected folder has changed */ @@ -269,6 +281,11 @@ protected slots: void slotNewMessageToMailingList(); + /** For RMB move folder */ + virtual void moveSelectedToFolder( int menuId ); + /** For RMB copy folder */ + virtual void copySelectedToFolder( int menuId ); + protected: /** Catch palette changes */ virtual bool event(QEvent *e); @@ -307,6 +324,9 @@ protected: /** connect all signals */ void connectSignals(); + /** Move or copy the current folder to destination */ + void moveOrCopyCurrentFolder( KMFolder* destination, bool move=false ); + private: /** total column */ QListViewItemIterator mUpdateIterator; @@ -321,6 +341,9 @@ private: KMMainWidget *mMainWidget; bool mReloading; QMap mFolderToItem; + + /** Map menu id into a folder */ + KMMenuToFolder mMenuToFolder; }; #endif diff --git a/renamejob.cpp b/renamejob.cpp index d02b4e91a..dfe8c4b8b 100644 --- a/renamejob.cpp +++ b/renamejob.cpp @@ -53,7 +53,7 @@ using namespace KMail; -RenameJob::RenameJob( FolderStorage* storage, const QString& newName, +RenameJob::RenameJob( FolderStorage* storage, const QString& newName, KMFolderDir* newParent ) : FolderJob( 0, tOther, (storage ? storage->folder() : 0) ), mStorage( storage ), mNewParent( newParent ), @@ -101,7 +101,7 @@ void RenameJob::execute() if ( deftype < 0 || deftype > 1 ) deftype = 1; // the type of the new folder - KMFolderType typenew = + KMFolderType typenew = ( deftype == 0 ) ? KMFolderTypeMbox : KMFolderTypeMaildir; if ( mNewParent->owner() ) typenew = mNewParent->owner()->folderType(); @@ -123,7 +123,7 @@ void RenameJob::execute() // create it on the server and wait for the folderAdded signal connect( kmkernel->imapFolderMgr(), SIGNAL( changed() ), this, SLOT( slotMoveMessages() ) ); - KMFolderImap* imapFolder = + KMFolderImap* imapFolder = static_cast(mNewParent->owner()->storage()); imapFolder->createFolder( mNewName ); } else if ( mNewParent->type() == KMDImapDir ) @@ -149,12 +149,12 @@ void RenameJob::execute() return; } if ( mOldImapPath.isEmpty() ) - { + { // sanity emit renameDone( mNewName, false ); deleteLater(); return; - } else if ( mOldName == mNewName || mOldImapPath == "/INBOX/" ) { + } else if ( mOldName == mNewName ) { emit renameDone( mNewName, true ); // noop deleteLater(); return; @@ -225,7 +225,7 @@ void RenameJob::slotMoveMessages() assert( msgBase ); msgList.append( msgBase ); } - if ( msgList.count() == 0 ) + if ( msgList.count() == 0 ) { slotMoveCompleted( 0 ); } else @@ -242,7 +242,7 @@ void RenameJob::slotMoveCompleted( KMCommand* command ) kdDebug(5006) << k_funcinfo << (command?command->result():0) << endl; disconnect( command, SIGNAL( completed( KMCommand * ) ), this, SLOT( slotMoveCompleted( KMCommand * ) ) ); - if ( !command || command->result() == KMCommand::OK ) + if ( !command || command->result() == KMCommand::OK ) { kdDebug(5006) << "deleting old folder" << endl; // move complete or not necessary @@ -251,38 +251,38 @@ void RenameJob::slotMoveCompleted( KMCommand* command ) KConfig* config = KMKernel::config(); QMap entries = config->entryMap( oldconfig ); KConfigGroupSaver saver(config, "Folder-" + mNewFolder->idString()); - for ( QMap::Iterator it = entries.begin(); - it != entries.end(); ++it ) + for ( QMap::Iterator it = entries.begin(); + it != entries.end(); ++it ) { - if ( it.key() == "Id" || it.key() == "ImapPath" || + if ( it.key() == "Id" || it.key() == "ImapPath" || it.key() == "UidValidity" ) continue; config->writeEntry( it.key(), it.data() ); } mNewFolder->readConfig( config ); - + // delete the old folder mStorage->blockSignals( false ); if ( mStorage->folderType() == KMFolderTypeImap ) { kmkernel->imapFolderMgr()->remove( mStorage->folder() ); - } else if ( mStorage->folderType() == KMFolderTypeCachedImap ) + } else if ( mStorage->folderType() == KMFolderTypeCachedImap ) { // tell the account (see KMFolderCachedImap::listDirectory2) KMAcctCachedImap* acct = static_cast(mStorage)->account(); if ( acct ) acct->addDeletedFolder( mOldImapPath ); kmkernel->dimapFolderMgr()->remove( mStorage->folder() ); - } else if ( mStorage->folderType() == KMFolderTypeSearch ) + } else if ( mStorage->folderType() == KMFolderTypeSearch ) { // invalid kdWarning(5006) << k_funcinfo << "cannot remove a search folder" << endl; } else { kmkernel->folderMgr()->remove( mStorage->folder() ); } - + emit renameDone( mNewName, true ); - } else + } else { kdDebug(5006) << "rollback - deleting folder" << endl; // move failed - rollback the last transaction @@ -291,7 +291,7 @@ void RenameJob::slotMoveCompleted( KMCommand* command ) if ( mNewFolder->folderType() == KMFolderTypeImap ) { kmkernel->imapFolderMgr()->remove( mNewFolder ); - } else if ( mNewFolder->folderType() == KMFolderTypeCachedImap ) + } else if ( mNewFolder->folderType() == KMFolderTypeCachedImap ) { // tell the account (see KMFolderCachedImap::listDirectory2) KMFolderCachedImap* folder = static_cast(mNewFolder->storage()); @@ -299,14 +299,14 @@ void RenameJob::slotMoveCompleted( KMCommand* command ) if ( acct ) acct->addDeletedFolder( folder->imapPath() ); kmkernel->dimapFolderMgr()->remove( mNewFolder ); - } else if ( mNewFolder->folderType() == KMFolderTypeSearch ) + } else if ( mNewFolder->folderType() == KMFolderTypeSearch ) { // invalid kdWarning(5006) << k_funcinfo << "cannot remove a search folder" << endl; } else { kmkernel->folderMgr()->remove( mNewFolder ); } - + emit renameDone( mNewName, false ); } deleteLater();