/* * This file is part of KMail. * Copyright (c) 2009 Constantin Berzan * * Parts based on KMail code by: * Various authors. * * 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; either version 2 of the License, or * (at your option) any later version. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "attachmentcontrollerbase.h" #include "messagecomposer/attachment/attachmentmodel.h" #include "messagecomposer/job/attachmentjob.h" #include "messagecomposer/job/attachmentfrompublickeyjob.h" #include "messagecomposer/composer/composer.h" #include "messagecomposer/part/globalpart.h" #include "messageviewer/viewer/editorwatcher.h" #include "messageviewer/viewer/nodehelper.h" #include "messageviewer/utils/util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace MessageComposer; using namespace MessageCore; class MessageComposer::AttachmentControllerBase::Private { public: Private( AttachmentControllerBase *qq ); ~Private(); void attachmentRemoved( AttachmentPart::Ptr part ); // slot void compressJobResult( KJob *job ); // slot void loadJobResult( KJob *job ); // slot void openSelectedAttachments(); // slot void viewSelectedAttachments(); // slot void editSelectedAttachment(); // slot void editSelectedAttachmentWith(); // slot void removeSelectedAttachments(); // slot void saveSelectedAttachmentAs(); // slot void selectedAttachmentProperties(); // slot void editDone( MessageViewer::EditorWatcher *watcher ); // slot void attachPublicKeyJobResult( KJob *job ); // slot void slotAttachmentContentCreated( KJob *job ); // slot void addAttachmentPart( AttachmentPart::Ptr part ); void selectedAllAttachment(); void createOpenWithMenu( QMenu *topMenu, AttachmentPart::Ptr part ); AttachmentControllerBase *const q; bool encryptEnabled; bool signEnabled; MessageComposer::AttachmentModel *model; QWidget *wParent; QHash editorPart; QHash editorTempFile; AttachmentPart::List selectedParts; KActionCollection *mActionCollection; QAction *attachPublicKeyAction; QAction *attachMyPublicKeyAction; QAction *openContextAction; QAction *viewContextAction; QAction *editContextAction; QAction *editWithContextAction; QAction *removeAction; QAction *removeContextAction; QAction *saveAsAction; QAction *saveAsContextAction; QAction *propertiesAction; QAction *propertiesContextAction; QAction *addAction; QAction *addContextAction; QAction *selectAllAction; KActionMenu *attachmentMenu; QAction *addOwnVcardAction; // If part p is compressed, uncompressedParts[p] is the uncompressed part. QHash uncompressedParts; }; AttachmentControllerBase::Private::Private( AttachmentControllerBase *qq ) : q( qq ) , encryptEnabled( false ) , signEnabled( false ) , model( 0 ) , wParent( 0 ) , attachPublicKeyAction( 0 ) , attachMyPublicKeyAction( 0 ) , openContextAction( 0 ) , viewContextAction( 0 ) , editContextAction( 0 ) , editWithContextAction( 0 ) , removeAction( 0 ) , removeContextAction( 0 ) , saveAsAction( 0 ) , saveAsContextAction( 0 ) , propertiesAction( 0 ) , propertiesContextAction( 0 ) , addAction( 0 ) , addContextAction( 0 ) , selectAllAction( 0 ) , attachmentMenu( 0 ) , addOwnVcardAction( 0 ) { } AttachmentControllerBase::Private::~Private() { } void AttachmentControllerBase::setSelectedParts( const AttachmentPart::List &selectedParts) { d->selectedParts = selectedParts; const int selectedCount = selectedParts.count(); const bool enableEditAction = (selectedCount == 1) && ( !selectedParts.first()->isMessageOrMessageCollection() ); d->openContextAction->setEnabled( selectedCount > 0 ); d->viewContextAction->setEnabled( selectedCount > 0 ); d->editContextAction->setEnabled( enableEditAction ); d->editWithContextAction->setEnabled( enableEditAction ); d->removeAction->setEnabled( selectedCount > 0 ); d->removeContextAction->setEnabled( selectedCount > 0 ); d->saveAsAction->setEnabled( selectedCount == 1 ); d->saveAsContextAction->setEnabled( selectedCount == 1 ); d->propertiesAction->setEnabled( selectedCount == 1 ); d->propertiesContextAction->setEnabled( selectedCount == 1 ); } void AttachmentControllerBase::Private::attachmentRemoved( AttachmentPart::Ptr part ) { if( uncompressedParts.contains( part ) ) { uncompressedParts.remove( part ); } } void AttachmentControllerBase::Private::compressJobResult( KJob *job ) { if( job->error() ) { KMessageBox::sorry( wParent, job->errorString(), i18n( "Failed to compress attachment" ) ); return; } Q_ASSERT( dynamic_cast( job ) ); AttachmentCompressJob *ajob = static_cast( job ); //AttachmentPart *originalPart = const_cast( ajob->originalPart() ); AttachmentPart::Ptr originalPart = ajob->originalPart(); AttachmentPart::Ptr compressedPart = ajob->compressedPart(); if( ajob->isCompressedPartLarger() ) { const int result = KMessageBox::questionYesNo( wParent, i18n( "The compressed attachment is larger than the original. " "Do you want to keep the original one?" ), QString( /*caption*/ ), KGuiItem( i18nc( "Do not compress", "Keep" ) ), KGuiItem( i18n( "Compress" ) ) ); if( result == KMessageBox::Yes ) { // The user has chosen to keep the uncompressed file. return; } } kDebug() << "Replacing uncompressed part in model."; uncompressedParts[ compressedPart ] = originalPart; bool ok = model->replaceAttachment( originalPart, compressedPart ); if( !ok ) { // The attachment was removed from the model while we were compressing. kDebug() << "Compressed a zombie."; } } void AttachmentControllerBase::Private::loadJobResult( KJob *job ) { if( job->error() ) { KMessageBox::sorry( wParent, job->errorString(), i18n( "Failed to attach file" ) ); return; } Q_ASSERT( dynamic_cast( job ) ); AttachmentLoadJob *ajob = static_cast( job ); AttachmentPart::Ptr part = ajob->attachmentPart(); q->addAttachment( part ); } void AttachmentControllerBase::Private::openSelectedAttachments() { Q_ASSERT( selectedParts.count() >= 1 ); foreach( AttachmentPart::Ptr part, selectedParts ) { q->openAttachment( part ); } } void AttachmentControllerBase::Private::viewSelectedAttachments() { Q_ASSERT( selectedParts.count() >= 1 ); foreach( AttachmentPart::Ptr part, selectedParts ) { q->viewAttachment( part ); } } void AttachmentControllerBase::Private::editSelectedAttachment() { Q_ASSERT( selectedParts.count() == 1 ); q->editAttachment( selectedParts.first(), false /*openWith*/ ); // TODO nicer api would be enum { OpenWithDialog, NoOpenWithDialog } } void AttachmentControllerBase::Private::editSelectedAttachmentWith() { Q_ASSERT( selectedParts.count() == 1 ); q->editAttachment( selectedParts.first(), true /*openWith*/ ); } void AttachmentControllerBase::Private::removeSelectedAttachments() { Q_ASSERT( selectedParts.count() >= 1 ); foreach( AttachmentPart::Ptr part, selectedParts ) { model->removeAttachment( part ); } } void AttachmentControllerBase::Private::saveSelectedAttachmentAs() { Q_ASSERT( selectedParts.count() == 1 ); q->saveAttachmentAs( selectedParts.first() ); } void AttachmentControllerBase::Private::selectedAttachmentProperties() { Q_ASSERT( selectedParts.count() == 1 ); q->attachmentProperties( selectedParts.first() ); } void AttachmentControllerBase::Private::editDone( MessageViewer::EditorWatcher *watcher ) { AttachmentPart::Ptr part = editorPart.take( watcher ); Q_ASSERT( part ); KTemporaryFile *tempFile = editorTempFile.take( watcher ); Q_ASSERT( tempFile ); if( watcher->fileChanged() ) { kDebug() << "File has changed."; // Read the new data and update the part in the model. tempFile->reset(); QByteArray data = tempFile->readAll(); part->setData( data ); model->updateAttachment( part ); } delete tempFile; // The watcher deletes itself. } void AttachmentControllerBase::Private::createOpenWithMenu( QMenu *topMenu, AttachmentPart::Ptr part ) { const QString contentTypeStr = QString::fromLatin1(part->mimeType()); const KService::List offers = KFileItemActions::associatedApplications(QStringList()< 1) { // submenu 'open with' menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu); menu->menuAction()->setObjectName(QLatin1String("openWith_submenu")); // for the unittest topMenu->addMenu(menu); } //kDebug() << offers.count() << "offers" << topMenu << menu; KService::List::ConstIterator it = offers.constBegin(); KService::List::ConstIterator end = offers.constEnd(); for(; it != end; ++it) { KAction* act = MessageViewer::Util::createAppAction(*it, // no submenu -> prefix single offer menu == topMenu, actionGroup,menu); menu->addAction(act); } QString openWithActionName; if (menu != topMenu) { // submenu menu->addSeparator(); openWithActionName = i18nc("@action:inmenu Open With", "&Other..."); } else { openWithActionName = i18nc("@title:menu", "&Open With..."); } KAction *openWithAct = new KAction(menu); openWithAct->setText(openWithActionName); QObject::connect(openWithAct, SIGNAL(triggered()), q, SLOT(slotOpenWithDialog())); menu->addAction(openWithAct); } else { // no app offers -> Open With... KAction *act = new KAction(topMenu); act->setText(i18nc("@title:menu", "&Open With...")); QObject::connect(act, SIGNAL(triggered()), q, SLOT(slotOpenWithDialog())); topMenu->addAction(act); } } void AttachmentControllerBase::exportPublicKey( const QString &fingerprint ) { if( fingerprint.isEmpty() || !Kleo::CryptoBackendFactory::instance()->openpgp() ) { kWarning() << "Tried to export key with empty fingerprint, or no OpenPGP."; Q_ASSERT( false ); // Can this happen? return; } MessageComposer::AttachmentFromPublicKeyJob *ajob = new MessageComposer::AttachmentFromPublicKeyJob( fingerprint, this ); connect( ajob, SIGNAL(result(KJob*)), this, SLOT(attachPublicKeyJobResult(KJob*)) ); ajob->start(); } void AttachmentControllerBase::Private::attachPublicKeyJobResult( KJob *job ) { // The only reason we can't use loadJobResult() and need a separate method // is that we want to show the proper caption ("public key" instead of "file")... if( job->error() ) { KMessageBox::sorry( wParent, job->errorString(), i18n( "Failed to attach public key" ) ); return; } Q_ASSERT( dynamic_cast( job ) ); MessageComposer::AttachmentFromPublicKeyJob *ajob = static_cast( job ); AttachmentPart::Ptr part = ajob->attachmentPart(); q->addAttachment( part ); } static KTemporaryFile *dumpAttachmentToTempFile( const AttachmentPart::Ptr part ) // local { KTemporaryFile *file = new KTemporaryFile; if( !file->open() ) { kError() << "Could not open tempfile" << file->fileName(); delete file; return 0; } if( file->write( part->data() ) == -1 ) { kError() << "Could not dump attachment to tempfile."; delete file; return 0; } file->flush(); return file; } AttachmentControllerBase::AttachmentControllerBase( MessageComposer::AttachmentModel *model, QWidget *wParent, KActionCollection *actionCollection ) : QObject( wParent ) , d( new Private( this ) ) { d->model = model; connect( model, SIGNAL(attachUrlsRequested(KUrl::List)), this, SLOT(addAttachments(KUrl::List)) ); connect( model, SIGNAL(attachmentRemoved(MessageCore::AttachmentPart::Ptr)), this, SLOT(attachmentRemoved(MessageCore::AttachmentPart::Ptr)) ); connect( model, SIGNAL(attachmentCompressRequested(MessageCore::AttachmentPart::Ptr,bool)), this, SLOT(compressAttachment(MessageCore::AttachmentPart::Ptr,bool)) ); connect( model, SIGNAL(encryptEnabled(bool)), this, SLOT(setEncryptEnabled(bool)) ); connect( model, SIGNAL(signEnabled(bool)), this, SLOT(setSignEnabled(bool)) ); d->wParent = wParent; d->mActionCollection = actionCollection; } AttachmentControllerBase::~AttachmentControllerBase() { delete d; } void AttachmentControllerBase::createActions() { // Create the actions. d->attachPublicKeyAction = new KAction( i18n( "Attach &Public Key..." ), this ); connect( d->attachPublicKeyAction, SIGNAL(triggered(bool)), this, SLOT(showAttachPublicKeyDialog()) ); d->attachMyPublicKeyAction = new KAction( i18n( "Attach &My Public Key" ), this ); connect( d->attachMyPublicKeyAction, SIGNAL(triggered(bool)), this, SLOT(attachMyPublicKey()) ); d->attachmentMenu = new KActionMenu( KIcon( QLatin1String( "mail-attachment" ) ), i18n( "Attach" ), this ); connect( d->attachmentMenu, SIGNAL(triggered(bool)), this, SLOT(showAddAttachmentDialog()) ); d->attachmentMenu->setDelayed(true); d->addAction = new KAction( KIcon( QLatin1String( "mail-attachment" ) ), i18n( "&Attach File..." ), this ); d->addAction->setIconText( i18n( "Attach" ) ); d->addContextAction = new KAction( KIcon( QLatin1String( "mail-attachment" ) ), i18n( "Add Attachment..." ), this ); connect( d->addAction, SIGNAL(triggered(bool)), this, SLOT(showAddAttachmentDialog()) ); connect( d->addContextAction, SIGNAL(triggered(bool)), this, SLOT(showAddAttachmentDialog()) ); d->addOwnVcardAction = new KAction( i18n("Attach Own vCard"),this ); d->addOwnVcardAction->setIconText( i18n( "Own vCard" ) ); d->addOwnVcardAction->setCheckable(true); connect(d->addOwnVcardAction, SIGNAL(triggered(bool)), this, SIGNAL(addOwnVcard(bool))); d->attachmentMenu->addAction(d->addAction); d->attachmentMenu->addSeparator(); d->attachmentMenu->addAction(d->addOwnVcardAction); d->removeAction = new KAction( KIcon(QLatin1String("edit-delete")), i18n( "&Remove Attachment" ), this ); d->removeContextAction = new KAction( KIcon(QLatin1String("edit-delete")), i18n( "Remove" ), this ); // FIXME need two texts. is there a better way? connect( d->removeAction, SIGNAL(triggered(bool)), this, SLOT(removeSelectedAttachments()) ); connect( d->removeContextAction, SIGNAL(triggered(bool)), this, SLOT(removeSelectedAttachments()) ); d->openContextAction = new KAction( i18nc( "to open", "Open" ), this ); connect( d->openContextAction, SIGNAL(triggered(bool)), this, SLOT(openSelectedAttachments()) ); d->viewContextAction = new KAction( i18nc( "to view", "View" ), this ); connect( d->viewContextAction, SIGNAL(triggered(bool)), this, SLOT(viewSelectedAttachments()) ); d->editContextAction = new KAction( i18nc( "to edit", "Edit" ), this ); connect( d->editContextAction, SIGNAL(triggered(bool)), this, SLOT(editSelectedAttachment()) ); d->editWithContextAction = new KAction( i18n( "Edit With..." ), this ); connect( d->editWithContextAction, SIGNAL(triggered(bool)), this, SLOT(editSelectedAttachmentWith()) ); d->saveAsAction = new KAction( KIcon( QLatin1String( "document-save-as" ) ), i18n( "&Save Attachment As..." ), this ); d->saveAsContextAction = new KAction( KIcon( QLatin1String( "document-save-as" ) ), i18n( "Save As..." ), this ); connect( d->saveAsAction, SIGNAL(triggered(bool)), this, SLOT(saveSelectedAttachmentAs()) ); connect( d->saveAsContextAction, SIGNAL(triggered(bool)), this, SLOT(saveSelectedAttachmentAs()) ); d->propertiesAction = new KAction( i18n( "Attachment Pr&operties..." ), this ); d->propertiesContextAction = new KAction( i18n( "Properties" ), this ); connect( d->propertiesAction, SIGNAL(triggered(bool)), this, SLOT(selectedAttachmentProperties()) ); connect( d->propertiesContextAction, SIGNAL(triggered(bool)), this, SLOT(selectedAttachmentProperties()) ); d->selectAllAction = new KAction( i18n("Select All"), this); connect( d->selectAllAction, SIGNAL(triggered(bool)), this, SIGNAL(selectedAllAttachment()) ); // Insert the actions into the composer window's menu. KActionCollection *collection = d->mActionCollection; collection->addAction( QLatin1String( "attach_public_key" ), d->attachPublicKeyAction ); collection->addAction( QLatin1String( "attach_my_public_key" ), d->attachMyPublicKeyAction ); collection->addAction( QLatin1String( "attach" ), d->addAction ); collection->addAction( QLatin1String( "remove" ), d->removeAction ); collection->addAction( QLatin1String( "attach_save" ), d->saveAsAction ); collection->addAction( QLatin1String( "attach_properties" ), d->propertiesAction ); collection->addAction( QLatin1String( "select_all_attachment"), d->selectAllAction); collection->addAction( QLatin1String( "attach_menu"), d->attachmentMenu ); collection->addAction( QLatin1String( "attach_own_vcard"), d->addOwnVcardAction ); setSelectedParts( AttachmentPart::List()); emit actionsCreated(); } void AttachmentControllerBase::setEncryptEnabled( bool enabled ) { d->encryptEnabled = enabled; } void AttachmentControllerBase::setSignEnabled( bool enabled ) { d->signEnabled = enabled; } void AttachmentControllerBase::compressAttachment( AttachmentPart::Ptr part, bool compress ) { if( compress ) { kDebug() << "Compressing part."; AttachmentCompressJob *ajob = new AttachmentCompressJob( part, this ); connect( ajob, SIGNAL(result(KJob*)), this, SLOT(compressJobResult(KJob*)) ); ajob->start(); } else { kDebug() << "Uncompressing part."; // Replace the compressed part with the original uncompressed part, and delete // the compressed part. AttachmentPart::Ptr originalPart = d->uncompressedParts.take( part ); Q_ASSERT( originalPart ); // Found in uncompressedParts. bool ok = d->model->replaceAttachment( part, originalPart ); Q_ASSERT( ok ); Q_UNUSED( ok ); } } void AttachmentControllerBase::showContextMenu() { emit refreshSelection(); const int numberOfParts(d->selectedParts.count()); QMenu *menu = new QMenu; const bool enableEditAction = (numberOfParts == 1) && ( !d->selectedParts.first()->isMessageOrMessageCollection() ); if(numberOfParts>0) { if(numberOfParts == 1) d->createOpenWithMenu(menu, d->selectedParts.first()); else menu->addAction(d->openContextAction); menu->addAction(d->viewContextAction); } if(enableEditAction) { menu->addAction(d->editWithContextAction); menu->addAction(d->editContextAction); } if(numberOfParts>0) { menu->addAction(d->removeContextAction); } if(numberOfParts == 1) { menu->addAction(d->saveAsContextAction); menu->addAction(d->propertiesContextAction); } const int nbAttachment = d->model->rowCount(); if (nbAttachment != numberOfParts) { menu->addSeparator(); menu->addAction(d->selectAllAction); } if (numberOfParts == 0) { menu->addSeparator(); menu->addAction(d->addContextAction); } menu->exec( QCursor::pos() ); delete menu; } void AttachmentControllerBase::slotOpenWithDialog() { openWith(); } void AttachmentControllerBase::slotOpenWithAction(QAction*act) { KService::Ptr app = act->data().value(); Q_ASSERT( d->selectedParts.count() == 1 ); openWith(app); } void AttachmentControllerBase::openWith(KService::Ptr offer) { KTemporaryFile *tempFile = dumpAttachmentToTempFile( d->selectedParts.first() ); if( !tempFile ) { KMessageBox::sorry( d->wParent, i18n( "KMail was unable to write the attachment to a temporary file." ), i18n( "Unable to open attachment" ) ); return; } KUrl::List lst; KUrl url = KUrl::fromPath(tempFile->fileName()); lst.append( url ); bool result = false; if(offer) { result = KRun::run( *offer, lst, d->wParent, false ); } else { result = KRun::displayOpenWithDialog( lst, d->wParent, false ); } if ( !result ) { delete tempFile; tempFile = 0; } else { // The file was opened. Delete it only when the composer is closed // (and this object is destroyed). tempFile->setParent( this ); // Manages lifetime. } } void AttachmentControllerBase::openAttachment( AttachmentPart::Ptr part ) { KTemporaryFile *tempFile = dumpAttachmentToTempFile( part ); if( !tempFile ) { KMessageBox::sorry( d->wParent, i18n( "KMail was unable to write the attachment to a temporary file." ), i18n( "Unable to open attachment" ) ); return; } bool success = KRun::runUrl( KUrl::fromPath( tempFile->fileName() ), QString::fromLatin1( part->mimeType() ), d->wParent, true /*tempFile*/, false /*runExecutables*/ ); if( !success ) { if( KMimeTypeTrader::self()->preferredService( QString::fromLatin1( part->mimeType() ) ).isNull() ) { // KRun showed an Open-With dialog, and it was canceled. } else { // KRun failed. KMessageBox::sorry( d->wParent, i18n( "KMail was unable to open the attachment." ), i18n( "Unable to open attachment" ) ); } delete tempFile; tempFile = 0; } else { // The file was opened. Delete it only when the composer is closed // (and this object is destroyed). tempFile->setParent( this ); // Manages lifetime. } } void AttachmentControllerBase::viewAttachment( AttachmentPart::Ptr part ) { MessageComposer::Composer *composer = new MessageComposer::Composer; composer->globalPart()->setFallbackCharsetEnabled( true ); MessageComposer::AttachmentJob *attachmentJob = new MessageComposer::AttachmentJob( part, composer ); connect( attachmentJob, SIGNAL(result(KJob*)), this, SLOT(slotAttachmentContentCreated(KJob*)) ); attachmentJob->start(); } void AttachmentControllerBase::Private::slotAttachmentContentCreated( KJob *job ) { if ( !job->error() ) { const MessageComposer::AttachmentJob * const attachmentJob = dynamic_cast( job ); Q_ASSERT( attachmentJob ); emit q->showAttachment( attachmentJob->content(), QByteArray() ); } else { // TODO: show warning to the user kWarning() << "Error creating KMime::Content for attachment:" << job->errorText(); } } void AttachmentControllerBase::editAttachment( AttachmentPart::Ptr part, bool openWith ) { KTemporaryFile *tempFile = dumpAttachmentToTempFile( part ); if( !tempFile ) { KMessageBox::sorry( d->wParent, i18n( "KMail was unable to write the attachment to a temporary file." ), i18n( "Unable to edit attachment" ) ); return; } MessageViewer::EditorWatcher *watcher = new MessageViewer::EditorWatcher( KUrl::fromPath( tempFile->fileName() ), QString::fromLatin1( part->mimeType() ), openWith, this, d->wParent ); connect( watcher, SIGNAL(editDone(MessageViewer::EditorWatcher*)), this, SLOT(editDone(MessageViewer::EditorWatcher*)) ); if( watcher->start() ) { // The attachment is being edited. // We will clean things up in editDone(). d->editorPart[ watcher ] = part; d->editorTempFile[ watcher ] = tempFile; // Delete the temp file if the composer is closed (and this object is destroyed). tempFile->setParent( this ); // Manages lifetime. } else { kWarning() << "Could not start EditorWatcher."; delete watcher; delete tempFile; } } void AttachmentControllerBase::editAttachmentWith( AttachmentPart::Ptr part ) { editAttachment( part, true ); } void AttachmentControllerBase::saveAttachmentAs( AttachmentPart::Ptr part ) { QString pname = part->name(); if( pname.isEmpty() ) { pname = i18n( "unnamed" ); } KUrl url = KFileDialog::getSaveUrl( pname, QString( /*filter*/ ), d->wParent, i18n( "Save Attachment As" ) ); if( url.isEmpty() ) { kDebug() << "Save Attachment As dialog canceled."; return; } byteArrayToRemoteFile(part->data(), url); } void AttachmentControllerBase::byteArrayToRemoteFile(const QByteArray &aData, const KUrl &aURL, bool overwrite) { KIO::StoredTransferJob *job = KIO::storedPut( aData, aURL, -1, overwrite ? KIO::Overwrite : KIO::DefaultFlags ); connect( job, SIGNAL(result(KJob*)), SLOT(slotPutResult(KJob*)) ); } void AttachmentControllerBase::slotPutResult(KJob *job) { KIO::StoredTransferJob *_job = qobject_cast( job ); if (job->error()) { if (job->error() == KIO::ERR_FILE_ALREADY_EXIST) { if (KMessageBox::warningContinueCancel(0, i18n("File %1 exists.\nDo you want to replace it?", _job->url().toLocalFile()), i18n("Save to File"), KGuiItem(i18n("&Replace"))) == KMessageBox::Continue) byteArrayToRemoteFile(_job->data(), _job->url(), true); } else { KIO::JobUiDelegate *ui = static_cast( job )->ui(); ui->showErrorMessage(); } } } void AttachmentControllerBase::attachmentProperties( AttachmentPart::Ptr part ) { QPointer dialog = new AttachmentPropertiesDialog( part, false, d->wParent ); dialog->setEncryptEnabled( d->encryptEnabled ); dialog->setSignEnabled( d->signEnabled ); if( dialog->exec() && dialog ) { d->model->updateAttachment( part ); } delete dialog; } void AttachmentControllerBase::showAddAttachmentDialog() { #ifndef KDEPIM_MOBILE_UI QPointer dialog = new KEncodingFileDialog( QString( /*startDir*/ ), QString( /*encoding*/ ), QString( /*filter*/ ), i18n( "Attach File" ), KFileDialog::Other, d->wParent ); dialog->okButton()->setGuiItem( KGuiItem( i18n("&Attach"), QLatin1String( "document-open" ) ) ); dialog->setMode( KFile::Files|KFile::Directory ); if( dialog->exec() == KDialog::Accepted && dialog ) { const KUrl::List files = dialog->selectedUrls(); const QString encoding = MessageViewer::NodeHelper::fixEncoding( dialog->selectedEncoding() ); const int numberOfFiles(files.count()); for (int i=0; iname() == QLatin1String( "inode/directory" ) ) { const int rc = KMessageBox::warningYesNo( d->wParent,i18n("Do you really want to attach this directory \"%1\" ?", url.toLocalFile() ),i18n( "Attach directory" ) ); if ( rc == KMessageBox::Yes ) { addAttachment( urlWithEncoding ); } } else { addAttachment( urlWithEncoding ); } } } delete dialog; #else // use native dialog, while being much simpler, it actually fits on the screen much better than our own monster dialog const QString fileName = KFileDialog::getOpenFileName( KUrl(), QString(), d->wParent, i18n("Attach File" ) ); if ( !fileName.isEmpty() ) { addAttachment( KUrl::fromLocalFile( fileName ) ); } #endif } void AttachmentControllerBase::addAttachment( AttachmentPart::Ptr part ) { part->setEncrypted( d->model->isEncryptSelected() ); part->setSigned( d->model->isSignSelected() ); d->model->addAttachment( part ); if( MessageComposer::MessageComposerSettings::self()->showMessagePartDialogOnAttach() ) { attachmentProperties( part ); } emit fileAttached(); } MessageCore::AttachmentFromUrlBaseJob * AttachmentControllerBase::createAttachmentJob(const KUrl &url) { MessageCore::AttachmentFromUrlBaseJob *ajob = 0; if( KMimeType::findByUrl( url )->name() == QLatin1String( "inode/directory" ) ) { kDebug() << "Creating attachment from folder"; ajob = new AttachmentFromFolderJob ( url, this ); } else { ajob = new AttachmentFromUrlJob( url, this ); kDebug() << "Creating attachment from file"; } if( MessageComposer::MessageComposerSettings::maximumAttachmentSize() > 0 ) { ajob->setMaximumAllowedSize( MessageComposer::MessageComposerSettings::maximumAttachmentSize() ); } return ajob; } void AttachmentControllerBase::addAttachmentUrlSync(const KUrl &url) { MessageCore::AttachmentFromUrlBaseJob *ajob = createAttachmentJob(url); if(ajob->exec()) { AttachmentPart::Ptr part = ajob->attachmentPart(); addAttachment( part ); } else { if( ajob->error() ) { KMessageBox::sorry( d->wParent, ajob->errorString(), i18n( "Failed to attach file" ) ); } } } void AttachmentControllerBase::addAttachment( const KUrl &url ) { MessageCore::AttachmentFromUrlBaseJob *ajob = createAttachmentJob(url); connect( ajob, SIGNAL(result(KJob*)), this, SLOT(loadJobResult(KJob*)) ); ajob->start(); } void AttachmentControllerBase::addAttachments( const KUrl::List &urls ) { foreach( const KUrl &url, urls ) { addAttachment( url ); } } void AttachmentControllerBase::showAttachPublicKeyDialog() { using Kleo::KeySelectionDialog; QPointer dialog = new KeySelectionDialog( i18n( "Attach Public OpenPGP Key" ), i18n( "Select the public key which should be attached." ), std::vector(), KeySelectionDialog::PublicKeys|KeySelectionDialog::OpenPGPKeys, false /* no multi selection */, false /* no remember choice box */, d->wParent, "attach public key selection dialog" ); if( dialog->exec() == KDialog::Accepted && dialog ) { exportPublicKey( dialog->fingerprint() ); } delete dialog; } void AttachmentControllerBase::enableAttachPublicKey( bool enable ) { d->attachPublicKeyAction->setEnabled( enable ); } void AttachmentControllerBase::enableAttachMyPublicKey( bool enable ) { d->attachMyPublicKeyAction->setEnabled( enable ); } void AttachmentControllerBase::setAttachOwnVcard(bool attachVcard) { d->addOwnVcardAction->setChecked(attachVcard); } bool AttachmentControllerBase::attachOwnVcard() const { return d->addOwnVcardAction->isChecked(); } void AttachmentControllerBase::setIdentityHasOwnVcard(bool state) { d->addOwnVcardAction->setEnabled(state); } #include "moc_attachmentcontrollerbase.cpp"