diff --git a/core/document.cpp b/core/document.cpp index 2bb2917d4..33836d76e 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -57,6 +57,7 @@ #include "interfaces/configinterface.h" #include "interfaces/guiinterface.h" #include "interfaces/printinterface.h" +#include "interfaces/saveinterface.h" #include "observer.h" #include "page.h" #include "page_p.h" @@ -484,6 +485,16 @@ ConfigInterface* DocumentPrivate::generatorConfig( GeneratorInfo& info ) return info.config; } +SaveInterface* DocumentPrivate::generatorSave( GeneratorInfo& info ) +{ + if ( info.saveChecked ) + return info.save; + + info.save = qobject_cast< Okular::SaveInterface * >( info.generator ); + info.saveChecked = true; + return info.save; +} + void DocumentPrivate::saveDocumentInfo() const { if ( m_xmlFileName.isEmpty() ) @@ -2646,6 +2657,36 @@ const KComponentData* Document::componentData() const return kcd; } +bool Document::canSaveChanges() const +{ + if ( !d->m_generator ) + return false; + Q_ASSERT( !d->m_generatorName.isEmpty() ); + + QHash< QString, GeneratorInfo >::iterator genIt = d->m_loadedGenerators.find( d->m_generatorName ); + Q_ASSERT( genIt != d->m_loadedGenerators.end() ); + SaveInterface* saveIface = d->generatorSave( genIt.value() ); + if ( !saveIface ) + return false; + + return saveIface->supportsOption( SaveInterface::SaveChanges ); +} + +bool Document::saveChanges( const QString &fileName ) +{ + if ( !d->m_generator || fileName.isEmpty() ) + return false; + Q_ASSERT( !d->m_generatorName.isEmpty() ); + + QHash< QString, GeneratorInfo >::iterator genIt = d->m_loadedGenerators.find( d->m_generatorName ); + Q_ASSERT( genIt != d->m_loadedGenerators.end() ); + SaveInterface* saveIface = d->generatorSave( genIt.value() ); + if ( !saveIface || !saveIface->supportsOption( SaveInterface::SaveChanges ) ) + return false; + + return saveIface->save( fileName, SaveInterface::SaveChanges ); +} + void DocumentPrivate::requestDone( PixmapRequest * req ) { if ( !req ) diff --git a/core/document.h b/core/document.h index 110ae2d68..892e0c074 100644 --- a/core/document.h +++ b/core/document.h @@ -510,6 +510,22 @@ class OKULAR_EXPORT Document : public QObject */ const KComponentData* componentData() const; + /** + * Returns whether the changes to the document (modified annotations, + * values in form fields, etc) can be saved to another document. + * + * @since 0.7 (KDE 4.1) + */ + bool canSaveChanges() const; + + /** + * Save the document and the optional changes to it to the specified + * @p fileName. + * + * @since 0.7 (KDE 4.1) + */ + bool saveChanges( const QString &fileName ); + public Q_SLOTS: /** * This slot is called whenever the user changes the @p rotation of diff --git a/core/document_p.h b/core/document_p.h index caefe9e6d..1cc189cca 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -35,19 +35,24 @@ struct RunningSearch; namespace Okular { class ConfigInterface; +class SaveInterface; } struct GeneratorInfo { GeneratorInfo( const KComponentData &_data ) - : generator( 0 ), data( _data ), config( 0 ), configChecked( false ) + : generator( 0 ), data( _data ), + config( 0 ), save( 0 ), + configChecked( false ), saveChecked( false ) {} Okular::Generator * generator; KComponentData data; QString catalogName; Okular::ConfigInterface * config; + Okular::SaveInterface * save; bool configChecked : 1; + bool saveChecked : 1; }; namespace Okular { @@ -92,6 +97,7 @@ class DocumentPrivate void cacheExportFormats(); void setRotationInternal( int r, bool notify ); ConfigInterface* generatorConfig( GeneratorInfo& info ); + SaveInterface* generatorSave( GeneratorInfo& info ); // private slots void saveDocumentInfo() const; diff --git a/interfaces/saveinterface.h b/interfaces/saveinterface.h new file mode 100644 index 000000000..f1a7da97c --- /dev/null +++ b/interfaces/saveinterface.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 by Pino Toscano * + * * + * 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. * + ***************************************************************************/ + +#ifndef _OKULAR_SAVEINTERFACE_H_ +#define _OKULAR_SAVEINTERFACE_H_ + +#include + +#include + +namespace Okular { + +class AnnotationProxy; + +/** + * @short Abstract interface for saving + * + * This interface defines a way to save (or help saving) the document opened + * by the Generator. + * + * How to use it in a custom Generator: + * @code + class MyGenerator : public Okular::Generator, public Okular::SaveInterface + { + Q_OBJECT + Q_INTERFACES( Okular::SaveInterface ) + + ... + }; + * @endcode + * and - of course - implementing its methods. + */ +class OKULAR_EXPORT SaveInterface +{ + public: + /** + * The possible options for the saving. + */ + enum SaveOption + { + NoOption = 0, + SaveChanges = 1 ///< The possibility to save with the current changes to the document. + }; + Q_DECLARE_FLAGS( SaveOptions, SaveOption ) + + /** + * Destroys the save interface. + */ + virtual ~SaveInterface() {} + + /** + * Query for the supported saving options. + * + * @note NoOption is never queried + */ + virtual bool supportsOption( SaveOption option ) const = 0; + + /** + * Save to the specified @p fileName with the specified @p options. + */ + virtual bool save( const QString &fileName, SaveOptions options ) = 0; +}; + +} + +Q_DECLARE_INTERFACE( Okular::SaveInterface, "org.kde.okular.SaveInterface/0.1" ) +Q_DECLARE_OPERATORS_FOR_FLAGS( Okular::SaveInterface::SaveOptions ) + +#endif diff --git a/part.cpp b/part.cpp index 0b130783b..f86ca8be1 100644 --- a/part.cpp +++ b/part.cpp @@ -358,9 +358,12 @@ m_cliPresentation(false), m_generatorGuiClient(0) m_findNext = KStandardAction::findNext( this, SLOT( slotFindNext() ), ac); m_findNext->setEnabled( false ); + m_saveCopyAs = KStandardAction::saveAs( this, SLOT( slotSaveCopyAs() ), ac ); + m_saveCopyAs->setText( i18n( "Save &Copy As..." ) ); + ac->addAction( "file_save_copy", m_saveCopyAs ); + m_saveCopyAs->setEnabled( false ); + m_saveAs = KStandardAction::saveAs( this, SLOT( slotSaveFileAs() ), ac ); - m_saveAs->setText( i18n( "Save &Copy As..." ) ); - ac->addAction("save",m_saveAs); m_saveAs->setEnabled( false ); QAction * prefs = KStandardAction::preferences( this, SLOT( slotPreferences() ), ac); @@ -760,7 +763,8 @@ bool Part::openFile() // update one-time actions m_find->setEnabled( ok && canSearch ); m_findNext->setEnabled( ok && canSearch ); - m_saveAs->setEnabled( ok ); + m_saveAs->setEnabled( ok && m_document->canSaveChanges() ); + m_saveCopyAs->setEnabled( ok ); m_printPreview->setEnabled( ok && m_document->printingSupport() != Okular::Document::NoPrinting ); m_showProperties->setEnabled( ok ); bool hasEmbeddedFiles = ok && m_document->embeddedFiles() && m_document->embeddedFiles()->count() > 0; @@ -851,6 +855,7 @@ bool Part::closeUrl() m_find->setEnabled( false ); m_findNext->setEnabled( false ); m_saveAs->setEnabled( false ); + m_saveCopyAs->setEnabled( false ); m_printPreview->setEnabled( false ); m_showProperties->setEnabled( false ); m_showEmbeddedFiles->setEnabled( false ); @@ -1222,6 +1227,60 @@ void Part::slotSaveFileAs() { if (m_dummyMode) return; + KUrl saveUrl = KFileDialog::getSaveUrl( url().isLocalFile() ? url().url() : url().fileName(), QString(), widget() ); + if ( !saveUrl.isValid() || saveUrl.isEmpty() ) + return; + + if ( saveUrl.isLocalFile() ) + { + const QString fileName = saveUrl.toLocalFile(); + if ( QFile::exists( fileName ) ) + { + if (KMessageBox::warningContinueCancel( widget(), i18n("A file named \"%1\" already exists. Are you sure you want to overwrite it?", saveUrl.fileName()), QString(), KGuiItem(i18n("Overwrite"))) != KMessageBox::Continue) + return; + } + + if ( !m_document->saveChanges( fileName ) ) + { + KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) ); + return; + } + } + else + { + if ( KIO::NetAccess::exists( saveUrl, KIO::NetAccess::DestinationSide, widget() ) ) + { + if (KMessageBox::warningContinueCancel( widget(), i18n("A file named \"%1\" already exists. Are you sure you want to overwrite it?", saveUrl.fileName()), QString(), KGuiItem(i18n("Overwrite"))) != KMessageBox::Continue) + return; + } + + KTemporaryFile tf; + QString fileName; + if ( !tf.open() ) + { + KMessageBox::information( widget(), i18n("File could not open the temporary file for saving." ) ); + return; + } + fileName = tf.fileName(); + tf.close(); + + if ( !m_document->saveChanges( fileName ) ) + { + KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) ); + return; + } + + KIO::Job *copyJob = KIO::file_copy( fileName, saveUrl, -1, KIO::Overwrite ); + if ( !KIO::NetAccess::synchronousRun( copyJob, widget() ) ) + KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", saveUrl.prettyUrl() ) ); + } +} + + +void Part::slotSaveCopyAs() +{ + if (m_dummyMode) return; + KUrl saveUrl = KFileDialog::getSaveUrl( url().isLocalFile() ? url().url() : url().fileName(), QString(), widget() ); if ( saveUrl.isValid() && !saveUrl.isEmpty() ) { diff --git a/part.h b/part.h index abb2063cd..10f6b4d02 100644 --- a/part.h +++ b/part.h @@ -129,6 +129,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi void slotNextBookmark(); void slotFindNext(); void slotSaveFileAs(); + void slotSaveCopyAs(); void slotGetNewStuff(); void slotNewConfig(); void slotNewGeneratorConfig(); @@ -219,6 +220,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi QAction *m_find; QAction *m_findNext; QAction *m_saveAs; + QAction *m_saveCopyAs; QAction *m_printPreview; QAction *m_showProperties; QAction *m_showEmbeddedFiles; diff --git a/part.rc b/part.rc index d27ccbadf..43efecfca 100644 --- a/part.rc +++ b/part.rc @@ -1,10 +1,11 @@ - + &File - + +