From b9aa52983f0b32489fb32dbd0e4185ee75cd9449 Mon Sep 17 00:00:00 2001 From: Till Adam Date: Sat, 20 Sep 2003 19:16:39 +0000 Subject: [PATCH] Make KMMoveCommand emit a completed(bool success) signal by hooking the command up to the target folder's msgAdded signals and ticking off each serial number as it comes in. To make that possible, use the metaDataMap for imap folders to restore serial numbers after the move (along with status). Also connect to the folderCompleted(bool) signal of imap folders to make sure we notice if not all messages make it to the other side. Connect kmheaders to the abortRequested signal and make sure cancelling moves behaves somewhat more gracefully and restores the state of messages remaining in the folder, making them selectable and setting their transfer status to false so they can be downloaded or moved again. svn path=/trunk/kdepim/; revision=252676 --- kmcommands.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++----- kmcommands.h | 10 +++++++ kmfolderimap.cpp | 16 +++++++--- kmfolderimap.h | 7 ++++- kmfoldermbox.cpp | 2 +- kmheaders.cpp | 48 +++++++++++++++++++++++++++++- kmheaders.h | 4 +++ 7 files changed, 148 insertions(+), 15 deletions(-) diff --git a/kmcommands.cpp b/kmcommands.cpp index f3943cf2a..d4b3cc959 100644 --- a/kmcommands.cpp +++ b/kmcommands.cpp @@ -1335,6 +1335,7 @@ KMMoveCommand::KMMoveCommand( KMFolder* destFolder, const QPtrList &msgList) :mDestFolder( destFolder ), mMsgList( msgList ) { + setDeletesItself( true ); } KMMoveCommand::KMMoveCommand( KMFolder* destFolder, @@ -1359,6 +1360,12 @@ void KMMoveCommand::execute() int index; QPtrList list; int undoId = -1; + + if (mDestFolder) { + connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)), + this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32))); + } + for (msgBase=mMsgList.first(); msgBase && !rc; msgBase=mMsgList.next()) { KMFolder *srcFolder = msgBase->parent(); if (srcFolder == mDestFolder) @@ -1380,9 +1387,16 @@ void KMMoveCommand::execute() } if (mDestFolder) { + mLostBoys.append(msg->getMsgSerNum()); if (mDestFolder->folderType() == KMFolderTypeImap) { + /* If we are moving to an imap folder, connect to it's completed + * siganl so we notice when all the mails should have showed up in it + * but haven't for some reason. */ + connect (mDestFolder, SIGNAL(folderComplete( KMFolderImap*, bool )), + this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool ))); list.append(msg); } else { + // We are moving to a local folder. rc = mDestFolder->moveMsg(msg, &index); if (rc == 0 && index != -1) { KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 ); @@ -1392,6 +1406,12 @@ void KMMoveCommand::execute() undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder ); kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() ); } + } else if (rc != 0) { + // Something went wrong. Stop processing here, it is likely that the + // other moves would fail as well. + emit completed( false); + deleteLater(); + return; } } } else { @@ -1406,17 +1426,57 @@ void KMMoveCommand::execute() } } } - if (!list.isEmpty() && mDestFolder) - mDestFolder->moveMsg(list, &index); + if (!list.isEmpty() && mDestFolder) { + mDestFolder->moveMsg(list, &index); + } else { + FolderToMessageListMap::Iterator it; + for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) { + it.key()->removeMsg(*it.data()); + delete it.data(); + } + if ( !mDestFolder ) { + emit completed( true ); + deleteLater(); + } + } +} - FolderToMessageListMap::Iterator it; - for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) { - it.key()->removeMsg(*it.data()); - delete it.data(); +void KMMoveCommand::slotImapFolderCompleted(KMFolderImap *, bool success) +{ + kdDebug(5006) << "KMMoveCommand::slotImapFolderCompleted: " << success << endl; + if ( success ) { + // the folder was checked successfully but we were still called, so check + // if we are still waiting for messages to show up. If so, uidValidity + // changed, or something else went wrong. Clean up. + + /* Unfortunately older UW imap servers change uid validity for each put job. + * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */ + if ( !mLostBoys.isEmpty() ) { + kdDebug(5006) << "### Not all moved messages reported back that they were " << endl + << "### added to the target folder. Did uidValidity change? " << endl; + } + } else { + // Should we inform the user here or leave that to the caller? } + emit completed( success ); + deleteLater(); +} - if (mDestFolder) { - mDestFolder->sync(); +void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum) +{ + if (folder != mDestFolder) { + kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different " + "folder or invalid serial number." << endl; + return; + } + mLostBoys.remove(serNum); + if ( mLostBoys.isEmpty() ) { + // we are done. All messages transferred to the host succesfully + if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) { + mDestFolder->sync(); + } + emit completed( true ); + deleteLater(); } } diff --git a/kmcommands.h b/kmcommands.h index f2cd8d592..4ba155629 100644 --- a/kmcommands.h +++ b/kmcommands.h @@ -14,6 +14,7 @@ class KProgressDialog; class KMComposeWin; class KMFilter; class KMFolder; +class KMFolderImap; class KMFolderNode; class KMHeaders; class KMMainWidget; @@ -75,6 +76,9 @@ private slots: void slotTransferCancelled(); signals: void messagesTransfered(bool); + /** Emitted when the command has completed. + * @success Success or error. */ + void completed( bool success); private: // ProgressDialog for transferring messages @@ -543,12 +547,18 @@ public: KMMoveCommand( KMFolder* destFolder, const QPtrList &msgList ); KMMoveCommand( KMFolder* destFolder, KMMessage * msg ); +private slots: + void slotImapFolderCompleted(KMFolderImap *folder, bool success); + void slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum); private: virtual void execute(); KMFolder *mDestFolder; QPtrList mMsgList; + // List of serial numbers that have to be transferred to a host. + // Ticked off as they come in via msgAdded signals. + QValueList mLostBoys; }; class KMDeleteMsgCommand : public KMMoveCommand diff --git a/kmfolderimap.cpp b/kmfolderimap.cpp index 1432c0fc1..e51307125 100644 --- a/kmfolderimap.cpp +++ b/kmfolderimap.cpp @@ -71,6 +71,8 @@ KMFolderImap::~KMFolderImap() { writeConfig(); if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(this); + mMetaDataMap.setAutoDelete( true ); + mMetaDataMap.clear(); } @@ -242,15 +244,17 @@ void KMFolderImap::slotRenameResult( KIO::Job *job ) void KMFolderImap::addMsgQuiet(KMMessage* aMsg) { KMFolder *folder = aMsg->parent(); + Q_UINT32 serNum = 0; aMsg->setTransferInProgress( false ); if (folder) { - kmkernel->undoStack()->pushSingleAction( aMsg->getMsgSerNum(), folder, this ); + serNum = aMsg->getMsgSerNum(); + kmkernel->undoStack()->pushSingleAction( serNum, folder, this ); int idx = folder->find( aMsg ); assert( idx != -1 ); folder->take( idx ); } // Remember the status, so it can be transfered to the new message. - mMetaDataMap.insert(aMsg->msgIdMD5(), new KMMsgMetaData(aMsg->status())); + mMetaDataMap.insert(aMsg->msgIdMD5(), new KMMsgMetaData(aMsg->status(), serNum)); delete aMsg; aMsg = 0; @@ -261,6 +265,8 @@ void KMFolderImap::addMsgQuiet(KMMessage* aMsg) void KMFolderImap::addMsgQuiet(QPtrList msgList) { KMFolder *folder = msgList.first()->parent(); + Q_UINT32 serNum = 0; + if (folder) serNum = msgList.first()->getMsgSerNum(); int undoId = -1; for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) { @@ -268,7 +274,7 @@ void KMFolderImap::addMsgQuiet(QPtrList msgList) undoId = kmkernel->undoStack()->newUndoAction( folder, this ); kmkernel->undoStack()->addMsgToAction( undoId, msg->getMsgSerNum() ); // Remember the status, so it can be transfered to the new message. - mMetaDataMap.insert(msg->msgIdMD5(), new KMMsgMetaData(msg->status())); + mMetaDataMap.insert(msg->msgIdMD5(), new KMMsgMetaData(msg->status(), serNum)); msg->setTransferInProgress( false ); } if (folder) folder->take(msgList); @@ -331,7 +337,7 @@ int KMFolderImap::addMsg(QPtrList& msgList, int* aIndex_ret) // we need the messages that belong to the current set to pass them to the ImapJob QPtrList temp_msgs = splitMessageList(*it, msgList); - imapJob = new ImapJob(temp_msgs, *it, ImapJob::tCopyMessage, this); + imapJob = new ImapJob(temp_msgs, *it, ImapJob::tMoveMessage, this); connect(imapJob, SIGNAL(messageCopied(QPtrList)), SLOT(addMsgQuiet(QPtrList))); imapJob->start(); @@ -962,6 +968,8 @@ void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data) if ( mMetaDataMap.find( id ) ) { KMMsgMetaData *md = mMetaDataMap[id]; msg->setStatus( md->status() ); + if ( md->serNum() != 0 ) + msg->setMsgSerNum( md->serNum() ); mMetaDataMap.remove( id ); delete md; } diff --git a/kmfolderimap.h b/kmfolderimap.h index 57cc6c255..a591df844 100644 --- a/kmfolderimap.h +++ b/kmfolderimap.h @@ -49,11 +49,16 @@ using KMail::ImapJob; class KMMsgMetaData { public: - KMMsgMetaData(KMMsgStatus aStatus) { mStatus = aStatus; } + KMMsgMetaData(KMMsgStatus aStatus) + :mStatus(aStatus), mSerNum(0) {} + KMMsgMetaData(KMMsgStatus aStatus, Q_UINT32 aSerNum) + :mStatus(aStatus), mSerNum(aSerNum) {} ~KMMsgMetaData() {}; const KMMsgStatus status() const { return mStatus; } + const Q_UINT32 serNum() const { return mSerNum; } private: KMMsgStatus mStatus; + Q_UINT32 mSerNum; }; diff --git a/kmfoldermbox.cpp b/kmfoldermbox.cpp index 34cf8ab46..18ce9ec8c 100644 --- a/kmfoldermbox.cpp +++ b/kmfoldermbox.cpp @@ -229,7 +229,7 @@ void KMFolderMbox::close(bool aForced) mOpenCount = 1; return; } - + if (mAutoCreateIndex) { if (KMFolderIndex::IndexOk != indexStatus()) { diff --git a/kmheaders.cpp b/kmheaders.cpp index e3bec9f07..ab8fc1a77 100644 --- a/kmheaders.cpp +++ b/kmheaders.cpp @@ -1580,6 +1580,10 @@ void KMHeaders::deleteMsg () KMMessageList msgList = *selectedMsgs(true); KMCommand *command = new KMDeleteMsgCommand( mFolder, msgList ); + connect (command, SIGNAL(completed( bool)), + this, SLOT(slotMoveCompleted( bool))); + connect(KMBroadcastStatus::instance(), SIGNAL(signalAbortRequested()), + this, SLOT(slotMoveAborted())); command->start(); finalizeMove( nextItem, contentX, contentY ); @@ -1692,15 +1696,57 @@ void KMHeaders::moveMsgToFolder (KMFolder* destFolder) // remember the message to select afterwards int contentX, contentY; KMHeaderItem *nextItem = prepareMove( &contentX, &contentY ); - + msgList = *selectedMsgs(true); KMCommand *command = new KMMoveCommand( destFolder, msgList ); + connect (command, SIGNAL(completed( bool)), + this, SLOT(slotMoveCompleted( bool))); + + connect(KMBroadcastStatus::instance(), SIGNAL(signalAbortRequested()), + this, SLOT(slotMoveAborted())); + command->start(); finalizeMove( nextItem, contentX, contentY ); } +void KMHeaders::slotMoveAborted( ) +{ + /* The user cancelled the move, reset the state of all messages involved and + * repaint. */ + KMBroadcastStatus::instance()->setStatusMsg(i18n("Moving messages cancelled.")); + disconnect(KMBroadcastStatus::instance(), SIGNAL(signalAbortRequested()), + this, SLOT(slotMoveAborted())); + + for (QListViewItemIterator it(this); it.current(); it++) { + KMHeaderItem *item = static_cast(it.current()); + if ( item->aboutToBeDeleted() ) { + item->setAboutToBeDeleted ( false ); + item->setSelectable ( true ); + KMMsgBase *msgBase = mFolder->getMsgBase(item->msgId()); + if ( msgBase->isMessage() ) { + KMMessage *msg = static_cast(msgBase); + if ( msg ) msg->setTransferInProgress( false, true ); + } + } + } + triggerUpdate(); +} + +void KMHeaders::slotMoveCompleted( bool success ) +{ + kdDebug(5006) << "KMHeaders::slotMoveCompleted: " << success << endl; + if (success) { + KMBroadcastStatus::instance()->setStatusMsg(i18n("Messages moved succesfully.")); + } else { + // FIXME dialog? Offer rollback? + KMBroadcastStatus::instance()->setStatusMsg(i18n("Moving messages failed.")); + } + disconnect(KMBroadcastStatus::instance(), SIGNAL(signalAbortRequested()), + this, SLOT(slotMoveAborted())); +} + bool KMHeaders::canUndo() const { return ( kmkernel->undoStack()->size() > 0 ); diff --git a/kmheaders.h b/kmheaders.h index 3fd249d51..67435222a 100644 --- a/kmheaders.h +++ b/kmheaders.h @@ -284,6 +284,10 @@ protected slots: /** show context menu */ void rightButtonPressed( QListViewItem *, const QPoint &, int ); +private slots: + void slotMoveCompleted( bool success ); + void slotMoveAborted( ); + private: /** Is equivalent to clearing the list and inserting an item for each message in the current folder */