diff --git a/core/document.cpp b/core/document.cpp index e27b7906a..7d834f54c 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2138,6 +2138,9 @@ KUrl Document::currentDocument() const bool Document::isAllowed( Permission action ) const { + if ( action == Okular::AllowNotes && !d->m_annotationEditingEnabled ) + return false; + #if !OKULAR_FORCE_DRM if ( KAuthorized::authorize( "skip_drm" ) && !Okular::Settings::obeyDRM() ) return true; @@ -2354,6 +2357,16 @@ void Document::requestTextPage( uint page ) d->m_generator->generateTextPage( kp ); } +void DocumentPrivate::notifyAnnotationChanges( int page ) +{ + int flags = DocumentObserver::Annotations; + + if ( canAddAnnotationsNatively() && m_containsExternalAnnotations ) + flags |= DocumentObserver::NeedSaveAs; // Annotations are not saved locally in this case + + foreachObserverD( notifyPageChanged( page, flags ) ); +} + void Document::addPageAnnotation( int page, Annotation * annotation ) { Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); @@ -2376,7 +2389,7 @@ void Document::addPageAnnotation( int page, Annotation * annotation ) proxy->notifyAddition( annotation, page ); // notify observers about the change - foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + d->notifyAnnotationChanges( page ); if ( annotation->flags() & Annotation::ExternallyDrawn ) { @@ -2432,7 +2445,7 @@ void Document::modifyPageAnnotation( int page, Annotation * annotation, bool app proxy->notifyModification( annotation, page, appearanceChanged ); // notify observers about the change - foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + d->notifyAnnotationChanges( page ); if ( appearanceChanged && (annotation->flags() & Annotation::ExternallyDrawn) ) { @@ -2507,7 +2520,7 @@ void Document::removePageAnnotation( int page, Annotation * annotation ) kp->removeAnnotation( annotation ); // Also destroys the object // in case of success, notify observers about the change - foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + d->notifyAnnotationChanges( page ); if ( isExternallyDrawn ) { @@ -2555,7 +2568,7 @@ void Document::removePageAnnotations( int page, const QList< Annotation * > &ann if ( changed ) { // in case we removed even only one annotation, notify observers about the change - foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + d->notifyAnnotationChanges( page ); if ( refreshNeeded ) { @@ -3717,6 +3730,12 @@ QPrinter::Orientation Document::orientation() const return (landscape > portrait) ? QPrinter::Landscape : QPrinter::Portrait; } +void Document::setAnnotationEditingEnabled( bool enable ) +{ + d->m_annotationEditingEnabled = enable; + foreachObserver( notifySetup( d->m_pagesVector, 0 ) ); +} + void DocumentPrivate::requestDone( PixmapRequest * req ) { if ( !req ) diff --git a/core/document.h b/core/document.h index cd36ae7f1..680cd9064 100644 --- a/core/document.h +++ b/core/document.h @@ -642,6 +642,14 @@ class OKULAR_EXPORT Document : public QObject */ QPrinter::Orientation orientation() const; + /** + * Control annotation editing (creation, modification and removal), + * which is enabled by default. + * + * @since 0.15 (KDE 4.9) + */ + void setAnnotationEditingEnabled( bool enable ); + public Q_SLOTS: /** diff --git a/core/document_p.h b/core/document_p.h index ac24cf9d7..1db982878 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -86,6 +86,7 @@ class DocumentPrivate m_archiveData( 0 ), m_fontsCached( false ), m_documentInfo( 0 ), + m_annotationEditingEnabled ( true ), m_annotationBeingMoved( false ) { calculateMaxTextPages(); @@ -115,6 +116,7 @@ class DocumentPrivate bool openDocumentInternal( const KService::Ptr& offer, bool isstdin, const QString& docFile, const QByteArray& filedata ); bool savePageDocumentInfo( KTemporaryFile *infoFile, int what ) const; DocumentViewport nextDocumentViewport() const; + void notifyAnnotationChanges( int page ); bool canAddAnnotationsNatively() const; bool canModifyExternalAnnotations() const; bool canRemoveExternalAnnotations() const; @@ -233,6 +235,7 @@ class DocumentPrivate QSet< View * > m_views; + bool m_annotationEditingEnabled; bool m_annotationBeingMoved; // is an annotation currently being moved? bool m_containsExternalAnnotations; // set on opening and never changed bool m_showWarningLimitedAnnotSupport; diff --git a/core/observer.h b/core/observer.h index 266993ce9..76c096c2a 100644 --- a/core/observer.h +++ b/core/observer.h @@ -72,8 +72,9 @@ class OKULAR_EXPORT DocumentObserver Bookmark = 2, ///< Bookmarks has been changed Highlights = 4, ///< Highlighting information has been changed TextSelection = 8, ///< Text selection has been changed - Annotations = 16, ///< Annotations has been changed - BoundingBox = 32 ///< Bounding boxes have been changed + Annotations = 16, ///< Annotations have been changed + BoundingBox = 32, ///< Bounding boxes have been changed + NeedSaveAs = 64 ///< Set along with Annotations when Save As is needed or annotation changes will be lost @since 0.15 (KDE 4.9) }; /** diff --git a/okular_part.desktop b/okular_part.desktop index 9307253d7..763d466c0 100644 --- a/okular_part.desktop +++ b/okular_part.desktop @@ -58,7 +58,7 @@ Name[uk]=Okular Name[x-test]=xxOkularxx Name[zh_CN]=Okular Name[zh_TW]=文件檢視_Okular -X-KDE-ServiceTypes=KParts/ReadOnlyPart +X-KDE-ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart X-KDE-Library=okularpart Type=Service MimeType=application/vnd.kde.okular-archive; diff --git a/part.cpp b/part.cpp index 4c6aac8ea..c344110d5 100644 --- a/part.cpp +++ b/part.cpp @@ -151,8 +151,25 @@ class FileKeeper std::FILE * m_handle; }; -K_PLUGIN_FACTORY( okularPartFactory, registerPlugin< Okular::Part >(); ) -K_EXPORT_PLUGIN( okularPartFactory( okularAboutData( "okular", I18N_NOOP( "Okular" ) ) ) ) +Okular::PartFactory::PartFactory() +: KPluginFactory(okularAboutData( "okular", I18N_NOOP( "Okular" ) )) +{ +} + +Okular::PartFactory::~PartFactory() +{ +} + +QObject *Okular::PartFactory::create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args, const QString &keyword) +{ + Q_UNUSED ( keyword ); + + Okular::Part *object = new Okular::Part( parentWidget, parent, args, componentData() ); + object->setReadWrite( QLatin1String(iface) == QLatin1String("KParts::ReadWritePart") ); + return object; +} + +K_EXPORT_PLUGIN( Okular::PartFactory() ) static QAction* actionForExportFormat( const Okular::ExportFormat& format, QObject *parent = 0 ) { @@ -264,8 +281,9 @@ namespace Okular Part::Part(QWidget *parentWidget, QObject *parent, -const QVariantList &args ) -: KParts::ReadOnlyPart(parent), +const QVariantList &args, +KComponentData componentData ) +: KParts::ReadWritePart(parent), m_tempfile( 0 ), m_fileWasRemoved( false ), m_showMenuBarAction( 0 ), m_showFullScreenAction( 0 ), m_actionsSearched( false ), m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args)), m_generatorGuiClient(0), m_keeper( 0 ) { @@ -300,7 +318,7 @@ m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args new OkularLiveConnectExtension( this ); // we need an instance - setComponentData(okularPartFactory::componentData()); + setComponentData( componentData ); GuiUtils::addIconLoader( iconLoader() ); @@ -1025,6 +1043,9 @@ void Part::notifyViewportChanged( bool /*smoothMove*/ ) void Part::notifyPageChanged( int page, int flags ) { + if ( flags & Okular::DocumentObserver::NeedSaveAs ) + setModified(); + if ( !(flags & Okular::DocumentObserver::Bookmark ) ) return; @@ -1258,6 +1279,10 @@ bool Part::openFile() bool Part::openUrl(const KUrl &_url) { + // Close current document if any + if ( !closeUrl() ) + return false; + KUrl url( _url ); if ( url.hasHTMLRef() ) { @@ -1281,7 +1306,7 @@ bool Part::openUrl(const KUrl &_url) } // this calls in sequence the 'closeUrl' and 'openFile' methods - bool openOk = KParts::ReadOnlyPart::openUrl( url ); + bool openOk = KParts::ReadWritePart::openUrl( url ); if ( openOk ) { @@ -1297,9 +1322,36 @@ bool Part::openUrl(const KUrl &_url) return openOk; } +bool Part::queryClose() +{ + if ( !isReadWrite() || !isModified() ) + return true; -bool Part::closeUrl() + const int res = KMessageBox::warningYesNoCancel( widget(), + i18n( "Do you want to save your annotation changes or discard them?" ), + i18n( "Close Document" ), + KStandardGuiItem::saveAs(), + KStandardGuiItem::discard() ); + + switch ( res ) + { + case KMessageBox::Yes: // Save as + slotSaveFileAs(); + return !isModified(); // Only allow closing if file was really saved + case KMessageBox::No: // Discard + return true; + default: // Cancel + return false; + } +} + +bool Part::closeUrl(bool promptToSave) { + if ( promptToSave && !queryClose() ) + return false; + + setModified( false ); + if (!m_temporaryLocalFile.isNull() && m_temporaryLocalFile != localFilePath()) { QFile::remove( m_temporaryLocalFile ); @@ -1358,17 +1410,22 @@ bool Part::closeUrl() #ifdef OKULAR_KEEP_FILE_OPEN m_keeper->close(); #endif - bool r = KParts::ReadOnlyPart::closeUrl(); + bool r = KParts::ReadWritePart::closeUrl(); setUrl(KUrl()); return r; } +bool Part::closeUrl() +{ + return closeUrl( true ); +} + void Part::guiActivateEvent(KParts::GUIActivateEvent *event) { updateViewActions(); - KParts::ReadOnlyPart::guiActivateEvent(event); + KParts::ReadWritePart::guiActivateEvent(event); } void Part::close() @@ -1473,7 +1530,12 @@ void Part::slotDoFileDirty() } // close and (try to) reopen the document - if ( KParts::ReadOnlyPart::openUrl( url() ) ) + KUrl oldUrl = url(); + + if ( !closeUrl() ) + return; + + if ( KParts::ReadWritePart::openUrl( oldUrl ) ) { // on successful opening, restore the previous viewport if ( m_viewportDirty.pageNumber >= (int) m_document->pages() ) @@ -1864,6 +1926,11 @@ void Part::slotFindPrev() m_findBar->findPrev(); } +bool Part::saveFile() +{ + kDebug() << "Okular part doesn't support saving the file in the location from which it was opened"; + return false; +} void Part::slotSaveFileAs() { @@ -1876,12 +1943,17 @@ void Part::slotSaveFileAs() if ( !saveUrl.isValid() || saveUrl.isEmpty() ) return; + saveAs( saveUrl ); +} + +bool Part::saveAs( const KUrl & saveUrl ) +{ KTemporaryFile tf; QString fileName; if ( !tf.open() ) { KMessageBox::information( widget(), i18n("Could not open the temporary file for saving." ) ); - return; + return false; } fileName = tf.fileName(); tf.close(); @@ -1897,12 +1969,18 @@ void Part::slotSaveFileAs() { KMessageBox::information( widget(), i18n("File could not be saved in '%1'. %2", fileName, errorText ) ); } - return; + return false; } 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() ) ); + return false; + } + + setModified( false ); + return true; } @@ -2577,6 +2655,12 @@ void Part::updateAboutBackendAction() } } +void Part::setReadWrite(bool readwrite) +{ + m_document->setAnnotationEditingEnabled( readwrite ); + ReadWritePart::setReadWrite( readwrite ); +} + } // namespace Okular #include "part.moc" diff --git a/part.h b/part.h index 39cdb7218..70b2afa2a 100644 --- a/part.h +++ b/part.h @@ -17,6 +17,7 @@ #define _PART_H_ #include +#include #include #include #include @@ -89,7 +90,7 @@ enum EmbedMode * @author Wilco Greven * @version 0.2 */ -class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, public KDocumentViewer, public Okular::ViewerInterface +class Part : public KParts::ReadWritePart, public Okular::DocumentObserver, public KDocumentViewer, public Okular::ViewerInterface { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.okular") @@ -104,7 +105,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi * which config file should be used by adding a string containing "ConfigFileName=" * to 'args'. **/ - Part(QWidget* parentWidget, QObject* parent, const QVariantList& args); + Part(QWidget* parentWidget, QObject* parent, const QVariantList& args, KComponentData componentData); // Destructor ~Part(); @@ -151,11 +152,17 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi void viewerMenuStateChange(bool enabled); protected: - // reimplemented from KParts::ReadOnlyPart + // reimplemented from KParts::ReadWritePart bool openFile(); bool openUrl(const KUrl &url); - bool closeUrl(); void guiActivateEvent(KParts::GUIActivateEvent *event); + public: + bool saveFile(); + bool queryClose(); + bool closeUrl(); + bool closeUrl(bool promptToSave); + void setReadWrite(bool readwrite); + bool saveAs(const KUrl & saveUrl); protected slots: // connected to actions @@ -319,6 +326,18 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi void slotHandleActivatedSourceReference(const QString& absFileName, int line, int col, bool *handled); }; +class PartFactory : public KPluginFactory +{ + Q_OBJECT + + public: + PartFactory(); + virtual ~PartFactory(); + + protected: + virtual QObject *create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args, const QString &keyword); +}; + } #endif diff --git a/shell/shell.cpp b/shell/shell.cpp index af4aa25f6..1c126f6e4 100644 --- a/shell/shell.cpp +++ b/shell/shell.cpp @@ -85,7 +85,7 @@ void Shell::init() // now that the Part is loaded, we cast it to a Part to get // our hands on it - m_part = factory->create< KParts::ReadOnlyPart >( this ); + m_part = factory->create< KParts::ReadWritePart >( this ); if (m_part) { // then, setup our actions @@ -234,6 +234,10 @@ QStringList Shell::fileFormats() const void Shell::fileOpen() { + // Don't open dialog if current document can't be closed + if ( !m_part->queryClose() ) + return; + // this slot is called whenever the File->Open menu is selected, // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar // button is clicked @@ -267,7 +271,10 @@ void Shell::fileOpen() return; KUrl url = dlg.selectedUrl(); if ( !url.isEmpty() ) + { + m_part->closeUrl( false ); openUrl( url ); + } } void Shell::slotQuit() @@ -331,6 +338,11 @@ QSize Shell::sizeHint() const return QApplication::desktop()->availableGeometry( this ).size() * 0.75; } +bool Shell::queryClose() +{ + return m_part ? m_part->closeUrl() : true; +} + #include "shell.moc" /* kate: replace-tabs on; indent-width 4; */ diff --git a/shell/shell.h b/shell/shell.h index 850e7e37f..8430b9c1c 100644 --- a/shell/shell.h +++ b/shell/shell.h @@ -68,6 +68,7 @@ protected: void readSettings(); void writeSettings(); void setFullScreen( bool ); + bool queryClose(); void showEvent(QShowEvent *event); @@ -92,7 +93,7 @@ private: private: KCmdLineArgs* m_args; - KParts::ReadOnlyPart* m_part; + KParts::ReadWritePart* m_part; KDocumentViewer* m_doc; KRecentFilesAction* m_recent; QStringList m_fileformats;