diff --git a/CMakeLists.txt b/CMakeLists.txt index 80ea76cb4..99536439e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ ENDIF(APPLE) target_link_libraries(okularcore ${OKULAR_IOKIT} ${KDE4_KIO_LIBS} ${KDE4_PHONON_LIBRARY} ${KDE4_KJSAPI_LIBRARY} ${MATH_LIB} ${KDE4_THREADWEAVER_LIBRARY} ) -set_target_properties(okularcore PROPERTIES VERSION 1.8.0 SOVERSION 1 ) +set_target_properties(okularcore PROPERTIES VERSION 2.0.0 SOVERSION 2 ) install(TARGETS okularcore ${INSTALL_TARGETS_DEFAULT_ARGS} ) @@ -170,6 +170,7 @@ set(okularpart_SRCS ui/searchwidget.cpp ui/sidebar.cpp ui/side_reviews.cpp + ui/snapshottaker.cpp ui/thumbnaillist.cpp ui/toc.cpp ui/tocmodel.cpp diff --git a/TODO b/TODO index 4d9d3104a..c96e3e5c7 100644 --- a/TODO +++ b/TODO @@ -102,7 +102,6 @@ More items (first items will enter 'In progress list' first): -> add okular manual in PDF format loaded on the first startup or on menu->help->manual this visually explains basic usage, mouse buttons functions & more.. -> ADD: click over image allows "save image" [60% done (activerect of type image)] --> zoom: fit text (with configurable margin) -> bookview: 3d opengl widget for viewing the document as a real book (turning pages, etc..) -> wallet: use asynchronous interface (to prevent ui-blocking) -> restore a location from a given url (like http:/someurl?stringForViewport) (BR99240) diff --git a/VERSION b/VERSION index 70feb6d0f..92466b4ed 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -okular v0.14.80 +okular v0.15.70 diff --git a/aboutdata.h b/aboutdata.h index d34bf240b..8d7d5d11f 100644 --- a/aboutdata.h +++ b/aboutdata.h @@ -39,6 +39,7 @@ inline KAboutData okularAboutData( const char* name, const char* iname ) about.addAuthor(ki18n("Enrico Ros"), ki18n("KPDF developer"), "eros.kde@email.it"); about.addCredit(ki18n("Eugene Trounev"), ki18n("Annotations artwork"), "eugene.trounev@gmail.com"); about.addCredit(ki18n("Jiri Baum - NICTA"), ki18n("Table selection tool"), "jiri@baum.com.au"); + about.addCredit(ki18n("Fabio D'Urso"), ki18n("Annotation improvements"), "fabiodurso@hotmail.it"); return about; } diff --git a/cmake/modules/FindPoppler.cmake b/cmake/modules/FindPoppler.cmake index ffa6950d2..1763ee949 100644 --- a/cmake/modules/FindPoppler.cmake +++ b/cmake/modules/FindPoppler.cmake @@ -97,17 +97,34 @@ int main() } " HAVE_POPPLER_0_20) +check_cxx_source_compiles(" +#include +#include + +int main() +{ + Poppler::MovieObject *movie = 0; + movie->showPosterImage(); + + const Poppler::Annotation::AdditionalActionType type = Poppler::Annotation::PageOpeningAction; + + return 0; +} +" HAVE_POPPLER_0_22) + set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) - if (HAVE_POPPLER_0_20) + if (HAVE_POPPLER_0_22) + set(popplerVersionMessage "0.22") + elseif (HAVE_POPPLER_0_20) set(popplerVersionMessage "0.20") elseif (HAVE_POPPLER_0_16) set(popplerVersionMessage "0.16") elseif (HAVE_POPPLER_0_12_1) set(popplerVersionMessage "0.12.1") - else (HAVE_POPPLER_0_20) + else (HAVE_POPPLER_0_22) set(popplerVersionMessage "0.5.4") - endif (HAVE_POPPLER_0_20) + endif (HAVE_POPPLER_0_22) if (NOT Poppler_FIND_QUIETLY) message(STATUS "Found Poppler-Qt4: ${POPPLER_LIBRARY}, (>= ${popplerVersionMessage})") endif (NOT Poppler_FIND_QUIETLY) diff --git a/conf/dlggeneralbase.ui b/conf/dlggeneralbase.ui index c167b3505..001d4e5fd 100644 --- a/conf/dlggeneralbase.ui +++ b/conf/dlggeneralbase.ui @@ -61,10 +61,71 @@ - Display document title in title bar + Display document title in title bar if available + + + + When not displaying document title: + + + + + + + KButtonGroup {border:0; } + + + true + + + 0 + + + + 8 + + + 0 + + + 0 + + + + + Display file name only + + + true + + + + + + + Display full file path + + + false + + + + + + + + + + 4 + + + 0 + + + @@ -313,6 +374,12 @@ For files which were opened before the previous zoom is applied. + + KButtonGroup + QGroupBox +
kbuttongroup.h
+ 1 +
KIntSpinBox QSpinBox diff --git a/conf/dlgperformancebase.ui b/conf/dlgperformancebase.ui index 8d58c9418..3a9f9aac4 100644 --- a/conf/dlgperformancebase.ui +++ b/conf/dlgperformancebase.ui @@ -52,13 +52,6 @@
- - - - Enable &background generation - - - diff --git a/conf/okular.kcfg b/conf/okular.kcfg index 6ad0e192b..91747337b 100644 --- a/conf/okular.kcfg +++ b/conf/okular.kcfg @@ -65,9 +65,6 @@ true - - true - Enabled @@ -167,6 +164,13 @@ true + + Name + + + + + true diff --git a/core/annotations.cpp b/core/annotations.cpp index 79d9b180f..21114af12 100644 --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -15,6 +15,7 @@ #include // local includes +#include "action.h" #include "document.h" #include "document_p.h" #include "movie.h" @@ -849,18 +850,18 @@ void Annotation::store( QDomNode & annNode, QDomDocument & document ) const } } -void AnnotationPrivate::annotationTransform( const QMatrix &matrix ) +void AnnotationPrivate::annotationTransform( const QTransform &matrix ) { resetTransformation(); transform( matrix ); } -void AnnotationPrivate::transform( const QMatrix &matrix ) +void AnnotationPrivate::transform( const QTransform &matrix ) { m_transformedBoundary.transform( matrix ); } -void AnnotationPrivate::baseTransform( const QMatrix &matrix ) +void AnnotationPrivate::baseTransform( const QTransform &matrix ) { m_boundary.transform( matrix ); } @@ -893,13 +894,13 @@ class Okular::TextAnnotationPrivate : public Okular::AnnotationPrivate public: TextAnnotationPrivate() : AnnotationPrivate(), m_textType( TextAnnotation::Linked ), - m_textIcon( "Note" ), m_inplaceAlign( 0 ), + m_textIcon( "Comment" ), m_inplaceAlign( 0 ), m_inplaceIntent( TextAnnotation::Unknown ) { } - virtual void transform( const QMatrix &matrix ); - virtual void baseTransform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); + virtual void baseTransform( const QTransform &matrix ); virtual void resetTransformation(); virtual void translate( const NormalizedPoint &coord ); virtual bool openDialogAfterCreation() const; @@ -1098,7 +1099,7 @@ void TextAnnotation::store( QDomNode & node, QDomDocument & document ) const // store the optional attributes if ( d->m_textType != Linked ) textElement.setAttribute( "type", (int)d->m_textType ); - if ( d->m_textIcon != "Comment" ) + if ( !d->m_textIcon.isEmpty() ) textElement.setAttribute( "icon", d->m_textIcon ); if ( d->m_textFont != QApplication::font() ) textElement.setAttribute( "font", d->m_textFont.toString() ); @@ -1130,7 +1131,7 @@ void TextAnnotation::store( QDomNode & node, QDomDocument & document ) const } } -void TextAnnotationPrivate::transform( const QMatrix &matrix ) +void TextAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); @@ -1139,7 +1140,7 @@ void TextAnnotationPrivate::transform( const QMatrix &matrix ) } } -void TextAnnotationPrivate::baseTransform( const QMatrix &matrix ) +void TextAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); @@ -1190,8 +1191,8 @@ class Okular::LineAnnotationPrivate : public Okular::AnnotationPrivate { } - virtual void transform( const QMatrix &matrix ); - virtual void baseTransform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); + virtual void baseTransform( const QTransform &matrix ); virtual void resetTransformation(); virtual void translate( const NormalizedPoint &coord ); @@ -1434,7 +1435,7 @@ void LineAnnotation::store( QDomNode & node, QDomDocument & document ) const } } -void LineAnnotationPrivate::transform( const QMatrix &matrix ) +void LineAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); @@ -1443,7 +1444,7 @@ void LineAnnotationPrivate::transform( const QMatrix &matrix ) it.next().transform( matrix ); } -void LineAnnotationPrivate::baseTransform( const QMatrix &matrix ) +void LineAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); @@ -1546,18 +1547,6 @@ QColor GeomAnnotation::geometricalInnerColor() const return d->m_geomInnerColor; } -void GeomAnnotation::setGeometricalPointWidth( int width ) -{ - Q_D( GeomAnnotation ); - d->m_style.setWidth( width ); -} - -int GeomAnnotation::geometricalPointWidth() const -{ - Q_D( const GeomAnnotation ); - return static_cast< int >( d->m_style.width() ); -} - Annotation::SubType GeomAnnotation::subType() const { return AGeom; @@ -1674,7 +1663,7 @@ double HighlightAnnotation::Quad::feather() const return d->m_feather; } -void HighlightAnnotation::Quad::transform( const QMatrix &matrix ) +void HighlightAnnotation::Quad::transform( const QTransform &matrix ) { for ( int i = 0; i < 4; ++i ) { d->m_transformedPoints[ i ] = d->m_points[ i ]; @@ -1691,8 +1680,8 @@ class Okular::HighlightAnnotationPrivate : public Okular::AnnotationPrivate { } - virtual void transform( const QMatrix &matrix ); - virtual void baseTransform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); + virtual void baseTransform( const QTransform &matrix ); HighlightAnnotation::HighlightType m_highlightType; QList< HighlightAnnotation::Quad > m_highlightQuads; @@ -1737,7 +1726,7 @@ HighlightAnnotation::HighlightAnnotation( const QDomNode & node ) q.setCapEnd( qe.hasAttribute( "end" ) ); q.setFeather( qe.attribute( "feather", "0.1" ).toDouble() ); - q.transform( QMatrix() ); + q.transform( QTransform() ); d->m_highlightQuads.append( q ); } @@ -1812,7 +1801,7 @@ Annotation::SubType HighlightAnnotation::subType() const return AHighlight; } -void HighlightAnnotationPrivate::transform( const QMatrix &matrix ) +void HighlightAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); @@ -1821,7 +1810,7 @@ void HighlightAnnotationPrivate::transform( const QMatrix &matrix ) it.next().transform( matrix ); } -void HighlightAnnotationPrivate::baseTransform( const QMatrix &matrix ) +void HighlightAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); @@ -1916,8 +1905,8 @@ class Okular::InkAnnotationPrivate : public Okular::AnnotationPrivate { } - virtual void transform( const QMatrix &matrix ); - virtual void baseTransform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); + virtual void baseTransform( const QTransform &matrix ); virtual void resetTransformation(); virtual void translate( const NormalizedPoint &coord ); @@ -2041,7 +2030,7 @@ void InkAnnotation::store( QDomNode & node, QDomDocument & document ) const } } -void InkAnnotationPrivate::transform( const QMatrix &matrix ) +void InkAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); @@ -2053,7 +2042,7 @@ void InkAnnotationPrivate::transform( const QMatrix &matrix ) } } -void InkAnnotationPrivate::baseTransform( const QMatrix &matrix ) +void InkAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); @@ -2423,3 +2412,149 @@ void MovieAnnotation::setMovie( Movie *movie ) Q_D( MovieAnnotation ); d->movie = movie; } + +/** ScreenAnnotation [Annotation] */ + +class Okular::ScreenAnnotationPrivate : public Okular::AnnotationPrivate +{ + public: + ~ScreenAnnotationPrivate(); + QMap< Okular::Annotation::AdditionalActionType, Okular::Action* > m_additionalActions; +}; + +ScreenAnnotationPrivate::~ScreenAnnotationPrivate() +{ + qDeleteAll( m_additionalActions ); +} + +ScreenAnnotation::ScreenAnnotation() + : Annotation( *new ScreenAnnotationPrivate() ) +{ +} + +ScreenAnnotation::ScreenAnnotation( const QDomNode & node ) + : Annotation( *new ScreenAnnotationPrivate(), node ) +{ + // loop through the whole children looking for a 'screen' element + QDomNode subNode = node.firstChild(); + while( subNode.isElement() ) + { + QDomElement e = subNode.toElement(); + subNode = subNode.nextSibling(); + if ( e.tagName() != "screen" ) + continue; + + // loading complete + break; + } +} + +ScreenAnnotation::~ScreenAnnotation() +{ +} + +void ScreenAnnotation::store( QDomNode & node, QDomDocument & document ) const +{ + // recurse to parent objects storing properties + Annotation::store( node, document ); + + // create [screen] element + QDomElement movieElement = document.createElement( "screen" ); + node.appendChild( movieElement ); +} + +Annotation::SubType ScreenAnnotation::subType() const +{ + return AScreen; +} + +void ScreenAnnotation::setAdditionalAction( AdditionalActionType type, Action *action ) +{ + Q_D( ScreenAnnotation ); + if ( d->m_additionalActions.contains( type ) ) + delete d->m_additionalActions.value( type ); + + d->m_additionalActions.insert( type, action ); +} + +Action* ScreenAnnotation::additionalAction( AdditionalActionType type ) const +{ + Q_D( const ScreenAnnotation ); + if ( !d->m_additionalActions.contains( type ) ) + return 0; + else + return d->m_additionalActions.value( type ); +} + +/** WidgetAnnotation [Annotation] */ + +class Okular::WidgetAnnotationPrivate : public Okular::AnnotationPrivate +{ + public: + ~WidgetAnnotationPrivate(); + QMap< Okular::Annotation::AdditionalActionType, Okular::Action* > m_additionalActions; +}; + +WidgetAnnotationPrivate::~WidgetAnnotationPrivate() +{ + qDeleteAll( m_additionalActions ); +} + +WidgetAnnotation::WidgetAnnotation() + : Annotation( *new WidgetAnnotationPrivate() ) +{ +} + +WidgetAnnotation::WidgetAnnotation( const QDomNode & node ) + : Annotation( *new WidgetAnnotationPrivate(), node ) +{ + // loop through the whole children looking for a 'widget' element + QDomNode subNode = node.firstChild(); + while( subNode.isElement() ) + { + QDomElement e = subNode.toElement(); + subNode = subNode.nextSibling(); + if ( e.tagName() != "widget" ) + continue; + + // loading complete + break; + } +} + +WidgetAnnotation::~WidgetAnnotation() +{ +} + +void WidgetAnnotation::store( QDomNode & node, QDomDocument & document ) const +{ + // recurse to parent objects storing properties + Annotation::store( node, document ); + + // create [widget] element + QDomElement movieElement = document.createElement( "widget" ); + node.appendChild( movieElement ); +} + +Annotation::SubType WidgetAnnotation::subType() const +{ + return AWidget; +} + +void WidgetAnnotation::setAdditionalAction( AdditionalActionType type, Action *action ) +{ + Q_D( WidgetAnnotation ); + if ( d->m_additionalActions.contains( type ) ) + delete d->m_additionalActions.value( type ); + + d->m_additionalActions.insert( type, action ); +} + +Action* WidgetAnnotation::additionalAction( AdditionalActionType type ) const +{ + Q_D( const WidgetAnnotation ); + if ( !d->m_additionalActions.contains( type ) ) + return 0; + else + return d->m_additionalActions.value( type ); +} diff --git a/core/annotations.h b/core/annotations.h index 23deaf3a9..5fefb290f 100644 --- a/core/annotations.h +++ b/core/annotations.h @@ -23,6 +23,7 @@ namespace Okular { +class Action; class Annotation; class AnnotationObjectRect; class AnnotationPrivate; @@ -42,6 +43,8 @@ class CaretAnnotationPrivate; class FileAttachmentAnnotationPrivate; class SoundAnnotationPrivate; class MovieAnnotationPrivate; +class ScreenAnnotationPrivate; +class WidgetAnnotationPrivate; /** * @short Helper class for (recursive) annotation retrieval/storage. @@ -83,9 +86,6 @@ class OKULAR_EXPORT AnnotationUtils * * An Annotation is an object (text note, highlight, sound, popup window, ..) * contained by a Page in the document. - * - * For current state in relations to pdf embedded annotations: - * @see generator_pdf/README.Annotations */ class OKULAR_EXPORT Annotation { @@ -112,6 +112,8 @@ class OKULAR_EXPORT Annotation AFileAttachment = 9, ///< A file attachment annotation ASound = 10, ///< A sound annotation AMovie = 11, ///< A movie annotation + AScreen = 12, ///< A screen annotation + AWidget = 13, ///< A widget annotation A_BASE = 0 ///< The annotation base class }; @@ -177,6 +179,17 @@ class OKULAR_EXPORT Annotation Completed = 64 ///< Has been completed }; + /** + * Describes the type of additional actions. + * + * @since 0.16 (KDE 4.10) + */ + enum AdditionalActionType + { + PageOpening, ///< Performed when the page containing the annotation is opened. + PageClosing ///< Performed when the page containing the annotation is closed. + }; + /** * A function to be called when the annotation is destroyed. * @@ -1050,16 +1063,6 @@ class OKULAR_EXPORT GeomAnnotation : public Annotation */ QColor geometricalInnerColor() const; - /** - * Sets the point @p width of the geometrical annotation. - */ - KDE_DEPRECATED void setGeometricalPointWidth( int width ); - - /** - * Returns the point width of the geometrical annotation. - */ - KDE_DEPRECATED int geometricalPointWidth() const; - /** * Returns the sub type of the geometrical annotation. */ @@ -1191,7 +1194,7 @@ class OKULAR_EXPORT HighlightAnnotation : public Annotation * Transforms the quad coordinates with the transformation defined * by @p matrix. */ - void transform( const QMatrix &matrix ); + void transform( const QTransform &matrix ); private: class Private; @@ -1524,6 +1527,117 @@ class OKULAR_EXPORT MovieAnnotation : public Annotation Q_DISABLE_COPY( MovieAnnotation ) }; +/** + * \short Screen annotation. + * + * The screen annotation specifies a region of a page upon which media clips + * may be played. It also serves as an object from which actions can be triggered. + * + * @since 0.16 (KDE 4.10) + */ +class OKULAR_EXPORT ScreenAnnotation : public Annotation +{ + public: + /** + * Creates a new screen annotation. + */ + ScreenAnnotation(); + + /** + * Creates a new screen annotation from the xml @p description + */ + ScreenAnnotation( const QDomNode &description ); + + /** + * Destroys the screen annotation. + */ + virtual ~ScreenAnnotation(); + + /** + * Returns the sub type of the screen annotation. + */ + SubType subType() const; + + /** + * Stores the screen annotation as xml in @p document + * under the given @p parentNode. + */ + void store( QDomNode &parentNode, QDomDocument &document ) const; + + /** + * Sets the additional @p action of the given @p type. + * + * @since 0.16 (KDE 4.10) + */ + void setAdditionalAction( AdditionalActionType type, Action *action ); + + /** + * Returns the additional action of the given @p type or @c 0 if no action has been defined. + * + * @since 0.16 (KDE 4.10) + */ + Action* additionalAction( AdditionalActionType type ) const; + + private: + Q_DECLARE_PRIVATE( ScreenAnnotation ) + Q_DISABLE_COPY( ScreenAnnotation ) +}; + +/** + * \short Widget annotation. + * + * The widget annotation represents a widget on a page. + * + * @since 0.16 (KDE 4.10) + */ +class OKULAR_EXPORT WidgetAnnotation : public Annotation +{ + public: + /** + * Creates a new widget annotation. + */ + WidgetAnnotation(); + + /** + * Creates a new widget annotation from the xml @p description + */ + WidgetAnnotation( const QDomNode &description ); + + /** + * Destroys the widget annotation. + */ + virtual ~WidgetAnnotation(); + + /** + * Returns the sub type of the widget annotation. + */ + SubType subType() const; + + /** + * Stores the widget annotation as xml in @p document + * under the given @p parentNode. + */ + void store( QDomNode &parentNode, QDomDocument &document ) const; + + /** + * Sets the additional @p action of the given @p type. + * + * @since 0.16 (KDE 4.10) + */ + void setAdditionalAction( AdditionalActionType type, Action *action ); + + /** + * Returns the additional action of the given @p type or @c 0 if no action has been defined. + * + * @since 0.16 (KDE 4.10) + */ + Action* additionalAction( AdditionalActionType type ) const; + + private: + Q_DECLARE_PRIVATE( WidgetAnnotation ) + Q_DISABLE_COPY( WidgetAnnotation ) +}; + } #endif diff --git a/core/annotations_p.h b/core/annotations_p.h index 36c37853f..221572df9 100644 --- a/core/annotations_p.h +++ b/core/annotations_p.h @@ -19,7 +19,7 @@ #include #include -class QMatrix; +class QTransform; namespace Okular { @@ -36,10 +36,10 @@ class AnnotationPrivate * Transforms the annotation coordinates with the transformation * defined by @p matrix. */ - void annotationTransform( const QMatrix &matrix ); + void annotationTransform( const QTransform &matrix ); - virtual void transform( const QMatrix &matrix ); - virtual void baseTransform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); + virtual void baseTransform( const QTransform &matrix ); virtual void resetTransformation(); virtual void translate( const NormalizedPoint &coord ); virtual bool openDialogAfterCreation() const; diff --git a/core/area.cpp b/core/area.cpp index 94b451426..825976079 100644 --- a/core/area.cpp +++ b/core/area.cpp @@ -40,7 +40,7 @@ NormalizedPoint& NormalizedPoint::operator=( const NormalizedPoint & p ) return *this; } -void NormalizedPoint::transform( const QMatrix &matrix ) +void NormalizedPoint::transform( const QTransform &matrix ) { qreal tmp_x = (qreal)x; qreal tmp_y = (qreal)y; @@ -190,7 +190,7 @@ QRect NormalizedRect::roundedGeometry( int xScale, int yScale ) const return QRect( l, t, r - l + 1, b - t + 1 ); } -void NormalizedRect::transform( const QMatrix &matrix ) +void NormalizedRect::transform( const QTransform &matrix ) { QRectF rect( left, top, right - left, bottom - top ); rect = matrix.mapRect( rect ); @@ -305,7 +305,7 @@ bool ObjectRect::contains( double x, double y, double, double ) const return m_transformedPath.contains( QPointF( x, y ) ); } -void ObjectRect::transform( const QMatrix &matrix ) +void ObjectRect::transform( const QTransform &matrix ) { m_transformedPath = matrix.map( m_path ); } @@ -385,7 +385,7 @@ AnnotationObjectRect::~AnnotationObjectRect() m_object = 0; } -void AnnotationObjectRect::transform( const QMatrix &matrix ) +void AnnotationObjectRect::transform( const QTransform &matrix ) { m_annotation->d_func()->annotationTransform( matrix ); } diff --git a/core/area.h b/core/area.h index a11d4bef4..4f63759bd 100644 --- a/core/area.h +++ b/core/area.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "global.h" @@ -69,7 +70,7 @@ class OKULAR_EXPORT NormalizedPoint /** * Transforms the normalized point with the operations defined by @p matrix. */ - void transform( const QMatrix &matrix ); + void transform( const QTransform &matrix ); /** * The normalized x coordinate. @@ -206,7 +207,7 @@ class OKULAR_EXPORT NormalizedRect /** * Transforms the normalized rectangle with the operations defined by @p matrix. */ - void transform( const QMatrix &matrix ); + void transform( const QTransform &matrix ); /** * Returns true if the point pt is located to the bottom of the rectangle @@ -370,7 +371,7 @@ class OKULAR_EXPORT ObjectRect /** * Transforms the object rectangle with the operations defined by @p matrix. */ - virtual void transform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); /** * Returns the square of the distance between the object and the point @p x, @p y @@ -425,7 +426,7 @@ class OKULAR_EXPORT AnnotationObjectRect : public ObjectRect /** * Transforms the annotation object rectangle with the operations defined by @p matrix. */ - virtual void transform( const QMatrix &matrix ); + virtual void transform( const QTransform &matrix ); private: Annotation * m_annotation; @@ -576,7 +577,7 @@ template class RegularArea : public QList< /** * Transforms the regular area with the operations defined by @p matrix. */ - void transform( const QMatrix &matrix ); + void transform( const QTransform &matrix ); }; template @@ -808,7 +809,7 @@ QList RegularArea::geometry( int xScale, int ySca } template -void RegularArea::transform( const QMatrix &matrix ) +void RegularArea::transform( const QTransform &matrix ) { if ( !this ) return; diff --git a/core/bookmarkmanager.cpp b/core/bookmarkmanager.cpp index b9f88c4fa..accd65f6f 100644 --- a/core/bookmarkmanager.cpp +++ b/core/bookmarkmanager.cpp @@ -73,29 +73,12 @@ static inline bool documentViewportFuzzyCompare( const DocumentViewport &vp1, co return true; } -static inline bool documentViewportLessThan( const DocumentViewport &vp1, const DocumentViewport &vp2 ) -{ - if ( vp1.pageNumber != vp2.pageNumber ) - return vp1.pageNumber < vp2.pageNumber; - - if ( !vp1.rePos.enabled && vp2.rePos.enabled ) - return true; - - if ( !vp2.rePos.enabled ) - return false; - - if ( vp1.rePos.normalizedY != vp2.rePos.normalizedY ) - return vp1.rePos.normalizedY < vp2.rePos.normalizedY; - - return vp1.rePos.normalizedX < vp2.rePos.normalizedX; -} - static inline bool bookmarkLessThan( const KBookmark &b1, const KBookmark &b2 ) { DocumentViewport vp1( b1.url().htmlRef() ); DocumentViewport vp2( b2.url().htmlRef() ); - return documentViewportLessThan( vp1, vp2 ); + return vp1 < vp2; } static inline bool okularBookmarkActionLessThan( QAction * a1, QAction * a2 ) @@ -103,7 +86,7 @@ static inline bool okularBookmarkActionLessThan( QAction * a1, QAction * a2 ) DocumentViewport vp1( static_cast< OkularBookmarkAction * >( a1 )->htmlRef() ); DocumentViewport vp2( static_cast< OkularBookmarkAction * >( a2 )->htmlRef() ); - return documentViewportLessThan( vp1, vp2 ); + return vp1 < vp2; } class BookmarkManager::Private : public KBookmarkOwner @@ -724,7 +707,7 @@ KBookmark BookmarkManager::nextBookmark( const DocumentViewport &viewport) const foreach ( const KBookmark &bm, bmarks ) { DocumentViewport vp( bm.url().htmlRef() ); - if ( documentViewportLessThan( viewport, vp ) ) + if ( viewport < vp ) { bookmark = bm; break; @@ -744,7 +727,7 @@ KBookmark BookmarkManager::previousBookmark( const DocumentViewport &viewport ) { KBookmark bm = *(it-1); DocumentViewport vp( bm.url().htmlRef() ); - if ( documentViewportLessThan( vp, viewport ) ) + if ( vp < viewport ) { bookmark = bm; break; diff --git a/core/document.cpp b/core/document.cpp index b06fb3693..143d59827 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -11,6 +11,7 @@ #include "document.h" #include "document_p.h" +#include #ifdef Q_OS_WIN #define _WIN32_WINNT 0x0500 #include @@ -173,11 +174,12 @@ QString DocumentPrivate::localizedSize(const QSizeF &size) const } } -void DocumentPrivate::cleanupPixmapMemory( qulonglong /*sure? bytesOffset*/ ) +qulonglong DocumentPrivate::calculateMemoryToFree() { // [MEM] choose memory parameters based on configuration profile qulonglong clipValue = 0; qulonglong memoryToFree = 0; + switch ( Settings::memoryLevel() ) { case Settings::EnumMemoryLevel::Low: @@ -201,7 +203,9 @@ void DocumentPrivate::cleanupPixmapMemory( qulonglong /*sure? bytesOffset*/ ) break; case Settings::EnumMemoryLevel::Greedy: { - const qulonglong memoryLimit = qMax(getFreeMemory(), getTotalMemory() / 2); + qulonglong freeSwap; + qulonglong freeMemory = getFreeMemory( &freeSwap ); + const qulonglong memoryLimit = qMin( qMax( freeMemory, getTotalMemory()/2 ), freeMemory+freeSwap ); if (m_allocatedPixmapsTotalMemory > memoryLimit) clipValue = (m_allocatedPixmapsTotalMemory - memoryLimit) / 2; } break; @@ -210,33 +214,80 @@ void DocumentPrivate::cleanupPixmapMemory( qulonglong /*sure? bytesOffset*/ ) if ( clipValue > memoryToFree ) memoryToFree = clipValue; + return memoryToFree; +} + +void DocumentPrivate::cleanupPixmapMemory() +{ + cleanupPixmapMemory( calculateMemoryToFree() ); +} + +void DocumentPrivate::cleanupPixmapMemory( qulonglong memoryToFree ) +{ if ( memoryToFree > 0 ) { // [MEM] free memory starting from older pixmaps int pagesFreed = 0; - QLinkedList< AllocatedPixmap * >::iterator pIt = m_allocatedPixmapsFifo.begin(); - QLinkedList< AllocatedPixmap * >::iterator pEnd = m_allocatedPixmapsFifo.end(); - while ( (pIt != pEnd) && (memoryToFree > 0) ) + while ( memoryToFree > 0 ) { - AllocatedPixmap * p = *pIt; - if ( m_observers.value( p->id )->canUnloadPixmap( p->page ) ) - { - // update internal variables - pIt = m_allocatedPixmapsFifo.erase( pIt ); - m_allocatedPixmapsTotalMemory -= p->memory; + AllocatedPixmap * p = searchLowestPriorityUnloadablePixmap( true ); + if ( !p ) // No pixmap to remove + break; + + kDebug().nospace() << "Evicting cache pixmap id=" << p->id << " page=" << p->page; + + // m_allocatedPixmapsTotalMemory can't underflow because we always add or remove + // the memory used by the AllocatedPixmap so at most it can reach zero + m_allocatedPixmapsTotalMemory -= p->memory; + // Make sure memoryToFree does not underflow + if ( p->memory > memoryToFree ) + memoryToFree = 0; + else memoryToFree -= p->memory; - pagesFreed++; - // delete pixmap - m_pagesVector.at( p->page )->deletePixmap( p->id ); - // delete allocation descriptor - delete p; - } else - ++pIt; + pagesFreed++; + // delete pixmap + m_pagesVector.at( p->page )->deletePixmap( p->id ); + // delete allocation descriptor + delete p; } - //p--rintf("freeMemory A:[%d -%d = %d] \n", m_allocatedPixmapsFifo.count() + pagesFreed, pagesFreed, m_allocatedPixmapsFifo.count() ); + //p--rintf("freeMemory A:[%d -%d = %d] \n", m_allocatedPixmaps.count() + pagesFreed, pagesFreed, m_allocatedPixmaps.count() ); } } +/* Returns the next pixmap to evict from cache, or NULL if no suitable pixmap + * is found. If thenRemoveIt is set, the pixmap is removed from + * m_allocatedPixmaps before returning it */ +AllocatedPixmap * DocumentPrivate::searchLowestPriorityUnloadablePixmap( bool thenRemoveIt ) +{ + QLinkedList< AllocatedPixmap * >::iterator pIt = m_allocatedPixmaps.begin(); + QLinkedList< AllocatedPixmap * >::iterator pEnd = m_allocatedPixmaps.end(); + QLinkedList< AllocatedPixmap * >::iterator farthestPixmap = pEnd; + const int currentViewportPage = (*m_viewportIterator).pageNumber; + + /* Find the pixmap that is farthest from the current viewport */ + int maxDistance = -1; + while ( pIt != pEnd ) + { + const AllocatedPixmap * p = *pIt; + const int distance = qAbs( p->page - currentViewportPage ); + if ( maxDistance < distance && m_observers.value( p->id )->canUnloadPixmap( p->page ) ) + { + maxDistance = distance; + farthestPixmap = pIt; + } + ++pIt; + } + + /* No pixmap to remove */ + if ( farthestPixmap == pEnd ) + return 0; + + AllocatedPixmap * selectedPixmap = *farthestPixmap; + if ( thenRemoveIt ) + m_allocatedPixmaps.erase( farthestPixmap ); + return selectedPixmap; +} + qulonglong DocumentPrivate::getTotalMemory() { static qulonglong cachedValue = 0; @@ -249,10 +300,8 @@ qulonglong DocumentPrivate::getTotalMemory() if ( !memFile.open( QIODevice::ReadOnly ) ) return (cachedValue = 134217728); - // read /proc/meminfo and sum up the contents of 'MemFree', 'Buffers' - // and 'Cached' fields. consider swapped memory as used memory. QTextStream readStream( &memFile ); - while ( true ) + while ( true ) { QString entry = readStream.readLine(); if ( entry.isNull() ) break; @@ -275,13 +324,23 @@ qulonglong DocumentPrivate::getTotalMemory() return (cachedValue = 134217728); } -qulonglong DocumentPrivate::getFreeMemory() +qulonglong DocumentPrivate::getFreeMemory( qulonglong *freeSwap ) { static QTime lastUpdate = QTime::currentTime().addSecs(-3); static qulonglong cachedValue = 0; + static qulonglong cachedFreeSwap = 0; if ( qAbs( lastUpdate.secsTo( QTime::currentTime() ) ) <= 2 ) + { + if (freeSwap) + *freeSwap = cachedFreeSwap; return cachedValue; + } + + /* Initialize the returned free swap value to 0. It is overwritten if the + * actual value is available */ + if (freeSwap) + *freeSwap = 0; #if defined(Q_OS_LINUX) // if /proc/meminfo doesn't exist, return MEMORY FULL @@ -294,22 +353,46 @@ qulonglong DocumentPrivate::getFreeMemory() qulonglong memoryFree = 0; QString entry; QTextStream readStream( &memFile ); + static const int nElems = 5; + QString names[nElems] = { "MemFree:", "Buffers:", "Cached:", "SwapFree:", "SwapTotal:" }; + qulonglong values[nElems] = { 0, 0, 0, 0, 0 }; + bool foundValues[nElems] = { false, false, false, false, false }; while ( true ) { entry = readStream.readLine(); if ( entry.isNull() ) break; - if ( entry.startsWith( "MemFree:" ) || - entry.startsWith( "Buffers:" ) || - entry.startsWith( "Cached:" ) || - entry.startsWith( "SwapFree:" ) ) - memoryFree += entry.section( ' ', -2, -2 ).toULongLong(); - if ( entry.startsWith( "SwapTotal:" ) ) - memoryFree -= entry.section( ' ', -2, -2 ).toULongLong(); + for ( int i = 0; i < nElems; ++i ) + { + if ( entry.startsWith( names[i] ) ) + { + values[i] = entry.section( ' ', -2, -2 ).toULongLong( &foundValues[i] ); + } + } } memFile.close(); + bool found = true; + for ( int i = 0; found && i < nElems; ++i ) + found = found && foundValues[i]; + if ( found ) + { + /* MemFree + Buffers + Cached - SwapUsed = + * = MemFree + Buffers + Cached - (SwapTotal - SwapFree) = + * = MemFree + Buffers + Cached + SwapFree - SwapTotal */ + memoryFree = values[0] + values[1] + values[2] + values[3]; + if ( values[4] > memoryFree ) + memoryFree = 0; + else + memoryFree -= values[4]; + } + else + { + return 0; + } lastUpdate = QTime::currentTime(); + if (freeSwap) + *freeSwap = ( cachedFreeSwap = (Q_UINT64_C(1024) * values[3]) ); return ( cachedValue = (Q_UINT64_C(1024) * memoryFree) ); #elif defined(Q_OS_FREEBSD) qulonglong cache, inact, free, psize; @@ -338,6 +421,8 @@ qulonglong DocumentPrivate::getFreeMemory() lastUpdate = QTime::currentTime(); + if (freeSwap) + *freeSwap = ( cachedFreeSwap = stat.ullAvailPageFile ); return ( cachedValue = stat.ullAvailPhys ); #else // tell the memory is full.. will act as in LOW profile @@ -777,15 +862,16 @@ void DocumentPrivate::warnLimitedAnnotSupport() return; m_showWarningLimitedAnnotSupport = false; // Show the warning once - if ( canAddAnnotationsNatively() ) + if ( m_annotationsNeedSaveAs ) { - // Show only if there are external annotations (we follow the usual XML path otherwise) - if ( m_containsExternalAnnotations ) - KMessageBox::sorry( m_parent->widget(), i18n("Your changes will not be saved automatically. Use File -> Save As... or your changes will be lost") ); + // Shown if the user is editing annotations in a file whose metadata is + // not stored locally (.okular archives belong to this category) + KMessageBox::information( m_parent->widget(), i18n("Your annotation changes will not be saved automatically. Use File -> Save As...\nor your changes will be lost once the document is closed"), QString(), "annotNeedSaveAs" ); } - else + else if ( !canAddAnnotationsNatively() ) { - KMessageBox::information( m_parent->widget(), i18n("You can save the annotated document using File -> Export As -> Document Archive"), QString(), "annotExportAsArchive" ); + // If the generator doesn't support native annotations + KMessageBox::information( m_parent->widget(), i18n("Your annotations are saved internally by Okular.\nYou can export the annotated document using File -> Export As -> Document Archive"), QString(), "annotExportAsArchive" ); } } @@ -810,8 +896,14 @@ void DocumentPrivate::saveDocumentInfo() const QDomElement pageList = doc.createElement( "pageList" ); root.appendChild( pageList ); PageItems saveWhat = AllPageItems; - if ( canAddAnnotationsNatively() && m_containsExternalAnnotations ) - saveWhat &= ~AnnotationPageItems; // Don't save local annotations in this case + if ( m_annotationsNeedSaveAs ) + { + /* In this case, if the user makes a modification, he's requested to + * save to a new document. Therefore, if there are existing local + * annotations, we save them back unmodified in the original + * document's metadata, so that it appears that it was not changed */ + saveWhat |= OriginalAnnotationPageItems; + } // .... save pages that hold data QVector< Page * >::const_iterator pIt = m_pagesVector.constBegin(), pEnd = m_pagesVector.constEnd(); for ( ; pIt != pEnd; ++pIt ) @@ -880,8 +972,22 @@ void DocumentPrivate::slotTimedMemoryCheck() cleanupPixmapMemory(); } -void DocumentPrivate::sendGeneratorRequest() +void DocumentPrivate::sendGeneratorPixmapRequest() { + /* If the pixmap cache will have to be cleaned in order to make room for the + * next request, get the distance from the current viewport of the page + * whose pixmap will be removed. We will ignore preload requests for pages + * that are at the same distance or farther */ + const qulonglong memoryToFree = calculateMemoryToFree(); + const int currentViewportPage = (*m_viewportIterator).pageNumber; + int maxDistance = INT_MAX; // Default: No maximum + if ( memoryToFree ) + { + AllocatedPixmap *pixmapToReplace = searchLowestPriorityUnloadablePixmap(); + if ( pixmapToReplace ) + maxDistance = qAbs( pixmapToReplace->page - currentViewportPage ); + } + // find a request PixmapRequest * request = 0; m_pixmapRequestsMutex.lock(); @@ -909,8 +1015,16 @@ void DocumentPrivate::sendGeneratorRequest() } delete r; } + else if ( !r->d->mForce && r->d->isPreload() && qAbs( r->pageNumber() - currentViewportPage ) >= maxDistance ) + { + m_pixmapRequestsStack.pop_back(); + //kDebug() << "Ignoring request that doesn't fit in cache"; + delete r; + } else + { request = r; + } } // if no request found (or already generated), return @@ -923,7 +1037,7 @@ void DocumentPrivate::sendGeneratorRequest() // [MEM] preventive memory freeing qulonglong pixmapBytes = 4 * request->width() * request->height(); if ( pixmapBytes > (1024 * 1024) ) - cleanupPixmapMemory( pixmapBytes ); + cleanupPixmapMemory( memoryToFree /* previously calculated value */ ); // submit the request to the generator if ( m_generator->canGeneratePixmap() ) @@ -945,7 +1059,7 @@ void DocumentPrivate::sendGeneratorRequest() { m_pixmapRequestsMutex.unlock(); // pino (7/4/2006): set the polling interval from 10 to 30 - QTimer::singleShot( 30, m_parent, SLOT(sendGeneratorRequest()) ); + QTimer::singleShot( 30, m_parent, SLOT(sendGeneratorPixmapRequest()) ); } } @@ -1008,11 +1122,8 @@ void DocumentPrivate::slotGeneratorConfigChanged( const QString& ) } // [MEM] remove allocation descriptors - QLinkedList< AllocatedPixmap * >::const_iterator aIt = m_allocatedPixmapsFifo.constBegin(); - QLinkedList< AllocatedPixmap * >::const_iterator aEnd = m_allocatedPixmapsFifo.constEnd(); - for ( ; aIt != aEnd; ++aIt ) - delete *aIt; - m_allocatedPixmapsFifo.clear(); + qDeleteAll( m_allocatedPixmaps ); + m_allocatedPixmaps.clear(); m_allocatedPixmapsTotalMemory = 0; // send reload signals to observers @@ -1021,7 +1132,7 @@ void DocumentPrivate::slotGeneratorConfigChanged( const QString& ) // free memory if in 'low' profile if ( Settings::memoryLevel() == Settings::EnumMemoryLevel::Low && - !m_allocatedPixmapsFifo.isEmpty() && !m_pagesVector.isEmpty() ) + !m_allocatedPixmaps.isEmpty() && !m_pagesVector.isEmpty() ) cleanupPixmapMemory(); } @@ -1057,66 +1168,73 @@ void DocumentPrivate::_o_configChanged() } } -void DocumentPrivate::doContinueNextMatchSearch(void *pagesToNotifySet, void * theMatch, int currentPage, int searchID, const QString & text, int theCaseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages) +void DocumentPrivate::doContinueDirectionMatchSearch(void *doContinueDirectionMatchSearchStruct) { - RegularAreaRect * match = static_cast(theMatch); - Qt::CaseSensitivity caseSensitivity = static_cast(theCaseSensitivity); - QSet< int > *pagesToNotify = static_cast< QSet< int > * >( pagesToNotifySet ); - RunningSearch *search = m_searches.value(searchID); + DoContinueDirectionMatchSearchStruct *searchStruct = static_cast(doContinueDirectionMatchSearchStruct); + RunningSearch *search = m_searches.value(searchStruct->searchID); - if ((m_searchCancelled && !match) || !search) + if ((m_searchCancelled && !searchStruct->match) || !search) { // if the user cancelled but he just got a match, give him the match! QApplication::restoreOverrideCursor(); if (search) search->isCurrentlySearching = false; - emit m_parent->searchFinished( searchID, Document::SearchCancelled ); - delete pagesToNotify; + emit m_parent->searchFinished( searchStruct->searchID, Document::SearchCancelled ); + delete searchStruct->pagesToNotify; + delete searchStruct; return; } + bool doContinue = false; // if no match found, loop through the whole doc, starting from currentPage - if ( !match ) + if ( !searchStruct->match ) { - int pageCount = m_pagesVector.count(); - if (donePages < pageCount) + const int pageCount = m_pagesVector.count(); + if (searchStruct->pagesDone < pageCount) { - bool doContinue = true; - if ( currentPage >= pageCount ) + doContinue = true; + if ( searchStruct->currentPage >= pageCount || searchStruct->currentPage < 0 ) { - if ( noDialogs || KMessageBox::questionYesNo(m_parent->widget(), i18n("End of document reached.\nContinue from the beginning?"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::Yes ) - currentPage = 0; + const QString question = searchStruct->forward ? i18n("End of document reached.\nContinue from the beginning?") : i18n("Beginning of document reached.\nContinue from the bottom?"); + if ( searchStruct->noDialogs || KMessageBox::questionYesNo(m_parent->widget(), question, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::Yes ) + searchStruct->currentPage = searchStruct->forward ? 0 : pageCount - 1; else doContinue = false; } - if (doContinue) - { - // get page - Page * page = m_pagesVector[ currentPage ]; - // request search page if needed - if ( !page->hasTextPage() ) - m_parent->requestTextPage( page->number() ); - // if found a match on the current page, end the loop - match = page->findText( searchID, text, FromTop, caseSensitivity ); - - if ( !match ) - { - currentPage++; - donePages++; - } - else - { - donePages = 1; - } - - QMetaObject::invokeMethod(m_parent, "doContinueNextMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotifySet), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, donePages)); - return; - } } } - doProcessSearchMatch( match, search, pagesToNotify, currentPage, searchID, moveViewport, color ); + if (doContinue) + { + // get page + Page * page = m_pagesVector[ searchStruct->currentPage ]; + // request search page if needed + if ( !page->hasTextPage() ) + m_parent->requestTextPage( page->number() ); + + // if found a match on the current page, end the loop + searchStruct->match = page->findText( searchStruct->searchID, searchStruct->text, searchStruct->forward ? FromTop : FromBottom, searchStruct->caseSensitivity ); + + if ( !searchStruct->match ) + { + if (searchStruct->forward) searchStruct->currentPage++; + else searchStruct->currentPage--; + searchStruct->pagesDone++; + } + else + { + searchStruct->pagesDone = 1; + } + + // Both of the previous if branches need to call doContinueDirectionMatchSearch + QMetaObject::invokeMethod(m_parent, "doContinueDirectionMatchSearch", Qt::QueuedConnection, Q_ARG(void *, searchStruct)); + } + else + { + doProcessSearchMatch( searchStruct->match, search, searchStruct->pagesToNotify, searchStruct->currentPage, searchStruct->searchID, searchStruct->moveViewport, searchStruct->color ); + delete searchStruct; + } } void DocumentPrivate::doProcessSearchMatch( RegularAreaRect *match, RunningSearch *search, QSet< int > *pagesToNotify, int currentPage, int searchID, bool moveViewport, const QColor & color ) @@ -1169,69 +1287,6 @@ void DocumentPrivate::doProcessSearchMatch( RegularAreaRect *match, RunningSearc delete pagesToNotify; } -void DocumentPrivate::doContinuePrevMatchSearch(void *pagesToNotifySet, void * theMatch, int currentPage, int searchID, const QString & text, int theCaseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages) -{ - RegularAreaRect * match = static_cast(theMatch); - Qt::CaseSensitivity caseSensitivity = static_cast(theCaseSensitivity); - QSet< int > *pagesToNotify = static_cast< QSet< int > * >( pagesToNotifySet ); - RunningSearch *search = m_searches.value(searchID); - - if ((m_searchCancelled && !match) || !search) - { - // if the user cancelled but he just got a match, give him the match! - QApplication::restoreOverrideCursor(); - - if (search) search->isCurrentlySearching = false; - - emit m_parent->searchFinished( searchID, Document::SearchCancelled ); - delete pagesToNotify; - return; - } - - - // if no match found, loop through the whole doc, starting from currentPage - if ( !match ) - { - int pageCount = m_pagesVector.count(); - if (donePages < pageCount) - { - bool doContinue = true; - if ( currentPage < 0 ) - { - if ( noDialogs || KMessageBox::questionYesNo(m_parent->widget(), i18n("Beginning of document reached.\nContinue from the bottom?"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::Yes ) - currentPage = pageCount - 1; - else - doContinue = false; - } - if (doContinue) - { - // get page - Page * page = m_pagesVector[ currentPage ]; - // request search page if needed - if ( !page->hasTextPage() ) - m_parent->requestTextPage( page->number() ); - // if found a match on the current page, end the loop - match = page->findText( searchID, text, FromBottom, caseSensitivity ); - - if ( !match ) - { - currentPage--; - donePages++; - } - else - { - donePages = 1; - } - - QMetaObject::invokeMethod(m_parent, "doContinuePrevMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotifySet), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, donePages)); - return; - } - } - } - - doProcessSearchMatch( match, search, pagesToNotify, currentPage, searchID, moveViewport, color ); -} - void DocumentPrivate::doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int theCaseSensitivity, const QColor & color) { QMap< Page *, QVector > *pageMatches = static_cast< QMap< Page *, QVector > * >(pageMatchesMap); @@ -1678,24 +1733,32 @@ bool Document::openDocument( const QString & docFile, const KUrl& url, const KMi } d->m_generatorName = offer->name(); - d->m_containsExternalAnnotations = false; - d->m_showWarningLimitedAnnotSupport = true; + + bool containsExternalAnnotations = false; foreach ( Page * p, d->m_pagesVector ) { p->d->m_doc = d; if ( !p->annotations().empty() ) - d->m_containsExternalAnnotations = true; + containsExternalAnnotations = true; } + // Be quiet while restoring local annotations + d->m_showWarningLimitedAnnotSupport = false; + d->m_annotationsNeedSaveAs = false; + // 2. load Additional Data (bookmarks, local annotations and metadata) about the document if ( d->m_archiveData ) { d->loadDocumentInfo( d->m_archiveData->metadataFileName ); + d->m_annotationsNeedSaveAs = true; } else { d->loadDocumentInfo(); + d->m_annotationsNeedSaveAs = ( d->canAddAnnotationsNatively() && containsExternalAnnotations ); } + + d->m_showWarningLimitedAnnotSupport = true; d->m_bookmarkManager->setUrl( d->m_url ); // 3. setup observers inernal lists and data @@ -1863,11 +1926,8 @@ void Document::closeDocument() d->m_pagesVector.clear(); // clear 'memory allocation' descriptors - QLinkedList< AllocatedPixmap * >::const_iterator aIt = d->m_allocatedPixmapsFifo.constBegin(); - QLinkedList< AllocatedPixmap * >::const_iterator aEnd = d->m_allocatedPixmapsFifo.constEnd(); - for ( ; aIt != aEnd; ++aIt ) - delete *aIt; - d->m_allocatedPixmapsFifo.clear(); + qDeleteAll( d->m_allocatedPixmaps ); + d->m_allocatedPixmaps.clear(); // clear 'running searches' descriptors QMap< int, RunningSearch * >::const_iterator rIt = d->m_searches.constBegin(); @@ -1925,14 +1985,14 @@ void Document::removeObserver( DocumentObserver * pObserver ) (*it)->deletePixmap( observerId ); // [MEM] free observer's allocation descriptors - QLinkedList< AllocatedPixmap * >::iterator aIt = d->m_allocatedPixmapsFifo.begin(); - QLinkedList< AllocatedPixmap * >::iterator aEnd = d->m_allocatedPixmapsFifo.end(); + QLinkedList< AllocatedPixmap * >::iterator aIt = d->m_allocatedPixmaps.begin(); + QLinkedList< AllocatedPixmap * >::iterator aEnd = d->m_allocatedPixmaps.end(); while ( aIt != aEnd ) { AllocatedPixmap * p = *aIt; if ( p->id == observerId ) { - aIt = d->m_allocatedPixmapsFifo.erase( aIt ); + aIt = d->m_allocatedPixmaps.erase( aIt ); delete p; } else @@ -1963,11 +2023,8 @@ void Document::reparseConfig() } // [MEM] remove allocation descriptors - QLinkedList< AllocatedPixmap * >::const_iterator aIt = d->m_allocatedPixmapsFifo.constBegin(); - QLinkedList< AllocatedPixmap * >::const_iterator aEnd = d->m_allocatedPixmapsFifo.constEnd(); - for ( ; aIt != aEnd; ++aIt ) - delete *aIt; - d->m_allocatedPixmapsFifo.clear(); + qDeleteAll( d->m_allocatedPixmaps ); + d->m_allocatedPixmaps.clear(); d->m_allocatedPixmapsTotalMemory = 0; // send reload signals to observers @@ -1976,7 +2033,7 @@ void Document::reparseConfig() // free memory if in 'low' profile if ( Settings::memoryLevel() == Settings::EnumMemoryLevel::Low && - !d->m_allocatedPixmapsFifo.isEmpty() && !d->m_pagesVector.isEmpty() ) + !d->m_allocatedPixmaps.isEmpty() && !d->m_pagesVector.isEmpty() ) d->cleanupPixmapMemory(); } @@ -2138,6 +2195,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; @@ -2297,7 +2357,6 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests, } // 2. [ADD TO STACK] add requests to stack - bool threadingDisabled = !Settings::enableThreading(); QLinkedList< PixmapRequest * >::const_iterator rIt = requests.constBegin(), rEnd = requests.constEnd(); for ( ; rIt != rEnd; ++rIt ) { @@ -2316,9 +2375,6 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests, if ( !request->asynchronous() ) request->d->mPriority = 0; - if ( request->asynchronous() && threadingDisabled ) - request->d->mAsynchronous = false; - // add request to the 'stack' at the right place if ( !request->priority() ) // add priority zero requests to the top of the stack @@ -2338,9 +2394,9 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests, // 3. [START FIRST GENERATION] if generator is ready, start a new generation, // or else (if gen is running) it will be started when the new contents will //come from generator (in requestDone()) - // all handling of requests put into sendGeneratorRequest + // all handling of requests put into sendGeneratorPixmapRequest // if ( generator->canRequestPixmap() ) - d->sendGeneratorRequest(); + d->sendGeneratorPixmapRequest(); } void Document::requestTextPage( uint page ) @@ -2354,6 +2410,16 @@ void Document::requestTextPage( uint page ) d->m_generator->generateTextPage( kp ); } +void DocumentPrivate::notifyAnnotationChanges( int page ) +{ + int flags = DocumentObserver::Annotations; + + if ( m_annotationsNeedSaveAs ) + flags |= DocumentObserver::NeedSaveAs; + + foreachObserverD( notifyPageChanged( page, flags ) ); +} + void Document::addPageAnnotation( int page, Annotation * annotation ) { Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); @@ -2376,7 +2442,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 +2498,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 +2573,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 +2621,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 ) { @@ -2662,6 +2728,8 @@ void Document::setViewport( const DocumentViewport & viewport, int excludeId, bo //if ( viewport == oldViewport ) // kDebug(OkularDebug) << "setViewport with the same viewport."; + const int oldPageNumber = oldViewport.pageNumber; + // set internal viewport taking care of history if ( oldViewport.pageNumber == viewport.pageNumber || !oldViewport.isValid() ) { @@ -2681,31 +2749,19 @@ void Document::setViewport( const DocumentViewport & viewport, int excludeId, bo d->m_viewportIterator = d->m_viewportHistory.insert( d->m_viewportHistory.end(), viewport ); } + const int currentViewportPage = (*d->m_viewportIterator).pageNumber; + + const bool currentPageChanged = (oldPageNumber != currentViewportPage); + // notify change to all other (different from id) observers QMap< int, DocumentObserver * >::const_iterator it = d->m_observers.constBegin(), end = d->m_observers.constEnd(); for ( ; it != end ; ++ it ) + { if ( it.key() != excludeId ) (*it)->notifyViewportChanged( smoothMove ); - // [MEM] raise position of currently viewed page in allocation queue - if ( d->m_allocatedPixmapsFifo.count() > 1 ) - { - const int page = viewport.pageNumber; - QLinkedList< AllocatedPixmap * > viewportPixmaps; - QLinkedList< AllocatedPixmap * >::iterator aIt = d->m_allocatedPixmapsFifo.begin(); - QLinkedList< AllocatedPixmap * >::iterator aEnd = d->m_allocatedPixmapsFifo.end(); - while ( aIt != aEnd ) - { - if ( (*aIt)->page == page ) - { - viewportPixmaps.append( *aIt ); - aIt = d->m_allocatedPixmapsFifo.erase( aIt ); - continue; - } - ++aIt; - } - if ( !viewportPixmaps.isEmpty() ) - d->m_allocatedPixmapsFifo += viewportPixmaps; + if ( currentPageChanged ) + (*it)->notifyCurrentPageChanged( oldPageNumber, currentViewportPage ); } } @@ -2825,37 +2881,14 @@ void Document::searchText( int searchID, const QString & text, bool fromStart, Q QMetaObject::invokeMethod(this, "doContinueAllDocumentSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, pageMatches), Q_ARG(int, 0), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(QColor, color)); } // 2. NEXTMATCH - find next matching item (or start from top) - else if ( type == NextMatch ) - { - // find out from where to start/resume search from - int viewportPage = (*d->m_viewportIterator).pageNumber; - int currentPage = fromStart ? 0 : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage); - Page * lastPage = fromStart ? 0 : d->m_pagesVector[ currentPage ]; - int pagesDone = 0; - - // continue checking last TextPage first (if it is the current page) - RegularAreaRect * match = 0; - if ( lastPage && lastPage->number() == s->continueOnPage ) - { - if ( newText ) - match = lastPage->findText( searchID, text, FromTop, caseSensitivity ); - else - match = lastPage->findText( searchID, text, NextResult, caseSensitivity, &s->continueOnMatch ); - if ( !match ) - { - currentPage++; - pagesDone++; - } - } - - QMetaObject::invokeMethod(this, "doContinueNextMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, pagesDone)); - } // 3. PREVMATCH - find previous matching item (or start from bottom) - else if ( type == PreviousMatch ) + else if ( type == NextMatch || type == PreviousMatch ) { // find out from where to start/resume search from - int viewportPage = (*d->m_viewportIterator).pageNumber; - int currentPage = fromStart ? d->m_pagesVector.count() - 1 : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage); + const bool forward = type == NextMatch; + const int viewportPage = (*d->m_viewportIterator).pageNumber; + const int fromStartSearchPage = forward ? 0 : d->m_pagesVector.count() - 1; + int currentPage = fromStart ? fromStartSearchPage : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage); Page * lastPage = fromStart ? 0 : d->m_pagesVector[ currentPage ]; int pagesDone = 0; @@ -2864,17 +2897,31 @@ void Document::searchText( int searchID, const QString & text, bool fromStart, Q if ( lastPage && lastPage->number() == s->continueOnPage ) { if ( newText ) - match = lastPage->findText( searchID, text, FromBottom, caseSensitivity ); + match = lastPage->findText( searchID, text, forward ? FromTop : FromBottom, caseSensitivity ); else - match = lastPage->findText( searchID, text, PreviousResult, caseSensitivity, &s->continueOnMatch ); + match = lastPage->findText( searchID, text, forward ? NextResult : PreviousResult, caseSensitivity, &s->continueOnMatch ); if ( !match ) { - currentPage--; + if (forward) currentPage++; + else currentPage--; pagesDone++; } } + + DoContinueDirectionMatchSearchStruct *searchStruct = new DoContinueDirectionMatchSearchStruct(); + searchStruct->forward = forward; + searchStruct->pagesToNotify = pagesToNotify; + searchStruct->match = match; + searchStruct->currentPage = currentPage; + searchStruct->searchID = searchID; + searchStruct->text = text; + searchStruct->caseSensitivity = caseSensitivity; + searchStruct->moveViewport = moveViewport; + searchStruct->color = color; + searchStruct->noDialogs = noDialogs; + searchStruct->pagesDone = pagesDone; - QMetaObject::invokeMethod(this, "doContinuePrevMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, pagesDone)); + QMetaObject::invokeMethod(this, "doContinueDirectionMatchSearch", Qt::QueuedConnection, Q_ARG(void *, searchStruct)); } // 4. GOOGLE* - process all document marking pages else if ( type == GoogleAll || type == GoogleAny ) @@ -3451,6 +3498,23 @@ bool Document::canSaveChanges() const return saveIface->supportsOption( SaveInterface::SaveChanges ); } +bool Document::canSaveChanges( SaveCapability cap ) const +{ + switch ( cap ) + { + case SaveFormsCapability: + /* Assume that if the generator supports saving, forms can be saved. + * We have no means to actually query the generator at the moment + * TODO: Add some method to query the generator in SaveInterface */ + return canSaveChanges(); + + case SaveAnnotationsCapability: + return d->canAddAnnotationsNatively(); + } + + return false; +} + bool Document::saveChanges( const QString &fileName ) { QString errorText; @@ -3597,6 +3661,7 @@ bool Document::openDocumentArchive( const QString & docFile, const KUrl & url ) const KMimeType::Ptr docMime = KMimeType::findByPath( tempFileName, 0, true /* local file */ ); d->m_archiveData = archiveData.get(); + d->m_archivedFileName = documentFileName; bool ret = openDocument( tempFileName, url, docMime ); if ( ret ) @@ -3616,7 +3681,9 @@ bool Document::saveDocumentArchive( const QString &fileName ) if ( !d->m_generator ) return false; - QString docFileName = d->m_url.fileName(); + /* If we opened an archive, use the name of original file (eg foo.pdf) + * instead of the archive's one (eg foo.okular) */ + QString docFileName = d->m_archiveData ? d->m_archivedFileName : d->m_url.fileName(); if ( docFileName == QLatin1String( "-" ) ) return false; @@ -3717,6 +3784,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 ) @@ -3739,13 +3812,13 @@ void DocumentPrivate::requestDone( PixmapRequest * req ) #endif // [MEM] 1.1 find and remove a previous entry for the same page and id - QLinkedList< AllocatedPixmap * >::iterator aIt = m_allocatedPixmapsFifo.begin(); - QLinkedList< AllocatedPixmap * >::iterator aEnd = m_allocatedPixmapsFifo.end(); + QLinkedList< AllocatedPixmap * >::iterator aIt = m_allocatedPixmaps.begin(); + QLinkedList< AllocatedPixmap * >::iterator aEnd = m_allocatedPixmaps.end(); for ( ; aIt != aEnd; ++aIt ) if ( (*aIt)->page == req->pageNumber() && (*aIt)->id == req->id() ) { AllocatedPixmap * p = *aIt; - m_allocatedPixmapsFifo.erase( aIt ); + m_allocatedPixmaps.erase( aIt ); m_allocatedPixmapsTotalMemory -= p->memory; delete p; break; @@ -3757,7 +3830,7 @@ void DocumentPrivate::requestDone( PixmapRequest * req ) // [MEM] 1.2 append memory allocation descriptor to the FIFO qulonglong memoryBytes = 4 * req->width() * req->height(); AllocatedPixmap * memoryPage = new AllocatedPixmap( req->id(), req->pageNumber(), memoryBytes ); - m_allocatedPixmapsFifo.append( memoryPage ); + m_allocatedPixmaps.append( memoryPage ); m_allocatedPixmapsTotalMemory += memoryBytes; // 2. notify an observer that its pixmap changed @@ -3779,7 +3852,7 @@ void DocumentPrivate::requestDone( PixmapRequest * req ) bool hasPixmaps = !m_pixmapRequestsStack.isEmpty(); m_pixmapRequestsMutex.unlock(); if ( hasPixmaps ) - sendGeneratorRequest(); + sendGeneratorPixmapRequest(); } void DocumentPrivate::setPageBoundingBox( int page, const NormalizedRect& boundingBox ) @@ -3892,11 +3965,8 @@ void Document::setPageSize( const PageSize &size ) for ( ; pIt != pEnd; ++pIt ) (*pIt)->d->changeSize( size ); // clear 'memory allocation' descriptors - QLinkedList< AllocatedPixmap * >::const_iterator aIt = d->m_allocatedPixmapsFifo.constBegin(); - QLinkedList< AllocatedPixmap * >::const_iterator aEnd = d->m_allocatedPixmapsFifo.constEnd(); - for ( ; aIt != aEnd; ++aIt ) - delete *aIt; - d->m_allocatedPixmapsFifo.clear(); + qDeleteAll( d->m_allocatedPixmaps ); + d->m_allocatedPixmaps.clear(); d->m_allocatedPixmapsTotalMemory = 0; // notify the generator that the current page size has changed d->m_generator->pageSizeChanged( size, d->m_pageSize ); @@ -4019,6 +4089,25 @@ bool DocumentViewport::operator==( const DocumentViewport & vp ) const return true; } +bool DocumentViewport::operator<( const DocumentViewport & vp ) const +{ + // TODO: Check autoFit and Position + + if ( pageNumber != vp.pageNumber ) + return pageNumber < vp.pageNumber; + + if ( !rePos.enabled && vp.rePos.enabled ) + return true; + + if ( !vp.rePos.enabled ) + return false; + + if ( rePos.normalizedY != vp.rePos.normalizedY ) + return rePos.normalizedY < vp.rePos.normalizedY; + + return rePos.normalizedX < vp.rePos.normalizedX; +} + /** DocumentInfo **/ diff --git a/core/document.h b/core/document.h index cd36ae7f1..d47acec8a 100644 --- a/core/document.h +++ b/core/document.h @@ -561,10 +561,34 @@ class OKULAR_EXPORT Document : public QObject */ const KComponentData* componentData() const; + /** + * Saving capabilities. Their availability varies according to the + * underlying generator and/or the document type. + * + * @see canSaveChanges (SaveCapability) + * @since 0.15 (KDE 4.9) + */ + enum SaveCapability + { + SaveFormsCapability = 1, ///< Can save form changes + SaveAnnotationsCapability = 2 ///< Can save annotation changes + }; + + /** + * Returns whether it's possible to save a given category of changes to + * another document. + * + * @since 0.15 (KDE 4.9) + */ + bool canSaveChanges( SaveCapability cap ) const; + /** * Returns whether the changes to the document (modified annotations, * values in form fields, etc) can be saved to another document. * + * Equivalent to the logical OR of canSaveChanges(SaveCapability) for + * each capability. + * * @since 0.7 (KDE 4.1) */ bool canSaveChanges() const; @@ -642,6 +666,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: /** @@ -779,7 +811,7 @@ class OKULAR_EXPORT Document : public QObject Q_PRIVATE_SLOT( d, void saveDocumentInfo() const ) Q_PRIVATE_SLOT( d, void slotTimedMemoryCheck() ) - Q_PRIVATE_SLOT( d, void sendGeneratorRequest() ) + Q_PRIVATE_SLOT( d, void sendGeneratorPixmapRequest() ) Q_PRIVATE_SLOT( d, void rotationFinished( int page, Okular::Page *okularPage ) ) Q_PRIVATE_SLOT( d, void fontReadingProgress( int page ) ) Q_PRIVATE_SLOT( d, void fontReadingGotFont( const Okular::FontInfo& font ) ) @@ -788,8 +820,7 @@ class OKULAR_EXPORT Document : public QObject Q_PRIVATE_SLOT( d, void _o_configChanged() ) // search thread simulators - Q_PRIVATE_SLOT( d, void doContinueNextMatchSearch(void *pagesToNotifySet, void * match, int currentPage, int searchID, const QString & text, int caseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages) ) - Q_PRIVATE_SLOT( d, void doContinuePrevMatchSearch(void *pagesToNotifySet, void * match, int currentPage, int searchID, const QString & text, int caseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages) ) + Q_PRIVATE_SLOT( d, void doContinueDirectionMatchSearch(void *doContinueDirectionMatchSearchStruct) ) Q_PRIVATE_SLOT( d, void doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int caseSensitivity, const QColor & color) ) Q_PRIVATE_SLOT( d, void doContinueGooglesDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QStringList & words, int caseSensitivity, const QColor & color, bool matchAll) ) }; @@ -829,6 +860,7 @@ class OKULAR_EXPORT DocumentViewport * @internal */ bool operator==( const DocumentViewport &other ) const; + bool operator<( const DocumentViewport &other ) const; /** * The number of the page nearest the center of the viewport. diff --git a/core/document_p.h b/core/document_p.h index ac24cf9d7..91a557746 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -63,6 +63,21 @@ namespace Okular { class FontExtractionThread; +struct DoContinueDirectionMatchSearchStruct +{ + bool forward; + QSet< int > *pagesToNotify; + RegularAreaRect *match; + int currentPage; + int searchID; + QString text; + Qt::CaseSensitivity caseSensitivity; + bool moveViewport; + QColor color; + bool noDialogs; + int pagesDone; +}; + class DocumentPrivate { public: @@ -86,6 +101,7 @@ class DocumentPrivate m_archiveData( 0 ), m_fontsCached( false ), m_documentInfo( 0 ), + m_annotationEditingEnabled ( true ), m_annotationBeingMoved( false ) { calculateMaxTextPages(); @@ -94,10 +110,13 @@ class DocumentPrivate // private methods QString pagesSizeString() const; QString localizedSize(const QSizeF &size) const; - void cleanupPixmapMemory( qulonglong bytesOffset = 0 ); + qulonglong calculateMemoryToFree(); + void cleanupPixmapMemory(); + void cleanupPixmapMemory( qulonglong memoryToFree ); + AllocatedPixmap * searchLowestPriorityUnloadablePixmap( bool thenRemoveIt = false ); void calculateMaxTextPages(); qulonglong getTotalMemory(); - qulonglong getFreeMemory(); + qulonglong getFreeMemory( qulonglong *freeSwap = 0 ); void loadDocumentInfo(); void loadDocumentInfo( const QString &fileName ); void loadViewsInfo( View *view, const QDomElement &e ); @@ -115,6 +134,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; @@ -123,15 +143,14 @@ class DocumentPrivate // private slots void saveDocumentInfo() const; void slotTimedMemoryCheck(); - void sendGeneratorRequest(); + void sendGeneratorPixmapRequest(); void rotationFinished( int page, Okular::Page *okularPage ); void fontReadingProgress( int page ); void fontReadingGotFont( const Okular::FontInfo& font ); void slotGeneratorConfigChanged( const QString& ); void refreshPixmaps( int ); void _o_configChanged(); - void doContinueNextMatchSearch(void *pagesToNotifySet, void * match, int currentPage, int searchID, const QString & text, int caseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages); - void doContinuePrevMatchSearch(void *pagesToNotifySet, void * theMatch, int currentPage, int searchID, const QString & text, int theCaseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages); + void doContinueDirectionMatchSearch(void *doContinueDirectionMatchSearchStruct); void doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int caseSensitivity, const QColor & color); void doContinueGooglesDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QStringList & words, int caseSensitivity, const QColor & color, bool matchAll); @@ -184,7 +203,7 @@ class DocumentPrivate QLinkedList< PixmapRequest * > m_pixmapRequestsStack; QLinkedList< PixmapRequest * > m_executingPixmapRequests; QMutex m_pixmapRequestsMutex; - QLinkedList< AllocatedPixmap * > m_allocatedPixmapsFifo; + QLinkedList< AllocatedPixmap * > m_allocatedPixmaps; qulonglong m_allocatedPixmapsTotalMemory; QList< int > m_allocatedTextPagesFifo; int m_maxAllocatedTextPages; @@ -225,6 +244,7 @@ class DocumentPrivate Scripter *m_scripter; ArchiveData *m_archiveData; + QString m_archivedFileName; QPointer< FontExtractionThread > m_fontThread; bool m_fontsCached; @@ -233,11 +253,14 @@ class DocumentPrivate QSet< View * > m_views; + bool m_annotationEditingEnabled; + bool m_annotationsNeedSaveAs; bool m_annotationBeingMoved; // is an annotation currently being moved? - bool m_containsExternalAnnotations; // set on opening and never changed bool m_showWarningLimitedAnnotSupport; }; } #endif + +/* kate: replace-tabs on; indent-width 4; */ diff --git a/core/fileprinter.cpp b/core/fileprinter.cpp index 28d283998..38c89f54e 100644 --- a/core/fileprinter.cpp +++ b/core/fileprinter.cpp @@ -33,13 +33,6 @@ using namespace Okular; -// Deprecated overload for binary compatibility -int FilePrinter::printFile( QPrinter &printer, const QString file, FileDeletePolicy fileDeletePolicy, - PageSelectPolicy pageSelectPolicy, const QString &pageRange ) -{ - return printFile( printer, file, QPrinter::Portrait, fileDeletePolicy, pageSelectPolicy, pageRange ); -} - int FilePrinter::printFile( QPrinter &printer, const QString file, QPrinter::Orientation documentOrientation, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange ) @@ -49,24 +42,6 @@ int FilePrinter::printFile( QPrinter &printer, const QString file, documentOrientation ); } -// Deprecated function kept for binary compatibility -// This is deprecated because it cannot support different original orientations -// for each document in the list. -int FilePrinter::printFiles( QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy ) -{ - FilePrinter fp; - return fp.doPrintFiles( printer, fileList, fileDeletePolicy, FilePrinter::ApplicationSelectsPages, QString(), - QPrinter::Portrait ); -} - -// Deprecated overload for binary compatibility -int FilePrinter::doPrintFiles( QPrinter &printer, QStringList fileList, FileDeletePolicy fileDeletePolicy, - PageSelectPolicy pageSelectPolicy, const QString &pageRange ) -{ - return doPrintFiles( printer, fileList, fileDeletePolicy, pageSelectPolicy, pageRange, - QPrinter::Portrait ); -} - int FilePrinter::doPrintFiles( QPrinter &printer, QStringList fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPrinter::Orientation documentOrientation ) @@ -380,15 +355,6 @@ Generator::PrintError FilePrinter::printError( int c ) -// Deprecated overload for binary compatibility -QStringList FilePrinter::printArguments( QPrinter &printer, FileDeletePolicy fileDeletePolicy, - PageSelectPolicy pageSelectPolicy, bool useCupsOptions, - const QString &pageRange, const QString &version ) -{ - return printArguments( printer, fileDeletePolicy, pageSelectPolicy, useCupsOptions, - pageRange, version, QPrinter::Portrait ); -} - QStringList FilePrinter::printArguments( QPrinter &printer, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, bool useCupsOptions, const QString &pageRange, const QString &version, @@ -516,12 +482,6 @@ QStringList FilePrinter::pages( QPrinter &printer, PageSelectPolicy pageSelectPo return QStringList(); // AllPages } -// Deprecated overload for binary compatibility -QStringList FilePrinter::cupsOptions( QPrinter &printer ) -{ - return cupsOptions( printer, QPrinter::Portrait ); -} - QStringList FilePrinter::cupsOptions( QPrinter &printer, QPrinter::Orientation documentOrientation ) { QStringList optionList; @@ -635,12 +595,6 @@ QString FilePrinter::mediaPaperSource( QPrinter &printer ) } } -// Deprecated overload for binary compatibility -QStringList FilePrinter::optionOrientation( QPrinter &printer ) -{ - return optionOrientation( printer, QPrinter::Portrait ); -} - QStringList FilePrinter::optionOrientation( QPrinter &printer, QPrinter::Orientation documentOrientation ) { // portrait and landscape options rotate the document according to the document orientation diff --git a/core/fileprinter.h b/core/fileprinter.h index 5fbb49263..c7cd32aef 100644 --- a/core/fileprinter.h +++ b/core/fileprinter.h @@ -18,9 +18,6 @@ #include #include -// For KDE_DEPRECATED -#include - #include "okular_export.h" #include "generator.h" @@ -84,60 +81,6 @@ public: PageSelectPolicy pageSelectPolicy = FilePrinter::ApplicationSelectsPages, const QString &pageRange = QString() ); - /** Print a file using the settings in QPrinter (compatibility overload) - * - * Only supports CUPS and LPR on *NIX. Page Range only supported in CUPS. - * Most settings unsupported by LPR, some settings unsupported by CUPS. - * - * @param printer the print settings to use - * @param file the file to print - * @param fileDeletePolicy if the application or system deletes the file - * @param pageSelectPolicy if the application or system selects the pages to print - * @param pageRange page range to print if SystemSlectsPages and user chooses Selection in Print Dialog - * - * @returns Returns exit code: - * -9 if lpr not found - * -8 if empty file name - * -7 if unable to find file - * -6 if invalid printer state - * -5 if print to file copy failed - * -2 if the KProcess could not be started - * -1 if the KProcess crashed - * otherwise the KProcess exit code - * - * @deprecated Use the overload which takes the documentOrientation instead. - */ - - static KDE_DEPRECATED int printFile( QPrinter &printer, const QString file, - FileDeletePolicy fileDeletePolicy = FilePrinter::ApplicationDeletesFiles, - PageSelectPolicy pageSelectPolicy = FilePrinter::ApplicationSelectsPages, - const QString &pageRange = QString() ); - - /** Print a list of files using the settings in QPrinter - * - * Only supports CUPS and LPR on *NIX. - * Most settings unsupported by LPR, some settings unsupported by CUPS. - * - * @param printer the print settings to use - * @param fileList the files to print - * @param fileDeletePolicy if the application or system deletes the file - * - * @returns Returns exit code: - * -9 if lpr not found - * -8 if empty file list - * -7 if unable to find a file - * -6 if invalid printer state - * -5 if print to file copy failed - * -2 if the KProcess could not be started - * -1 if the KProcess crashed - * otherwise the KProcess exit code - * - * @deprecated Use printFile instead, passing the documentOrientation for each file. - */ - - static KDE_DEPRECATED int printFiles( QPrinter &printer, const QStringList &fileList, - FileDeletePolicy fileDeletePolicy = FilePrinter::ApplicationDeletesFiles ); - /** Return the list of pages selected by the user in the Print Dialog * * @param printer the print settings to use @@ -209,17 +152,11 @@ protected: bool detectCupsService(); bool detectCupsConfig(); - KDE_DEPRECATED int doPrintFiles( QPrinter &printer, const QStringList fileList, - FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, - const QString &pageRange ); int doPrintFiles( QPrinter &printer, const QStringList fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPrinter::Orientation documentOrientation ); - KDE_DEPRECATED QStringList printArguments( QPrinter &printer, - FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, - bool useCupsOptions, const QString &pageRange, const QString &version ); QStringList printArguments( QPrinter &printer, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, bool useCupsOptions, const QString &pageRange, const QString &version, @@ -233,12 +170,10 @@ protected: QStringList pages( QPrinter &printer, PageSelectPolicy pageSelectPolicy, const QString &pageRange, bool useCupsOptions, const QString &version ); - KDE_DEPRECATED QStringList cupsOptions( QPrinter &printer ); QStringList cupsOptions( QPrinter &printer, QPrinter::Orientation documentOrientation ); QStringList optionMedia( QPrinter &printer ); QString mediaPageSize( QPrinter &printer ); QString mediaPaperSource( QPrinter &printer ); - KDE_DEPRECATED QStringList optionOrientation( QPrinter &printer ); QStringList optionOrientation( QPrinter &printer, QPrinter::Orientation documentOrientation ); QStringList optionDoubleSidedPrinting( QPrinter &printer ); QStringList optionPageOrder( QPrinter &printer ); diff --git a/core/generator.cpp b/core/generator.cpp index 2c63ca17f..72993c077 100644 --- a/core/generator.cpp +++ b/core/generator.cpp @@ -10,6 +10,7 @@ #include "generator.h" #include "generator_p.h" +#include "observer.h" #include #include @@ -102,9 +103,9 @@ void GeneratorPrivate::pixmapGenerationFinished() request->page()->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( img ) ) ); const int pageNumber = request->page()->number(); - q->signalPixmapRequestDone( request ); if ( mPixmapGenerationThread->calcBoundingBox() ) q->updatePageBoundingBox( pageNumber, mPixmapGenerationThread->boundingBox() ); + q->signalPixmapRequestDone( request ); } void GeneratorPrivate::textpageGenerationFinished() @@ -254,7 +255,6 @@ bool Generator::canGenerateTextPage() const void Generator::generateTextPage( Page *page ) { - Q_D( Generator ); TextPage *tp = textPage( page ); page->setTextPage( tp ); signalTextGenerationDone( page, tp ); @@ -478,6 +478,19 @@ void PixmapRequestPrivate::swap() qSwap( mWidth, mHeight ); } +bool PixmapRequestPrivate::isPreload() const +{ + switch ( mPriority ) + { + case PAGEVIEW_PRELOAD_PRIO: + case THUMBNAILS_PRELOAD_PRIO: + case PRESENTATION_PRELOAD_PRIO: + return true; + default: + return false; + } +} + class Okular::ExportFormatPrivate : public QSharedData { public: diff --git a/core/generator_p.h b/core/generator_p.h index f2d746a19..775da7f5b 100644 --- a/core/generator_p.h +++ b/core/generator_p.h @@ -70,6 +70,7 @@ class PixmapRequestPrivate { public: void swap(); + bool isPreload() const; int mId; int mPageNumber; diff --git a/core/movie.cpp b/core/movie.cpp index 6cffb8687..dd780eba0 100644 --- a/core/movie.cpp +++ b/core/movie.cpp @@ -12,6 +12,7 @@ // qt/kde includes #include +#include #include #include @@ -30,7 +31,8 @@ class Movie::Private m_playMode( PlayOnce ), m_tmp( 0 ), m_showControls( false ), - m_autoPlay( false ) + m_autoPlay( false ), + m_showPosterImage( false ) { } @@ -39,8 +41,10 @@ class Movie::Private Rotation m_rotation; PlayMode m_playMode; QTemporaryFile *m_tmp; + QImage m_posterImage; bool m_showControls : 1; bool m_autoPlay : 1; + bool m_showPosterImage : 1; }; Movie::Movie( const QString& fileName ) @@ -58,7 +62,7 @@ Movie::Movie( const QString& fileName, const QByteArray &data ) * GStreamer backend). Storing the data in a temporary file works fine * though, not to mention, it releases much needed memory. (gamaral) */ - d->m_tmp = new QTemporaryFile( QString( "%1/okrXXXXXX_%2" ).arg( QDir::tempPath() ).arg( fileName ) ); + d->m_tmp = new QTemporaryFile( QString( "%1/okrXXXXXX" ).arg( QDir::tempPath() ) ); if ( d->m_tmp->open() ) { d->m_tmp->write( data ); d->m_tmp->flush(); @@ -130,3 +134,22 @@ bool Movie::autoPlay() const return d->m_autoPlay; } +void Movie::setShowPosterImage( bool show ) +{ + d->m_showPosterImage = show; +} + +bool Movie::showPosterImage() const +{ + return d->m_showPosterImage; +} + +void Movie::setPosterImage( const QImage &image ) +{ + d->m_posterImage = image; +} + +QImage Movie::posterImage() const +{ + return d->m_posterImage; +} diff --git a/core/movie.h b/core/movie.h index cbe382e56..8d0e2fe70 100644 --- a/core/movie.h +++ b/core/movie.h @@ -16,6 +16,8 @@ #include +class QImage; + namespace Okular { /** @@ -107,6 +109,34 @@ class OKULAR_EXPORT Movie */ bool autoPlay() const; + /** + * Sets whether to show a poster image. + * + * @since 4.10 + */ + void setShowPosterImage( bool show ); + + /** + * Whether to show a poster image. + * + * @since 4.10 + */ + bool showPosterImage() const; + + /** + * Sets the poster image. + * + * @since 4.10 + */ + void setPosterImage( const QImage &image ); + + /** + * Returns the poster image. + * + * @since 4.10 + */ + QImage posterImage() const; + private: class Private; Private* const d; diff --git a/core/observer.cpp b/core/observer.cpp index 0201a1d8c..59bbb1163 100644 --- a/core/observer.cpp +++ b/core/observer.cpp @@ -48,3 +48,7 @@ bool DocumentObserver::canUnloadPixmap( int ) const { return true; } + +void DocumentObserver::notifyCurrentPageChanged( int, int ) +{ +} diff --git a/core/observer.h b/core/observer.h index 266993ce9..f7189beac 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) }; /** @@ -129,6 +130,16 @@ class OKULAR_EXPORT DocumentObserver */ virtual bool canUnloadPixmap( int page ) const; + /** + * This method is called after the current page of the document has been entered. + * + * @param previous The number of the previous page (is @c -1 for the initial page change). + * @param current The number of the current page. + * + * @since 0.16 (KDE 4.10) + */ + virtual void notifyCurrentPageChanged( int previous, int current ); + private: class Private; const Private* d; diff --git a/core/okularGenerator.desktop b/core/okularGenerator.desktop index 88881b2b7..6cea4fa64 100644 --- a/core/okularGenerator.desktop +++ b/core/okularGenerator.desktop @@ -16,7 +16,7 @@ Comment[en_GB]=File format backend for Okular Comment[es]=Motor de formatos de archivos para Okular Comment[et]=Okulari failivormingu taustaprogramm Comment[eu]=Okular-en fitxategi-formatuaren motorra -Comment[fi]=Tiedostomuodot-taustaosa Okular-lukijalle +Comment[fi]=Tiedostomuototaustaosa Okularille Comment[fr]=Interface du format de fichier d'Okular Comment[ga]=Inneall formáide comhaid le haghaidh Okular Comment[gl]=Infraestrutura dun formato de ficheiro para Okular diff --git a/core/page.cpp b/core/page.cpp index 641e8cfe3..d746382e1 100644 --- a/core/page.cpp +++ b/core/page.cpp @@ -102,9 +102,9 @@ void PagePrivate::imageRotationDone( RotationJob * job ) } } -QMatrix PagePrivate::rotationMatrix() const +QTransform PagePrivate::rotationMatrix() const { - QMatrix matrix; + QTransform matrix; matrix.rotate( (int)m_rotation * 90 ); switch ( m_rotation ) @@ -369,7 +369,7 @@ void PagePrivate::rotateAt( Rotation orientation ) /** * Rotate the object rects on the page. */ - const QMatrix matrix = rotationMatrix(); + const QTransform matrix = rotationMatrix(); QLinkedList< ObjectRect * >::const_iterator objectIt = m_page->m_rects.begin(), end = m_page->m_rects.end(); for ( ; objectIt != end; ++objectIt ) (*objectIt)->transform( matrix ); @@ -405,6 +405,19 @@ const ObjectRect * Page::objectRect( ObjectRect::ObjectType type, double x, doub return 0; } +QLinkedList< const ObjectRect * > Page::objectRects( ObjectRect::ObjectType type, double x, double y, double xScale, double yScale ) const +{ + QLinkedList< const ObjectRect * > result; + + QLinkedList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end(); + for ( ; it != end; ++it ) + if ( ( (*it)->objectType() == type ) && (*it)->contains( x, y, xScale, yScale ) ) + result.append( *it ); + + return result; +} + + const ObjectRect* Page::nearestObjectRect( ObjectRect::ObjectType type, double x, double y, double xScale, double yScale, double * distance ) const { ObjectRect * res = 0; @@ -506,7 +519,7 @@ void Page::setObjectRects( const QLinkedList< ObjectRect * > & rects ) /** * Rotate the object rects of the page. */ - const QMatrix matrix = d->rotationMatrix(); + const QTransform matrix = d->rotationMatrix(); QLinkedList< ObjectRect * >::const_iterator objectIt = rects.begin(), end = rects.end(); for ( ; objectIt != end; ++objectIt ) @@ -588,7 +601,7 @@ void Page::addAnnotation( Annotation * annotation ) AnnotationObjectRect *rect = new AnnotationObjectRect( annotation ); // Rotate the annotation on the page. - const QMatrix matrix = d->rotationMatrix(); + const QTransform matrix = d->rotationMatrix(); annotation->d_ptr->baseTransform( matrix.inverted() ); annotation->d_ptr->annotationTransform( matrix ); @@ -736,6 +749,9 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode ) QTime time; time.start(); #endif + // Clone annotationList as root node in restoredLocalAnnotationList + const QDomNode clonedNode = restoredLocalAnnotationList.importNode( childElement, true ); + restoredLocalAnnotationList.appendChild( clonedNode ); // iterate over all annotations QDomNode annotationNode = childElement.firstChild(); @@ -803,10 +819,6 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode ) void PagePrivate::saveLocalContents( QDomNode & parentNode, QDomDocument & document, PageItems what ) const { - // only add a node if there is some stuff to write into - if ( m_page->m_annotations.isEmpty() && formfields.isEmpty() ) - return; - // create the page node and set the 'number' attribute QDomElement pageElement = document.createElement( "page" ); pageElement.setAttribute( "number", m_number ); @@ -825,7 +837,17 @@ void PagePrivate::saveLocalContents( QDomNode & parentNode, QDomDocument & docum #endif // add annotations info if has got any - if ( ( what & AnnotationPageItems ) && !m_page->m_annotations.isEmpty() ) + if ( ( what & AnnotationPageItems ) && ( what & OriginalAnnotationPageItems ) ) + { + const QDomElement savedDocRoot = restoredLocalAnnotationList.documentElement(); + if ( !savedDocRoot.isNull() ) + { + // Import and append node in target document + const QDomNode importedNode = document.importNode( savedDocRoot, true ); + pageElement.appendChild( importedNode ); + } + } + else if ( ( what & AnnotationPageItems ) && !m_page->m_annotations.isEmpty() ) { // create the annotationList QDomElement annotListElement = document.createElement( "annotationList" ); diff --git a/core/page.h b/core/page.h index a2571b753..a8f276113 100644 --- a/core/page.h +++ b/core/page.h @@ -223,6 +223,12 @@ class OKULAR_EXPORT Page */ const ObjectRect * objectRect( ObjectRect::ObjectType type, double x, double y, double xScale, double yScale ) const; + /** + * Returns all object rects of the given @p type which are at point (@p x, @p y) at scale (@p xScale, @p yScale). + * @since 0.16 (KDE 4.10) + */ + QLinkedList< const ObjectRect * > objectRects( ObjectRect::ObjectType type, double x, double y, double xScale, double yScale ) const; + /** * Returns the object rect of the given @p type which is nearest to the point (@p x, @p y) at scale (@p xScale, @p yScale). * diff --git a/core/page_p.h b/core/page_p.h index 4cbfb193a..d25a282d9 100644 --- a/core/page_p.h +++ b/core/page_p.h @@ -14,16 +14,15 @@ // qt/kde includes #include #include -#include +#include #include +#include // local includes #include "global.h" #include "area.h" class QColor; -class QDomDocument; -class QDomNode; namespace Okular { @@ -43,7 +42,11 @@ enum PageItem None = 0, AnnotationPageItems = 0x01, FormFieldPageItems = 0x02, - AllPageItems = 0xff + AllPageItems = 0xff, + + /* If set along with AnnotationPageItems, tells saveLocalContents to save + * the original annotations (if any) instead of the modified ones */ + OriginalAnnotationPageItems = 0x100 }; Q_DECLARE_FLAGS(PageItems, PageItem) @@ -54,7 +57,7 @@ class PagePrivate ~PagePrivate(); void imageRotationDone( RotationJob * job ); - QMatrix rotationMatrix() const; + QTransform rotationMatrix() const; /** * Loads the local contents (e.g. annotations) of the page. @@ -125,6 +128,7 @@ class PagePrivate QString m_label; bool m_isBoundingBoxKnown : 1; + QDomDocument restoredLocalAnnotationList; // ... }; } diff --git a/core/rotationjob.cpp b/core/rotationjob.cpp index 625a5d2cc..5659b2d4a 100644 --- a/core/rotationjob.cpp +++ b/core/rotationjob.cpp @@ -9,7 +9,7 @@ #include "rotationjob_p.h" -#include +#include using namespace Okular; @@ -50,14 +50,14 @@ void RotationJob::run() return; } - QMatrix matrix = rotationMatrix( mOldRotation, mNewRotation ); + QTransform matrix = rotationMatrix( mOldRotation, mNewRotation ); mRotatedImage = mImage.transformed( matrix ); } -QMatrix RotationJob::rotationMatrix( Rotation from, Rotation to ) +QTransform RotationJob::rotationMatrix( Rotation from, Rotation to ) { - QMatrix matrix; + QTransform matrix; if ( from == Rotation0 ) { if ( to == Rotation90 ) diff --git a/core/rotationjob_p.h b/core/rotationjob_p.h index 9790b64b5..b855084d7 100644 --- a/core/rotationjob_p.h +++ b/core/rotationjob_p.h @@ -11,7 +11,7 @@ #define _OKULAR_ROTATIONJOB_P_H_ #include -#include +#include #include @@ -35,7 +35,7 @@ class RotationJob : public ThreadWeaver::Job int id() const; PagePrivate * page() const; - static QMatrix rotationMatrix( Rotation from, Rotation to ); + static QTransform rotationMatrix( Rotation from, Rotation to ); protected: virtual void run(); diff --git a/core/textpage.cpp b/core/textpage.cpp index 06acf95b0..ad090e6f8 100644 --- a/core/textpage.cpp +++ b/core/textpage.cpp @@ -174,7 +174,7 @@ class TinyTextEntity : QString::fromRawData( d.data, length ); } - inline NormalizedRect transformedArea( const QMatrix &matrix ) const + inline NormalizedRect transformedArea( const QTransform &matrix ) const { NormalizedRect transformed_area = area; transformed_area.transform( matrix ); @@ -215,7 +215,7 @@ NormalizedRect* TextEntity::area() const return m_area; } -NormalizedRect TextEntity::transformedArea(const QMatrix &matrix) const +NormalizedRect TextEntity::transformedArea(const QTransform &matrix) const { NormalizedRect transformed_area = *m_area; transformed_area.transform( matrix ); @@ -359,7 +359,7 @@ RegularAreaRect * TextPage::textArea ( TextSelection * sel) const */ RegularAreaRect * ret= new RegularAreaRect; - const QMatrix matrix = d->m_page ? d->m_page->rotationMatrix() : QMatrix(); + const QTransform matrix = d->m_page ? d->m_page->rotationMatrix() : QTransform(); #if 0 int it = -1; int itB = -1; @@ -840,7 +840,7 @@ RegularAreaRect* TextPagePrivate::findTextInternalForward( int searchID, const Q const TextList::ConstIterator &start, const TextList::ConstIterator &end ) { - const QMatrix matrix = m_page ? m_page->rotationMatrix() : QMatrix(); + const QTransform matrix = m_page ? m_page->rotationMatrix() : QTransform(); RegularAreaRect* ret=new RegularAreaRect; @@ -863,7 +863,6 @@ RegularAreaRect* TextPagePrivate::findTextInternalForward( int searchID, const Q { curEntity = *it; const QString &str = curEntity->text(); - kDebug() << str; if ( !offsetMoved && ( it == start ) ) { if ( m_searchPoints.contains( searchID ) ) @@ -956,7 +955,7 @@ RegularAreaRect* TextPagePrivate::findTextInternalBackward( int searchID, const const TextList::ConstIterator &start, const TextList::ConstIterator &end ) { - const QMatrix matrix = m_page ? m_page->rotationMatrix() : QMatrix(); + const QTransform matrix = m_page ? m_page->rotationMatrix() : QTransform(); RegularAreaRect* ret=new RegularAreaRect; diff --git a/core/textpage.h b/core/textpage.h index a0b41c7d6..36cbe3997 100644 --- a/core/textpage.h +++ b/core/textpage.h @@ -16,7 +16,7 @@ #include "okular_export.h" #include "global.h" -class QMatrix; +class QTransform; namespace Okular { @@ -70,7 +70,7 @@ class OKULAR_EXPORT TextEntity /** * Returns the transformed area of the text entity. */ - NormalizedRect transformedArea(const QMatrix &matrix) const; + NormalizedRect transformedArea(const QTransform &matrix) const; private: QString m_text; diff --git a/core/textpage_p.h b/core/textpage_p.h index 7c2be22cd..8ecf0c907 100644 --- a/core/textpage_p.h +++ b/core/textpage_p.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include class SearchPoint; class TinyTextEntity; diff --git a/core/version.h b/core/version.h index 27467c884..41c14a05f 100644 --- a/core/version.h +++ b/core/version.h @@ -10,10 +10,10 @@ #ifndef _OKULAR_VERSION_H_ #define _OKULAR_VERSION_H_ -#define OKULAR_VERSION_STRING "0.14.80" +#define OKULAR_VERSION_STRING "0.15.70" #define OKULAR_VERSION_MAJOR 0 -#define OKULAR_VERSION_MINOR 14 -#define OKULAR_VERSION_RELEASE 80 +#define OKULAR_VERSION_MINOR 15 +#define OKULAR_VERSION_RELEASE 70 #define OKULAR_MAKE_VERSION( a,b,c ) (((a) << 16) | ((b) << 8) | (c)) #define OKULAR_VERSION \ diff --git a/doc/bookmark-management.png b/doc/bookmark-management.png new file mode 100644 index 000000000..e958b9de9 Binary files /dev/null and b/doc/bookmark-management.png differ diff --git a/doc/configure-editor.png b/doc/configure-editor.png new file mode 100644 index 000000000..e567acd4c Binary files /dev/null and b/doc/configure-editor.png differ diff --git a/doc/configure.png b/doc/configure.png index b3a31071a..ff23ba796 100644 Binary files a/doc/configure.png and b/doc/configure.png differ diff --git a/doc/index.docbook b/doc/index.docbook index 7399fe258..90fba8360 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,6 +1,7 @@ + ATEX"> KPDF"> PDF"> @@ -36,8 +37,8 @@ Context menu actions like Rename Bookmarks etc) &FDLNotice; - 2010-10-05 - 0.11.80 (&kde; 4.6) + 2012-09-09 + 0.15.70 (&kde; 4.10) &okular; is a &kde; universal document viewer based on &kpdf; code. @@ -67,9 +68,9 @@ Context menu actions like Rename Bookmarks etc) improved presentation support and annotation support. - &okular; supports a lot of different formats like PDF, Postscript, Tiff, CHM, DjVU, Images (png, jpg, &etc;) + &okular; supports a lot of different formats like PDF, Postscript, Tiff, CHM, DjVU, Images (png, jpg, &etc;) XPS, Open Document (ODT), Fiction Books, Comic Book, Plucker, EPub and Fax. - For all supported formats and their features see + For all supported formats and their features see &okular; Document Format Handlers. @@ -106,8 +107,8 @@ Context menu actions like Rename Bookmarks etc) the FileOpen Recent menu. &okular; is the default &kde; application for PDF and Postscript files, launched when you click with the - &LMB; on such a file type in the filemanager. If you want to open any file whose format is supported by &okular; - use Open with...&okular; from context + &LMB; on such a file type in the filemanager. If you want to open any file whose format is supported by &okular; + use Open with...&okular; from context menu in the filemanager. @@ -123,7 +124,7 @@ Presentation mode: add Up (previous page) and Down (next page) keys Add a 'Find Previous' entry --> - There are multiple ways of scrolling the viewing area. One is to use the + There are multiple ways of scrolling the viewing area. One is to use the Up Arrow and Down Arrow keys. You may also use the scrollbar, your mousewheel or the Page Up and Page Down keys. @@ -135,8 +136,8 @@ Add a 'Find Previous' entry . If you want to read a document with several pages use the automatic scrolling feature of &okular;. - Start automatic scrolling with &Shift;Down Arrow or - &Shift;Up Arrow. Then use these keys to increase and + Start automatic scrolling with &Shift;Down Arrow or + &Shift;Up Arrow. Then use these keys to increase and decrease the scrolling speed. You can start or stop automatic scrolling temporarily by pressing the &Shift; key; pressing any other key deactivates this feature. @@ -163,15 +164,15 @@ Add a 'Find Previous' entry - If the document has bookmarks, enable the Bookmarks view + If the document has bookmarks, enable the Bookmarks view and click them to go to the associated page. - If bookmarks are not only shown for the current document, you can quickly + If bookmarks are not only shown for the current document, you can quickly switch to bookmarks in all recently opened files. - If the document has annotations, enable the Reviews view + If the document has annotations, enable the Reviews view and click the annotations or select them with the Up Arrow and Down Arrow keys and press Return to go to the associated page. @@ -190,9 +191,9 @@ Add a 'Find Previous' entry - You can go to the beginning of the document using + You can go to the beginning of the document using &Ctrl;Home or - using + using Go Beginning of the document @@ -201,9 +202,9 @@ Add a 'Find Previous' entry - You can go to the end of the document using + You can go to the end of the document using &Ctrl;End or - using + using Go End of the document @@ -214,7 +215,7 @@ Add a 'Find Previous' entry You can go forward in the document using Space or Page Down. To go to the next page of the document use the Next Page Toolbar - button or + button or Go Next Page @@ -225,7 +226,7 @@ Add a 'Find Previous' entry You can go back in the document using &Backspace; or Page Up. To go to the previous page of the document use Previous Page Toolbar - button or + button or Go Previous Page @@ -241,7 +242,7 @@ Add a 'Find Previous' entry enabled in ViewPresentation. It shows the document on a page per page basis. The pages are shown with - zoom to page, that means all the page is visible. + zoom to page, that means all the page is visible. @@ -277,12 +278,12 @@ Add a 'Find Previous' entry You can also draw on the current page with a pencil. Click on the Toggle Drawing Mode icon in the top bar to enable or disable the possibility - of draw in the presentation mode. The drawings are cleared automatically when leaving the presentation + of draw in the presentation mode. The drawings are cleared automatically when leaving the presentation mode, switching to another page, or manually selecting the Erase Drawings icon in the top bar. - The presentation mode has support for more than one screen in a multi-monitor configuration. - With more than one screen a new button will appear in the top bar, with the icon of a screen: + The presentation mode has support for more than one screen in a multi-monitor configuration. + With more than one screen a new button will appear in the top bar, with the icon of a screen: this is a drop down box that allows you to move the presentation to any of the other available screens. @@ -290,6 +291,42 @@ Add a 'Find Previous' entry description in the chapter Configuring &okular;. + + + Inverse Search between &latex; Editors and &okular; + + Inverse search is a very useful feature when you are writing a &latex; document yourself. If everything is set up properly, you can + click into &okular;'s window with the left mouse button while pressing &Shift;. After that editor loads the &latex; source file and jumps to + the proper paragraph. + + Inverse search cannot work unless: + + + The source file has been compiled successfully. + &okular; knows which editor you would like to use. + + + With this feature of &okular;, a left mouse click while pressing &Shift; in the &DVI; or &PDF; document will + result in editor opening the corresponding &latex; document and attempt to go to the + corresponding line. Remember to tell &okular; to use proper editor, in &okular;'s + menu item SettingsConfigure Okular... + (on the page Editor). + + For more details on editor configuration please refer to the corresponding section of this manual. + + + Configuring &okular; + + + + + + Configuring editor in &okular; + + Configuring editor in &okular; + + + @@ -298,7 +335,7 @@ Add a 'Find Previous' entry Embedded Files If the current document has some files embedded in it, when you open it a yellow bar - will appear above the page view to notify you about the embedded files. + will appear above the page view to notify you about the embedded files. The embedded files bar @@ -322,7 +359,7 @@ Add a 'Find Previous' entry Forms If the current document has forms, when you open it a bar - will appear above the page view where you can enable the forms. + will appear above the page view where you can enable the forms. The forms bar @@ -361,7 +398,7 @@ Add a 'Find Previous' entry &okular; has two different kind of annotations: - Text annotations like Yellow Highlighter and Black Underlining + Text annotations like Yellow Highlighter and Black Underlining for files with text like ⪚ PDF. Graphic annotations like Note, Inline Note, Green Freehand Line, Straight Yellow Line, Blue Polygon, Stamp and Cyan Ellipse for all formats supported by &okular;. Using the context menu either in the Reviews view of the navigation panel or in the main window you can open a Pop up Note for any kind of annotation and add or edit comments. @@ -369,6 +406,20 @@ Add a 'Find Previous' entry Since &kde; 4.2, &okular; has the "document archiving" feature. This is an &okular;-specific format for carrying the document plus various metadata related to it (currently only annotations). You can save a "document archive" from the open document by choosing FileExport AsDocument Archive. To open an &okular; document archive, just open it with &okular; as it would be ⪚ a &PDF; document. + + Since &okular; 0.15 you can also save annotations directly into PDF files. This feature is only available if &okular; has been built with version 0.20 or later of Poppler rendering library. You can use File Save As... to save the copy of &PDF; file with annotations. + + + + It is not possible to save annotations into &PDF; file if original file was encrypted and &okular; uses Poppler libraries of version which is lower than 0.22. + + + + + If you open a &PDF; with existing annotations, your annotation changes are not automatically saved in the internal local data folder, and you need to save the modified document (using FileSave As...) before closing it. Should you forget to do this &okular; will show confirmation window that allows you to save the document. + + + Due to DRM limitations (typically with &PDF; documents), adding, editing some properties @@ -406,7 +457,7 @@ Add a 'Find Previous' entry Using stamps a single click just places a square stamp (useful for icons). - To add a rectangular stamp you can click with the &LMB; and hold to place the top-left point, + To add a rectangular stamp you can click with the &LMB; and hold to place the top-left point, then drag to place the bottom-right one. @@ -446,7 +497,7 @@ Add a 'Find Previous' entry - To move an annotation, hold down the &Ctrl; key, move the mouse pointer on it and + To move an annotation, hold down the &Ctrl; key, move the mouse pointer on it and then start dragging with the &LMB;. @@ -457,8 +508,50 @@ Add a 'Find Previous' entry + + Bookmark Management + + &okular; has a very flexible bookmark system. &okular; saves the position on the page in bookmark and allows you to define more than one bookmark per page. + + + To manage bookmarks in &okular; you can use Bookmarks view from Navigation Panel, Bookmarks menu or context menu of document view (click with &RMB; to open it). + + + Bookmarks view + + To open Bookmarks view click on Bookmarks item on the Navigation Panel. If the Navigation Panel is not shown, use F7 SettingsShow Navigation Panel + main menu item to make it visible. + + + Bookmark view context menu + + + + + + Bookmark view context menu + + + + + The filter bar at the top of Bookmarks view can be used to filter the content of bookmark list pane according to the text in the box. + + + The list pane permits to view the bookmark list in a tree-like fashion: each document in the list can be expanded or collapsed by clicking on the < or v icon next to it. + + + Click on icon below the list to show only the bookmarks from the current document. + + + Right-click menu of document item can be used to open document, rename its item or remove it from the list. Remember that the removal of a document item leads to the removal of all bookmarks in the corresponding document. + + + Right-click menus of individual bookmark items allow you to go to the bookmark, rename or remove it. + + + - + The Menubar @@ -493,7 +586,7 @@ Add a 'Find Previous' entry Open a file which was used previously from a submenu. If a file is currently being displayed it - will be closed. For more information, see the section about + will be closed. For more information, see the section about Opening Files. @@ -534,7 +627,7 @@ Add a 'Find Previous' entry - Save the currently open file under a different name using the document backend. With the &PDF; backend (Poppler >= 0.8 required) it is possible to save the document with the changed values of the form fields. + Save the currently open file under a different name using the document backend. With the &PDF; backend (Poppler >= 0.8 required) it is possible to save the document with the changed values of the form fields. It can be possible (provided that the data were not secured using DRM) to save annotations with &PDF; files (Poppler >= 0.22 required). @@ -546,7 +639,7 @@ Add a 'Find Previous' entry - Save a copy of the currently open file under a different name + Save a copy of the currently open file under a different name without using the current document backend. @@ -578,7 +671,7 @@ Add a 'Find Previous' entry - + @@ -662,7 +755,7 @@ Add a 'Find Previous' entry - Copy the currently selected text in + Copy the currently selected text in Text Selection mode to the clipboard. @@ -678,7 +771,7 @@ Add a 'Find Previous' entry - Selects all the text (if the document provides it). This works only in + Selects all the text (if the document provides it). This works only in Text Selection mode. @@ -728,7 +821,7 @@ Add a 'Find Previous' entry - + The View Menu @@ -821,7 +914,7 @@ Add a 'Find Previous' entry This submenu makes you choose the view mode for the pages. The possible options are: Single Page (only one page per row), - Facing Pages (two pages per row, in a book style), + Facing Pages (two pages per row, in a book style), Facing Pages (Center First Page) and Overview (the number of columns is the one specified in the &okular; settings). @@ -1008,7 +1101,20 @@ Add a 'Find Previous' entry - Add or remove a bookmark for the current page. + Add or remove a bookmark for the current position. + + + + + + + Bookmarks + Rename Bookmark + + + + + Rename a bookmark for the current position. @@ -1020,8 +1126,8 @@ Add a 'Find Previous' entry - Go to the previous bookmarked page, or do nothing if there - are no bookmarked pages prior to the current one. + Go to the previous bookmark, or do nothing if there + are no bookmarks prior to the current one. @@ -1032,8 +1138,8 @@ Add a 'Find Previous' entry - Go to the next bookmarked page, or do nothing if there - are no bookmarked pages after the current one. + Go to the next bookmark, or do nothing if there + are no bookmarks after the current one. @@ -1044,9 +1150,9 @@ Add a 'Find Previous' entry - This is an always disabled action that appears in this menu only if the current document has - no bookmarks. Otherwise a list of all bookmarked pages is displayed here. Clicking on these bookmarks - allows you to go directly to the associated pages. + This is an always disabled action that appears in this menu only if the current document has + no bookmarks. Otherwise a list of all bookmarks is displayed here. Clicking on these bookmarks + allows you to go directly to the associated position. @@ -1159,9 +1265,9 @@ Add a 'Find Previous' entry Toggle the Menubar display on and off. Once - hidden it can be made visible using the shortcut + hidden it can be made visible using the shortcut &Ctrl;M again. - If the menubar is hidden, the context menu opened with a right mouse button + If the menubar is hidden, the context menu opened with a right mouse button click anywhere in the view area has an extra entry Show Menubar. @@ -1188,6 +1294,17 @@ Add a 'Find Previous' entry Toggle the navigation panel on and off. + + + + Settings + Show Page Bar + + + + Toggle the page bar at the bottom of document area on and off to save vertical place in &okular; window. + + @@ -1252,14 +1369,14 @@ Add a 'Find Previous' entry window. - + The Help Menu &help.menu.documentation; - + Configuring &okular; @@ -1332,6 +1449,21 @@ Add a 'Find Previous' entry load, &etc;. + + Display document title in title bar if available + + Whether to show the current document title in the title bar of &okular; window. If no metadata for title + found in the document or this item is unchecked &okular; shows filename of the document. + + + + + When not displaying document title + + You can choose any of two options, Display file name only or Display full file path. + + + Obey DRM limitations @@ -1368,7 +1500,15 @@ Add a 'Find Previous' entry Overview columns - This options represent the number of columns to use in the overview mode. + This option represents the number of columns to use in the overview mode. + + + + + Page Up/Down overlap + + + Here you can define the percentage of the current viewing area that should be visible after pressing Page Up/Page Down keys. @@ -1414,7 +1554,7 @@ Add a 'Find Previous' entry Change Dark & Light Colors - Changes the dark and light color to your preference, that means + Changes the dark and light color to your preference, that means black will not be rendered as black but as the selected dark color and white will not be rendered as white but as the selected light color. @@ -1440,21 +1580,14 @@ Add a 'Find Previous' entry outline or opaque fill styles and increase speed on selections. - - Enable background generation - - Use a background thread to generate the pages. By disabling - this option the user interface will become less reactive (will be blocked - if necessary), but pages will be displayed a bit faster. - - Memory Usage &okular; can achieve best performance by tuning the memory usage, based on your system and your tastes. The more memory you let it to use, the faster the program will behave. The Default profile is good for every system, but you can prevent &okular; from using more memory than necessary by selecting the Low - profile, or let it get the most out of your system using Aggressive. + profile, or let it get the most out of your system using Aggressive. Use Greedy profile to preload all + pages without risk of system memory overfull (only 50% of total memory or free memory will be used). @@ -1505,6 +1638,12 @@ Add a 'Find Previous' entry Whether to show a summary page at the beginning of the presentation with the title, author and number of pages of the document. + + Enable transitions + + Use this checkbox to enable or disable transition effects between pages. + + Default transition @@ -1515,7 +1654,7 @@ Add a 'Find Previous' entry Placement - In this section you can select the Screen used to display the presentation. + In this section you can select the Screen used to display the presentation. Current Screen is same screen of the &okular; window that starts the presentation mode. Default Screen is the screen marked as default in the xinerama configuration. Screen 0, Screen 1 &etc; are the available screens. @@ -1541,15 +1680,38 @@ Add a 'Find Previous' entry Editor - Choose the editor you want to launch when &okular; wants to open a source file. - This is the case when the document has references to the various points (usually row and column number) of sources it was generated from. The &DVI; format supports natively the addition of the information about the sources the LaTeX document was generated from. A similar system exists for &PDF; documents, called pdfsync, which stores these extra information in an external file named after the &PDF; file itself (for example mydocument.pdfsync for mydocument.pdf). + Choose the editor you want to launch when &okular; wants to open a source file. + This is the case when the document has references to the various points (usually row and column number) of sources it was generated from. The &DVI; format supports natively the addition of the information about the sources the LaTeX document was generated from. A similar system exists for &PDF; documents, called pdfsync, which stores these extra information in an external file named after the &PDF; file itself (for example mydocument.pdfsync for mydocument.pdf). + + &okular; ships with preconfigured settings for the following editors: &kate;, Kile, SciTE, Emacs client, and LyX client. + + + + To use inverse search in Kile, you have to compile your &latex; file with the Modern configuration. + + Command This is the command and its parameters to invoke the selected editor with the source file of the actual document. + This field will be filled automatically if you use one of the preconfigured editors. Otherwise, please choose Custom Text Editor in Editor drop-down list and refer to the documentation on your favorite editor to find the proper command. + + You can use the following placeholders: + + + %f - the file name + + + %l - the line of the file to be reached + + + %c - the column of the file to be reached + + + If %f is not specified, then the document name is appended to the specified command. @@ -1557,18 +1719,18 @@ Add a 'Find Previous' entry Credits and License - + Program Copyright: - Albert Astals Cidaacid@kde.org - Pino Toscano pino@kde.org Current maintainer - Enrico Roseros.kde@email.it &kpdf; developer + Albert Astals Cid aacid@kde.org Current maintainer + Pino Toscano pino@kde.org + Enrico Ros eros.kde@email.it &kpdf; developer Documentation Copyright: - Albert Astals Cidaacid@kde.org Author - Titus Laskatitus.laska@gmx.de Some updates and additions + Albert Astals Cid aacid@kde.org Author + Titus Laska titus.laska@gmx.de Some updates and additions Pino Toscano pino@kde.org @@ -1593,7 +1755,7 @@ Add a 'Find Previous' entry &install.compile.documentation; - &documentation.index; + &documentation.index; diff --git a/doc/rating.png b/doc/rating.png new file mode 100644 index 000000000..3630d8ff1 Binary files /dev/null and b/doc/rating.png differ diff --git a/generators/chm/kio-msits/msits.protocol b/generators/chm/kio-msits/msits.protocol index 1ad4ba172..1df224887 100644 --- a/generators/chm/kio-msits/msits.protocol +++ b/generators/chm/kio-msits/msits.protocol @@ -22,7 +22,7 @@ Description[eo]=Kio-sklavo por montri la WinHelp dosierojn Description[es]=Un «kioslave» para mostrar archivos WinHelp Description[et]=KIO-moodul WinHelp-failide näitamiseks Description[eu]=WinHelp fitxategiak bistaratzeko kioslave bat -Description[fi]=Kioslave WinHelp-tiedostojen näyttämiseksi +Description[fi]=WinHelp-tiedostoja näyttävä kioslave Description[fr]=Module d'entrées / sorties pour l'affichage des fichiers d'aide Win Help Description[ga]=Sclábhaí KIO a thaispeánann comhaid WinHelp Description[gl]=Un kioslave para mostrar ficheiros WinHelp diff --git a/generators/chm/libokularGenerator_chmlib.desktop b/generators/chm/libokularGenerator_chmlib.desktop index 27dca9cbb..bf5018200 100644 --- a/generators/chm/libokularGenerator_chmlib.desktop +++ b/generators/chm/libokularGenerator_chmlib.desktop @@ -75,7 +75,7 @@ Comment[en_GB]=Windows HTMLHelp backend for Okular Comment[es]=Motor de archivos de ayuda en HTML de Windows para Okular Comment[et]=Okulari Windowsi HTMLHelp-failide taustaprogramm Comment[eu]=Okular-en Windows HTMLHelp motorra -Comment[fi]=Windows HTMLHelp -taustaosa Okular-lukijalle +Comment[fi]=Windows HTMLHelp -taustaosa Okularille Comment[fr]=Interface d'Okular pour les fichiers d'aide Windows HTMLHelp Comment[ga]=Inneall HTMLHelp Windows le haghaidh Okular Comment[gl]=Infraestrutura de ficheiros Windows HTMLHelp para Okular diff --git a/generators/chm/okularApplication_chm.desktop b/generators/chm/okularApplication_chm.desktop index 8bc83a039..371c29c94 100755 --- a/generators/chm/okularApplication_chm.desktop +++ b/generators/chm/okularApplication_chm.desktop @@ -143,7 +143,9 @@ X-KDE-Keywords[ga]=chm X-KDE-Keywords[hu]=chm X-KDE-Keywords[it]=chm X-KDE-Keywords[kk]=chm +X-KDE-Keywords[km]=chm X-KDE-Keywords[ko]=chm +X-KDE-Keywords[lv]=chm X-KDE-Keywords[nb]=chm X-KDE-Keywords[nds]=CHM X-KDE-Keywords[nl]=chm @@ -159,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=chm,ЦХМ X-KDE-Keywords[sr@ijekavianlatin]=chm,CHM X-KDE-Keywords[sr@latin]=chm,CHM X-KDE-Keywords[sv]=chm +X-KDE-Keywords[tr]=chm X-KDE-Keywords[uk]=chm X-KDE-Keywords[x-test]=xxchmxx X-KDE-Keywords[zh_CN]=chm diff --git a/generators/comicbook/libokularGenerator_comicbook.desktop b/generators/comicbook/libokularGenerator_comicbook.desktop index bec8c92c3..d2937e301 100644 --- a/generators/comicbook/libokularGenerator_comicbook.desktop +++ b/generators/comicbook/libokularGenerator_comicbook.desktop @@ -16,7 +16,7 @@ Name[eo]=ComicBook Name[es]=Comic Book Name[et]=Koomiks Name[eu]=Komikia -Name[fi]=Sarjakuva +Name[fi]=Comic Book Name[fr]=«  Comic Book » Name[ga]=Comic Book Name[gl]=Libro de banda deseñada @@ -74,7 +74,7 @@ Comment[en_GB]=Comic book backend for Okular Comment[es]=Motor de libros de Comic para Okular Comment[et]=Okulari koomiksite taustaprogramm Comment[eu]=Okular-en komiki motorra -Comment[fi]=Sarjakuvat-taustaosa Okular-ohjelmalle +Comment[fi]=Comic book -taustaosa Okularille Comment[fr]=Interface d'Okular pour les bandes dessinés Comment[ga]=Inneall Comic Book le haghaidh Okular Comment[gl]=Infraestruturas de banda deseñada para Okular diff --git a/generators/comicbook/okularApplication_comicbook.desktop b/generators/comicbook/okularApplication_comicbook.desktop index c48684b0f..ad0455706 100755 --- a/generators/comicbook/okularApplication_comicbook.desktop +++ b/generators/comicbook/okularApplication_comicbook.desktop @@ -137,13 +137,15 @@ X-KDE-Keywords[de]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[el]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[es]=cbr, cbz, cbt, libro de cómic X-KDE-Keywords[et]=cbr, cbz, cbt, Comic Book, koomiks -X-KDE-Keywords[fi]=cbr, cbz, cbt, Sarjakuva +X-KDE-Keywords[fi]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[fr]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[ga]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[hu]=cbr, cbz, cbt, Képregény X-KDE-Keywords[it]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[kk]=cbr, cbz, cbt, Comic Book +X-KDE-Keywords[km]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[ko]=cbr, cbz, cbt, Comic Book,만화책 +X-KDE-Keywords[lv]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[nb]=cbr, cbz, cbt, tegneserie X-KDE-Keywords[nds]=cbr, cbz, cbt, Comicbook X-KDE-Keywords[nl]=cbr, cbz, cbt, Stripverhaal @@ -159,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=cbr, cbz, cbt, Comic Book,комикбук X-KDE-Keywords[sr@ijekavianlatin]=cbr, cbz, cbt, Comic Book,ComicBook X-KDE-Keywords[sr@latin]=cbr, cbz, cbt, Comic Book,ComicBook X-KDE-Keywords[sv]=cbr, cbz, cbt, Comic Book +X-KDE-Keywords[tr]=cbr, cbz, cbt, Comic Book X-KDE-Keywords[uk]=cbr,cbz,cbt,Comic Book,комікс X-KDE-Keywords[x-test]=xxcbr, cbz, cbt, Comic Bookxx X-KDE-Keywords[zh_CN]=cbr, cbz, cbt, Comic Book,漫画书,漫画,连环画 diff --git a/generators/djvu/libokularGenerator_djvu.desktop b/generators/djvu/libokularGenerator_djvu.desktop index 1093b8b52..384e9af6e 100644 --- a/generators/djvu/libokularGenerator_djvu.desktop +++ b/generators/djvu/libokularGenerator_djvu.desktop @@ -76,7 +76,7 @@ Comment[en_GB]=DjVu backend for Okular Comment[es]=Motor DjVu para Okular Comment[et]=Okulari DjVu taustaprogramm Comment[eu]=Okular-en DjVu motorra -Comment[fi]=DjVu-taustaosa Okular-lukijalle +Comment[fi]=DjVu-taustaosa Okularille Comment[fr]=Interface d'Okular pour DjVu Comment[ga]=Inneall DjVu le haghaidh Okular Comment[gl]=Infraestrutura de DjVu para Okular diff --git a/generators/djvu/okularApplication_djvu.desktop b/generators/djvu/okularApplication_djvu.desktop index ed0e3b22e..377d231ea 100755 --- a/generators/djvu/okularApplication_djvu.desktop +++ b/generators/djvu/okularApplication_djvu.desktop @@ -143,7 +143,9 @@ X-KDE-Keywords[ga]=djvu X-KDE-Keywords[hu]=djvu X-KDE-Keywords[it]=djvu X-KDE-Keywords[kk]=djvu +X-KDE-Keywords[km]=djvu X-KDE-Keywords[ko]=djvu +X-KDE-Keywords[lv]=djvu X-KDE-Keywords[nb]=djvu X-KDE-Keywords[nds]=DjVu X-KDE-Keywords[nl]=djvu @@ -159,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=djvu,ДјВу X-KDE-Keywords[sr@ijekavianlatin]=djvu,DjVu X-KDE-Keywords[sr@latin]=djvu,DjVu X-KDE-Keywords[sv]=djvu +X-KDE-Keywords[tr]=djvu X-KDE-Keywords[ug]=djvu X-KDE-Keywords[uk]=djvu X-KDE-Keywords[x-test]=xxdjvuxx diff --git a/generators/dvi/TeXFont_PFB.cpp b/generators/dvi/TeXFont_PFB.cpp index cc866310a..6b2709361 100644 --- a/generators/dvi/TeXFont_PFB.cpp +++ b/generators/dvi/TeXFont_PFB.cpp @@ -25,7 +25,7 @@ TeXFont_PFB::TeXFont_PFB(TeXFontDefinition *parent, fontEncoding *enc, double slant) - : TeXFont(parent) + : TeXFont(parent), face(0) { #ifdef DEBUG_PFB if (enc != 0) diff --git a/generators/dvi/libokularGenerator_dvi.desktop b/generators/dvi/libokularGenerator_dvi.desktop index c5224f06b..62b160774 100644 --- a/generators/dvi/libokularGenerator_dvi.desktop +++ b/generators/dvi/libokularGenerator_dvi.desktop @@ -76,7 +76,7 @@ Comment[en_GB]=DVI backend for Okular Comment[es]=Motor DVI para Okular Comment[et]=Okulari DVI taustaprogramm Comment[eu]=Okular-en DVI motorra -Comment[fi]=DVI-taustaosa Okular-lukijalle +Comment[fi]=DVI-taustaosa Okularille Comment[fr]=Interface d'Okular pour le DVI Comment[ga]=Inneall DVI le haghaidh Okular Comment[gl]=Infraestrutura de DVI para Okular diff --git a/generators/dvi/okularApplication_dvi.desktop b/generators/dvi/okularApplication_dvi.desktop index 43d68a26f..84bddb3cf 100755 --- a/generators/dvi/okularApplication_dvi.desktop +++ b/generators/dvi/okularApplication_dvi.desktop @@ -143,7 +143,9 @@ X-KDE-Keywords[ga]=dvi X-KDE-Keywords[hu]=dvi X-KDE-Keywords[it]=dvi X-KDE-Keywords[kk]=dvi +X-KDE-Keywords[km]=dvi X-KDE-Keywords[ko]=dvi +X-KDE-Keywords[lv]=dvi X-KDE-Keywords[nb]=dvi X-KDE-Keywords[nds]=DVI X-KDE-Keywords[nl]=dvi @@ -159,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=dvi,ДВИ X-KDE-Keywords[sr@ijekavianlatin]=dvi,DVI X-KDE-Keywords[sr@latin]=dvi,DVI X-KDE-Keywords[sv]=dvi +X-KDE-Keywords[tr]=dvi X-KDE-Keywords[ug]=dvi X-KDE-Keywords[uk]=dvi X-KDE-Keywords[x-test]=xxdvixx diff --git a/generators/epub/converter.cpp b/generators/epub/converter.cpp index 96652b50d..74df151a3 100644 --- a/generators/epub/converter.cpp +++ b/generators/epub/converter.cpp @@ -188,7 +188,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if (mSectionMap.contains(link)) { block = mSectionMap.value(link); } else { // load missing resource - char *data; + char *data = 0; int size = epub_get_data(mTextDocument->getEpub(), clink, &data); if (data) { _cursor->insertBlock(); diff --git a/generators/epub/generator_epub.cpp b/generators/epub/generator_epub.cpp index c329dd857..59bb2bf54 100644 --- a/generators/epub/generator_epub.cpp +++ b/generators/epub/generator_epub.cpp @@ -19,7 +19,7 @@ static KAboutData createAboutData() "okular_epub", "okular_epub", ki18n("EPub Backend"), - "0.2", + "0.2.1", ki18n("An EPub backend"), KAboutData::License_GPL, ki18n("© 2008 Ely Levy") diff --git a/generators/epub/libokularGenerator_epub.desktop b/generators/epub/libokularGenerator_epub.desktop index 63ba0b140..f8e578432 100644 --- a/generators/epub/libokularGenerator_epub.desktop +++ b/generators/epub/libokularGenerator_epub.desktop @@ -9,7 +9,7 @@ Name[ca]=Document EPub Name[ca@valencia]=Document EPub Name[cs]=EPub dokument Name[da]=EPub-dokument -Name[de]=EPub-Dokument +Name[de]=EPUB-Dokument Name[el]=Έγγραφο EPub Name[en_GB]=EPub document Name[es]=Documento EPub @@ -65,13 +65,13 @@ Comment[ca]=Dorsal d'EPub per a l'Okular Comment[ca@valencia]=Dorsal d'EPub per a l'Okular Comment[cs]=Implementace Epub pro Okular Comment[da]=EPub-motor til Okular -Comment[de]=Anzeigemodul für EPub in Okular +Comment[de]=Anzeigemodul für EPUB in Okular Comment[el]=Σύστημα υποστήριξης EPub για το Okular Comment[en_GB]=EPub backend for Okular Comment[es]=Motor EPub para Okular Comment[et]=Okulari EPubi taustaprogramm Comment[eu]=Okular-en EPub motorra -Comment[fi]=EPub-taustaosa Okular-lukijalle +Comment[fi]=EPub-taustaosa Okularille Comment[fr]=Interface d'Okular pour EPub Comment[ga]=Inneall EPub le haghaidh Okular Comment[gl]=Infraestrutura de EPub para Okular diff --git a/generators/epub/okularApplication_epub.desktop b/generators/epub/okularApplication_epub.desktop index 91e2f7523..16f6a13d2 100755 --- a/generators/epub/okularApplication_epub.desktop +++ b/generators/epub/okularApplication_epub.desktop @@ -143,7 +143,9 @@ X-KDE-Keywords[ga]=epub, ríomhleabhar, r-leabhar X-KDE-Keywords[hu]=epub, e-book X-KDE-Keywords[it]=epub, e-book X-KDE-Keywords[kk]=epub, e-book +X-KDE-Keywords[km]=epub, e-book X-KDE-Keywords[ko]=epub, e-book,전자책,이북 +X-KDE-Keywords[lv]=epub, e-book, e-grāmata X-KDE-Keywords[nb]=epub, e-bok X-KDE-Keywords[nds]=epub, e-book X-KDE-Keywords[nl]=epub, e-book @@ -159,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=epub, e-book,ЕПУБ,е‑књига X-KDE-Keywords[sr@ijekavianlatin]=epub, e-book,EPUB,e‑knjiga X-KDE-Keywords[sr@latin]=epub, e-book,EPUB,e‑knjiga X-KDE-Keywords[sv]=epub, e-bok +X-KDE-Keywords[tr]=epub, e-book X-KDE-Keywords[uk]=epub,e-book,електронна,книга X-KDE-Keywords[x-test]=xxepub, e-bookxx X-KDE-Keywords[zh_CN]=epub, e-book,电子书 diff --git a/generators/fax/libokularGenerator_fax.desktop b/generators/fax/libokularGenerator_fax.desktop index 83537677e..145f59498 100644 --- a/generators/fax/libokularGenerator_fax.desktop +++ b/generators/fax/libokularGenerator_fax.desktop @@ -71,7 +71,7 @@ Comment[en_GB]=G3/G4 Fax backend for Okular Comment[es]=Motor de fax G3/G4 para Okular Comment[et]=Okulari G3/G4 faksi taustaprogramm Comment[eu]=Okular-en G3/G4 faxaren motorra -Comment[fi]=G3/G4-Faksitaustaosa Okular-lukijalle +Comment[fi]=G3/G4-faksitaustaosa Okularille Comment[fr]=Interface d'Okular pour les fax de type G3/G4 Comment[ga]=Inneall facsála G3/G4 le haghaidh Okular Comment[gl]=Infraestrutura de fax G3/G4 para Okular diff --git a/generators/fictionbook/converter.cpp b/generators/fictionbook/converter.cpp index c7c8d3b66..9f3ec26d3 100644 --- a/generators/fictionbook/converter.cpp +++ b/generators/fictionbook/converter.cpp @@ -67,7 +67,7 @@ QTextDocument* Converter::convert( const QString &fileName ) Document fbDocument( fileName ); if ( !fbDocument.open() ) { emit error( fbDocument.lastErrorString(), -1 ); - return false; + return 0; } mTextDocument = new QTextDocument; @@ -97,7 +97,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( documentElement.tagName() != QLatin1String( "FictionBook" ) ) { emit error( i18n( "Document is not a valid FictionBook" ), -1 ); delete mCursor; - return false; + return 0; } /** @@ -108,7 +108,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( element.tagName() == QLatin1String( "binary" ) ) { if ( !convertBinary( element ) ) { delete mCursor; - return false; + return 0; } } @@ -123,7 +123,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( element.tagName() == QLatin1String( "description" ) ) { if ( !convertDescription( element ) ) { delete mCursor; - return false; + return 0; } } else if ( element.tagName() == QLatin1String( "body" ) ) { if ( !mTitleInfo->mCoverPage.isNull() ) { @@ -165,7 +165,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( !convertBody( element ) ) { delete mCursor; - return false; + return 0; } } diff --git a/generators/fictionbook/generator_fb.cpp b/generators/fictionbook/generator_fb.cpp index 60750dec7..2317083c3 100644 --- a/generators/fictionbook/generator_fb.cpp +++ b/generators/fictionbook/generator_fb.cpp @@ -20,7 +20,7 @@ static KAboutData createAboutData() "okular_fictionbook", "okular_fictionbook", ki18n( "Fiction Book Backend" ), - "0.1.4", + "0.1.5", ki18n( "A renderer for FictionBook eBooks" ), KAboutData::License_GPL, ki18n( "© 2007-2008 Tobias Koenig" ) diff --git a/generators/fictionbook/libokularGenerator_fb.desktop b/generators/fictionbook/libokularGenerator_fb.desktop index c94b9e664..dcaf5ca0d 100644 --- a/generators/fictionbook/libokularGenerator_fb.desktop +++ b/generators/fictionbook/libokularGenerator_fb.desktop @@ -73,7 +73,7 @@ Comment[en_GB]=FictionBook backend for Okular Comment[es]=Motor FictionBook para Okular Comment[et]=Okulari FictionBooki taustaprogramm Comment[eu]=Okular-en fikzio-libururen motorra -Comment[fi]=FictionBook-taustaosa Okular-lukijalle +Comment[fi]=FictionBook-taustaosa Okularille Comment[fr]=Interface d'Okular pour « FictionBook » Comment[ga]=Inneall FictionBook le haghaidh Okular Comment[gl]=Infraestrutura FictionBook para Okular diff --git a/generators/fictionbook/okularApplication_fb.desktop b/generators/fictionbook/okularApplication_fb.desktop index b89b6ac02..eafaa0d11 100755 --- a/generators/fictionbook/okularApplication_fb.desktop +++ b/generators/fictionbook/okularApplication_fb.desktop @@ -131,6 +131,7 @@ X-KDE-Keywords[ar]=FictionBook, e-book, fb2 X-KDE-Keywords[bg]=FictionBook, e-book, fb2 X-KDE-Keywords[ca]=FictionBook, e-book, fb2 X-KDE-Keywords[ca@valencia]=FictionBook, e-book, fb2 +X-KDE-Keywords[cs]=FictionBook, e-book, fb2 X-KDE-Keywords[da]=FictionBook,e-book,fb2 X-KDE-Keywords[de]=FictionBook, e-book, fb2 X-KDE-Keywords[el]=FictionBook, e-book, fb2 @@ -142,7 +143,9 @@ X-KDE-Keywords[ga]=FictionBook, r-leabhar, ríomhleabhar, fb2 X-KDE-Keywords[hu]=FictionBook, e-book, fb2 X-KDE-Keywords[it]=FictionBook, e-book, fb2 X-KDE-Keywords[kk]=FictionBook, e-book, fb2 +X-KDE-Keywords[km]=FictionBook, e-book, fb2 X-KDE-Keywords[ko]=FictionBook, e-book, fb2 +X-KDE-Keywords[lv]=FictionBook, e-book, fb2, e-grāmata X-KDE-Keywords[nb]=FictionBook, e-bok, fb2 X-KDE-Keywords[nds]=FictionBook, e-book, fb2 X-KDE-Keywords[nl]=Fictieboek, e-book, fb2 @@ -158,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=FictionBook, e-book, fb2,фикшнбук,е‑књ X-KDE-Keywords[sr@ijekavianlatin]=FictionBook, e-book, fb2,FictionBook,e‑knjiga X-KDE-Keywords[sr@latin]=FictionBook, e-book, fb2,FictionBook,e‑knjiga X-KDE-Keywords[sv]=FictionBook, e-bok, fb2 +X-KDE-Keywords[tr]=FictionBook, e-book, fb2 X-KDE-Keywords[uk]=FictionBook,fb2,електронна,книга X-KDE-Keywords[x-test]=xxFictionBook, e-book, fb2xx X-KDE-Keywords[zh_CN]=FictionBook, e-book, fb2,电子书,虚拟书籍 diff --git a/generators/kimgio/libokularGenerator_kimgio.desktop b/generators/kimgio/libokularGenerator_kimgio.desktop index 0b4429b89..340724275 100644 --- a/generators/kimgio/libokularGenerator_kimgio.desktop +++ b/generators/kimgio/libokularGenerator_kimgio.desktop @@ -76,7 +76,7 @@ Comment[en_GB]=Image backend for Okular Comment[es]=Motor de imágenes para Okular Comment[et]=Okulari piltide taustaprogramm Comment[eu]=Okular-en irudi motorra -Comment[fi]=Kuvataustaosta Okular-lukijalle +Comment[fi]=Kuvataustaosta Okularille Comment[fr]=Interface d'Okular pour les images Comment[ga]=Inneall íomhánna le haghaidh Okular Comment[gl]=Infraestrutura de imaxes para Okular diff --git a/generators/ooo/converter.cpp b/generators/ooo/converter.cpp index 6371f2d66..1124e2a51 100644 --- a/generators/ooo/converter.cpp +++ b/generators/ooo/converter.cpp @@ -96,7 +96,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( !document.setContent( &source, &reader, &errorMsg ) ) { emit error( i18n( "Invalid XML document: %1", errorMsg ), -1 ); delete mCursor; - return false; + return 0; } mStyleInformation = new StyleInformation(); @@ -109,7 +109,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( !styleParser.parse() ) { emit error( i18n( "Unable to read style information" ), -1 ); delete mCursor; - return false; + return 0; } /** @@ -156,7 +156,7 @@ QTextDocument* Converter::convert( const QString &fileName ) if ( !convertBody( element ) ) { emit error( i18n( "Unable to convert document content" ), -1 ); delete mCursor; - return false; + return 0; } } diff --git a/generators/ooo/generator_ooo.cpp b/generators/ooo/generator_ooo.cpp index 6123387e9..793ee5893 100644 --- a/generators/ooo/generator_ooo.cpp +++ b/generators/ooo/generator_ooo.cpp @@ -20,7 +20,7 @@ static KAboutData createAboutData() "okular_ooo", "okular_ooo", ki18n( "OpenOffice Document Backend" ), - "0.2.2", + "0.2.3", ki18n( "A renderer for OpenOffice text documents" ), KAboutData::License_GPL, ki18n( "© 2006-2008 Tobias Koenig" ) diff --git a/generators/ooo/libokularGenerator_ooo.desktop b/generators/ooo/libokularGenerator_ooo.desktop index cf33e82fa..f20838400 100644 --- a/generators/ooo/libokularGenerator_ooo.desktop +++ b/generators/ooo/libokularGenerator_ooo.desktop @@ -16,7 +16,7 @@ Name[eo]=OpenDocument formato Name[es]=Formato OpenDocument Name[et]=OpenDocumenti vorming Name[eu]=OpenDocument formatua -Name[fi]=OpenDocument -muoto +Name[fi]=OpenDocument-muoto Name[fr]=Format OpenDocument Name[ga]=Formáid OpenDocument Name[gl]=Formato OpenDocument @@ -75,7 +75,7 @@ Comment[en_GB]=OpenDocument backend for Okular Comment[es]=Motor OpenDocument para Okular Comment[et]=Okulari OpenDocumenti taustaprogramm Comment[eu]=Okular-en OpenDocument motorra -Comment[fi]=OpenDocument-taustaosa Okular-lukijalle +Comment[fi]=OpenDocument-taustaosa Okularille Comment[fr]=Interface d'Okular pour le format OpenDocument Comment[ga]=Inneall OpenDocument le haghaidh Okular Comment[gl]=Infraestrutura de OpenDocument para Okular diff --git a/generators/plucker/generator_plucker.cpp b/generators/plucker/generator_plucker.cpp index 3bd22b9b2..4ffdead26 100644 --- a/generators/plucker/generator_plucker.cpp +++ b/generators/plucker/generator_plucker.cpp @@ -131,20 +131,15 @@ const Okular::DocumentInfo* PluckerGenerator::generateDocumentInfo() return &mDocumentInfo; } -bool PluckerGenerator::canGeneratePixmap() const -{ - return true; -} - -void PluckerGenerator::generatePixmap( Okular::PixmapRequest * request ) +QImage PluckerGenerator::image( Okular::PixmapRequest *request ) { const QSizeF size = mPages[ request->pageNumber() ]->size(); - QPixmap *pixmap = new QPixmap( request->width(), request->height() ); - pixmap->fill( Qt::white ); + QImage image( request->width(), request->height(), QImage::Format_ARGB32_Premultiplied ); + image.fill( Qt::white ); QPainter p; - p.begin( pixmap ); + p.begin( &image ); qreal width = request->width(); qreal height = request->height(); @@ -153,9 +148,6 @@ void PluckerGenerator::generatePixmap( Okular::PixmapRequest * request ) mPages[ request->pageNumber() ]->drawContents( &p ); p.end(); - request->page()->setPixmap( request->id(), pixmap ); - - if ( !mLinkAdded.contains( request->pageNumber() ) ) { QLinkedList objects; for ( int i = 0; i < mLinks.count(); ++i ) { @@ -176,7 +168,7 @@ void PluckerGenerator::generatePixmap( Okular::PixmapRequest * request ) mLinkAdded.insert( request->pageNumber() ); } - signalPixmapRequestDone( request ); + return image; } Okular::ExportFormat::List PluckerGenerator::exportFormats() const diff --git a/generators/plucker/generator_plucker.h b/generators/plucker/generator_plucker.h index 61f056189..8dac4b830 100644 --- a/generators/plucker/generator_plucker.h +++ b/generators/plucker/generator_plucker.h @@ -32,9 +32,9 @@ class PluckerGenerator : public Okular::Generator // [INHERITED] document information const Okular::DocumentInfo * generateDocumentInfo(); - - bool canGeneratePixmap() const; - void generatePixmap( Okular::PixmapRequest *request ); + + // [INHERITED] perform actions on document / pages + QImage image( Okular::PixmapRequest *request ); // [INHERITED] text exporting Okular::ExportFormat::List exportFormats() const; diff --git a/generators/plucker/libokularGenerator_plucker.desktop b/generators/plucker/libokularGenerator_plucker.desktop index 579c1ee8f..f50d7b9a6 100644 --- a/generators/plucker/libokularGenerator_plucker.desktop +++ b/generators/plucker/libokularGenerator_plucker.desktop @@ -75,7 +75,7 @@ Comment[en_GB]=Plucker backend for Okular Comment[es]=Motor Plucker para Okular Comment[et]=Okulari Pluckeri taustaprogramm Comment[eu]=Okular-en Plucker motorra -Comment[fi]=Plucker-taustaosa Okular-lukijalle +Comment[fi]=Plucker-taustaosa Okularille Comment[fr]=Interface d'Okular pour les fichiers Plucker Comment[ga]=Inneall Plucker le haghaidh Okular Comment[gl]=Infraestrutura de Plucker para Okular diff --git a/generators/plucker/okularApplication_plucker.desktop b/generators/plucker/okularApplication_plucker.desktop index beb866fd2..7d06d93fc 100755 --- a/generators/plucker/okularApplication_plucker.desktop +++ b/generators/plucker/okularApplication_plucker.desktop @@ -131,6 +131,7 @@ X-KDE-Keywords[ar]=plucker X-KDE-Keywords[bg]=plucker X-KDE-Keywords[ca]=plucker X-KDE-Keywords[ca@valencia]=plucker +X-KDE-Keywords[cs]=plucker X-KDE-Keywords[da]=plucker X-KDE-Keywords[de]=plucker X-KDE-Keywords[el]=plucker @@ -142,7 +143,9 @@ X-KDE-Keywords[ga]=plucker X-KDE-Keywords[hu]=plucker X-KDE-Keywords[it]=plucker X-KDE-Keywords[kk]=plucker +X-KDE-Keywords[km]=plucker X-KDE-Keywords[ko]=plucker +X-KDE-Keywords[lv]=plucker X-KDE-Keywords[nb]=plucker X-KDE-Keywords[nds]=Plucker X-KDE-Keywords[nl]=plucker @@ -158,6 +161,7 @@ X-KDE-Keywords[sr@ijekavian]=plucker,Плакер X-KDE-Keywords[sr@ijekavianlatin]=plucker,Plucker X-KDE-Keywords[sr@latin]=plucker,Plucker X-KDE-Keywords[sv]=plucker +X-KDE-Keywords[tr]=plucker X-KDE-Keywords[uk]=plucker X-KDE-Keywords[x-test]=xxpluckerxx X-KDE-Keywords[zh_CN]=plucker diff --git a/generators/poppler/README.Annotations b/generators/poppler/README.Annotations deleted file mode 100644 index c4587b4a3..000000000 --- a/generators/poppler/README.Annotations +++ /dev/null @@ -1,161 +0,0 @@ -Summary: PDF 1.6 Annotations specs and okular support. -Author: Enrico Ros - KPDF project - 2005 - okular team - 2006 - -** Relations to okular: - okular tries to support ALL annotations and ALL parameters in PDF specs. If - this can't be done, we must support at least the most common ones and the - most common parameters. - Current Data Structure status: - complete: - [markup], popup, text, freetext, line, polygon, polyline, highlight, - underline, squiggly, strikeout, stamp, ink - partial: - base{P,AP,AS,A,AA,StructPar,OC}, geom{RD} - missing: - link (handled in another way), caret, {fileattachment, sound, movie}, - screen, widget, {printermark, trapnet, watermark}, 3d - -** Notes on parsing in relation to okular: - Different Data Structures: - To adapt the 'plain' pdf storage to okular data structures we use a couple - of queues to store cross-dependant objects for each page and then adapt - objects to our DSs resolving crossdeps at the end of the loops. - - -** PDF file structure (annotations section): - -COMMON FIELDS (note: '*' is required) - Type name 'Annot' - *Subtype name [Text...3D] - \ see second table (below) - *Rect rectangle norma rect - Contents text string alternate text / description - P dictionary page reference (mandatory for Screen) - NM text string unique name - M date or string last modify date - F integer flags (default:0) - \ OR-ed flags: Invisible(0), Hidden(1), Print(2), NoZoom(3), NoRotate(4), - \NoView(5), ReadOnly(6), Locked(7), ToggleNoView(8) - BS dictionary border styles (also style for: Line,Square,Circle,Ink) - \ Type (name='Border'), W (number), S (name), D (array) - BE dictionary border effect (only for square, circle, and polygon) - \ S (name (effect[S:no effect,C: cloudy])), I (number (intensity(0..2)) - AP dictionary visual representation (handler should provide own impl) - \ renderable data to be used with composition algorithm 8.1 (in pdf spec) - AS name visual state (indexes an AP representation) - Border array pre-BS: x-corner-rad, y-corner-rad, width [,dash array] - C array color (3 components in range [0..1]) - A dictionary action (NA on link, specialized on Movie) - \ 'LinkAction' to extract those and use internal handler instead of external one - AA dictionary additional actions for events (used by Widget only) - StructPar. integer annotation's key in the structural tree(not used) - OC dictionary optional content properties (custom visi-check) - -MARKUP -additional- fields (see 'X' marks in the Subtype table): - Markup annotations have an associated pop-up window that may contain text. - If the popup id is given, that popup is used for displaying text, otherwise a - popup is created on annotation opening but the popup is rendered 'in place' - with the annotation and can not be moved without moving the annotation. - T text string titlebar text (creator name by convention) - Popup dictionary indirect refrence to pupup annot for editing text - CA number opacity (def: 1.0) - RC text string/eam rich text displayed if opened (overrides Contents) - CreationD. date creation date - IRT dictionary reference to the ann this is 'in reply to' (if RT) - Subj text string short subject addressed - RT name 'R':in reply 'Group':grouped (if IRT) - IT name annotation intent (specialized for certail types) - [annotation states? pg 585] - -CUSTOM FIELDS (for each subType) (X: is markup): - Popup . no gfx only a parent (inherits Contents,M,C,T) - Parent dictionary indirect reference to parent (from wich Mark. are inh) - Open boolean initially displayed opened (def:false) - - Text X a 'sticky note' attached to a point in document - Open boolean default:false - Name name icon[Comment,Key,Note,Help,NewParagraph,Paragraph,Insert] - [M]State text string if RT,IRT,StateModel are set - [M]StateM. text string if RT,IRT,State are set - - FreeText X like Text but the text is always visible - *DA string appearance string (AP takes precedence) - Q integer 0(Left-justified) 1(Centered) 2(Right-justified) - RC text string/eam rich text string (overrides Contents) - DS text string default text string - CL array 2 or 3 {x,y} couples for callout line - [M]IT name not present,FreeTextCallout,FreeTextTypeWriter - - Line X a single straight line on the page (has popup note) - *L array 4 numbers (2 x,y couples) - BS dictionary width and dash pattern to be used in drawing the line - LE array 2 names (start and end styles) (def:None,None) - \ values [Square,Circle,Diamond,OpenArrow,ClosedArrow,None, - Butt,ROpenArrow,RClosedArrow,Slash] - IC array interior color (3 components in range [0..1]) - LL number leader line fwd (if LLE) in points - LLE number leader line bk (if LL) in points - Cap boolean has caption (RC or Contents) (def:false) - [M]IT name not present,LineArrow,LineDimension - - Polygon X closed polygon on the page - PolyLine X polygon without first and last vtx closed - *Vertices array n*{x,y} pairs of all line vertices - LE array 2 names (start and end styles) (def:None,None) - BS dictionary width and dash pattern - IC array interior color (3 components in range [0..1]) - BE dictionary border effect - IT name not present,PolygonCloud - - Square X rect or ellipse on the page (has popup note) the square - Circle X or circle have 18pt border are inscribed into rect - BS dictionary line width and dash pattern - IC array interior color (3 components in range [0..1]) - BE dictionary border effect - RD rectangle negative border offsets (4 positive coords) - - Highlight X - Underline X appears as highlights, underlines, strikeouts. has - Squiggly X popup text of associated note) - StrikeOut X - *QuadPo. array array of ccw quadrilats (8 x n numbers) (AP takes prec) - - Caret X visual symbol that indicates the presence of text - RD rectangle rect displacement from effective rect to annotation one - Sy name 'P':paragraph symbol, 'None':no symbol(defaulr) - - Stamp X displays text or graphics intended to look as rubber stamps - Name name [Approved,Experimental,NotApproved,AsIs,Expired, - NotForPublicRelease,Confidential,Final,Sold,Departmental, - ForComment,TopSecret,Draft,ForPublicRelease] - - Ink X freehand ?scribble? composed of one or more disjoint paths - *InkList array array or arrays of {x,y} userspace couples - BS dictionary line width and dash pattern - -UNUSED / INCOMPLETE: - Link . hypertext link to a location in document or action - Dest arr,nam,str if A not present - H name N(none) I(invert) O(outline) P(sunken) - PA dictionary URI action - QuadPoints array array of quadrilaterals (8 x n numbers) - - FileAttachment X reference to file (typically embedded) - *FS file file associated - Name name icon [Graph,PushPin,Paperclip,Tag] - - Sound X like Text but contains sound - *Sound stream sound to be played when annot is activated - Name name icon [Speaker,Mic,_more_] - - Movie . contains animated graphics and sound - Movie dictionary the movie to be played when annot is actived - A boolean whether and how to play the movie (def:true) - - Screen . specifies a region of a page on which play media clips - Widget . appearance of the fields for user interaction - PrinterMark . a graphic symbol used to assist production personnel - TrapNet . add color marks along colour boundaries to avoid artifacts - Watermark . graphics to be printed at a fixed size and position on a page - 3D . the mean by which 3D artwork is represented in a document diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index ad8189873..02813a13d 100644 --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -310,6 +310,13 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * break; } +#ifdef HAVE_POPPLER_0_22 + case Poppler::Annotation::AWidget: + { + annotation = new Okular::WidgetAnnotation(); + break; + } +#endif #ifdef HAVE_POPPLER_0_20 case Poppler::Annotation::AScreen: { @@ -350,12 +357,16 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * } if ( annotation ) { + // the Contents field might have lines separated by \r + QString contents = ann->contents(); + contents.replace( QLatin1Char( '\r' ), QLatin1Char( '\n' ) ); + annotation->setAuthor( ann->author() ); - annotation->setContents( ann->contents() ); + annotation->setContents( contents ); annotation->setUniqueName( ann->uniqueName() ); annotation->setModificationDate( ann->modificationDate() ); annotation->setCreationDate( ann->creationDate() ); - annotation->setFlags( ann->flags() ); + annotation->setFlags( ann->flags() | Okular::Annotation::External ); annotation->setBoundingRectangle( Okular::NormalizedRect::fromQRectF( ann->boundary() ) ); if (externallyDrawn) @@ -378,6 +389,27 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * } } + if ( annotation->subType() == Okular::Annotation::AText ) + { + Okular::TextAnnotation * txtann = static_cast( annotation ); + + if ( txtann->textType() == Okular::TextAnnotation::InPlace ) + { +#ifndef HAVE_POPPLER_0_20 + // Poppler before 0.20 returns the inplaceText in contents + txtann->setInplaceText( txtann->contents() ); +#endif + } + else if ( txtann->textType() == Okular::TextAnnotation::Linked ) + { + Poppler::TextAnnotation * ppl_txtann = static_cast( ann ); + + // Poppler and Okular assume a different default icon name in XML + // We re-read it via getter, which always tells the right one + txtann->setTextIcon( ppl_txtann->textIcon() ); + } + } + // TODO clone style // TODO clone window // TODO clone revisions diff --git a/generators/poppler/config-okular-poppler.h.cmake b/generators/poppler/config-okular-poppler.h.cmake index df89d6882..2ce6a4a65 100644 --- a/generators/poppler/config-okular-poppler.h.cmake +++ b/generators/poppler/config-okular-poppler.h.cmake @@ -6,3 +6,6 @@ /* Defined if we have the 0.20 version of the Poppler library */ #cmakedefine HAVE_POPPLER_0_20 1 + +/* Defined if we have the 0.22 version of the Poppler library */ +#cmakedefine HAVE_POPPLER_0_22 1 diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index 86a69d044..fcc8dc453 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -82,7 +82,7 @@ class PDFOptionsPage : public QWidget layout->addWidget(m_forceRaster); layout->addStretch(1); -#ifndef HAVE_POPPLER_0_20 +#if defined(Q_WS_WIN) || !defined(HAVE_POPPLER_0_20) m_printAnnots->setVisible( false ); #endif setPrintAnnots( true ); // Default value @@ -180,6 +180,10 @@ Okular::Movie* createMovieFromPopplerMovie( const Poppler::MovieObject *popplerM movie->setShowControls( popplerMovie->showControls() ); movie->setPlayMode( (Okular::Movie::PlayMode)popplerMovie->playMode() ); movie->setAutoPlay( false ); // will be triggered by external MovieAnnotation +#ifdef HAVE_POPPLER_0_22 + movie->setShowPosterImage( popplerMovie->showPosterImage() ); + movie->setPosterImage( popplerMovie->posterImage() ); +#endif return movie; } @@ -358,7 +362,7 @@ static KAboutData createAboutData() "okular_poppler", "okular_poppler", ki18n( "PDF Backend" ), - "0.5", + "0.6.2", ki18n( "A PDF file renderer" ), KAboutData::License_GPL, ki18n( "© 2005-2008 Albert Astals Cid" ) @@ -649,8 +653,10 @@ const Okular::DocumentInfo * PDFGenerator::generateDocumentInfo() docInfo.set( Okular::DocumentInfo::ModificationDate, KGlobal::locale()->formatDateTime( pdfdoc->date("ModDate"), KLocale::LongDate, true ) ); - docInfo.set( "format", i18nc( "PDF v. ", "PDF v. %1", - QString::number( pdfdoc->pdfVersion() ) ), i18n( "Format" ) ); + int major, minor; + pdfdoc->getPdfVersion(&major, &minor); + docInfo.set( "format", i18nc( "PDF v. ", "PDF v. %1.%2", + major, minor ), i18n( "Format" ) ); docInfo.set( "encryption", pdfdoc->isEncrypted() ? i18n( "Encrypted" ) : i18n( "Unencrypted" ), i18n("Security") ); docInfo.set( "optimization", pdfdoc->isLinearized() ? i18n( "Yes" ) : i18n( "No" ), @@ -939,6 +945,23 @@ void PDFGenerator::resolveMovieLinkReferences( Okular::Page *page ) resolveMovieLinkReference( const_cast( page->pageAction( Okular::Page::Opening ) ), page ); resolveMovieLinkReference( const_cast( page->pageAction( Okular::Page::Closing ) ), page ); + foreach ( Okular::Annotation *annotation, page->annotations() ) + { + if ( annotation->subType() == Okular::Annotation::AScreen ) + { + Okular::ScreenAnnotation *screenAnnotation = static_cast( annotation ); + resolveMovieLinkReference( screenAnnotation->additionalAction( Okular::Annotation::PageOpening ), page ); + resolveMovieLinkReference( screenAnnotation->additionalAction( Okular::Annotation::PageClosing ), page ); + } + + if ( annotation->subType() == Okular::Annotation::AWidget ) + { + Okular::WidgetAnnotation *widgetAnnotation = static_cast( annotation ); + resolveMovieLinkReference( widgetAnnotation->additionalAction( Okular::Annotation::PageOpening ), page ); + resolveMovieLinkReference( widgetAnnotation->additionalAction( Okular::Annotation::PageClosing ), page ); + } + } + foreach ( Okular::FormField *field, page->formFields() ) resolveMovieLinkReference( field->activationAction(), page ); } @@ -1238,7 +1261,7 @@ bool PDFGenerator::exportTo( const QString &fileName, const Okular::ExportFormat Poppler::Page *pp = pdfdoc->page(i); if (pp) { - text = pp->text(QRect()); + text = pp->text(QRect()).normalized(QString::NormalizationForm_KC); } userMutex()->unlock(); ts << text; @@ -1364,52 +1387,47 @@ void PDFGenerator::addAnnotations( Poppler::Page * popplerPage, Okular::Page * p foreach(Poppler::Annotation *a, popplerAnnotations) { - //a->window.width = (int)(page->width() * a->window.width); - //a->window.height = (int)(page->height() * a->window.height); - //a->window.width = a->window.width < 200 ? 200 : a->window.width; - // a->window.height = a->window.height < 120 ? 120 : a->window.height; - // resize annotation's geometry to an icon - // TODO okular geom.right = geom.left + 22.0 / page->width(); - // TODO okular geom.bottom = geom.top + 22.0 / page->height(); - /* - QString szanno; - QTextStream(&szanno)<<"PopplerAnnotation={author:"<author - <<", contents:"<contents - <<", uniqueName:"<uniqueName - <<", modifyDate:"<modifyDate.toString("hh:mm:ss, dd.MM.yyyy") - <<", creationDate:"<creationDate.toString("hh:mm:ss, dd.MM.yyyy") - <<", flags:"<flags - <<", boundary:"<boundary.left()<<","<boundary.top()<<","<boundary.right()<<","<boundary.bottom() - <<", style.color:"<style.color.name() - <<", style.opacity:"<style.opacity - <<", style.width:"<style.width - <<", style.LineStyle:"<style.style - <<", style.xyCorners:"<style.xCorners<<","<style.yCorners - <<", style.marks:"<style.marks - <<", style.spaces:"<style.spaces - <<", style.LineEffect:"<style.effect - <<", style.effectIntensity:"<style.effectIntensity - <<", window.flags:"<window.flags - <<", window.topLeft:"<<(a->window.topLeft.x()) - <<","<<(a->window.topLeft.y()) - <<", window.width,height:"<window.width<<","<window.height - <<", window.title:"<window.title - <<", window.summary:"<window.summary - <<", window.text:"<window.text; - kDebug(PDFDebug) << "astario: " << szanno; */ - //TODO add annotations after poppler write feather is full suported bool doDelete = true; Okular::Annotation * newann = createAnnotationFromPopplerAnnotation( a, &doDelete ); if (newann) { - // the Contents field has lines separated by \r - QString contents = newann->contents(); - contents.replace( QLatin1Char( '\r' ), QLatin1Char( '\n' ) ); - newann->setContents( contents ); - // explicitly mark as external - newann->setFlags( newann->flags() | Okular::Annotation::External ); page->addAnnotation(newann); +#ifdef HAVE_POPPLER_0_22 + if ( a->subType() == Poppler::Annotation::AScreen ) + { +/* + // TODO: (tokoe) This has been disabled for the moment, since we return MovieAnnotation objects + // for AScreen annotations, which will lead to a crash down here. + + Poppler::ScreenAnnotation *annotScreen = static_cast( a ); + Okular::ScreenAnnotation *screenAnnotation = static_cast( newann ); + + const Poppler::Link *pageOpeningLink = annotScreen->additionalAction( Poppler::Annotation::PageOpeningAction ); + if ( pageOpeningLink ) + screenAnnotation->setAdditionalAction( Okular::Annotation::PageOpening, createLinkFromPopplerLink( pageOpeningLink ) ); + + const Poppler::Link *pageClosingLink = annotScreen->additionalAction( Poppler::Annotation::PageClosingAction ); + if ( pageClosingLink ) + screenAnnotation->setAdditionalAction( Okular::Annotation::PageClosing, createLinkFromPopplerLink( pageClosingLink ) ); +*/ + } + + if ( a->subType() == Poppler::Annotation::AWidget ) + { + Poppler::WidgetAnnotation *annotWidget = static_cast( a ); + Okular::WidgetAnnotation *widgetAnnotation = static_cast( newann ); + + const Poppler::Link *pageOpeningLink = annotWidget->additionalAction( Poppler::Annotation::PageOpeningAction ); + if ( pageOpeningLink ) + widgetAnnotation->setAdditionalAction( Okular::Annotation::PageOpening, createLinkFromPopplerLink( pageOpeningLink ) ); + + const Poppler::Link *pageClosingLink = annotWidget->additionalAction( Poppler::Annotation::PageClosingAction ); + if ( pageClosingLink ) + widgetAnnotation->setAdditionalAction( Okular::Annotation::PageClosing, createLinkFromPopplerLink( pageClosingLink ) ); + } +#endif + if ( !doDelete ) annotationsHash.insert( newann, a ); } @@ -1672,7 +1690,7 @@ const Okular::SourceReference * PDFGenerator::dynamicSourceReference( int pageNr } const char *name = synctex_scanner_get_name( synctex_scanner, synctex_node_tag( node ) ); - Okular::SourceReference * sourceRef = new Okular::SourceReference( name, line, col ); + Okular::SourceReference * sourceRef = new Okular::SourceReference( QString::fromLocal8Bit( name ), line, col ); return sourceRef; } } @@ -1755,9 +1773,13 @@ bool PDFGenerator::supportsOption( SaveOption option ) const { case SaveChanges: { + // Saving files with /Encrypt is not supported before Poppler 0.22 +#ifndef HAVE_POPPLER_0_22 QMutexLocker locker( userMutex() ); - // Saving files with /Encrypt is not supported return pdfdoc->isEncrypted() ? false : true; +#else + return true; +#endif } default: ; } @@ -1780,7 +1802,10 @@ bool PDFGenerator::save( const QString &fileName, SaveOptions options, QString * switch (pdfConv->lastError()) { case Poppler::BaseConverter::NotSupportedInputFileError: +#ifndef HAVE_POPPLER_0_22 + // This can only happen with Poppler before 0.22 *errorText = i18n("Saving files with /Encrypt is not supported."); +#endif break; case Poppler::BaseConverter::NoError: diff --git a/generators/poppler/libokularGenerator_poppler.desktop b/generators/poppler/libokularGenerator_poppler.desktop index 9962dc301..7dd547c3d 100644 --- a/generators/poppler/libokularGenerator_poppler.desktop +++ b/generators/poppler/libokularGenerator_poppler.desktop @@ -75,7 +75,7 @@ Comment[en_GB]=PDF backend for Okular using poppler Comment[es]=Motor PDF para Okular que usa Poppler Comment[et]=Okulari PDF-i taustaprogramm poppleri vahendusel Comment[eu]=Okular-en PDF motorra poppler erabiliz -Comment[fi]=Poppler-kirjastoa käyttävä PDF-taustaosa Okular-lukijalle +Comment[fi]=Poppler-kirjastoa käyttävä PDF-taustaosa Okularille Comment[fr]=Interface d'Okular pour les PDF (utilise poppler) Comment[ga]=Inneall PDF a úsáideann poppler le haghaidh Okular Comment[gl]=Infraestrutura de PDF mediante poppler para Okular diff --git a/generators/poppler/okularApplication_pdf.desktop b/generators/poppler/okularApplication_pdf.desktop index 030e4312a..9949772f1 100755 --- a/generators/poppler/okularApplication_pdf.desktop +++ b/generators/poppler/okularApplication_pdf.desktop @@ -142,7 +142,9 @@ X-KDE-Keywords[ga]=PDF, Portable Document Format X-KDE-Keywords[hu]=PDF, Portable Document Format X-KDE-Keywords[it]=PDF, Portable Document Format, formato per documenti portabile X-KDE-Keywords[kk]=PDF, Portable Document Format +X-KDE-Keywords[km]=PDF, Portable Document Format X-KDE-Keywords[ko]=PDF, Portable Document Format +X-KDE-Keywords[lv]=PDF, Portable Document formāts X-KDE-Keywords[nb]=PDF, Portabelt Dokument Format X-KDE-Keywords[nds]=PDF, Porteerbor Dokmentformaat X-KDE-Keywords[nl]=PDF, Portable Document Format @@ -158,6 +160,7 @@ X-KDE-Keywords[sr@ijekavian]=PDF, Portable Document Format,ПДФ X-KDE-Keywords[sr@ijekavianlatin]=PDF, Portable Document Format,PDF X-KDE-Keywords[sr@latin]=PDF, Portable Document Format,PDF X-KDE-Keywords[sv]=PDF, Portabelt dokumentformat +X-KDE-Keywords[tr]=PDF, Taşınabilir Belge Biçimi X-KDE-Keywords[uk]=PDF,Portable Document Format,пдф X-KDE-Keywords[x-test]=xxPDF, Portable Document Formatxx X-KDE-Keywords[zh_CN]=PDF, Portable Document Format,开放文档格式 diff --git a/generators/spectre/generator_ghostview.cpp b/generators/spectre/generator_ghostview.cpp index 02c6875d0..77afbfb64 100644 --- a/generators/spectre/generator_ghostview.cpp +++ b/generators/spectre/generator_ghostview.cpp @@ -39,7 +39,7 @@ static KAboutData createAboutData() "okular_ghostview", "okular_ghostview", ki18n( "PS Backend" ), - "0.1.5", + "0.1.6", ki18n( "A PostScript file renderer." ), KAboutData::License_GPL, ki18n( "© 2007-2008 Albert Astals Cid" ), diff --git a/generators/spectre/libokularGenerator_ghostview.desktop b/generators/spectre/libokularGenerator_ghostview.desktop index 78e104b32..6780bac9d 100644 --- a/generators/spectre/libokularGenerator_ghostview.desktop +++ b/generators/spectre/libokularGenerator_ghostview.desktop @@ -75,7 +75,7 @@ Comment[en_GB]=Ghostscript PS/PDF backend for Okular Comment[es]=Motor PS/PDF de Ghostscript para Okular Comment[et]=Okulari Ghostscript PS/PDF-i taustaprogramm Comment[eu]=Okular-en Ghostscript PS/PDF motorra -Comment[fi]=Ghostscript-pohjainen PS/PDF-taustaosa ohjelmaa Okular varten +Comment[fi]=Ghostscript-pohjainen PS/PDF-taustaosa Okularille Comment[fr]=Interface d'Okular pour les fichiers Ghostscript PS/PDF Comment[ga]=Inneall PS/PDF Ghostscript le haghaidh Okular Comment[gl]=Infraestrutura Ghostscript PS/PDF para Okular diff --git a/generators/spectre/rendererthread.cpp b/generators/spectre/rendererthread.cpp index f185d2277..2edbd62b7 100644 --- a/generators/spectre/rendererthread.cpp +++ b/generators/spectre/rendererthread.cpp @@ -70,6 +70,13 @@ void GSRendererThread::run() spectre_page_render(req.spectrePage, m_renderContext, &data, &row_length); + // Qt needs the missing alpha of QImage::Format_RGB32 to be 0xff + if (data[3] != 0xff) + { + for (int i = 3; i < row_length * wantedHeight; i += 4) + data[i] = 0xff; + } + QImage img; if (row_length == wantedWidth * 4) { @@ -86,7 +93,7 @@ void GSRendererThread::run() { case Okular::Rotation90: { - QMatrix m; + QTransform m; m.rotate(90); img = img.transformed( m ); break; @@ -94,14 +101,14 @@ void GSRendererThread::run() case Okular::Rotation180: { - QMatrix m; + QTransform m; m.rotate(180); img = img.transformed( m ); break; } case Okular::Rotation270: { - QMatrix m; + QTransform m; m.rotate(270); img = img.transformed( m ); } diff --git a/generators/tiff/libokularGenerator_tiff.desktop b/generators/tiff/libokularGenerator_tiff.desktop index 2a7388e1d..e9faff8e3 100644 --- a/generators/tiff/libokularGenerator_tiff.desktop +++ b/generators/tiff/libokularGenerator_tiff.desktop @@ -15,7 +15,7 @@ Name[en_GB]=Okular TIFF Library Name[es]=Biblioteca TIFF de Okular Name[et]=Okulari TIFF-i teek Name[eu]=Okular TIFF liburutegia -Name[fi]=Okular TIFF-kirjasto +Name[fi]=TIFF-kirjasto Okularille Name[fr]=Librairie d'Okular pour le format TIFF Name[ga]=Leabharlann TIFF Okular Name[gl]=Biblioteca TIFF de Okular @@ -71,7 +71,7 @@ Comment[en_GB]=TIFF backend for Okular Comment[es]=Motor TIFF para Okular Comment[et]=Okulari TIFF-i taustaprogramm Comment[eu]=Okular-en TIFF motorra -Comment[fi]=TIFF-taustaosa ohjelmaa Okular varten +Comment[fi]=TIFF-taustaosa Okularille Comment[fr]=Interface d'Okular pour les fichiers TIFF Comment[ga]=Inneall TIFF le haghaidh Okular Comment[gl]=Infraestrutura de TIFF para Okular diff --git a/generators/xps/generator_xps.cpp b/generators/xps/generator_xps.cpp index 97f4450a8..2399a9656 100644 --- a/generators/xps/generator_xps.cpp +++ b/generators/xps/generator_xps.cpp @@ -393,18 +393,18 @@ static QPainterPath parseAbbreviatedPathData( const QString &data) /** Parse a "Matrix" attribute string \param csv the comma separated list of values - \return the QMatrix corresponding to the affine transform + \return the QTransform corresponding to the affine transform given in the attribute \see XPS specification 7.4.1 */ -static QMatrix attsToMatrix( const QString &csv ) +static QTransform attsToMatrix( const QString &csv ) { QStringList values = csv.split( ',' ); if ( values.count() != 6 ) { - return QMatrix(); // that is an identity matrix - no effect + return QTransform(); // that is an identity matrix - no effect } - return QMatrix( values.at(0).toDouble(), values.at(1).toDouble(), + return QTransform( values.at(0).toDouble(), values.at(1).toDouble(), values.at(2).toDouble(), values.at(3).toDouble(), values.at(4).toDouble(), values.at(5).toDouble() ); } @@ -440,12 +440,12 @@ static QPen parseRscRefColorForPen( const QString &data ) /** \return Matrix specified by given data or by referenced dictionary */ -static QMatrix parseRscRefMatrix( const QString &data ) +static QTransform parseRscRefMatrix( const QString &data ) { if (data[0] == '{') { //TODO kDebug(XpsDebug) << "Reference" << data; - return QMatrix(); + return QTransform(); } else { return attsToMatrix( data ); } @@ -878,7 +878,7 @@ void XpsHandler::processGlyph( XpsRenderNode &node ) //RenderTransform att = node.attributes.value("RenderTransform"); if (!att.isEmpty()) { - m_painter->setWorldMatrix( parseRscRefMatrix( att ), true); + m_painter->setWorldTransform( parseRscRefMatrix( att ), true); } // Clip @@ -984,27 +984,27 @@ void XpsHandler::processImageBrush( XpsRenderNode &node ) QImage image = m_page->loadImageFromFile( node.attributes.value( "ImageSource" ) ); // Matrix which can transform [0, 0, 1, 1] rectangle to given viewbox - QMatrix viewboxMatrix = QMatrix( viewbox.width() * image.physicalDpiX() / 96, 0, 0, viewbox.height() * image.physicalDpiY() / 96, viewbox.x(), viewbox.y() ); + QTransform viewboxMatrix = QTransform( viewbox.width() * image.physicalDpiX() / 96, 0, 0, viewbox.height() * image.physicalDpiY() / 96, viewbox.x(), viewbox.y() ); // Matrix which can transform [0, 0, 1, 1] rectangle to given viewport //TODO Take ViewPort into account - QMatrix viewportMatrix; + QTransform viewportMatrix; att = node.attributes.value( "Transform" ); if ( att.isEmpty() ) { QVariant data = node.getChildData( "ImageBrush.Transform" ); - if (data.canConvert()) { - viewportMatrix = data.value(); + if (data.canConvert()) { + viewportMatrix = data.value(); } else { - viewportMatrix = QMatrix(); + viewportMatrix = QTransform(); } } else { viewportMatrix = parseRscRefMatrix( att ); } - viewportMatrix = viewportMatrix * QMatrix( viewport.width(), 0, 0, viewport.height(), viewport.x(), viewport.y() ); + viewportMatrix = viewportMatrix * QTransform( viewport.width(), 0, 0, viewport.height(), viewport.x(), viewport.y() ); brush = QBrush( image ); - brush.setMatrix( viewboxMatrix.inverted() * viewportMatrix ); + brush.setTransform( viewboxMatrix.inverted() * viewportMatrix ); node.data = qVariantFromValue( brush ); } @@ -1135,10 +1135,10 @@ void XpsHandler::processPath( XpsRenderNode &node ) // RenderTransform att = node.attributes.value( "RenderTransform" ); if (! att.isEmpty() ) { - m_painter->setWorldMatrix( parseRscRefMatrix( att ), true ); + m_painter->setWorldTransform( parseRscRefMatrix( att ), true ); } if ( !pathdata->transform.isIdentity() ) { - m_painter->setWorldMatrix( pathdata->transform, true ); + m_painter->setWorldTransform( pathdata->transform, true ); } Q_FOREACH ( XpsPathFigure *figure, pathdata->paths ) { @@ -1292,7 +1292,7 @@ void XpsHandler::processStartElement( XpsRenderNode &node ) m_painter->save(); QString att = node.attributes.value( "RenderTransform" ); if ( !att.isEmpty() ) { - m_painter->setWorldMatrix( parseRscRefMatrix( att ), true ); + m_painter->setWorldTransform( parseRscRefMatrix( att ), true ); } att = node.attributes.value( "Opacity" ); if ( !att.isEmpty() ) { @@ -1316,11 +1316,11 @@ void XpsHandler::processEndElement( XpsRenderNode &node ) processPath( node ); } else if (node.name == "MatrixTransform") { //TODO Ignoring x:key - node.data = qVariantFromValue( QMatrix( attsToMatrix( node.attributes.value( "Matrix" ) ) ) ); + node.data = qVariantFromValue( QTransform( attsToMatrix( node.attributes.value( "Matrix" ) ) ) ); } else if ((node.name == "Canvas.RenderTransform") || (node.name == "Glyphs.RenderTransform") || (node.name == "Path.RenderTransform")) { QVariant data = node.getRequiredChildData( "MatrixTransform" ); - if (data.canConvert()) { - m_painter->setWorldMatrix( data.value(), true ); + if (data.canConvert()) { + m_painter->setWorldTransform( data.value(), true ); } } else if (node.name == "Canvas") { m_painter->restore(); @@ -1461,7 +1461,7 @@ bool XpsPage::renderToPainter( QPainter *painter ) { XpsHandler handler( this ); handler.m_painter = painter; - handler.m_painter->setWorldMatrix(QMatrix().scale((qreal)painter->device()->width() / size().width(), (qreal)painter->device()->height() / size().height())); + handler.m_painter->setWorldTransform(QTransform().scale((qreal)painter->device()->width() / size().width(), (qreal)painter->device()->height() / size().height())); QXmlSimpleReader parser; parser.setContentHandler( &handler ); parser.setErrorHandler( &handler ); @@ -1617,9 +1617,9 @@ Okular::TextPage* XpsPage::textPage() QXmlStreamReader xml; xml.addData( readFileOrDirectoryParts( pageFile ) ); - QMatrix matrix = QMatrix(); - QStack matrices; - matrices.push( QMatrix() ); + QTransform matrix = QTransform(); + QStack matrices; + matrices.push( QTransform() ); bool useMatrix = false; QXmlStreamAttributes glyphsAtts; diff --git a/generators/xps/generator_xps.h b/generators/xps/generator_xps.h index b4fa59409..4c56e8c73 100644 --- a/generators/xps/generator_xps.h +++ b/generators/xps/generator_xps.h @@ -76,8 +76,8 @@ struct XpsGradient Types of data in XpsRenderNode::data. Name of each type consist of Xps and name of xml element which data it holds */ -typedef QMatrix XpsMatrixTransform; -typedef QMatrix XpsRenderTransform; +typedef QTransform XpsMatrixTransform; +typedef QTransform XpsRenderTransform; typedef QBrush XpsFill; struct XpsPathFigure { diff --git a/generators/xps/libokularGenerator_xps.desktop b/generators/xps/libokularGenerator_xps.desktop index 40409aaa5..c6908bb77 100644 --- a/generators/xps/libokularGenerator_xps.desktop +++ b/generators/xps/libokularGenerator_xps.desktop @@ -15,7 +15,7 @@ Name[en_GB]=Okular XPS Plugin Name[es]=Complemento XPS de Okular Name[et]=Okulari XPS-i plugin Name[eu]=Okular-en XPS plugina -Name[fi]=XPS-liitännäinen ohjelmaa Okular varten +Name[fi]=XPS-liitännäinen Okularille Name[fr]=Module externe XPS pour Okular Name[ga]=Breiseán XPS Okular Name[gl]=Extensión XPS para Okular @@ -71,7 +71,7 @@ Comment[en_GB]=XPS backend for Okular Comment[es]=Motor XPS para Okular Comment[et]=Okulari XPS-i taustaprogramm Comment[eu]=Okular-en XPS motorra -Comment[fi]=XPS-taustaosa ohjelmaa Okular varten +Comment[fi]=XPS-taustaosa Okularille Comment[fr]=Interface d'Okular pour les fichiers XPS Comment[ga]=Inneall XPS le haghaidh Okular Comment[gl]=Infraestrutura de XPS para Okular diff --git a/generators/xps/okularApplication_xps.desktop b/generators/xps/okularApplication_xps.desktop index 635aa6cfd..71e636848 100755 --- a/generators/xps/okularApplication_xps.desktop +++ b/generators/xps/okularApplication_xps.desktop @@ -142,7 +142,9 @@ X-KDE-Keywords[ga]=XPS X-KDE-Keywords[hu]=XPS X-KDE-Keywords[it]=XPS X-KDE-Keywords[kk]=XPS +X-KDE-Keywords[km]=XPS X-KDE-Keywords[ko]=XPS +X-KDE-Keywords[lv]=XPS X-KDE-Keywords[nb]=XPS X-KDE-Keywords[nds]=XPS X-KDE-Keywords[nl]=XPS @@ -158,6 +160,7 @@ X-KDE-Keywords[sr@ijekavian]=XPS,ИксПС X-KDE-Keywords[sr@ijekavianlatin]=XPS,XPS X-KDE-Keywords[sr@latin]=XPS,XPS X-KDE-Keywords[sv]=XPS +X-KDE-Keywords[tr]=XPS X-KDE-Keywords[uk]=XPS X-KDE-Keywords[x-test]=xxXPSxx X-KDE-Keywords[zh_CN]=mXPS 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-viewermode.rc b/part-viewermode.rc index dbd8e4215..bccd4912f 100644 --- a/part-viewermode.rc +++ b/part-viewermode.rc @@ -1,5 +1,5 @@ - + &File @@ -24,7 +24,6 @@ - diff --git a/part.cpp b/part.cpp index 4c6aac8ea..2ef39d8ed 100644 --- a/part.cpp +++ b/part.cpp @@ -86,6 +86,7 @@ #include "conf/preferencesdialog.h" #include "settings.h" #include "core/action.h" +#include "core/annotations.h" #include "core/bookmarkmanager.h" #include "core/document.h" #include "core/generator.h" @@ -151,8 +152,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 ) { @@ -259,13 +277,16 @@ static bool keepFileOpen() } #endif +int Okular::Part::numberOfParts = 0; + 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 ) { @@ -283,8 +304,13 @@ m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args } } Okular::Settings::instance( configFileName ); - - QDBusConnection::sessionBus().registerObject("/okular", this, QDBusConnection::ExportScriptableSlots); + + numberOfParts++; + if (numberOfParts == 1) { + QDBusConnection::sessionBus().registerObject("/okular", this, QDBusConnection::ExportScriptableSlots); + } else { + QDBusConnection::sessionBus().registerObject(QString("/okular%1").arg(numberOfParts), this, QDBusConnection::ExportScriptableSlots); + } // connect the started signal to tell the job the mimetypes we like, // and get some more information from it @@ -300,7 +326,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() ); @@ -421,6 +447,7 @@ m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args connect( m_findBar, SIGNAL(forwardKeyPressEvent(QKeyEvent*)), m_pageView, SLOT(externalKeyPressEvent(QKeyEvent*))); connect( m_miniBar, SIGNAL(forwardKeyPressEvent(QKeyEvent*)), m_pageView, SLOT(externalKeyPressEvent(QKeyEvent*))); + connect( m_pageView, SIGNAL(escPressed()), m_findBar, SLOT(resetSearch()) ); connect( m_pageNumberTool, SIGNAL(forwardKeyPressEvent(QKeyEvent*)), m_pageView, SLOT(externalKeyPressEvent(QKeyEvent*))); connect( m_reviewsWidget, SIGNAL(openAnnotationWindow(Okular::Annotation*,int)), @@ -764,14 +791,17 @@ void Part::setupActions() ac->addAction( "switch_blackscreen_mode", blackscreenAction ); blackscreenAction->setShortcut( QKeySequence( Qt::Key_B ) ); blackscreenAction->setIcon( KIcon( "view-presentation" ) ); + blackscreenAction->setEnabled( false ); KToggleAction *drawingAction = new KToggleAction( i18n( "Toggle Drawing Mode" ), ac ); ac->addAction( "presentation_drawing_mode", drawingAction ); drawingAction->setIcon( KIcon( "draw-freehand" ) ); + drawingAction->setEnabled( false ); KAction *eraseDrawingAction = new KAction( i18n( "Erase Drawings" ), ac ); ac->addAction( "presentation_erase_drawings", eraseDrawingAction ); eraseDrawingAction->setIcon( KIcon( "draw-eraser" ) ); + eraseDrawingAction->setEnabled( false ); } Part::~Part() @@ -780,7 +810,7 @@ Part::~Part() m_document->removeObserver( this ); if ( m_document->isOpened() ) - Part::closeUrl(); + Part::closeUrl( false ); delete m_toc; delete m_pageView; @@ -968,7 +998,7 @@ void Part::setWindowTitleFromDocument() { // If 'DocumentTitle' should be used, check if the document has one. If // either case is false, use the file name. - QString title = realUrl().fileName(); + QString title = Okular::Settings::displayDocumentNameOrPath() == Okular::Settings::EnumDisplayDocumentNameOrPath::Path ? realUrl().pathOrUrl() : realUrl().fileName(); if ( Okular::Settings::displayDocumentTitle() ) { @@ -1015,6 +1045,7 @@ void Part::notifySetup( const QVector< Okular::Page * > & /*pages*/, int setupFl rebuildBookmarkMenu(); updateAboutBackendAction(); + m_findBar->resetSearch(); m_searchWidget->setEnabled( m_document->supportsSearching() ); } @@ -1025,6 +1056,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; @@ -1154,20 +1188,27 @@ bool Part::openFile() mime = KMimeType::findByPath( fileNameToOpen ); } bool ok = false; + isDocumentArchive = false; if ( uncompressOk ) { if ( mime->is( "application/vnd.kde.okular-archive" ) ) + { ok = m_document->openDocumentArchive( fileNameToOpen, url() ); + isDocumentArchive = true; + } else + { ok = m_document->openDocument( fileNameToOpen, url(), mime ); + } } bool canSearch = m_document->supportsSearching(); // update one-time actions + emit enableCloseAction( ok ); m_find->setEnabled( ok && canSearch ); m_findNext->setEnabled( ok && canSearch ); m_findPrev->setEnabled( ok && canSearch ); - if( m_saveAs ) m_saveAs->setEnabled( ok && m_document->canSaveChanges() ); + if( m_saveAs ) m_saveAs->setEnabled( ok && (m_document->canSaveChanges() || isDocumentArchive) ); if( m_saveCopyAs ) m_saveCopyAs->setEnabled( ok ); emit enablePrintAction( ok && m_document->printingSupport() != Okular::Document::NoPrinting ); m_printPreview->setEnabled( ok && m_document->printingSupport() != Okular::Document::NoPrinting ); @@ -1214,6 +1255,7 @@ bool Part::openFile() // if can't open document, update windows so they display blank contents m_pageView->viewport()->update(); m_thumbnailList->update(); + setUrl( KUrl() ); return false; } @@ -1258,6 +1300,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 +1327,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 +1343,36 @@ bool Part::openUrl(const KUrl &_url) return openOk; } - -bool Part::closeUrl() +bool Part::queryClose() { + if ( !isReadWrite() || !isModified() ) + return true; + + 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 ); @@ -1307,6 +1380,7 @@ bool Part::closeUrl() } slotHidePresentation(); + emit enableCloseAction( false ); m_find->setEnabled( false ); m_findNext->setEnabled( false ); m_findPrev->setEnabled( false ); @@ -1358,17 +1432,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 +1552,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,30 +1948,79 @@ 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() { if ( m_embedMode == PrintPreviewMode ) return; + /* Show a warning before saving if the generator can't save annotations, + * unless we are going to save a .okular archive. */ + if ( !isDocumentArchive && !m_document->canSaveChanges( Document::SaveAnnotationsCapability ) ) + { + /* Search local annotations */ + bool containsLocalAnnotations = false; + const int pagecount = m_document->pages(); + + for ( int pageno = 0; pageno < pagecount; ++pageno ) + { + const Okular::Page *page = m_document->page( pageno ); + foreach ( const Okular::Annotation *ann, page->annotations() ) + { + if ( !(ann->flags() & Okular::Annotation::External) ) + { + containsLocalAnnotations = true; + break; + } + } + if ( containsLocalAnnotations ) + break; + } + + /* Don't show it if there are no local annotations */ + if ( containsLocalAnnotations ) + { + int res = KMessageBox::warningContinueCancel( widget(), "Your annotations will not be exported.\nYou can export the annotated document using File -> Export As -> Document Archive" ); + if ( res != KMessageBox::Continue ) + return; // Canceled + } + } + KUrl saveUrl = KFileDialog::getSaveUrl( KUrl("kfiledialog:///okular/" + url().fileName()), QString(), widget(), QString(), KFileDialog::ConfirmOverwrite ); 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(); QString errorText; - if ( !m_document->saveChanges( fileName, &errorText ) ) + bool saved; + + if ( isDocumentArchive ) + saved = m_document->saveDocumentArchive( fileName ); + else + saved = m_document->saveChanges( fileName, &errorText ); + + if ( !saved ) { if (errorText.isEmpty()) { @@ -1897,12 +2030,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; } @@ -2434,9 +2573,11 @@ void Part::unsetDummyMode() // add back and next in history m_historyBack = KStandardAction::documentBack( this, SLOT(slotHistoryBack()), actionCollection() ); m_historyBack->setWhatsThis( i18n( "Go to the place you were before" ) ); + connect(m_pageView, SIGNAL(mouseBackButtonClick()), m_historyBack, SLOT(trigger())); m_historyNext = KStandardAction::documentForward( this, SLOT(slotHistoryNext()), actionCollection()); m_historyNext->setWhatsThis( i18n( "Go to the place you were after" ) ); + connect(m_pageView, SIGNAL(mouseForwardButtonClick()), m_historyNext, SLOT(trigger())); m_pageView->setupActions( actionCollection() ); @@ -2577,6 +2718,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..adac19afc 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(); @@ -149,13 +150,20 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi void enablePrintAction(bool enable); void openSourceReference(const QString& absFileName, int line, int column); void viewerMenuStateChange(bool enabled); + void enableCloseAction(bool enable); 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 @@ -223,12 +231,15 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi void updateAboutBackendAction(); void unsetDummyMode(); void slotRenameBookmark( const DocumentViewport &viewport ); + + static int numberOfParts; KTemporaryFile *m_tempfile; // the document Okular::Document * m_document; QString m_temporaryLocalFile; + bool isDocumentArchive; // main widgets Sidebar *m_sidebar; @@ -319,6 +330,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/part.rc b/part.rc index 33d3829d7..39c1571e7 100644 --- a/part.rc +++ b/part.rc @@ -1,5 +1,5 @@ - + &File @@ -29,7 +29,6 @@ - diff --git a/shell/main.cpp b/shell/main.cpp index 0ded37614..226dab850 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "aboutdata.h" #include "shellutils.h" @@ -26,13 +27,15 @@ static bool attachUniqueInstance(KCmdLineArgs* args) return false; QDBusInterface iface("org.kde.okular", "/okular", "org.kde.okular"); - if (!iface.isValid()) + QDBusInterface iface2("org.kde.okular", "/okularshell", "org.kde.okular"); + if (!iface.isValid() || !iface2.isValid()) return false; if (args->isSet("page")) iface.call("openDocument", ShellUtils::urlFromArg(args->arg(0), ShellUtils::qfileExistFunc(), args->getOption("page")).url()); else iface.call("openDocument", ShellUtils::urlFromArg(args->arg(0), ShellUtils::qfileExistFunc()).url()); + iface2.call("tryRaise"); return true; } @@ -72,6 +75,12 @@ int main(int argc, char** argv) Shell* widget = new Shell(args); widget->show(); } + else if (args->isSet( "unique" ) && args->count() > 1) + { + QTextStream stream(stderr); + stream << i18n( "Error: Can't open more than one document with the --unique switch" ) << endl; + return -1; + } else { for (int i = 0; i < args->count(); ++i) diff --git a/shell/shell.cpp b/shell/shell.cpp index af4aa25f6..70e2e9934 100644 --- a/shell/shell.cpp +++ b/shell/shell.cpp @@ -42,6 +42,7 @@ #include #include #include +#include // local includes #include "kdocumentviewer.h" @@ -85,7 +86,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 @@ -101,13 +102,19 @@ void Shell::init() connect( this, SIGNAL(restoreDocument(KConfigGroup)),m_part, SLOT(restoreDocument(KConfigGroup))); connect( this, SIGNAL(saveDocumentRestoreInfo(KConfigGroup&)), m_part, SLOT(saveDocumentRestoreInfo(KConfigGroup&))); connect( m_part, SIGNAL(enablePrintAction(bool)), m_printAction, SLOT(setEnabled(bool))); + connect( m_part, SIGNAL(enableCloseAction(bool)), m_closeAction, SLOT(setEnabled(bool))); readSettings(); - if (m_args && m_args->isSet("unique") && m_args->count() == 1) + m_unique = false; + if (m_args && m_args->isSet("unique") && m_args->count() <= 1) { - QDBusConnection::sessionBus().registerService("org.kde.okular"); + m_unique = QDBusConnection::sessionBus().registerService("org.kde.okular"); + if (!m_unique) + KMessageBox::information(this, i18n("There is already a unique Okular instance running. This instance won't be the unique one.")); } + + QDBusConnection::sessionBus().registerObject("/okularshell", this, QDBusConnection::ExportScriptableSlots); if (m_openUrl.isValid()) QTimer::singleShot(0, this, SLOT(delayedOpen())); } @@ -117,10 +124,19 @@ void Shell::delayedOpen() openUrl( m_openUrl ); } +void Shell::showOpenRecentMenu() +{ + m_recent->menu()->popup(QCursor::pos()); +} + Shell::~Shell() { - if ( m_part ) writeSettings(); - delete m_part; + if ( m_part ) + { + writeSettings(); + m_part->closeUrl( false ); + } + m_part = 0; // It is deleted by the KPart/QObject machinery if ( m_args ) m_args->clear(); } @@ -129,20 +145,40 @@ void Shell::openUrl( const KUrl & url ) { if ( m_part ) { - if ( m_doc && m_args && m_args->isSet( "presentation" ) ) - m_doc->startPresentation(); - bool openOk = m_part->openUrl( url ); - const bool isstdin = url.fileName( KUrl::ObeyTrailingSlash ) == QLatin1String( "-" ); - if ( !isstdin ) + if( !m_part->url().isEmpty() ) { - if ( openOk ) - m_recent->addUrl( url ); + if( m_unique ) + { + KMessageBox::error(this, i18n("Can't open more than one document in the unique Okular instance.")); + } else - m_recent->removeUrl( url ); + { + Shell* newShell = new Shell(); + newShell->openUrl( url ); + newShell->show(); + } + } + else + { + if ( m_doc && m_args && m_args->isSet( "presentation" ) ) + m_doc->startPresentation(); + bool openOk = m_part->openUrl( url ); + const bool isstdin = url.fileName( KUrl::ObeyTrailingSlash ) == QLatin1String( "-" ); + if ( !isstdin ) + { + if ( openOk ) + m_recent->addUrl( url ); + else + m_recent->removeUrl( url ); + } } } } +void Shell::closeUrl() +{ + m_part->closeUrl(); +} void Shell::readSettings() { @@ -178,12 +214,13 @@ void Shell::setupActions() KStandardAction::open(this, SLOT(fileOpen()), actionCollection()); m_recent = KStandardAction::openRecent( this, SLOT(openUrl(KUrl)), actionCollection() ); m_recent->setToolBarMode( KRecentFilesAction::MenuMode ); - m_recent->setToolButtonPopupMode( QToolButton::DelayedPopup ); - connect( m_recent, SIGNAL(triggered()), this, SLOT(fileOpen()) ); + connect( m_recent, SIGNAL(triggered()), this, SLOT(showOpenRecentMenu()) ); m_recent->setToolTip( i18n("Click to open a file\nClick and hold to open a recent file") ); m_recent->setWhatsThis( i18n( "Click to open a file or Click and hold to select a recent file" ) ); m_printAction = KStandardAction::print( m_part, SLOT(slotPrint()), actionCollection() ); m_printAction->setEnabled( false ); + m_closeAction = KStandardAction::close( this, SLOT(closeUrl()), actionCollection() ); + m_closeAction->setEnabled( false ); KStandardAction::quit(this, SLOT(slotQuit()), actionCollection()); setStandardToolBarMenuEnabled(true); @@ -267,7 +304,9 @@ void Shell::fileOpen() return; KUrl url = dlg.selectedUrl(); if ( !url.isEmpty() ) + { openUrl( url ); + } } void Shell::slotQuit() @@ -275,6 +314,14 @@ void Shell::slotQuit() close(); } +void Shell::tryRaise() +{ + if (m_unique) + { + KWindowSystem::forceActiveWindow( window()->effectiveWinId() ); + } +} + // only called when starting the program void Shell::setFullScreen( bool useFullScreen ) { @@ -331,6 +378,11 @@ QSize Shell::sizeHint() const return QApplication::desktop()->availableGeometry( this ).size() * 0.75; } +bool Shell::queryClose() +{ + return m_part ? m_part->queryClose() : true; +} + #include "shell.moc" /* kate: replace-tabs on; indent-width 4; */ diff --git a/shell/shell.h b/shell/shell.h index 850e7e37f..393d6c625 100644 --- a/shell/shell.h +++ b/shell/shell.h @@ -18,6 +18,8 @@ #include +#include + class KCmdLineArgs; class KRecentFilesAction; class KToggleAction; @@ -36,6 +38,7 @@ class Part; class Shell : public KParts::MainWindow { Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.okular") public: /** @@ -51,6 +54,8 @@ public: QSize sizeHint() const; public slots: void slotQuit(); + + Q_SCRIPTABLE Q_NOREPLY void tryRaise(); protected: /** @@ -68,6 +73,7 @@ protected: void readSettings(); void writeSettings(); void setFullScreen( bool ); + bool queryClose(); void showEvent(QShowEvent *event); @@ -79,6 +85,8 @@ private slots: void openUrl( const KUrl & url ); void delayedOpen(); + void showOpenRecentMenu(); + void closeUrl(); signals: void restoreDocument(const KConfigGroup &group); @@ -92,15 +100,17 @@ private: private: KCmdLineArgs* m_args; - KParts::ReadOnlyPart* m_part; + KParts::ReadWritePart* m_part; KDocumentViewer* m_doc; KRecentFilesAction* m_recent; QStringList m_fileformats; bool m_fileformatsscanned; KAction* m_printAction; + KAction* m_closeAction; KToggleAction* m_fullScreenAction; KToggleAction* m_showMenuBarAction; bool m_menuBarWasShown, m_toolBarWasShown; + bool m_unique; KUrl m_openUrl; }; diff --git a/ui/annotationpopup.cpp b/ui/annotationpopup.cpp index 1c3a71714..26b285994 100644 --- a/ui/annotationpopup.cpp +++ b/ui/annotationpopup.cpp @@ -18,9 +18,11 @@ #include "core/document.h" #include "guiutils.h" -AnnotationPopup::AnnotationPopup( Okular::Document *document, +Q_DECLARE_METATYPE( AnnotationPopup::AnnotPagePair ) + +AnnotationPopup::AnnotationPopup( Okular::Document *document, MenuMode mode, QWidget *parent ) - : mParent( parent ), mDocument( document ) + : mParent( parent ), mDocument( document ), mMenuMode( mode ) { } @@ -38,57 +40,114 @@ void AnnotationPopup::exec( const QPoint &point ) KMenu menu( mParent ); - QAction *popoutWindow = 0; - QAction *deleteNote = 0; - QAction *showProperties = 0; - QAction *saveAttachment = 0; + QAction *action = 0; Okular::FileAttachmentAnnotation *fileAttachAnnot = 0; - const bool onlyOne = mAnnotations.count() == 1; + const char *actionTypeId = "actionType"; - menu.addTitle( i18np( "Annotation", "%1 Annotations", mAnnotations.count() ) ); - popoutWindow = menu.addAction( KIcon( "comment" ), i18n( "&Open Pop-up Note" ) ); - popoutWindow->setEnabled( onlyOne ); - deleteNote = menu.addAction( KIcon( "list-remove" ), i18n( "&Delete" ) ); - deleteNote->setEnabled( mDocument->isAllowed( Okular::AllowNotes ) ); + const QString openId = QString::fromLatin1( "open" ); + const QString deleteId = QString::fromLatin1( "delete" ); + const QString deleteAllId = QString::fromLatin1( "deleteAll" ); + const QString propertiesId = QString::fromLatin1( "properties" ); + const QString saveId = QString::fromLatin1( "save" ); - const AnnotPagePair &firstAnnotPagePair = mAnnotations.at(0); - foreach ( const AnnotPagePair& pair, mAnnotations ) + if ( mMenuMode == SingleAnnotationMode ) { - if ( !mDocument->canRemovePageAnnotation(pair.annotation) ) - deleteNote->setEnabled( false ); + const bool onlyOne = (mAnnotations.count() == 1); + + const AnnotPagePair &pair = mAnnotations.at(0); + + menu.addTitle( i18np( "Annotation", "%1 Annotations", mAnnotations.count() ) ); + + action = menu.addAction( KIcon( "comment" ), i18n( "&Open Pop-up Note" ) ); + action->setData( QVariant::fromValue( pair ) ); + action->setEnabled( onlyOne ); + action->setProperty( actionTypeId, openId ); + + action = menu.addAction( KIcon( "list-remove" ), i18n( "&Delete" ) ); + action->setEnabled( mDocument->isAllowed( Okular::AllowNotes ) ); + action->setProperty( actionTypeId, deleteAllId ); + + foreach ( const AnnotPagePair& pair, mAnnotations ) + { + if ( !mDocument->canRemovePageAnnotation( pair.annotation ) ) + action->setEnabled( false ); + } + + action = menu.addAction( KIcon( "configure" ), i18n( "&Properties" ) ); + action->setData( QVariant::fromValue( pair ) ); + action->setEnabled( onlyOne ); + action->setProperty( actionTypeId, propertiesId ); + + if ( onlyOne && pair.annotation->subType() == Okular::Annotation::AFileAttachment ) + { + menu.addSeparator(); + fileAttachAnnot = static_cast< Okular::FileAttachmentAnnotation * >( pair.annotation ); + const QString saveText = i18nc( "%1 is the name of the file to save", "&Save '%1'...", fileAttachAnnot->embeddedFile()->name() ); + + action = menu.addAction( KIcon( "document-save" ), saveText ); + action->setData( QVariant::fromValue( pair ) ); + action->setProperty( actionTypeId, saveId ); + } } - - showProperties = menu.addAction( KIcon( "configure" ), i18n( "&Properties" ) ); - showProperties->setEnabled( onlyOne ); - - if ( onlyOne && firstAnnotPagePair.annotation->subType() == Okular::Annotation::AFileAttachment ) + else { - menu.addSeparator(); - fileAttachAnnot = static_cast< Okular::FileAttachmentAnnotation * >( firstAnnotPagePair.annotation ); - const QString saveText = i18nc( "%1 is the name of the file to save", "&Save '%1'...", fileAttachAnnot->embeddedFile()->name() ); - saveAttachment = menu.addAction( KIcon( "document-save" ), saveText ); + foreach ( const AnnotPagePair& pair, mAnnotations ) + { + menu.addTitle( GuiUtils::captionForAnnotation( pair.annotation ) ); + + action = menu.addAction( KIcon( "comment" ), i18n( "&Open Pop-up Note" ) ); + action->setData( QVariant::fromValue( pair ) ); + action->setProperty( actionTypeId, openId ); + + action = menu.addAction( KIcon( "list-remove" ), i18n( "&Delete" ) ); + action->setEnabled( mDocument->isAllowed( Okular::AllowNotes ) && + mDocument->canRemovePageAnnotation( pair.annotation ) ); + action->setData( QVariant::fromValue( pair ) ); + action->setProperty( actionTypeId, deleteId ); + + action = menu.addAction( KIcon( "configure" ), i18n( "&Properties" ) ); + action->setData( QVariant::fromValue( pair ) ); + action->setProperty( actionTypeId, propertiesId ); + + if ( pair.annotation->subType() == Okular::Annotation::AFileAttachment ) + { + menu.addSeparator(); + fileAttachAnnot = static_cast< Okular::FileAttachmentAnnotation * >( pair.annotation ); + const QString saveText = i18nc( "%1 is the name of the file to save", "&Save '%1'...", fileAttachAnnot->embeddedFile()->name() ); + + action = menu.addAction( KIcon( "document-save" ), saveText ); + action->setData( QVariant::fromValue( pair ) ); + action->setProperty( actionTypeId, saveId ); + } + } } QAction *choice = menu.exec( point.isNull() ? QCursor::pos() : point ); // check if the user really selected an action if ( choice ) { - if ( choice == popoutWindow ) { - emit openAnnotationWindow( firstAnnotPagePair.annotation, firstAnnotPagePair.pageNumber ); - } else if( choice == deleteNote ) { + const AnnotPagePair pair = choice->data().value(); + + const QString actionType = choice->property( actionTypeId ).toString(); + if ( actionType == openId ) { + emit openAnnotationWindow( pair.annotation, pair.pageNumber ); + } else if( actionType == deleteId ) { + if ( pair.pageNumber != -1 ) + mDocument->removePageAnnotation( pair.pageNumber, pair.annotation ); + } else if( actionType == deleteAllId ) { Q_FOREACH ( const AnnotPagePair& pair, mAnnotations ) { if ( pair.pageNumber != -1 ) mDocument->removePageAnnotation( pair.pageNumber, pair.annotation ); } - } else if( choice == showProperties ) { - if ( firstAnnotPagePair.pageNumber != -1 ) { - AnnotsPropertiesDialog propdialog( mParent, mDocument, firstAnnotPagePair.pageNumber, firstAnnotPagePair.annotation ); + } else if( actionType == propertiesId ) { + if ( pair.pageNumber != -1 ) { + AnnotsPropertiesDialog propdialog( mParent, mDocument, pair.pageNumber, pair.annotation ); propdialog.exec(); } - } else if( choice == saveAttachment ) { - Q_ASSERT( fileAttachAnnot ); + } else if( actionType == saveId ) { + const Okular::FileAttachmentAnnotation * fileAttachAnnot = static_cast< Okular::FileAttachmentAnnotation * >( pair.annotation ); GuiUtils::saveEmbeddedFile( fileAttachAnnot->embeddedFile(), mParent ); } } diff --git a/ui/annotationpopup.h b/ui/annotationpopup.h index e74284c5d..71946050d 100644 --- a/ui/annotationpopup.h +++ b/ui/annotationpopup.h @@ -25,8 +25,16 @@ class AnnotationPopup : public QObject Q_OBJECT public: - explicit AnnotationPopup( Okular::Document *document, - QWidget *parent = 0 ); + /** + * Describes the structure of the popup menu. + */ + enum MenuMode + { + SingleAnnotationMode, ///< The menu shows only entries to manipulate a single annotation, or multiple annotations as a group. + MultiAnnotationMode ///< The menu shows entries to manipulate multiple annotations. + }; + + AnnotationPopup( Okular::Document *document, MenuMode mode, QWidget *parent = 0 ); void addAnnotation( Okular::Annotation* annotation, int pageNumber ); @@ -35,9 +43,11 @@ class AnnotationPopup : public QObject Q_SIGNALS: void openAnnotationWindow( Okular::Annotation *annotation, int pageNumber ); - private: - QWidget *mParent; + public: struct AnnotPagePair { + AnnotPagePair() : annotation( 0 ), pageNumber( -1 ) + { } + AnnotPagePair( Okular::Annotation *a, int pn ) : annotation( a ), pageNumber( pn ) { } @@ -50,8 +60,13 @@ class AnnotationPopup : public QObject Okular::Annotation* annotation; int pageNumber; }; + + private: + QWidget *mParent; + QList< AnnotPagePair > mAnnotations; Okular::Document *mDocument; + MenuMode mMenuMode; }; diff --git a/ui/annotationtools.cpp b/ui/annotationtools.cpp index cfa47b5b6..40fa6fe6b 100644 --- a/ui/annotationtools.cpp +++ b/ui/annotationtools.cpp @@ -50,6 +50,10 @@ AnnotatorEngine::~AnnotatorEngine() { } +SmoothPath::SmoothPath( const QLinkedList &points, const QPen &pen ) + : points ( points ), pen ( pen ) +{ +} /** SmoothPathEngine */ SmoothPathEngine::SmoothPathEngine( const QDomElement & engineElement ) @@ -112,12 +116,20 @@ QRect SmoothPathEngine::event( EventType type, Button button, double nX, double } void SmoothPathEngine::paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ ) +{ + // use engine's color for painting + const SmoothPath path( points, QPen(m_engineColor, 1) ); + + // draw the path + path.paint( painter, xScale, yScale ); +} + +void SmoothPath::paint( QPainter * painter, double xScale, double yScale ) const { // draw SmoothPaths with at least 2 points if ( points.count() > 1 ) { - // use engine's color for painting - painter->setPen( QPen( m_engineColor, 1 ) ); + painter->setPen( pen ); QLinkedList::const_iterator pIt = points.begin(), pEnd = points.end(); Okular::NormalizedPoint pA = *pIt; @@ -173,3 +185,17 @@ QList< Okular::Annotation* > SmoothPathEngine::end() return QList< Okular::Annotation* >() << ann; } +SmoothPath SmoothPathEngine::endSmoothPath() +{ + m_creationCompleted = false; + + double width = 1; + if ( m_annotElement.hasAttribute( "width" ) ) + width = m_annotElement.attribute( "width" ).toDouble(); + + QColor color( m_annotElement.hasAttribute( "color" ) ? + m_annotElement.attribute( "color" ) : m_engineColor ); + + return SmoothPath( points, QPen(color, width) ); +} + diff --git a/ui/annotationtools.h b/ui/annotationtools.h index 600de6c6d..7107042e0 100644 --- a/ui/annotationtools.h +++ b/ui/annotationtools.h @@ -12,6 +12,7 @@ #include #include +#include #include #include "core/area.h" @@ -64,6 +65,17 @@ class AnnotatorEngine PageViewItem * m_item; }; +class SmoothPath +{ + public: + SmoothPath( const QLinkedList &points, const QPen &pen ); + void paint( QPainter * painter, double xScale, double yScale ) const; + + private: + const QLinkedList points; + const QPen pen; +}; + /** @short SmoothPathEngine */ class SmoothPathEngine : public AnnotatorEngine @@ -75,7 +87,9 @@ class SmoothPathEngine void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ ); + // These are two alternative ways to get the resulting path. Don't call them both! QList< Okular::Annotation* > end(); + SmoothPath endSmoothPath(); private: // data diff --git a/ui/annotwindow.cpp b/ui/annotwindow.cpp index dc84b6f7e..c1bafb9c4 100644 --- a/ui/annotwindow.cpp +++ b/ui/annotwindow.cpp @@ -202,9 +202,6 @@ AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular:: if (!canEditAnnotation) textEdit->setReadOnly(true); - m_latexRenderer = new GuiUtils::LatexRenderer(); - emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( GuiUtils::contents( m_annot ) ) ); - QVBoxLayout * mainlay = new QVBoxLayout( this ); mainlay->setMargin( 2 ); mainlay->setSpacing( 0 ); @@ -217,6 +214,9 @@ AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular:: QSizeGrip * sb = new QSizeGrip( this ); lowerlay->addWidget( sb ); + m_latexRenderer = new GuiUtils::LatexRenderer(); + emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( GuiUtils::contents( m_annot ) ) ); + m_title->setTitle( m_annot->window().summary() ); m_title->connectOptionButton( this, SLOT(slotOptionBtn()) ); diff --git a/ui/bookmarklist.cpp b/ui/bookmarklist.cpp index 034afa4a4..e1b386904 100644 --- a/ui/bookmarklist.cpp +++ b/ui/bookmarklist.cpp @@ -62,9 +62,7 @@ class BookmarkItem : public QTreeWidgetItem if ( other.type() == BookmarkItemType ) { const BookmarkItem *cmp = static_cast< const BookmarkItem* >( &other ); - const int v = m_viewport.pageNumber - cmp->m_viewport.pageNumber; - if ( v != 0 ) - return v < 0; + return m_viewport < cmp->m_viewport; } return QTreeWidgetItem::operator<( other ); } @@ -229,7 +227,8 @@ void BookmarkList::slotChanged( QTreeWidgetItem * item ) FileItem* fItem = dynamic_cast( item ); if ( fItem ) { - m_document->bookmarkManager()->renameBookmark( m_document->currentDocument(), fItem->text( 0 ) ); + const KUrl url = fItem->data( 0, UrlRole ).value< KUrl >(); + m_document->bookmarkManager()->renameBookmark( url, fItem->text( 0 ) ); m_document->bookmarkManager()->save(); } } diff --git a/ui/data/sources/tool-ellipse-okular.svgz b/ui/data/sources/tool-ellipse-okular.svgz new file mode 100644 index 000000000..a880e3ab6 Binary files /dev/null and b/ui/data/sources/tool-ellipse-okular.svgz differ diff --git a/ui/data/sources/tool-highlighter-okular.svgz b/ui/data/sources/tool-highlighter-okular.svgz new file mode 100644 index 000000000..a0e71d09a Binary files /dev/null and b/ui/data/sources/tool-highlighter-okular.svgz differ diff --git a/ui/data/sources/tool-ink-okular.svgz b/ui/data/sources/tool-ink-okular.svgz new file mode 100644 index 000000000..035b5fdd7 Binary files /dev/null and b/ui/data/sources/tool-ink-okular.svgz differ diff --git a/ui/data/sources/tool-line-okular.svgz b/ui/data/sources/tool-line-okular.svgz new file mode 100644 index 000000000..71594e326 Binary files /dev/null and b/ui/data/sources/tool-line-okular.svgz differ diff --git a/ui/data/sources/tool-note-inline-okular.svgz b/ui/data/sources/tool-note-inline-okular.svgz new file mode 100644 index 000000000..4da04f3ee Binary files /dev/null and b/ui/data/sources/tool-note-inline-okular.svgz differ diff --git a/ui/data/sources/tool-note-okular.svgz b/ui/data/sources/tool-note-okular.svgz new file mode 100644 index 000000000..40bd0c13f Binary files /dev/null and b/ui/data/sources/tool-note-okular.svgz differ diff --git a/ui/data/sources/tool-polygon-okular.svgz b/ui/data/sources/tool-polygon-okular.svgz new file mode 100644 index 000000000..dac8f0e57 Binary files /dev/null and b/ui/data/sources/tool-polygon-okular.svgz differ diff --git a/ui/data/sources/tool-stamp-okular.svgz b/ui/data/sources/tool-stamp-okular.svgz new file mode 100644 index 000000000..89b8ecc86 Binary files /dev/null and b/ui/data/sources/tool-stamp-okular.svgz differ diff --git a/ui/data/sources/tool-underline-okular.svgz b/ui/data/sources/tool-underline-okular.svgz new file mode 100644 index 000000000..3aa9201d0 Binary files /dev/null and b/ui/data/sources/tool-underline-okular.svgz differ diff --git a/ui/fileprinterpreview.cpp b/ui/fileprinterpreview.cpp index b19dd0768..85fa32aeb 100644 --- a/ui/fileprinterpreview.cpp +++ b/ui/fileprinterpreview.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -128,6 +129,7 @@ FilePrinterPreview::FilePrinterPreview( const QString &filename, QWidget *parent // Set up the dialog setCaption(i18n("Print Preview")); setButtons(KDialog::Close); + button(KDialog::Close)->setAutoDefault(false); restoreDialogSize(d->config->group("Print Preview")); } diff --git a/ui/findbar.cpp b/ui/findbar.cpp index 745376ea1..f00f8833b 100644 --- a/ui/findbar.cpp +++ b/ui/findbar.cpp @@ -149,6 +149,11 @@ void FindBar::findPrev() m_search->lineEdit()->findPrev(); } +void FindBar::resetSearch() +{ + m_search->lineEdit()->resetSearch(); +} + void FindBar::caseSensitivityChanged() { m_search->lineEdit()->setSearchCaseSensitivity( m_caseSensitiveAct->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive ); diff --git a/ui/findbar.h b/ui/findbar.h index 2ee712f25..4db285572 100644 --- a/ui/findbar.h +++ b/ui/findbar.h @@ -40,6 +40,7 @@ class FindBar public slots: void findNext(); void findPrev(); + void resetSearch(); private slots: void caseSensitivityChanged(); diff --git a/ui/guiutils.cpp b/ui/guiutils.cpp index f66dc4792..30be525ce 100644 --- a/ui/guiutils.cpp +++ b/ui/guiutils.cpp @@ -99,6 +99,12 @@ QString captionForAnnotation( const Okular::Annotation * ann ) case Okular::Annotation::AMovie: ret = i18n( "Movie" ); break; + case Okular::Annotation::AScreen: + ret = i18nc( "Caption for a screen annotation", "Screen" ); + break; + case Okular::Annotation::AWidget: + ret = i18nc( "Caption for a widget annotation", "Widget" ); + break; case Okular::Annotation::A_BASE: break; } diff --git a/ui/minibar.cpp b/ui/minibar.cpp index 051df720e..b9e12916d 100644 --- a/ui/minibar.cpp +++ b/ui/minibar.cpp @@ -42,7 +42,6 @@ class HoverButton : public QToolButton MiniBarLogic::MiniBarLogic( QObject * parent, Okular::Document * document ) : QObject(parent) , m_document( document ) - , m_currentPage( -1 ) { } @@ -68,7 +67,7 @@ Okular::Document *MiniBarLogic::document() const int MiniBarLogic::currentPage() const { - return m_currentPage; + return m_document->currentPage(); } void MiniBarLogic::notifySetup( const QVector< Okular::Page * > & pageVector, int setupFlags ) @@ -81,7 +80,6 @@ void MiniBarLogic::notifySetup( const QVector< Okular::Page * > & pageVector, in const int pages = pageVector.count(); if ( pages < 1 ) { - m_currentPage = -1; foreach ( MiniBar *miniBar, m_miniBars ) { miniBar->setEnabled( false ); @@ -124,24 +122,24 @@ void MiniBarLogic::notifySetup( const QVector< Okular::Page * > & pageVector, in } } -void MiniBarLogic::notifyViewportChanged( bool /*smoothMove*/ ) +void MiniBarLogic::notifyCurrentPageChanged( int previousPage, int currentPage ) { + Q_UNUSED( previousPage ) + // get current page number - const int page = m_document->viewport().pageNumber; const int pages = m_document->pages(); // if the document is opened and page is changed - if ( page != m_currentPage && pages > 0 ) + if ( pages > 0 ) { - m_currentPage = page; - const QString pageNumber = QString::number( page + 1 ); - const QString pageLabel = m_document->page(page)->label(); - + const QString pageNumber = QString::number( currentPage + 1 ); + const QString pageLabel = m_document->page( currentPage )->label(); + foreach ( MiniBar *miniBar, m_miniBars ) { // update prev/next button state - miniBar->m_prevButton->setEnabled( page > 0 ); - miniBar->m_nextButton->setEnabled( page < ( pages - 1 ) ); + miniBar->m_prevButton->setEnabled( currentPage > 0 ); + miniBar->m_nextButton->setEnabled( currentPage < ( pages - 1 ) ); // update text on widgets miniBar->m_pageNumberEdit->setText( pageNumber ); miniBar->m_pageNumberLabel->setText( pageNumber ); @@ -292,7 +290,7 @@ void MiniBar::resizeForPage( int pages ) ProgressWidget::ProgressWidget( QWidget * parent, Okular::Document * document ) : QWidget( parent ), m_document( document ), - m_currentPage( -1 ), m_progressPercentage( -1 ) + m_progressPercentage( -1 ) { setObjectName( QLatin1String( "progress" ) ); setAttribute( Qt::WA_OpaquePaintEvent, true ); @@ -305,18 +303,18 @@ ProgressWidget::~ProgressWidget() m_document->removeObserver( this ); } -void ProgressWidget::notifyViewportChanged( bool /*smoothMove*/ ) +void ProgressWidget::notifyCurrentPageChanged( int previousPage, int currentPage ) { + Q_UNUSED( previousPage ) + // get current page number - int page = m_document->viewport().pageNumber; int pages = m_document->pages(); // if the document is opened and page is changed - if ( page != m_currentPage && pages > 0 ) + if ( pages > 0 ) { // update percentage - m_currentPage = page; - float percentage = pages < 2 ? 1.0 : (float)page / (float)(pages - 1); + const float percentage = pages < 2 ? 1.0 : (float)currentPage / (float)(pages - 1); setProgress( percentage ); } } @@ -332,7 +330,7 @@ void ProgressWidget::slotGotoNormalizedPage( float index ) // figure out page number and go to that page int number = (int)( index * (float)m_document->pages() ); if ( number >= 0 && number < (int)m_document->pages() && - number != m_currentPage ) + number != (int)m_document->currentPage() ) m_document->setViewportPage( number ); } diff --git a/ui/minibar.h b/ui/minibar.h index acb1163be..a0c0514d0 100644 --- a/ui/minibar.h +++ b/ui/minibar.h @@ -90,12 +90,11 @@ class MiniBarLogic : public QObject, public Okular::DocumentObserver // [INHERITED] from DocumentObserver uint observerId() const { return MINIBAR_ID; } void notifySetup( const QVector< Okular::Page * > & pages, int setupFlags ); - void notifyViewportChanged( bool smoothMove ); + void notifyCurrentPageChanged( int previous, int current ); private: QSet m_miniBars; Okular::Document * m_document; - int m_currentPage; }; /** @@ -147,7 +146,7 @@ class ProgressWidget : public QWidget, public Okular::DocumentObserver // [INHERITED] from DocumentObserver uint observerId() const { return PROGRESSWIDGET_ID; } - void notifyViewportChanged( bool smoothMove ); + void notifyCurrentPageChanged( int previous, int current ); void slotGotoNormalizedPage( float index ); @@ -165,7 +164,6 @@ class ProgressWidget : public QWidget, public Okular::DocumentObserver private: Okular::Document * m_document; - int m_currentPage; float m_progressPercentage; }; diff --git a/ui/pagesizelabel.cpp b/ui/pagesizelabel.cpp index 4a8077924..aa4a043b8 100644 --- a/ui/pagesizelabel.cpp +++ b/ui/pagesizelabel.cpp @@ -13,7 +13,7 @@ PageSizeLabel::PageSizeLabel( QWidget * parent, Okular::Document * document ) : QLabel( parent ), m_document( document ), - m_currentPage( -1 ), m_antiWidget( NULL ) + m_antiWidget( NULL ) { } @@ -55,20 +55,17 @@ void PageSizeLabel::notifySetup( const QVector< Okular::Page * > & pageVector, i } } -void PageSizeLabel::notifyViewportChanged( bool /*smoothMove*/ ) +void PageSizeLabel::notifyCurrentPageChanged( int previousPage, int currentPage ) { + Q_UNUSED( previousPage ) + if (isVisible()) { - // get current page number - int page = m_document->viewport().pageNumber; - int pages = m_document->pages(); - - // if the document is opened and page is changed - if ( page != m_currentPage && pages > 0 ) + // if the document is opened + if ( m_document->pages() > 0 ) { - m_currentPage = page; - setText( m_document->pageSizeString(page) ); - m_antiWidget->setFixedSize(sizeHint()); + setText( m_document->pageSizeString( currentPage ) ); + m_antiWidget->setFixedSize( sizeHint() ); } } } diff --git a/ui/pagesizelabel.h b/ui/pagesizelabel.h index ea508b87c..7c4a1e2fc 100644 --- a/ui/pagesizelabel.h +++ b/ui/pagesizelabel.h @@ -34,11 +34,10 @@ class PageSizeLabel : public QLabel, public Okular::DocumentObserver // [INHERITED] from DocumentObserver uint observerId() const { return PAGESIZELABEL_ID; } void notifySetup( const QVector< Okular::Page * > & pages, int setupFlags ); - void notifyViewportChanged( bool smoothMove ); + void notifyCurrentPageChanged( int previous, int current ); private: Okular::Document * m_document; - int m_currentPage; QWidget *m_antiWidget; }; diff --git a/ui/pageview.cpp b/ui/pageview.cpp index 3f59d3087..2ef10a1e2 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -193,7 +193,6 @@ public: KAction * aZoomOut; KToggleAction * aZoomFitWidth; KToggleAction * aZoomFitPage; - KToggleAction * aZoomFitText; KActionMenu * aViewMode; KToggleAction * aViewContinuous; QAction * aPrevAction; @@ -301,7 +300,6 @@ PageView::PageView( QWidget *parent, Okular::Document *document ) d->aToggleAnnotator = 0; d->aZoomFitWidth = 0; d->aZoomFitPage = 0; - d->aZoomFitText = 0; d->aViewMode = 0; d->aViewContinuous = 0; d->aPrevAction = 0; @@ -458,12 +456,6 @@ void PageView::setupViewerActions( KActionCollection * ac ) ac->addAction("view_fit_to_page", d->aZoomFitPage ); connect( d->aZoomFitPage, SIGNAL(toggled(bool)), SLOT(slotFitToPageToggled(bool)) ); -/* - d->aZoomFitText = new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Text"), this); - ac->addAction("zoom_fit_text", d->aZoomFitText ); - connect( d->aZoomFitText, SIGNAL(toggled(bool)), SLOT(slotFitToTextToggled(bool)) ); -*/ - // View-Layout actions d->aViewMode = new KActionMenu( KIcon( "view-split-left-right" ), i18n( "&View Mode" ), this ); d->aViewMode->setDelayed( false ); @@ -587,16 +579,28 @@ void PageView::setupActions( KActionCollection * ac ) // Other actions KAction * su = new KAction(i18n("Scroll Up"), this); ac->addAction("view_scroll_up", su ); - connect( su, SIGNAL(triggered()), this, SLOT(slotScrollUp()) ); + connect( su, SIGNAL(triggered()), this, SLOT(slotAutoScrollUp()) ); su->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_Up) ); addAction(su); KAction * sd = new KAction(i18n("Scroll Down"), this); ac->addAction("view_scroll_down", sd ); - connect( sd, SIGNAL(triggered()), this, SLOT(slotScrollDown()) ); + connect( sd, SIGNAL(triggered()), this, SLOT(slotAutoScrollDown()) ); sd->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_Down) ); addAction(sd); + KAction * spu = new KAction(i18n("Scroll Page Up"), this); + ac->addAction( "view_scroll_page_up", spu ); + connect( spu, SIGNAL(triggered()), this, SLOT(slotScrollUp()) ); + spu->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_Space) ); + addAction( spu ); + + KAction * spd = new KAction(i18n("Scroll Page Down"), this); + ac->addAction( "view_scroll_page_down", spd ); + connect( spd, SIGNAL(triggered()), this, SLOT(slotScrollDown()) ); + spd->setShortcut( QKeySequence(Qt::Key_Space) ); + addAction( spd ); + d->aToggleForms = new KAction( this ); ac->addAction( "view_toggle_forms", d->aToggleForms ); connect( d->aToggleForms, SIGNAL(triggered()), this, SLOT(slotToggleForms()) ); @@ -616,7 +620,6 @@ void PageView::fitPageWidth( int page ) Okular::Settings::setViewMode( 0 ); d->aZoomFitWidth->setChecked( true ); d->aZoomFitPage->setChecked( false ); -// d->aZoomFitText->setChecked( false ); d->aViewMode->menu()->actions().at( 0 )->setChecked( true ); viewport()->setUpdatesEnabled( false ); slotRelayoutPages(); @@ -860,7 +863,7 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setup Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a ); VideoWidget * vw = new VideoWidget( movieAnn, d->document, viewport() ); item->videoWidgets().insert( movieAnn->movie(), vw ); - vw->hide(); + vw->pageInitialized(); } } } @@ -937,8 +940,6 @@ void PageView::updateActionState( bool haspages, bool documentChanged, bool hasf d->aZoomFitWidth->setEnabled( haspages ); if ( d->aZoomFitPage ) d->aZoomFitPage->setEnabled( haspages ); - if ( d->aZoomFitText ) - d->aZoomFitText->setEnabled( haspages ); if ( d->aZoom ) { @@ -1231,6 +1232,30 @@ bool PageView::canUnloadPixmap( int pageNumber ) const // if hidden premit unloading return true; } + +void PageView::notifyCurrentPageChanged( int previous, int current ) +{ + if ( previous != -1 ) + { + PageViewItem * item = d->items.at( previous ); + if ( item ) + { + Q_FOREACH ( VideoWidget *videoWidget, item->videoWidgets() ) + videoWidget->pageLeft(); + } + } + + if ( current != -1 ) + { + PageViewItem * item = d->items.at( current ); + if ( item ) + { + Q_FOREACH ( VideoWidget *videoWidget, item->videoWidgets() ) + videoWidget->pageEntered(); + } + } +} + //END DocumentObserver inherited methods //BEGIN View inherited methods @@ -1567,67 +1592,46 @@ void PageView::keyPressEvent( QKeyEvent * e ) case Qt::Key_K: case Qt::Key_Down: case Qt::Key_PageDown: - case Qt::Key_Space: case Qt::Key_Up: case Qt::Key_PageUp: case Qt::Key_Backspace: if ( e->key() == Qt::Key_Down || e->key() == Qt::Key_PageDown - || e->key() == Qt::Key_J - || ( e->key() == Qt::Key_Space && ( e->modifiers() & Qt::ShiftModifier ) != Qt::ShiftModifier ) ) + || e->key() == Qt::Key_J ) { - // if in single page mode and at the bottom of the screen, go to next page - if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() < verticalScrollBar()->maximum() ) - { - if ( e->key() == Qt::Key_Down || e->key() == Qt::Key_J ) - verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd ); - else - verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepAdd ); - } - else if ( (int)d->document->currentPage() < d->items.count() - 1 ) - { - // more optimized than document->setNextPage and then move view to top - Okular::DocumentViewport newViewport = d->document->viewport(); - newViewport.pageNumber += viewColumns(); - if ( newViewport.pageNumber >= (int)d->items.count() ) - newViewport.pageNumber = d->items.count() - 1; - newViewport.rePos.enabled = true; - newViewport.rePos.normalizedY = 0.0; - d->document->setViewport( newViewport ); - } + bool singleStep = e->key() == Qt::Key_Down || e->key() == Qt::Key_J; + slotScrollDown( singleStep ); } else { - // if in single page mode and at the top of the screen, go to \ page - if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() > verticalScrollBar()->minimum() ) - { - if ( e->key() == Qt::Key_Up || e->key() == Qt::Key_K ) - verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub ); - else - verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepSub ); - } - else if ( d->document->currentPage() > 0 ) - { - // more optimized than document->setPrevPage and then move view to bottom - Okular::DocumentViewport newViewport = d->document->viewport(); - newViewport.pageNumber -= viewColumns(); - if ( newViewport.pageNumber < 0 ) - newViewport.pageNumber = 0; - newViewport.rePos.enabled = true; - newViewport.rePos.normalizedY = 1.0; - d->document->setViewport( newViewport ); - } + bool singleStep = e->key() == Qt::Key_Up || e->key() == Qt::Key_K; + slotScrollUp( singleStep ); } break; case Qt::Key_Left: case Qt::Key_H: - horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub ); + if ( horizontalScrollBar()->maximum() == 0 ) + { + //if we cannot scroll we go to the previous page vertically + int next_page = d->document->currentPage() - viewColumns(); + d->document->setViewportPage(next_page); + } + else + horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub ); break; case Qt::Key_Right: case Qt::Key_L: - horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd ); + if ( horizontalScrollBar()->maximum() == 0 ) + { + //if we cannot scroll we advance the page vertically + int next_page = d->document->currentPage() + viewColumns(); + d->document->setViewportPage(next_page); + } + else + horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd ); break; case Qt::Key_Escape: + emit escPressed(); selectionClear( d->tableDividersGuessed ? ClearOnlyDividers : ClearAllSelection ); d->mousePressPos = QPoint(); if ( d->aPrevAction ) @@ -1849,7 +1853,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) d->aMouseSelect->trigger(); QPoint newPos = eventPos + QPoint( deltaX, deltaY ); selectionStart( newPos, palette().color( QPalette::Active, QPalette::Highlight ).light( 120 ), false ); - selectionEndPoint( eventPos ); + updateSelection( eventPos ); break; } } @@ -1865,7 +1869,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) case Okular::Settings::EnumMouseMode::TableSelect: // set second corner of selection if ( d->mouseSelecting ) - selectionEndPoint( eventPos ); + updateSelection( eventPos ); break; case Okular::Settings::EnumMouseMode::TextSelect: // if mouse moves 5 px away from the press point and the document soupports text extraction, do 'textselection' @@ -1873,27 +1877,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) { d->mouseTextSelecting = true; } - if ( d->mouseTextSelecting ) - { - int first = -1; - QList< Okular::RegularAreaRect * > selections = textSelections( eventPos, d->mouseSelectPos, first ); - QSet< int > pagesWithSelectionSet; - for ( int i = 0; i < selections.count(); ++i ) - pagesWithSelectionSet.insert( i + first ); - - QSet< int > noMoreSelectedPages = d->pagesWithTextSelection - pagesWithSelectionSet; - // clear the selection from pages not selected anymore - foreach( int p, noMoreSelectedPages ) - { - d->document->setPageTextSelection( p, 0, QColor() ); - } - // set the new selection for the selected pages - foreach( int p, pagesWithSelectionSet ) - { - d->document->setPageTextSelection( p, selections[ p - first ], palette().color( QPalette::Active, QPalette::Highlight ) ); - } - d->pagesWithTextSelection = pagesWithSelectionSet; - } + updateSelection( eventPos ); updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); break; } @@ -1935,6 +1919,18 @@ void PageView::mousePressEvent( QMouseEvent * e ) return; } + // trigger history navigation for additional mouse buttons + if ( e->button() == Qt::XButton1 ) + { + emit mouseBackButtonClick(); + return; + } + if ( e->button() == Qt::XButton2 ) + { + emit mouseForwardButtonClick(); + return; + } + // update press / 'start drag' mouse position d->mousePressPos = e->globalPos(); @@ -1988,14 +1984,20 @@ void PageView::mousePressEvent( QMouseEvent * e ) const QRect & itemRect = pageItem->uncroppedGeometry(); double nX = pageItem->absToPageX(eventPos.x()); double nY = pageItem->absToPageY(eventPos.y()); - Okular::Annotation * ann = 0; - const Okular::ObjectRect * orect = pageItem->page()->objectRect( Okular::ObjectRect::OAnnotation, nX, nY, itemRect.width(), itemRect.height() ); - if ( orect ) - ann = ( (Okular::AnnotationObjectRect *)orect )->annotation(); - if ( ann ) + + const QLinkedList< const Okular::ObjectRect *> orects = pageItem->page()->objectRects( Okular::ObjectRect::OAnnotation, nX, nY, itemRect.width(), itemRect.height() ); + + if ( !orects.isEmpty() ) { - AnnotationPopup popup( d->document, this ); - popup.addAnnotation( ann, pageItem->pageNumber() ); + AnnotationPopup popup( d->document, AnnotationPopup::MultiAnnotationMode, this ); + + foreach ( const Okular::ObjectRect * orect, orects ) + { + Okular::Annotation * ann = ( (Okular::AnnotationObjectRect *)orect )->annotation(); + if ( ann ) + popup.addAnnotation( ann, pageItem->pageNumber() ); + + } connect( &popup, SIGNAL(openAnnotationWindow(Okular::Annotation*,int)), this, SLOT(openAnnotationWindow(Okular::Annotation*,int)) ); @@ -2912,7 +2914,11 @@ void PageView::dragMoveEvent( QDragMoveEvent * ev ) void PageView::dropEvent( QDropEvent * ev ) { if ( KUrl::List::canDecode( ev->mimeData() ) ) - emit urlDropped( KUrl::List::fromMimeData( ev->mimeData() ).first() ); + { + const KUrl::List list = KUrl::List::fromMimeData( ev->mimeData() ); + if ( !list.isEmpty() ) + emit urlDropped( list.first() ); + } } bool PageView::viewportEvent( QEvent * e ) @@ -3270,11 +3276,8 @@ void PageView::selectionStart( const QPoint & pos, const QColor & color, bool /* } } -void PageView::selectionEndPoint( const QPoint & pos ) +void PageView::scrollPosIntoView( const QPoint & pos ) { - if ( !d->mouseSelecting ) - return; - if (pos.x() < horizontalScrollBar()->value()) d->dragScrollVector.setX(pos.x() - horizontalScrollBar()->value()); else if (horizontalScrollBar()->value() + viewport()->width() < pos.x()) d->dragScrollVector.setX(pos.x() - horizontalScrollBar()->value() - viewport()->width()); else d->dragScrollVector.setX(0); @@ -3288,13 +3291,42 @@ void PageView::selectionEndPoint( const QPoint & pos ) if (!d->dragScrollTimer.isActive()) d->dragScrollTimer.start(100); } else d->dragScrollTimer.stop(); +} - // update the selection rect - QRect updateRect = d->mouseSelectionRect; - d->mouseSelectionRect.setBottomLeft( pos ); - updateRect |= d->mouseSelectionRect; - updateRect.translate( -contentAreaPosition() ); - viewport()->update( updateRect.adjusted( -1, -1, 1, 1 ) ); +void PageView::updateSelection( const QPoint & pos ) +{ + if ( d->mouseSelecting ) + { + scrollPosIntoView( pos ); + // update the selection rect + QRect updateRect = d->mouseSelectionRect; + d->mouseSelectionRect.setBottomLeft( pos ); + updateRect |= d->mouseSelectionRect; + updateRect.translate( -contentAreaPosition() ); + viewport()->update( updateRect.adjusted( -1, -1, 1, 1 ) ); + } + else if ( d->mouseTextSelecting) + { + scrollPosIntoView( pos ); + int first = -1; + const QList< Okular::RegularAreaRect * > selections = textSelections( pos, d->mouseSelectPos, first ); + QSet< int > pagesWithSelectionSet; + for ( int i = 0; i < selections.count(); ++i ) + pagesWithSelectionSet.insert( i + first ); + + const QSet< int > noMoreSelectedPages = d->pagesWithTextSelection - pagesWithSelectionSet; + // clear the selection from pages not selected anymore + foreach( int p, noMoreSelectedPages ) + { + d->document->setPageTextSelection( p, 0, QColor() ); + } + // set the new selection for the selected pages + foreach( int p, pagesWithSelectionSet ) + { + d->document->setPageTextSelection( p, selections[ p - first ], palette().color( QPalette::Active, QPalette::Highlight ) ); + } + d->pagesWithTextSelection = pagesWithSelectionSet; + } } static Okular::NormalizedPoint rotateInNormRect( const QPoint &rotated, const QRect &rect, Okular::Rotation rotation ) @@ -3404,9 +3436,6 @@ void PageView::updateZoom( ZoomMode newZoomMode ) case ZoomFitPage: checkedZoomAction = d->aZoomFitPage; break; - case ZoomFitText: - checkedZoomAction = d->aZoomFitText; - break; case ZoomRefreshCurrent: newZoomMode = ZoomFixed; d->zoomFactor = -1; @@ -3436,7 +3465,6 @@ void PageView::updateZoom( ZoomMode newZoomMode ) { d->aZoomFitWidth->setChecked( checkedZoomAction == d->aZoomFitWidth ); d->aZoomFitPage->setChecked( checkedZoomAction == d->aZoomFitPage ); -// d->aZoomFitText->setChecked( checkedZoomAction == d->aZoomFitText ); } } else if ( newZoomMode == ZoomFixed && newFactor == d->zoomFactor ) @@ -3456,13 +3484,12 @@ void PageView::updateZoomText() // add items that describe fit actions QStringList translated; - translated << i18n("Fit Width") << i18n("Fit Page") /*<< i18n("Fit Text")*/; + translated << i18n("Fit Width") << i18n("Fit Page"); // add percent items QString double_oh( "00" ); const float zoomValue[10] = { 0.12, 0.25, 0.33, 0.50, 0.66, 0.75, 1.00, 1.25, 1.50, 2.00 }; - int idx = 0, - selIdx = 2; // use 3 if "fit text" present + int idx = 0, selIdx = 2; bool inserted = false; //use: "d->zoomMode != ZoomFixed" to hide Fit/* zoom ratio while ( idx < 10 || !inserted ) { @@ -3489,8 +3516,6 @@ void PageView::updateZoomText() selIdx = 0; else if ( d->zoomMode == ZoomFitPage ) selIdx = 1; - else if ( d->zoomMode == ZoomFitText ) - selIdx = 2; // we have to temporarily enable the actions as otherwise we can't set a new current item d->aZoom->setEnabled( true ); d->aZoom->selectableActionGroup()->setEnabled( true ); @@ -3700,8 +3725,9 @@ void PageView::slotRelayoutPages() QRect viewportRect( horizontalScrollBar()->value(), verticalScrollBar()->value(), viewportWidth, viewportHeight ); // handle the 'center first page in row' stuff - const bool facing = Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::Facing; - const bool facingCentered = Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::FacingFirstCentered; + const bool facing = Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::Facing && pageCount > 1; + const bool facingCentered = Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::FacingFirstCentered || + (Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::Facing && pageCount == 1); const bool overrideCentering = facingCentered && pageCount < 3; const bool centerFirstPage = facingCentered && !overrideCentering; const bool facingPages = facing || centerFirstPage; @@ -3924,7 +3950,7 @@ void PageView::slotRequestVisiblePixmaps( int newValue ) if ( vw->isPlaying() && viewportRectAtZeroZero.intersect( vw->geometry() ).isEmpty() ) { vw->stop(); - vw->hide(); + vw->pageLeft(); } } @@ -3981,8 +4007,7 @@ void PageView::slotRequestVisiblePixmaps( int newValue ) // if preloading is enabled, add the pages before and after in preloading if ( !d->visibleItems.isEmpty() && - Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low && - Okular::Settings::enableThreading() ) + Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low ) { // as the requests are done in the order as they appear in the list, // request first the next page and then the previous @@ -4005,10 +4030,7 @@ void PageView::slotRequestVisiblePixmaps( int newValue ) requestedPixmaps.push_back( new Okular::PixmapRequest( PAGEVIEW_ID, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO, true ) ); } - } - for( int j = 1; j <= pagesToPreload; j++ ) - { // add the page before the 'visible series' in preload int headRequest = d->visibleItems.first()->pageNumber() - j; if ( headRequest >= 0 ) @@ -4019,6 +4041,10 @@ void PageView::slotRequestVisiblePixmaps( int newValue ) requestedPixmaps.push_back( new Okular::PixmapRequest( PAGEVIEW_ID, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO, true ) ); } + + // stop if we've already reached both ends of the document + if ( headRequest < 0 && tailRequest >= (int)d->items.count() ) + break; } } @@ -4098,7 +4124,7 @@ void PageView::slotDragScroll() horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->dragScrollVector.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() + d->dragScrollVector.y()); QPoint p = contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ); - selectionEndPoint( p ); + updateSelection( p ); } void PageView::slotShowWelcome() @@ -4163,11 +4189,6 @@ void PageView::slotFitToPageToggled( bool on ) if ( on ) updateZoom( ZoomFitPage ); } -void PageView::slotFitToTextToggled( bool on ) -{ - if ( on ) updateZoom( ZoomFitText ); -} - void PageView::slotViewMode( QAction *action ) { const int nr = action->data().toInt(); @@ -4196,9 +4217,9 @@ void PageView::slotSetMouseNormal() Okular::Settings::setMouseMode( Okular::Settings::EnumMouseMode::Browse ); // hide the messageWindow d->messageWindow->hide(); - // reshow the annotator toolbar if hiding was forced - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - slotToggleAnnotator( true ); + // reshow the annotator toolbar if hiding was forced (and if it is not already visible) + if ( d->annotator && d->annotator->hidingWasForced() && d->aToggleAnnotator && !d->aToggleAnnotator->isChecked() ) + d->aToggleAnnotator->trigger(); // force an update of the cursor updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); Okular::Settings::self()->writeConfig(); @@ -4210,8 +4231,11 @@ void PageView::slotSetMouseZoom() // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Select zooming area. Right-click to zoom out." ), QString(), PageViewMessage::Info, -1 ); // force hiding of annotator toolbar - if ( d->annotator ) - d->annotator->setEnabled( false ); + if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) + { + d->aToggleAnnotator->trigger(); + d->annotator->setHidingForced( true ); + } // force an update of the cursor updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); Okular::Settings::self()->writeConfig(); @@ -4223,8 +4247,11 @@ void PageView::slotSetMouseSelect() // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Draw a rectangle around the text/graphics to copy." ), QString(), PageViewMessage::Info, -1 ); // force hiding of annotator toolbar - if ( d->annotator ) - d->annotator->setEnabled( false ); + if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) + { + d->aToggleAnnotator->trigger(); + d->annotator->setHidingForced( true ); + } // force an update of the cursor updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); Okular::Settings::self()->writeConfig(); @@ -4236,8 +4263,11 @@ void PageView::slotSetMouseTextSelect() // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Select text" ), QString(), PageViewMessage::Info, -1 ); // force hiding of annotator toolbar - if ( d->annotator ) - d->annotator->setEnabled( false ); + if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) + { + d->aToggleAnnotator->trigger(); + d->annotator->setHidingForced( true ); + } // force an update of the cursor updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); Okular::Settings::self()->writeConfig(); @@ -4251,8 +4281,11 @@ void PageView::slotSetMouseTableSelect() "Draw a rectangle around the table, then click near edges to divide up; press Esc to clear." ), QString(), PageViewMessage::Info, -1 ); // force hiding of annotator toolbar - if ( d->annotator ) - d->annotator->setEnabled( false ); + if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) + { + d->aToggleAnnotator->trigger(); + d->annotator->setHidingForced( true ); + } // force an update of the cursor updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); Okular::Settings::self()->writeConfig(); @@ -4309,11 +4342,12 @@ void PageView::slotToggleAnnotator( bool on ) // initialize/reset annotator (and show/hide toolbar) d->annotator->setEnabled( on ); + d->annotator->setHidingForced( false ); inHere = false; } -void PageView::slotScrollUp() +void PageView::slotAutoScrollUp() { if ( d->scrollIncrement < -9 ) return; @@ -4322,7 +4356,7 @@ void PageView::slotScrollUp() setFocus(); } -void PageView::slotScrollDown() +void PageView::slotAutoScrollDown() { if ( d->scrollIncrement > 9 ) return; @@ -4331,6 +4365,52 @@ void PageView::slotScrollDown() setFocus(); } +void PageView::slotScrollUp( bool singleStep ) +{ + // if in single page mode and at the top of the screen, go to \ page + if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() > verticalScrollBar()->minimum() ) + { + if ( singleStep ) + verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub ); + else + verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepSub ); + } + else if ( d->document->currentPage() > 0 ) + { + // more optimized than document->setPrevPage and then move view to bottom + Okular::DocumentViewport newViewport = d->document->viewport(); + newViewport.pageNumber -= viewColumns(); + if ( newViewport.pageNumber < 0 ) + newViewport.pageNumber = 0; + newViewport.rePos.enabled = true; + newViewport.rePos.normalizedY = 1.0; + d->document->setViewport( newViewport ); + } +} + +void PageView::slotScrollDown( bool singleStep ) +{ + // if in single page mode and at the bottom of the screen, go to next page + if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() < verticalScrollBar()->maximum() ) + { + if ( singleStep ) + verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd ); + else + verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepAdd ); + } + else if ( (int)d->document->currentPage() < d->items.count() - 1 ) + { + // more optimized than document->setNextPage and then move view to top + Okular::DocumentViewport newViewport = d->document->viewport(); + newViewport.pageNumber += viewColumns(); + if ( newViewport.pageNumber >= (int)d->items.count() ) + newViewport.pageNumber = d->items.count() - 1; + newViewport.rePos.enabled = true; + newViewport.rePos.normalizedY = 0.0; + d->document->setViewport( newViewport ); + } +} + void PageView::slotRotateClockwise() { int id = ( (int)d->document->rotation() + 1 ) % 4; diff --git a/ui/pageview.h b/ui/pageview.h index bd9bdd967..c1b36f4be 100644 --- a/ui/pageview.h +++ b/ui/pageview.h @@ -56,8 +56,8 @@ Q_OBJECT PageView( QWidget *parent, Okular::Document *document ); ~PageView(); - // Zoom mode ( last 4 are internally used only! ) - enum ZoomMode { ZoomFixed = 0, ZoomFitWidth = 1, ZoomFitPage = 2, ZoomFitText, + // Zoom mode ( last 3 are internally used only! ) + enum ZoomMode { ZoomFixed = 0, ZoomFitWidth = 1, ZoomFitPage = 2, ZoomIn, ZoomOut, ZoomRefreshCurrent }; enum ClearMode { ClearAllSelection, ClearOnlyDividers }; @@ -82,6 +82,7 @@ Q_OBJECT void notifyContentsCleared( int changedFlags ); void notifyZoom(int factor); bool canUnloadPixmap( int pageNum ) const; + void notifyCurrentPageChanged( int previous, int current ); // inherited from View uint viewId() const { return observerId(); } @@ -131,6 +132,9 @@ Q_OBJECT signals: void urlDropped( const KUrl& ); void rightClick( const Okular::Page *, const QPoint & ); + void mouseBackButtonClick(); + void mouseForwardButtonClick(); + void escPressed(); protected: void resizeEvent( QResizeEvent* ); @@ -165,10 +169,11 @@ Q_OBJECT PageViewItem * pickItemOnPoint( int x, int y ); // start / modify / clear selection rectangle void selectionStart( const QPoint & pos, const QColor & color, bool aboveAll = false ); - void selectionEndPoint( const QPoint & pos ); void selectionClear( const ClearMode mode = ClearAllSelection ); void drawTableDividers(QPainter * screenPainter); void guessTableDividers(); + // update either text or rectangle selection + void updateSelection( const QPoint & pos ); // update internal zoom values and end in a slotRelayoutPages(); void updateZoom( ZoomMode newZm ); // update the text on the label using global zoom value or current page's one @@ -187,6 +192,8 @@ Q_OBJECT void updatePageStep(); void addWebShortcutsMenu( KMenu * menu, const QString & text ); + // used when selecting stuff, makes the view scroll as necessary to keep the mouse inside the view + void scrollPosIntoView( const QPoint & pos ); // don't want to expose classes in here class PageViewPrivate * d; @@ -220,7 +227,6 @@ Q_OBJECT void slotZoomOut(); void slotFitToWidthToggled( bool ); void slotFitToPageToggled( bool ); - void slotFitToTextToggled( bool ); void slotViewMode( QAction *action ); void slotContinuousToggled( bool ); void slotSetMouseNormal(); @@ -229,8 +235,10 @@ Q_OBJECT void slotSetMouseTextSelect(); void slotSetMouseTableSelect(); void slotToggleAnnotator( bool ); - void slotScrollUp(); - void slotScrollDown(); + void slotAutoScrollUp(); + void slotAutoScrollDown(); + void slotScrollUp( bool singleStep = false ); + void slotScrollDown( bool singleStep = false ); void slotRotateClockwise(); void slotRotateCounterClockwise(); void slotRotateOriginal(); diff --git a/ui/pageviewannotator.cpp b/ui/pageviewannotator.cpp index 3f2b38382..9754b5eeb 100644 --- a/ui/pageviewannotator.cpp +++ b/ui/pageviewannotator.cpp @@ -187,6 +187,7 @@ class PickPointEngine : public AnnotatorEngine Okular::TextAnnotation * ta = new Okular::TextAnnotation(); ann = ta; ta->setTextType( Okular::TextAnnotation::Linked ); + ta->setTextIcon( "Note" ); ta->window().setText( QString() ); //ta->window.flags &= ~(Okular::Annotation::Hidden); double iconhei=0.03; @@ -603,7 +604,7 @@ class TextSelectorEngine : public AnnotatorEngine PageViewAnnotator::PageViewAnnotator( PageView * parent, Okular::Document * storage ) : QObject( parent ), m_document( storage ), m_pageView( parent ), m_toolBar( 0 ), m_engine( 0 ), m_textToolsEnabled( false ), m_toolsEnabled( false ), - m_continuousMode( false ), m_lastToolID( -1 ), m_lockedItem( 0 ) + m_continuousMode( false ), m_hidingWasForced( false ), m_lastToolID( -1 ), m_lockedItem( 0 ) { // load the tools from the 'xml tools definition' file. store the tree internally. QFile infoFile( KStandardDirs::locate("data", "okular/tools.xml") ); @@ -705,6 +706,16 @@ void PageViewAnnotator::setToolsEnabled( bool enabled ) m_toolBar->setToolsEnabled( m_toolsEnabled ); } +void PageViewAnnotator::setHidingForced( bool forced ) +{ + m_hidingWasForced = forced; +} + +bool PageViewAnnotator::hidingWasForced() const +{ + return m_hidingWasForced; +} + bool PageViewAnnotator::routeEvents() const { return m_engine && m_toolBar; diff --git a/ui/pageviewannotator.h b/ui/pageviewannotator.h index e847208a2..a2ef90d48 100644 --- a/ui/pageviewannotator.h +++ b/ui/pageviewannotator.h @@ -60,6 +60,9 @@ class PageViewAnnotator : public QObject void setToolsEnabled( bool enabled ); + void setHidingForced( bool forced ); + bool hidingWasForced() const; + // methods used when creating the annotation bool routeEvents() const; QRect routeEvent( QMouseEvent * event, PageViewItem * item ); @@ -85,6 +88,7 @@ class PageViewAnnotator : public QObject bool m_textToolsEnabled; bool m_toolsEnabled; bool m_continuousMode; + bool m_hidingWasForced; // creation related variables int m_lastToolID; diff --git a/ui/presentationwidget.cpp b/ui/presentationwidget.cpp index a3b8be447..9ba942b2b 100644 --- a/ui/presentationwidget.cpp +++ b/ui/presentationwidget.cpp @@ -100,6 +100,7 @@ struct PresentationFrame const Okular::Page * page; QRect geometry; QHash< Okular::Movie *, VideoWidget * > videoWidgets; + QLinkedList< SmoothPath > drawings; }; @@ -132,7 +133,8 @@ PresentationWidget::PresentationWidget( QWidget * parent, Okular::Document * doc m_pressedLink( 0 ), m_handCursor( false ), m_drawingEngine( 0 ), m_parentWidget( parent ), m_document( doc ), m_frameIndex( -1 ), m_topBar( 0 ), m_pagesEdit( 0 ), m_searchBar( 0 ), - m_screenSelect( 0 ), m_isSetup( false ), m_blockNotifications( false ), m_inBlackScreenMode( false ) + m_screenSelect( 0 ), m_isSetup( false ), m_blockNotifications( false ), m_inBlackScreenMode( false ), + m_showSummaryView( Okular::Settings::slidesShowSummary() ) { Q_UNUSED( parent ) setAttribute( Qt::WA_DeleteOnClose ); @@ -173,9 +175,11 @@ PresentationWidget::PresentationWidget( QWidget * parent, Okular::Document * doc m_topBar->addSeparator(); QAction *drawingAct = collection->action( "presentation_drawing_mode" ); connect( drawingAct, SIGNAL(toggled(bool)), SLOT(togglePencilMode(bool)) ); + drawingAct->setEnabled( true ); m_topBar->addAction( drawingAct ); addAction( drawingAct ); QAction *eraseDrawingAct = collection->action( "presentation_erase_drawings" ); + eraseDrawingAct->setEnabled( true ); connect( eraseDrawingAct, SIGNAL(triggered()), SLOT(clearDrawings()) ); m_topBar->addAction( eraseDrawingAct ); addAction( eraseDrawingAct ); @@ -264,9 +268,15 @@ PresentationWidget::~PresentationWidget() QAction *drawingAct = m_ac->action( "presentation_drawing_mode" ); disconnect( drawingAct, 0, this, 0 ); - if ( drawingAct->isChecked() ) - drawingAct->toggle(); - m_document->removePageAnnotations( m_document->viewport().pageNumber, m_currentPageDrawings ); + drawingAct->setChecked( false ); + drawingAct->setEnabled( false ); + + QAction *eraseDrawingAct = m_ac->action( "presentation_erase_drawings" ); + eraseDrawingAct->setEnabled( false ); + + QAction *blackScreenAct = m_ac->action( "switch_blackscreen_mode" ); + blackScreenAct->setChecked( false ); + blackScreenAct->setEnabled( false ); delete m_drawingEngine; // delete frames @@ -308,7 +318,7 @@ void PresentationWidget::notifySetup( const QVector< Okular::Page * > & pageSet, Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a ); VideoWidget * vw = new VideoWidget( movieAnn, m_document, this ); frame->videoWidgets.insert( movieAnn->movie(), vw ); - vw->hide(); + vw->pageInitialized(); } } frame->recalcGeometry( m_width, m_height, screenRatio ); @@ -334,10 +344,6 @@ void PresentationWidget::notifySetup( const QVector< Okular::Page * > & pageSet, void PresentationWidget::notifyViewportChanged( bool /*smoothMove*/ ) { - // discard notifications if displaying the summary - if ( m_frameIndex == -1 && Okular::Settings::slidesShowSummary() ) - return; - // display the current page changePage( m_document->viewport().pageNumber ); @@ -356,6 +362,89 @@ void PresentationWidget::notifyPageChanged( int pageNumber, int changedFlags ) generatePage( changedFlags & ( DocumentObserver::Annotations | DocumentObserver::Highlights ) ); } +void PresentationWidget::notifyCurrentPageChanged( int previousPage, int currentPage ) +{ + if ( previousPage != -1 ) + { + // stop video playback + Q_FOREACH ( VideoWidget *vw, m_frames[ previousPage ]->videoWidgets ) + { + vw->stop(); + vw->pageLeft(); + } + + // stop audio playback, if any + Okular::AudioPlayer::instance()->stopPlaybacks(); + + // perform the page closing action, if any + if ( m_document->page( previousPage )->pageAction( Okular::Page::Closing ) ) + m_document->processAction( m_document->page( previousPage )->pageAction( Okular::Page::Closing ) ); + + // perform the additional actions of the page's annotations, if any + Q_FOREACH ( const Okular::Annotation *annotation, m_document->page( previousPage )->annotations() ) + { + Okular::Action *action = 0; + + if ( annotation->subType() == Okular::Annotation::AScreen ) + action = static_cast( annotation )->additionalAction( Okular::Annotation::PageClosing ); + else if ( annotation->subType() == Okular::Annotation::AWidget ) + action = static_cast( annotation )->additionalAction( Okular::Annotation::PageClosing ); + + if ( action ) + m_document->processAction( action ); + } + } + + if ( currentPage != -1 ) + { + m_frameIndex = currentPage; + + // check if pixmap exists or else request it + PresentationFrame * frame = m_frames[ m_frameIndex ]; + int pixW = frame->geometry.width(); + int pixH = frame->geometry.height(); + + bool signalsBlocked = m_pagesEdit->signalsBlocked(); + m_pagesEdit->blockSignals( true ); + m_pagesEdit->setText( QString::number( m_frameIndex + 1 ) ); + m_pagesEdit->blockSignals( signalsBlocked ); + + // if pixmap not inside the Okular::Page we request it and wait for + // notifyPixmapChanged call or else we can proceed to pixmap generation + if ( !frame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) + { + requestPixmaps(); + } + else + { + // make the background pixmap + generatePage(); + } + + // perform the page opening action, if any + if ( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) ) + m_document->processAction( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) ); + + // perform the additional actions of the page's annotations, if any + Q_FOREACH ( const Okular::Annotation *annotation, m_document->page( m_frameIndex )->annotations() ) + { + Okular::Action *action = 0; + + if ( annotation->subType() == Okular::Annotation::AScreen ) + action = static_cast( annotation )->additionalAction( Okular::Annotation::PageOpening ); + else if ( annotation->subType() == Okular::Annotation::AWidget ) + action = static_cast( annotation )->additionalAction( Okular::Annotation::PageOpening ); + + if ( action ) + m_document->processAction( action ); + } + + // start autoplay video playback + Q_FOREACH ( VideoWidget *vw, m_frames[ m_frameIndex ]->videoWidgets ) + vw->pageEntered(); + } +} + bool PresentationWidget::canUnloadPixmap( int pageNumber ) const { if ( Okular::Settings::memoryLevel() == Okular::Settings::EnumMemoryLevel::Low || @@ -383,6 +472,7 @@ void PresentationWidget::setupActions( KActionCollection * collection ) QAction *action = m_ac->action( "switch_blackscreen_mode" ); connect( action, SIGNAL(toggled(bool)), SLOT(toggleBlackScreenMode(bool)) ); + action->setEnabled( true ); addAction( action ); } @@ -526,15 +616,17 @@ void PresentationWidget::mouseReleaseEvent( QMouseEvent * e ) (void)r; if ( m_drawingEngine->creationCompleted() ) { - QList< Okular::Annotation * > annots = m_drawingEngine->end(); + // add drawing to current page + m_frames[ m_frameIndex ]->drawings << m_drawingEngine->endSmoothPath(); + // manually disable and re-enable the pencil mode, so we can do // cleaning of the actual drawer and create a new one just after // that - that gives continuous drawing togglePencilMode( false ); togglePencilMode( true ); - foreach( Okular::Annotation * ann, annots ) - m_document->addPageAnnotation( m_frameIndex, ann ); - m_currentPageDrawings << annots; + + // schedule repaint + update(); } return; } @@ -573,6 +665,7 @@ void PresentationWidget::mouseMoveEvent( QMouseEvent * e ) if ( m_drawingEngine && e->buttons() != Qt::NoButton ) { QRect r = routeMouseDrawingEvent( e ); + if ( r.isValid() ) { m_drawingRect |= r.translated( m_frames[ m_frameIndex ]->geometry.topLeft() ); update( m_drawingRect ); @@ -659,12 +752,21 @@ void PresentationWidget::paintEvent( QPaintEvent * pe ) // copy the rendered pixmap to the screen painter.drawPixmap( r.topLeft(), m_lastRenderedPixmap, r ); } - if ( m_drawingEngine && m_drawingRect.intersects( pe->rect() ) ) + + // paint drawings + if ( m_frameIndex != -1 ) { const QRect & geom = m_frames[ m_frameIndex ]->geometry; painter.save(); painter.translate( geom.topLeft() ); - m_drawingEngine->paint( &painter, geom.width(), geom.height(), m_drawingRect.intersect( pe->rect() ) ); + painter.setRenderHints( QPainter::Antialiasing ); + + foreach ( const SmoothPath &drawing, m_frames[ m_frameIndex ]->drawings ) + drawing.paint( &painter, geom.width(), geom.height() ); + + if ( m_drawingEngine && m_drawingRect.intersects( pe->rect() ) ) + m_drawingEngine->paint( &painter, geom.width(), geom.height(), m_drawingRect.intersect( pe->rect() ) ); + painter.restore(); } painter.end(); @@ -775,78 +877,20 @@ void PresentationWidget::overlayClick( const QPoint & position ) void PresentationWidget::changePage( int newPage ) { + if ( m_showSummaryView ) { + m_showSummaryView = false; + m_frameIndex = -1; + return; + } + if ( m_frameIndex == newPage ) return; - const int oldIndex = m_frameIndex; - // check if pixmap exists or else request it - m_frameIndex = newPage; - PresentationFrame * frame = m_frames[ m_frameIndex ]; - int pixW = frame->geometry.width(); - int pixH = frame->geometry.height(); + // switch to newPage + m_document->setViewportPage( newPage, PRESENTATION_ID ); - bool signalsBlocked = m_pagesEdit->signalsBlocked(); - m_pagesEdit->blockSignals( true ); - m_pagesEdit->setText( QString::number( m_frameIndex + 1 ) ); - m_pagesEdit->blockSignals( signalsBlocked ); - - // if pixmap not inside the Okular::Page we request it and wait for - // notifyPixmapChanged call or else we can proceed to pixmap generation - if ( !frame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) - { - requestPixmaps(); - } - else - { - // make the background pixmap - generatePage(); - } - - // set a new viewport in document if page number differs - if ( m_frameIndex != -1 && m_frameIndex != m_document->viewport().pageNumber ) - { - // stop the audio playback, if any - Okular::AudioPlayer::instance()->stopPlaybacks(); - // perform the page closing action, if any - if ( m_document->page( m_document->viewport().pageNumber )->pageAction( Okular::Page::Closing ) ) - m_document->processAction( m_document->page( m_document->viewport().pageNumber )->pageAction( Okular::Page::Closing ) ); - - // remove the drawing on the old page before switching - clearDrawings(); - m_document->setViewportPage( m_frameIndex, PRESENTATION_ID ); - - // perform the page opening action, if any - if ( m_document->page( m_frameIndex)->pageAction( Okular::Page::Opening ) ) - m_document->processAction( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) ); - - Q_FOREACH ( VideoWidget *vw, m_frames[ m_frameIndex ]->videoWidgets ) - { - vw->pageEntered(); - } - } - - if ( oldIndex != m_frameIndex ) - { - if ( oldIndex != -1 ) - { - Q_FOREACH ( VideoWidget *vw, m_frames[ oldIndex ]->videoWidgets ) - { - vw->stop(); - vw->hide(); - } - } - else - { - // we have just opened the presentation view - if ( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) ) - m_document->processAction( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) ); - - Q_FOREACH ( VideoWidget *vw, m_frames[ m_frameIndex ]->videoWidgets ) - { - vw->pageEntered(); - } - } - } + if ( (Okular::Settings::slidesShowSummary() && !m_showSummaryView) || m_frameIndex == -1 ) + notifyCurrentPageChanged( -1, newPage ); } void PresentationWidget::generatePage( bool disableTransition ) @@ -1094,6 +1138,9 @@ void PresentationWidget::generateOverlay() QRect PresentationWidget::routeMouseDrawingEvent( QMouseEvent * e ) { + if ( m_frameIndex == -1 ) // Can't draw on the summary page + return QRect(); + const QRect & geom = m_frames[ m_frameIndex ]->geometry; const Okular::Page * page = m_frames[ m_frameIndex ]->page; @@ -1180,36 +1227,39 @@ void PresentationWidget::requestPixmaps() // restore cursor QApplication::restoreOverrideCursor(); // ask for next and previous page if not in low memory usage setting - if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low && Okular::Settings::enableThreading() ) + if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low ) { - if ( m_frameIndex + 1 < (int)m_document->pages() ) - { - PresentationFrame *nextFrame = m_frames[ m_frameIndex + 1 ]; - pixW = nextFrame->geometry.width(); - pixH = nextFrame->geometry.height(); - if ( !nextFrame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) - requests.push_back( new Okular::PixmapRequest( PRESENTATION_ID, m_frameIndex + 1, pixW, pixH, PRESENTATION_PRELOAD_PRIO, true ) ); - } - if ( m_frameIndex - 1 >= 0 ) - { - PresentationFrame *prevFrame = m_frames[ m_frameIndex - 1 ]; - pixW = prevFrame->geometry.width(); - pixH = prevFrame->geometry.height(); - if ( !prevFrame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) - requests.push_back( new Okular::PixmapRequest( PRESENTATION_ID, m_frameIndex - 1, pixW, pixH, PRESENTATION_PRELOAD_PRIO, true ) ); - } + int pagesToPreload = 1; // If greedy, preload everything if (Okular::Settings::memoryLevel() == Okular::Settings::EnumMemoryLevel::Greedy) + pagesToPreload = (int)m_document->pages(); + + for( int j = 1; j <= pagesToPreload; j++ ) { - for(int i = 0; i < (int)m_document->pages(); ++i) + int tailRequest = m_frameIndex + j; + if ( tailRequest < (int)m_document->pages() ) { - PresentationFrame *loopFrame = m_frames[ i ]; - pixW = loopFrame->geometry.width(); - pixH = loopFrame->geometry.height(); - if ( !loopFrame->page->hasPixmap( PRESENTATION_ID, pixW, pixH )) - requests.push_back( new Okular::PixmapRequest( PRESENTATION_ID, i, pixW, pixH, PRESENTATION_PRELOAD_PRIO, true ) ); + PresentationFrame *nextFrame = m_frames[ tailRequest ]; + pixW = nextFrame->geometry.width(); + pixH = nextFrame->geometry.height(); + if ( !nextFrame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) + requests.push_back( new Okular::PixmapRequest( PRESENTATION_ID, tailRequest, pixW, pixH, PRESENTATION_PRELOAD_PRIO, true ) ); } + + int headRequest = m_frameIndex - j; + if ( headRequest >= 0 ) + { + PresentationFrame *prevFrame = m_frames[ headRequest ]; + pixW = prevFrame->geometry.width(); + pixH = prevFrame->geometry.height(); + if ( !prevFrame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) + requests.push_back( new Okular::PixmapRequest( PRESENTATION_ID, headRequest, pixW, pixH, PRESENTATION_PRELOAD_PRIO, true ) ); + } + + // stop if we've already reached both ends of the document + if ( headRequest < 0 && tailRequest >= (int)m_document->pages() ) + break; } } m_document->requestPixmaps( requests ); @@ -1218,14 +1268,16 @@ void PresentationWidget::requestPixmaps() void PresentationWidget::slotNextPage() { - // loop when configured - if ( m_frameIndex == (int)m_frames.count() - 1 && Okular::Settings::slidesLoop() ) - m_frameIndex = -1; + int nextIndex = m_frameIndex + 1; - if ( m_frameIndex < (int)m_frames.count() - 1 ) + // loop when configured + if ( nextIndex == m_frames.count() && Okular::Settings::slidesLoop() ) + nextIndex = 0; + + if ( nextIndex < m_frames.count() ) { // go to next page - changePage( m_frameIndex + 1 ); + changePage( nextIndex ); // auto advance to the next page if set startAutoChangeTimer(); } @@ -1365,8 +1417,9 @@ void PresentationWidget::togglePencilMode( bool on ) void PresentationWidget::clearDrawings() { - m_document->removePageAnnotations( m_document->viewport().pageNumber, m_currentPageDrawings ); - m_currentPageDrawings.clear(); + if ( m_frameIndex != -1 ) + m_frames[ m_frameIndex ]->drawings.clear(); + update(); } void PresentationWidget::screenResized( int screen ) diff --git a/ui/presentationwidget.h b/ui/presentationwidget.h index 126985eb5..4cb4b2b98 100644 --- a/ui/presentationwidget.h +++ b/ui/presentationwidget.h @@ -23,7 +23,7 @@ class QToolBar; class QTimer; class KActionCollection; class KSelectAction; -class AnnotatorEngine; +class SmoothPathEngine; struct PresentationFrame; class PresentationSearchBar; @@ -53,6 +53,7 @@ class PresentationWidget : public QWidget, public Okular::DocumentObserver void notifyViewportChanged( bool smoothMove ); void notifyPageChanged( int pageNumber, int changedFlags ); bool canUnloadPixmap( int pageNumber ) const; + void notifyCurrentPageChanged( int previous, int current ); public slots: void slotFind(); @@ -104,8 +105,7 @@ class PresentationWidget : public QWidget, public Okular::DocumentObserver QRect m_overlayGeometry; const Okular::Action * m_pressedLink; bool m_handCursor; - QList< Okular::Annotation * > m_currentPageDrawings; - AnnotatorEngine * m_drawingEngine; + SmoothPathEngine * m_drawingEngine; QRect m_drawingRect; int m_screen; int m_screenInhibitCookie; @@ -133,6 +133,7 @@ class PresentationWidget : public QWidget, public Okular::DocumentObserver bool m_isSetup; bool m_blockNotifications; bool m_inBlackScreenMode; + bool m_showSummaryView; private slots: void slotNextPage(); diff --git a/ui/propertiesdialog.cpp b/ui/propertiesdialog.cpp index fb2dfab0a..2ef8220d0 100644 --- a/ui/propertiesdialog.cpp +++ b/ui/propertiesdialog.cpp @@ -85,8 +85,9 @@ PropertiesDialog::PropertiesDialog(QWidget *parent, Okular::Document *doc) const QString valueString = element.attribute( "value" ); if ( titleString.isEmpty() || valueString.isEmpty() ) continue; - if ( !orderedProperties.contains( titleString ) ) - orderedProperties << titleString; + if ( !orderedProperties.contains( element.tagName() ) ) { + orderedProperties << element.tagName(); + } } QDomNodeList list; diff --git a/ui/searchlineedit.cpp b/ui/searchlineedit.cpp index 21c4d562a..31be8fb7b 100644 --- a/ui/searchlineedit.cpp +++ b/ui/searchlineedit.cpp @@ -65,7 +65,9 @@ void SearchLineEdit::setSearchType( Okular::Document::SearchType type ) return; m_searchType = type; - m_changed = ( m_searchType != Okular::Document::NextMatch && m_searchType != Okular::Document::PreviousMatch ); + + if ( !m_changed ) + m_changed = ( m_searchType != Okular::Document::NextMatch && m_searchType != Okular::Document::PreviousMatch ); } void SearchLineEdit::setSearchId( int id ) @@ -90,6 +92,22 @@ void SearchLineEdit::setSearchFromStart( bool fromStart ) m_fromStart = fromStart; } +void SearchLineEdit::resetSearch() +{ + // Stop the currently running search, if any + stopSearch(); + + // Clear highlights + if ( m_id != -1 ) + m_document->resetSearch( m_id ); + + // Make sure that the search will be reset at the next one + m_changed = true; + + // Reset input box color + prepareLineEditForSearch(); +} + bool SearchLineEdit::isSearchRunning() const { return m_searchRunning; @@ -173,6 +191,7 @@ void SearchLineEdit::slotReturnPressed( const QString &text ) { m_inputDelayTimer->stop(); prepareLineEditForSearch(); + m_searchType = Okular::Document::NextMatch; findNext(); } diff --git a/ui/searchlineedit.h b/ui/searchlineedit.h index 5108f5a65..63ff4965b 100644 --- a/ui/searchlineedit.h +++ b/ui/searchlineedit.h @@ -36,6 +36,7 @@ class SearchLineEdit : public KLineEdit void setSearchColor( const QColor &color ); void setSearchMoveViewport( bool move ); void setSearchFromStart( bool fromStart ); + void resetSearch(); bool isSearchRunning() const; diff --git a/ui/side_reviews.cpp b/ui/side_reviews.cpp index fc4b307ef..a35f9ed56 100644 --- a/ui/side_reviews.cpp +++ b/ui/side_reviews.cpp @@ -158,9 +158,11 @@ Reviews::~Reviews() } //BEGIN DocumentObserver Notifies -void Reviews::notifyViewportChanged( bool ) +void Reviews::notifyCurrentPageChanged( int previousPage, int currentPage ) { - m_filterProxy->setCurrentPage( m_document->currentPage() ); + Q_UNUSED( previousPage ) + + m_filterProxy->setCurrentPage( currentPage ); } //END DocumentObserver Notifies @@ -252,7 +254,7 @@ QModelIndexList Reviews::retrieveAnnotations(const QModelIndex& idx) const void Reviews::contextMenuRequested( const QPoint &pos ) { - AnnotationPopup popup( m_document, this ); + AnnotationPopup popup( m_document, AnnotationPopup::SingleAnnotationMode, this ); connect( &popup, SIGNAL(openAnnotationWindow(Okular::Annotation*,int)), this, SIGNAL(openAnnotationWindow(Okular::Annotation*,int)) ); diff --git a/ui/side_reviews.h b/ui/side_reviews.h index d063b7b7a..bbd8324ae 100644 --- a/ui/side_reviews.h +++ b/ui/side_reviews.h @@ -42,7 +42,7 @@ class Reviews : public QWidget, public Okular::DocumentObserver // [INHERITED] from DocumentObserver uint observerId() const { return REVIEWS_ID; } - void notifyViewportChanged( bool smoothMove ); + void notifyCurrentPageChanged( int previous, int current ); void reparseConfig(); diff --git a/ui/snapshottaker.cpp b/ui/snapshottaker.cpp new file mode 100644 index 000000000..362d2497a --- /dev/null +++ b/ui/snapshottaker.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2012 by Tobias Koening * + * * + * 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. * + ***************************************************************************/ + +#include "snapshottaker.h" + +#include +#include + +#include + +SnapshotTaker::SnapshotTaker( const QString &url, QObject *parent ) + : QObject( parent ) + , m_player( new Phonon::VideoPlayer( Phonon::NoCategory, 0 ) ) +{ + m_player->load( url ); + m_player->hide(); + + connect(m_player->mediaObject(), SIGNAL(stateChanged(Phonon::State, Phonon::State)), + this, SLOT(stateChanged(Phonon::State, Phonon::State))); + + m_player->play(); +} + +SnapshotTaker::~SnapshotTaker() +{ + m_player->stop(); + delete m_player; +} + +void SnapshotTaker::stateChanged(Phonon::State newState, Phonon::State) +{ + if (newState == Phonon::PlayingState) { + const QImage image = m_player->videoWidget()->snapshot(); + if (!image.isNull()) + emit finished( image ); + + m_player->stop(); + deleteLater(); + } +} diff --git a/ui/snapshottaker.h b/ui/snapshottaker.h new file mode 100644 index 000000000..e34c3e0be --- /dev/null +++ b/ui/snapshottaker.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2012 by Tobias Koening * + * * + * 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 SNAPSHOTTAKER_H +#define SNAPSHOTTAKER_H + +#include + +#include + +class QImage; + +class SnapshotTaker : public QObject +{ + Q_OBJECT + + public: + SnapshotTaker( const QString &url, QObject *parent = 0 ); + ~SnapshotTaker(); + + Q_SIGNALS: + void finished( const QImage &image ); + + private Q_SLOTS: + void stateChanged(Phonon::State, Phonon::State); + + private: + Phonon::VideoPlayer *m_player; +}; + +#endif diff --git a/ui/thumbnaillist.cpp b/ui/thumbnaillist.cpp index 0511d9342..60288cb53 100644 --- a/ui/thumbnaillist.cpp +++ b/ui/thumbnaillist.cpp @@ -310,11 +310,12 @@ void ThumbnailList::notifySetup( const QVector< Okular::Page * > & pages, int se d->delayedRequestVisiblePixmaps( 200 ); } -void ThumbnailList::notifyViewportChanged( bool /*smoothMove*/ ) +void ThumbnailList::notifyCurrentPageChanged( int previousPage, int currentPage ) { + Q_UNUSED( previousPage ) + // skip notifies for the current page (already selected) - const int newPage = d->m_document->viewport().pageNumber; - if ( d->m_selected && d->m_selected->pageNumber() == newPage ) + if ( d->m_selected && d->m_selected->pageNumber() == currentPage ) return; // deselect previous thumbnail @@ -327,7 +328,7 @@ void ThumbnailList::notifyViewportChanged( bool /*smoothMove*/ ) QVector::const_iterator tIt = d->m_thumbnails.constBegin(), tEnd = d->m_thumbnails.constEnd(); for ( ; tIt != tEnd; ++tIt ) { - if ( (*tIt)->pageNumber() == newPage ) + if ( (*tIt)->pageNumber() == currentPage ) { d->m_selected = *tIt; d->m_selected->setSelected( true ); @@ -618,7 +619,11 @@ void ThumbnailList::dragEnterEvent( QDragEnterEvent * ev ) void ThumbnailList::dropEvent( QDropEvent * ev ) { if ( KUrl::List::canDecode( ev->mimeData() ) ) - emit urlDropped( KUrl::List::fromMimeData( ev->mimeData() ).first() ); + { + const KUrl::List list = KUrl::List::fromMimeData( ev->mimeData() ); + if ( !list.isEmpty() ) + emit urlDropped( list.first() ); + } } //END widget events diff --git a/ui/thumbnaillist.h b/ui/thumbnaillist.h index 0d7136ba0..20c434fa0 100644 --- a/ui/thumbnaillist.h +++ b/ui/thumbnaillist.h @@ -41,7 +41,7 @@ Q_OBJECT // inherited: create thumbnails ( inherited as a DocumentObserver ) void notifySetup( const QVector< Okular::Page * > & pages, int setupFlags ); // inherited: hilihght current thumbnail ( inherited as DocumentObserver ) - void notifyViewportChanged( bool smoothMove ); + void notifyCurrentPageChanged( int previous, int current ); // inherited: redraw thumbnail ( inherited as DocumentObserver ) void notifyPageChanged( int pageNumber, int changedFlags ); // inherited: request all visible pixmap (due to a global shange or so..) diff --git a/ui/toc.cpp b/ui/toc.cpp index 3203c79f8..4c84b62c3 100644 --- a/ui/toc.cpp +++ b/ui/toc.cpp @@ -25,7 +25,7 @@ #include "core/document.h" #include "settings.h" -TOC::TOC(QWidget *parent, Okular::Document *document) : QWidget(parent), m_document(document), m_currentPage(-1) +TOC::TOC(QWidget *parent, Okular::Document *document) : QWidget(parent), m_document(document) { QVBoxLayout *mainlay = new QVBoxLayout( this ); mainlay->setMargin( 0 ); @@ -69,7 +69,6 @@ void TOC::notifySetup( const QVector< Okular::Page * > & /*pages*/, int setupFla // clear contents m_model->clear(); - m_currentPage = -1; // request synopsis description (is a dom tree) const Okular::DocumentSynopsis * syn = m_document->documentSynopsis(); @@ -86,14 +85,8 @@ void TOC::notifySetup( const QVector< Okular::Page * > & /*pages*/, int setupFla emit hasTOC( !m_model->isEmpty() ); } -void TOC::notifyViewportChanged( bool /*smoothMove*/ ) +void TOC::notifyCurrentPageChanged( int, int ) { - int newpage = m_document->viewport().pageNumber; - if ( m_currentPage == newpage ) - return; - - m_currentPage = newpage; - m_model->setCurrentViewport( m_document->viewport() ); } diff --git a/ui/toc.h b/ui/toc.h index 4e63ef65d..eeeff9800 100644 --- a/ui/toc.h +++ b/ui/toc.h @@ -33,7 +33,7 @@ Q_OBJECT // inherited from DocumentObserver uint observerId() const; void notifySetup( const QVector< Okular::Page * > & pages, int setupFlags ); - void notifyViewportChanged( bool smoothMove ); + void notifyCurrentPageChanged( int previous, int current ); void reparseConfig(); @@ -49,7 +49,6 @@ Q_OBJECT QTreeView *m_treeView; KTreeViewSearchLine *m_searchLine; TOCModel *m_model; - int m_currentPage; }; #endif diff --git a/ui/videowidget.cpp b/ui/videowidget.cpp index 7d80b4344..592ec7940 100644 --- a/ui/videowidget.cpp +++ b/ui/videowidget.cpp @@ -11,10 +11,13 @@ // qt/kde includes #include +#include #include #include +#include #include #include +#include #include #include #include @@ -22,6 +25,7 @@ #include #include +#include #include #include @@ -29,6 +33,7 @@ #include "core/annotations.h" #include "core/document.h" #include "core/movie.h" +#include "snapshottaker.h" static QAction* createToolBarButtonWithWidgetPopup( QToolBar* toolBar, QWidget *widget, const QIcon &icon ) { @@ -54,14 +59,24 @@ class VideoWidget::Private { public: Private( Okular::MovieAnnotation *ma, Okular::Document *doc, VideoWidget *qq ) - : q( qq ), anno( ma ), document( doc ), loaded( false ) + : q( qq ), anno( ma ), document( doc ), player( 0 ), loaded( false ) { } + ~Private() + { + if ( player ) + player->stop(); + } + enum PlayPauseMode { PlayMode, PauseMode }; void load(); void setupPlayPauseAction( PlayPauseMode mode ); + void setPosterImage( const QImage& ); + void takeSnapshot(); + void videoStopped(); + void stateChanged(Phonon::State, Phonon::State); // slots void finished(); @@ -78,6 +93,8 @@ public: QAction *stopAction; QAction *seekSliderAction; QAction *seekSliderMenuAction; + QStackedLayout *pageLayout; + QLabel *posterImagePage; bool loaded : 1; }; @@ -104,6 +121,9 @@ void VideoWidget::Private::load() else player->load( newurl ); + connect( player->mediaObject(), SIGNAL( stateChanged( Phonon::State, Phonon::State ) ), + q, SLOT( stateChanged( Phonon::State, Phonon::State ) ) ); + seekSlider->setEnabled( true ); } @@ -121,6 +141,37 @@ void VideoWidget::Private::setupPlayPauseAction( PlayPauseMode mode ) } } +void VideoWidget::Private::takeSnapshot() +{ + const QString url = anno->movie()->url(); + KUrl newurl; + if ( QDir::isRelativePath( url ) ) + { + newurl = document->currentDocument(); + newurl.setFileName( url ); + } + else + { + newurl = url; + } + + SnapshotTaker *taker = 0; + if ( newurl.isLocalFile() ) + taker = new SnapshotTaker( newurl.toLocalFile(), q ); + else + taker = new SnapshotTaker( newurl.url(), q ); + + q->connect( taker, SIGNAL( finished( const QImage& ) ), q, SLOT( setPosterImage( const QImage& ) ) ); +} + +void VideoWidget::Private::videoStopped() +{ + if ( anno->movie()->showPosterImage() ) + pageLayout->setCurrentIndex( 1 ); + else + q->hide(); +} + void VideoWidget::Private::finished() { switch ( anno->movie()->playMode() ) @@ -132,7 +183,7 @@ void VideoWidget::Private::finished() setupPlayPauseAction( PlayMode ); if ( anno->movie()->playMode() == Okular::Movie::PlayOnce ) controlBar->setVisible( false ); - q->setVisible(false); + videoStopped(); break; case Okular::Movie::PlayRepeat: // repeat the playback @@ -158,6 +209,23 @@ void VideoWidget::Private::playOrPause() } } +void VideoWidget::Private::setPosterImage( const QImage &image ) +{ + if ( !image.isNull() ) + { + // cache the snapshot image + anno->movie()->setPosterImage( image ); + } + + posterImagePage->setPixmap( QPixmap::fromImage( image ) ); +} + +void VideoWidget::Private::stateChanged( Phonon::State newState, Phonon::State ) +{ + if ( newState == Phonon::PlayingState ) + pageLayout->setCurrentIndex( 0 ); +} + VideoWidget::VideoWidget( Okular::MovieAnnotation *movieann, Okular::Document *document, QWidget *parent ) : QWidget( parent ), d( new Private( movieann, document, this ) ) { @@ -165,15 +233,18 @@ VideoWidget::VideoWidget( Okular::MovieAnnotation *movieann, Okular::Document *d // they should be tied to this widget, not spread around... setAttribute( Qt::WA_NoMousePropagation ); - QVBoxLayout *mainlay = new QVBoxLayout( this ); + // Setup player page + QWidget *playerPage = new QWidget; + + QVBoxLayout *mainlay = new QVBoxLayout( playerPage ); mainlay->setMargin( 0 ); mainlay->setSpacing( 0 ); - d->player = new Phonon::VideoPlayer( Phonon::NoCategory, this ); - d->player->installEventFilter( this ); + d->player = new Phonon::VideoPlayer( Phonon::NoCategory, playerPage ); + d->player->installEventFilter( playerPage ); mainlay->addWidget( d->player ); - d->controlBar = new QToolBar( this ); + d->controlBar = new QToolBar( playerPage ); d->controlBar->setIconSize( QSize( 16, 16 ) ); d->controlBar->setAutoFillBackground( true ); mainlay->addWidget( d->controlBar ); @@ -203,6 +274,38 @@ VideoWidget::VideoWidget( Okular::MovieAnnotation *movieann, Okular::Document *d connect( d->playPauseAction, SIGNAL(triggered()), this, SLOT(playOrPause()) ); d->geom = movieann->transformedBoundingRectangle(); + + // Setup poster image page + d->posterImagePage = new QLabel; + d->posterImagePage->setScaledContents( true ); + d->posterImagePage->installEventFilter( this ); + d->posterImagePage->setCursor( Qt::PointingHandCursor ); + + d->pageLayout = new QStackedLayout( this ); + d->pageLayout->setMargin( 0 ); + d->pageLayout->setSpacing( 0 ); + d->pageLayout->addWidget( playerPage ); + d->pageLayout->addWidget( d->posterImagePage ); + + + if ( movieann->movie()->showPosterImage() ) + { + d->pageLayout->setCurrentIndex( 1 ); + + const QImage posterImage = movieann->movie()->posterImage(); + if ( posterImage.isNull() ) + { + d->takeSnapshot(); + } + else + { + d->setPosterImage( posterImage ); + } + } + else + { + d->pageLayout->setCurrentIndex( 0 ); + } } VideoWidget::~VideoWidget() @@ -225,14 +328,34 @@ bool VideoWidget::isPlaying() const return d->player->isPlaying(); } +void VideoWidget::pageInitialized() +{ + hide(); +} + void VideoWidget::pageEntered() { - if ( d->anno->movie()->autoPlay() ) { + if ( d->anno->movie()->showPosterImage() ) + { + d->pageLayout->setCurrentIndex( 1 ); + show(); + } + + if ( d->anno->movie()->autoPlay() ) + { show(); QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection); } } +void VideoWidget::pageLeft() +{ + d->player->stop(); + d->videoStopped(); + + hide(); +} + void VideoWidget::play() { d->load(); @@ -256,7 +379,7 @@ void VideoWidget::pause() bool VideoWidget::eventFilter( QObject * object, QEvent * event ) { - if ( object == d->player ) + if ( object == d->player || object == d->posterImagePage ) { switch ( event->type() ) { @@ -272,6 +395,18 @@ bool VideoWidget::eventFilter( QObject * object, QEvent * event ) event->accept(); } } + case QEvent::Wheel: + { + if ( object == d->posterImagePage ) + { + QWheelEvent * we = static_cast< QWheelEvent * >( event ); + + // forward wheel events to parent widget + QWheelEvent *copy = new QWheelEvent( we->pos(), we->globalPos(), we->delta(), we->buttons(), we->modifiers(), we->orientation() ); + QCoreApplication::postEvent( parentWidget(), copy ); + } + break; + } default: ; } } diff --git a/ui/videowidget.h b/ui/videowidget.h index e82753c87..f5d22b9ed 100644 --- a/ui/videowidget.h +++ b/ui/videowidget.h @@ -30,11 +30,21 @@ class VideoWidget : public QWidget bool isPlaying() const; + /** + * This method is called when the page the video widget is located on has been initialized. + */ + void pageInitialized(); + /** * This method is called when the page the video widget is located on has been entered. */ void pageEntered(); + /** + * This method is called when the page the video widget is located on has been left. + */ + void pageLeft(); + public slots: void play(); void pause(); @@ -48,6 +58,8 @@ class VideoWidget : public QWidget private: Q_PRIVATE_SLOT( d, void finished() ) Q_PRIVATE_SLOT( d, void playOrPause() ) + Q_PRIVATE_SLOT( d, void setPosterImage( const QImage& ) ) + Q_PRIVATE_SLOT( d, void stateChanged( Phonon::State, Phonon::State ) ) // private storage class Private;