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 */