diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,6 +346,7 @@ part.cpp extensions.cpp ui/embeddedfilesdialog.cpp + ui/annotationactionhandler.cpp ui/annotwindow.cpp ui/annotationmodel.cpp ui/annotationpopup.cpp diff --git a/conf/dlgannotations.cpp b/conf/dlgannotations.cpp --- a/conf/dlgannotations.cpp +++ b/conf/dlgannotations.cpp @@ -20,9 +20,9 @@ Ui_DlgAnnotationsBase dlg; dlg.setupUi( this ); - WidgetAnnotTools * kcfg_AnnotationTools = new WidgetAnnotTools( dlg.annotToolsGroup ); - dlg.annotToolsPlaceholder->addWidget( kcfg_AnnotationTools ); - kcfg_AnnotationTools->setObjectName( QStringLiteral("kcfg_AnnotationTools") ); + WidgetAnnotTools * kcfg_FavoriteAnnotationTools = new WidgetAnnotTools( dlg.annotToolsGroup ); + dlg.annotToolsPlaceholder->addWidget( kcfg_FavoriteAnnotationTools ); + kcfg_FavoriteAnnotationTools->setObjectName( QStringLiteral("kcfg_FavoriteAnnotationTools") ); KConfigDialogManager::changedMap()->insert( QStringLiteral("WidgetAnnotTools") , SIGNAL(changed()) ); } diff --git a/conf/dlgannotationsbase.ui b/conf/dlgannotationsbase.ui --- a/conf/dlgannotationsbase.ui +++ b/conf/dlgannotationsbase.ui @@ -84,7 +84,7 @@ - Annotation tools + Favorite annotation tools diff --git a/conf/okular.kcfg b/conf/okular.kcfg --- a/conf/okular.kcfg +++ b/conf/okular.kcfg @@ -117,6 +117,11 @@ annotationTools + + + + true + diff --git a/part.rc b/part.rc --- a/part.rc +++ b/part.rc @@ -1,5 +1,5 @@ - + &File @@ -106,4 +106,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell/shell.h b/shell/shell.h --- a/shell/shell.h +++ b/shell/shell.h @@ -140,7 +140,7 @@ QStringList fileFormats() const; void openNewTab( const QUrl& url, const QString &serializedOptions ); void applyOptionsToPart( QObject* part, const QString &serializedOptions ); - void connectPart( QObject* part ); + void configurePart( KParts::ReadWritePart* part ); int findTabIndex( QObject* sender ); private: diff --git a/shell/shell.cpp b/shell/shell.cpp --- a/shell/shell.cpp +++ b/shell/shell.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -120,7 +121,7 @@ // and integrate the part's GUI with the shell's setupGUI(Keys | ToolBar | Save); createGUI(firstPart); - connectPart( firstPart ); + configurePart( firstPart ); m_tabs.append( firstPart ); m_tabWidget->addTab( firstPart->widget(), QString() ); @@ -661,7 +662,7 @@ // Make new part m_tabs.append( m_partFactory->create(this) ); - connectPart( m_tabs[newIndex].part ); + configurePart( m_tabs[newIndex].part ); // Update GUI KParts::ReadWritePart* const part = m_tabs[newIndex].part; @@ -692,14 +693,19 @@ QMetaObject::invokeMethod( part, "enableStartWithFind", Q_ARG( const QString &, find )); } -void Shell::connectPart( QObject* part ) +void Shell::configurePart( KParts::ReadWritePart* part ) { connect( this, SIGNAL(moveSplitter(int)), part, SLOT(moveSplitter(int)) ); connect( part, SIGNAL(enablePrintAction(bool)), this, SLOT(setPrintEnabled(bool))); connect( part, SIGNAL(enableCloseAction(bool)), this, SLOT(setCloseEnabled(bool))); connect( part, SIGNAL(mimeTypeChanged(QMimeType)), this, SLOT(setTabIcon(QMimeType))); connect( part, SIGNAL(urlsDropped(QList)), this, SLOT(handleDroppedUrls(QList)) ); connect( part, SIGNAL(fitWindowToPage(QSize,QSize)), this, SLOT(slotFitWindowToPage(QSize,QSize)) ); + + KToggleToolBarAction * aToggleAnnotator = new KToggleToolBarAction( toolBar("annotationToolBar"), i18n("&Review"), this ); + aToggleAnnotator->setIcon( QIcon::fromTheme( QStringLiteral("draw-freehand") ) ); + part->actionCollection()->addAction( QStringLiteral("mouse_toggle_annotate"), aToggleAnnotator ); + part->actionCollection()->setDefaultShortcut( aToggleAnnotator, Qt::Key_F6 ); } void Shell::print() diff --git a/shell/shell.rc b/shell/shell.rc --- a/shell/shell.rc +++ b/shell/shell.rc @@ -1,5 +1,5 @@ - + @@ -23,4 +23,7 @@ + + Annotations toolbar + diff --git a/ui/annotationactionhandler.h b/ui/annotationactionhandler.h new file mode 100644 --- /dev/null +++ b/ui/annotationactionhandler.h @@ -0,0 +1,59 @@ +/************************************************************************** +* Copyright (C) 2019 by Simone Gaiarin * +* * +* 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 _ANNOTATIONACTIONHANDLER_H_ +#define _ANNOTATIONACTIONHANDLER_H_ + +#include + +class QAction; +class QColor; +class QFont; +class KActionCollection; +class PageViewAnnotator; +class AnnotationActionHandlerPrivate; + +/** + * @short Handles all the actions of the annotation tools toolbar + * + */ +class AnnotationActionHandler : public QObject +{ + Q_OBJECT + + public: + AnnotationActionHandler( PageViewAnnotator * parent, KActionCollection * ac ); + ~AnnotationActionHandler(); + + void deselectAllAnnotationActions(); + + void reparseTools(); + void setToolsEnabled( bool on ); + void setTextToolsEnabled( bool on ); + + Q_SIGNALS: + void advancedSettingsTriggered(); + + private: + void addFavoriteAnnotations(); + + friend class AnnotationActionHandlerPrivate; + class AnnotationActionHandlerPrivate * d; + + private Q_SLOTS: + void slotToolSelected( int toolID ); + void slotFavoriteToolSelected( int favToolID ); + void slotSelectColor(); + void slotSelectInnerColor(); + void slotSelectAnnotationFont(); + void slotSelectAnnotationWidth( int width ); + void slotSelectAnnotationOpacity( int opacity ); +}; + +#endif // _ANNOTATIONACTIONHANDLER_H_ diff --git a/ui/annotationactionhandler.cpp b/ui/annotationactionhandler.cpp new file mode 100644 --- /dev/null +++ b/ui/annotationactionhandler.cpp @@ -0,0 +1,598 @@ +/************************************************************************** +* Copyright (C) 2019 by Simone Gaiarin * +* * +* 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 "annotationactionhandler.h" + +// qt includes +#include +#include +#include +#include +#include + +// kde includes +#include +#include +#include + +// local includes +#include "pageviewannotator.h" +#include "toolaction.h" + +class AnnotationActionHandlerPrivate +{ +public: + AnnotationActionHandlerPrivate( AnnotationActionHandler * qq ) + : q( qq ), m_selectedTool( -1 ), m_currentColor( QColor("yellow") ), + m_currentInnerColor( QColor("#00000000") ), m_currentFont( QFont() ), + m_annotator( nullptr ) + { + } + + void parseTool( int toolID ); + + QIcon colorizeIcon(QIcon &icon, const QColor &color); + void setColorActionIcon( QColor color ); + void setInnerColorActionIcon( QColor color ); + void enableConfigActionsForAnnotation( QString annotType = "none" ); + void updateConfigActionsToolTips( QString annotType = "none" ); + + QIcon widthIcon( double width ); + QIcon opacityIcon( int opacity ); + + AnnotationActionHandler * q; + + QList * m_textAnnotationTools; + QActionGroup * m_annotationToolsGroup; + + KSelectAction * m_favorites; + QAction * m_bookmark; + QAction * m_colorAction; + QAction * m_innerColorAction; + KSelectAction * m_opacityAction; + QAction * m_fontAction; + KSelectAction * m_widthAction; + QAction * m_annAdvancedSettings; + KToggleAction * m_continuousModeAction; + + int m_selectedTool; + + QColor m_currentColor; + QColor m_currentInnerColor; + QFont m_currentFont; + int m_currentWidth; + + PageViewAnnotator * m_annotator; +}; + +void AnnotationActionHandlerPrivate::parseTool( int toolID ) +{ + if( toolID == -1 ) + { + enableConfigActionsForAnnotation( "none" ); + updateConfigActionsToolTips( "none" ); + return; + } + + QDomElement toolElement = m_annotator->builtinTool( toolID ); + const QString annotType = toolElement.attribute( QStringLiteral("type") ); + QDomElement engineElement = toolElement.firstChildElement( QStringLiteral("engine") ); + QDomElement annElement = engineElement.firstChildElement( QStringLiteral("annotation") ); + + if( annElement.hasAttribute( QStringLiteral( "color" ) ) ) + { + if (annotType == "note-inline" || annotType == "typewriter" ) + { + m_currentInnerColor = QColor( annElement.attribute( QStringLiteral( "color" ) ) ); + setInnerColorActionIcon( m_currentInnerColor ); + } + else + { + m_currentColor = QColor( annElement.attribute( QStringLiteral( "color" ) ) ); + setColorActionIcon( m_currentColor ); + } + } + + if( annElement.hasAttribute( QStringLiteral( "innerColor" ) ) ) + m_currentInnerColor = QColor( annElement.attribute( QStringLiteral( "innerColor" ) ) ); + else + m_currentInnerColor = Qt::transparent; + setInnerColorActionIcon( m_currentInnerColor ); + + if( annElement.hasAttribute( QStringLiteral( "textColor" ) ) ) + { + m_currentColor = QColor( annElement.attribute( QStringLiteral( "textColor" ) ) ); + setColorActionIcon( m_currentColor ); + } + + if( annElement.hasAttribute( QStringLiteral( "font" ) ) ) + m_currentFont.fromString( annElement.attribute( QStringLiteral( "font" ) ) ); + + if( annElement.hasAttribute( QStringLiteral( "width" ) ) ) + { + double width = annElement.attribute( QStringLiteral( "width" ) ).toDouble(); + int widthNumber = int( 2*width ); + // FIXME: Add some checks here + if ( widthNumber > 0 ) + m_widthAction->setCurrentItem( widthNumber - 1 ); + } + if( annElement.hasAttribute( QStringLiteral( "opacity" ) ) ) + { + double opacity = annElement.attribute( QStringLiteral( "opacity" ) ).toDouble(); + m_opacityAction->setCurrentItem( int( 10 * opacity) ); + } + else + m_opacityAction->setCurrentItem( 10 ); + + enableConfigActionsForAnnotation( annotType ); + updateConfigActionsToolTips( annotType ); +} + +QIcon AnnotationActionHandlerPrivate::colorizeIcon(QIcon &icon, const QColor &color) +{ + //FIXME: Use GuiUtils::colorize if possible instead or write a decent version of this method + QPixmap iconPixmap = icon.pixmap(32, 32); + QBitmap mask = iconPixmap.createMaskFromColor(QColor(Qt::transparent), Qt::MaskInColor); + iconPixmap.fill(color); + iconPixmap.setMask(mask); + return QIcon(iconPixmap); +} + +void AnnotationActionHandlerPrivate::setColorActionIcon( QColor color ) +{ + // Cannot use the current icon of m_ColorAction to define the mask to avoid trubles + // the alpha channel is zero + QIcon icon = QIcon::fromTheme( QStringLiteral("color-picker") ); + QIcon colorizedColorPicker = colorizeIcon( icon, color ); + m_colorAction->setIcon( colorizedColorPicker ); +} + +void AnnotationActionHandlerPrivate::setInnerColorActionIcon( QColor color ) +{ + // Cannot use the current icon of m_ColorAction to define the mask to avoid trubles + // the alpha channel is zero + QIcon icon = QIcon::fromTheme( QStringLiteral("color-picker") ); + QIcon colorizedColorPicker = colorizeIcon( icon, color ); + m_innerColorAction->setIcon( colorizedColorPicker ); +} + +void AnnotationActionHandlerPrivate::enableConfigActionsForAnnotation( QString annotType ) +{ + if( annotType == QStringLiteral( "note-inline" ) || annotType == QStringLiteral( "typewriter" ) ) { + m_fontAction->setEnabled( true ); + } else { + m_fontAction->setEnabled( false ); + } + if( annotType == QStringLiteral( "stamp" ) || annotType == QStringLiteral( "none" ) ) { + setColorActionIcon( Qt::transparent); + m_colorAction->setEnabled( false ); + m_opacityAction->setEnabled( false ); + } else { + m_colorAction->setEnabled( true ); + m_opacityAction->setEnabled( true ); + } + if( annotType == QStringLiteral( "rectangle" ) || annotType == QStringLiteral( "ellipse" ) + || annotType == QStringLiteral( "polygon" ) || annotType == QStringLiteral( "note-inline" ) ) { + m_innerColorAction->setEnabled( true ); + } else { + setInnerColorActionIcon( Qt::transparent); + m_innerColorAction->setEnabled( false ); + } + if( annotType == QStringLiteral( "ink" ) || + annotType == QStringLiteral( "straight-line" ) || annotType == QStringLiteral( "rectangle" ) + || annotType == QStringLiteral( "ellipse" ) || annotType == QStringLiteral( "polygon" ) ) + { + m_widthAction->setEnabled( true ); + } else { + m_widthAction->setEnabled( false ); + } + if ( annotType == QStringLiteral( "none" ) ) { + m_annAdvancedSettings->setEnabled( false ); + m_bookmark->setEnabled( false ); + } else { + m_annAdvancedSettings->setEnabled( true ); + m_bookmark->setEnabled( true ); + } + +} + +void AnnotationActionHandlerPrivate::updateConfigActionsToolTips( QString annotType ) +{ + if( annotType == QStringLiteral( "note-inline" ) ) + { + m_colorAction->setToolTip( i18n("Annotation text and border color") ); + } + else if ( annotType == QStringLiteral( "typewriter" ) ) + { + m_colorAction->setToolTip( i18n("Annotation text color") ); + } + else if( annotType == QStringLiteral( "rectangle" ) || annotType == QStringLiteral( "ellipse" ) + || annotType == QStringLiteral( "polygon" ) || annotType == QStringLiteral( "note-inline" ) ) + { + m_colorAction->setToolTip( i18n("Annotation border color") ); + } + else if( annotType == QStringLiteral( "none" ) ) + { + m_colorAction->setToolTip( i18n("Annotation color (No annotation selected)") ); + } + else + { + m_colorAction->setToolTip( i18n("Annotation color") ); + } + + if( annotType == QStringLiteral( "rectangle" ) || annotType == QStringLiteral( "ellipse" ) + || annotType == QStringLiteral( "polygon" ) || annotType == QStringLiteral( "note-inline" ) ) + { + m_innerColorAction->setToolTip( i18n("Annotation fill color") ); + } + else if( annotType == QStringLiteral( "none" ) ) + { + m_innerColorAction->setToolTip( i18n("Annotation fill color (No annotation selected)") ); + } + else + { + m_innerColorAction->setToolTip( i18n("Annotation fill color (Current annotation has no fill color)") ); + } + + if( annotType == QStringLiteral( "ink" ) || + annotType == QStringLiteral( "straight-line" ) || annotType == QStringLiteral( "rectangle" ) + || annotType == QStringLiteral( "ellipse" ) || annotType == QStringLiteral( "polygon" ) ) + { + m_widthAction->setToolTip( i18n("Annotation line width") ); + } + else if( annotType == QStringLiteral( "none" ) ) + { + m_widthAction->setToolTip( i18n("Annotation line width (No annotation selected)") ); + } + else + { + m_widthAction->setToolTip( i18n("Annotation line width (Current annotation has no line width)") ); + } + + if( annotType == QStringLiteral( "stamp" ) ) + { + m_opacityAction->setToolTip( i18n("Annotation opacity (Cannot change opacity of current annotation)") ); + } + else if( annotType == QStringLiteral( "none" ) ) + { + m_opacityAction->setToolTip( i18n("Annotation opacity (No annotation selected)") ); + } + else + { + m_opacityAction->setToolTip( i18n("Annotation opacity") ); + } + + if( annotType == QStringLiteral( "note-inline" ) || annotType == QStringLiteral( "typewriter" ) ) + { + m_fontAction->setToolTip( i18n("Annotation font") ); + } + else if( annotType == QStringLiteral( "none" ) ) + { + m_fontAction->setToolTip( i18n("Annotation font (No annotation selected)") ); + } + else + { + m_fontAction->setToolTip( i18n("Annotation font (Current annotation has no font)") ); + } + + if( annotType == QStringLiteral( "none" ) ) + { + m_bookmark->setToolTip( i18n("Add the current annotation to the favorites (No annotation selected)") ); + m_annAdvancedSettings->setToolTip( i18n("Advanced settings for the current annotation tool (No annotation selected)") ); + } + else + { + m_bookmark->setToolTip( i18n("Add the current annotation to the favorites") ); + m_annAdvancedSettings->setToolTip( i18n("Advanced settings for the current annotation tool") ); + } +} + +QIcon AnnotationActionHandlerPrivate::widthIcon( double width ) +{ + QPixmap pm(16, 16); //TODO: Is fixed size ok here? + pm.fill( Qt::transparent ); + QPainter p(&pm); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(QPen(Qt::black, width, Qt::SolidLine, Qt::RoundCap)); + p.drawLine(0, pm.height()/2, pm.width(), pm.height()/2); + p.end(); + + return QIcon( pm ); +} + +QIcon AnnotationActionHandlerPrivate::opacityIcon( int opacity ) +{ + QPixmap pm( 32, 32 ); //TODO: Is fixed size ok here? + pm.fill( Qt::transparent ); + QPainter p( &pm ); + p.setRenderHint( QPainter::Antialiasing ); + p.setPen( Qt::NoPen ); + QColor color( Qt::black ); + color.setAlpha( opacity*255.0/100.0 ); + p.setBrush( QBrush( color ) ); + p.drawRect( 4, 4, 24, 24 ); + p.end(); + + return QIcon( pm ); +} + +AnnotationActionHandler::AnnotationActionHandler( PageViewAnnotator * parent, KActionCollection * ac ) + : QObject( parent ), d( new AnnotationActionHandlerPrivate( this ) ) +{ + d->m_annotator = parent; + + // Text markup + KToggleAction * annHighlighter = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-eraser") ), i18n("Highlighter"), this); + ac->addAction(QStringLiteral("annotation_highlighter"), annHighlighter ); + + KToggleAction * annUnderline = new KToggleAction(QIcon::fromTheme( QStringLiteral("format-text-underline") ), i18n("Underline"), this); + ac->addAction(QStringLiteral("annotation_underline"), annUnderline ); + + KToggleAction * annSquiggle = new KToggleAction(QIcon::fromTheme( QStringLiteral("format-text-underline") ), i18n("Squiggle"), this); + ac->addAction(QStringLiteral("annotation_squiggle"), annSquiggle ); + + KToggleAction * annStrikeout= new KToggleAction(QIcon::fromTheme( QStringLiteral("format-text-strikethrough") ), i18n("Strike out"), this); + ac->addAction(QStringLiteral("annotation_strike_out"), annStrikeout ); + + // Notes + KToggleAction * annTypewriter = new KToggleAction(QIcon::fromTheme( QStringLiteral("text-convert-to-regular") ), i18n("Typewriter"), this); + ac->addAction(QStringLiteral("annotation_typewriter"), annTypewriter ); + + KToggleAction * annInlineNote = new KToggleAction(QIcon::fromTheme( QStringLiteral("note") ), i18n("Inline note"), this); + ac->addAction(QStringLiteral("annotation_inline_note"), annInlineNote ); + + KToggleAction * annPopupNote = new KToggleAction(QIcon::fromTheme( QStringLiteral("edit-comment") ), i18n("Popup note"), this); + ac->addAction(QStringLiteral("annotation_popup_note"), annPopupNote ); + + KToggleAction * annFreehandLine = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-freehand") ), i18n("Freehand line"), this); + ac->addAction(QStringLiteral("annotation_freehand_line"), annFreehandLine ); + + // Geometrical shapes + KToggleAction * annStraightLine = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-line") ), i18n("Straight line"), this); + KToggleAction * annArrow = new KToggleAction(QIcon::fromTheme( QStringLiteral("circular-arrow-shape") ), i18n("Arrow"), this); + KToggleAction * annRectangle = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-rectangle") ), i18n("Rectangle"), this); + KToggleAction * annEllipse = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-ellipse") ), i18n("Ellipse"), this); + KToggleAction * annPolygon = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-polyline") ), i18n("Polygon"), this); + ToolAction *ta = new ToolAction( this ); + ac->addAction( QStringLiteral("annotation_geometrical_shape"), ta ); + ta->addAction( annStraightLine ); + ta->addAction( annArrow ); + ta->addAction( annRectangle ); + ta->addAction( annEllipse ); + ta->addAction( annPolygon ); + + // Favorite annotations + d->m_favorites = new KSelectAction( QIcon::fromTheme( QStringLiteral("bookmarks") ), i18n("Favorites"), this ); + d->m_favorites->setToolTip( i18n("Annotation tools with user-defined settings") ); + d->m_favorites->setToolBarMode( KSelectAction::MenuMode ); + ac->addAction( QStringLiteral("annotation_favorites"), d->m_favorites ); + addFavoriteAnnotations(); + + d->m_bookmark = new QAction(QIcon::fromTheme( QStringLiteral("favorite") ), i18n("Add to favorites"), this); + ac->addAction( QStringLiteral("annotation_bookmark"), d->m_bookmark ); + + // Settings + d->m_widthAction = new KSelectAction( QIcon::fromTheme( QStringLiteral("format-line-spacing-double") ), i18n("Line width"), this ); + d->m_widthAction->setToolBarMode( KSelectAction::MenuMode ); + ac->addAction( QStringLiteral("annotation_settings_width"), d->m_widthAction ); + for( int i = 1; i <= 10; i++ ) { + KToggleAction * ann = new KToggleAction( d->widthIcon( i ), i18n("Width %1").arg( i/2.0 ), this); + d->m_widthAction->addAction( ann ); + connect( ann, &QAction::triggered, [this, i] () { slotSelectAnnotationWidth( i ); } ); + } + + d->m_colorAction = new QAction( QIcon(), i18n("Color"), this); + ac->addAction(QStringLiteral("annotation_settings_color"), d->m_colorAction ); + + d->m_innerColorAction = new QAction( QIcon(), i18n("Inner color"), this); + ac->addAction(QStringLiteral("annotation_settings_inner_color"), d->m_innerColorAction ); + + d->m_fontAction = new QAction( QIcon::fromTheme( QStringLiteral("font-face") ), i18n("Font"), this ); + ac->addAction(QStringLiteral("annotation_settings_font"), d->m_fontAction ); + + d->m_opacityAction = new KSelectAction( QIcon::fromTheme( QStringLiteral("colors-luma") ), i18n("Opacity"), this ); + d->m_opacityAction->setToolBarMode( KSelectAction::MenuMode ); + ac->addAction( QStringLiteral("annotation_settings_opacity"), d->m_opacityAction ); + for( int opacity = 0; opacity <= 100; opacity += 10 ) { + KToggleAction * ann = new KToggleAction( d->opacityIcon( opacity ), QString( "%1\%" ).arg( opacity ), this); + d->m_opacityAction->addAction( ann ); + connect( ann, &QAction::triggered, [this, opacity] () { slotSelectAnnotationOpacity( opacity ); } ); + } + + d->m_annAdvancedSettings = new QAction( QIcon::fromTheme( QStringLiteral("configure-shortcuts") ), i18n("Annotation settings"), this ); + ac->addAction(QStringLiteral("annotation_settings_advanced"), d->m_annAdvancedSettings ); + + d->m_continuousModeAction = new KToggleAction(QIcon::fromTheme( QStringLiteral("port") ), i18n("Pin"), this); + d->m_continuousModeAction->setToolTip( i18n("Keep the annotation tool active after use") ); + d->m_continuousModeAction->setChecked( d->m_annotator->continuousMode() ); + ac->addAction(QStringLiteral("annotation_settings_pin"), d->m_continuousModeAction ); + + // The order in which the actions are added is relevant to connect + // them to the correct tool id. See for loop below. + d->m_annotationToolsGroup = new QActionGroup(this); + d->m_annotationToolsGroup->addAction(annHighlighter); + d->m_annotationToolsGroup->addAction(annUnderline); + d->m_annotationToolsGroup->addAction(annSquiggle); + d->m_annotationToolsGroup->addAction(annStrikeout); + d->m_annotationToolsGroup->addAction(annTypewriter); + d->m_annotationToolsGroup->addAction(annInlineNote); + d->m_annotationToolsGroup->addAction(annPopupNote); + d->m_annotationToolsGroup->addAction(annFreehandLine); + d->m_annotationToolsGroup->addAction(annStraightLine); + d->m_annotationToolsGroup->addAction(annArrow); + d->m_annotationToolsGroup->addAction(annRectangle); + d->m_annotationToolsGroup->addAction(annEllipse); + d->m_annotationToolsGroup->addAction(annPolygon); + + d->m_textAnnotationTools = new QList(); + d->m_textAnnotationTools->append(annHighlighter); + d->m_textAnnotationTools->append(annUnderline); + d->m_textAnnotationTools->append(annSquiggle); + d->m_textAnnotationTools->append(annStrikeout); + + d->enableConfigActionsForAnnotation(); + d->updateConfigActionsToolTips(); + + int i = 1; + foreach( QAction * ann, d->m_annotationToolsGroup->actions() ) + { + connect( ann, &QAction::toggled, [this, i] ( bool checked ) { + if( checked ) + slotToolSelected( i ); + } ); + i++; + } + + connect( d->m_bookmark, &QAction::triggered, d->m_annotator, &PageViewAnnotator::bookmarkAnnotation ); + + connect( d->m_colorAction, &QAction::triggered, this, &AnnotationActionHandler::slotSelectColor ); + connect( d->m_innerColorAction, &QAction::triggered, this, &AnnotationActionHandler::slotSelectInnerColor ); + connect( d->m_fontAction, &QAction::triggered, this, &AnnotationActionHandler::slotSelectAnnotationFont ); + connect( d->m_annAdvancedSettings, &QAction::triggered, this, &AnnotationActionHandler::advancedSettingsTriggered ); + connect( d->m_continuousModeAction, &QAction::toggled , d->m_annotator, &PageViewAnnotator::setContinuousMode ); + + // Workaround to allow unchecking the currently selected annotation action in the QActionGroup + connect( d->m_annotationToolsGroup, &QActionGroup::triggered, [this]( QAction* action ) { + static QAction* lastAction = nullptr; + if (action == lastAction) { + lastAction = nullptr; + d->m_annotationToolsGroup->checkedAction()->setChecked( false ); + d->enableConfigActionsForAnnotation(); + d->updateConfigActionsToolTips(); + d->m_selectedTool = -1; + d->m_annotator->selectTool( -1 ); + } else { + lastAction = action; + } + } ); +} + +void AnnotationActionHandler::addFavoriteAnnotations() +{ + d->m_favorites->removeAllActions(); + int favToolID = 1; + QDomElement favToolElement = d->m_annotator->favoriteTool( favToolID ); + while( !favToolElement.isNull() ) { + QString itemText = favToolElement.attribute( "name" ); + if ( itemText.isEmpty() ) + itemText = PageViewAnnotator::defaultToolName( favToolElement ); + QIcon toolIcon = QIcon( PageViewAnnotator::makeToolPixmap( favToolElement ) ); + QAction * annFav = new QAction( toolIcon, itemText, this); + d->m_favorites->addAction( annFav ); + connect( annFav, &QAction::triggered, [this, favToolID] () { slotFavoriteToolSelected( favToolID ); } ); + favToolElement = d->m_annotator->favoriteTool( ++favToolID ); + } +} + +AnnotationActionHandler::~AnnotationActionHandler() +{ + // delete the private data storage structure + delete d; +} + +void AnnotationActionHandler::deselectAllAnnotationActions() +{ + QAction * checkedAction = d->m_annotationToolsGroup->checkedAction(); + if( checkedAction ) + checkedAction->trigger(); +} + +void AnnotationActionHandler::setToolsEnabled( bool on ) +{ + foreach( QAction * ann, d->m_annotationToolsGroup->actions() ) { + ann->setEnabled( on ); + } + d->m_favorites->setEnabled( on && d->m_favorites->actions().count() > 0 ); + d->m_continuousModeAction->setEnabled( on ); +} + +void AnnotationActionHandler::setTextToolsEnabled( bool on ) +{ + foreach( QAction * ann, *d->m_textAnnotationTools ) { + ann->setEnabled( on ); + } +} + +void AnnotationActionHandler::reparseTools() +{ + d->parseTool( d->m_selectedTool ); + addFavoriteAnnotations(); +} + +void AnnotationActionHandler::slotToolSelected( int toolID ) +{ + d->m_selectedTool = toolID; + d->parseTool( toolID ); + d->m_annotator->selectTool( toolID ); +} + +void AnnotationActionHandler::slotFavoriteToolSelected( int favToolID ) +{ + int toolID = d->m_annotator->setFavoriteTool( favToolID ); + d->m_annotationToolsGroup->actions()[ toolID - 1 ]->trigger(); +} + +void AnnotationActionHandler::slotSelectColor() +{ + QColor selectedColor = QColorDialog::getColor(d->m_currentColor, nullptr, i18n("Select annotation color") ); + if (!selectedColor.isValid()) + return; + + d->m_currentColor = selectedColor; + + d->setColorActionIcon( d->m_currentColor ); + + if ( d->m_currentInnerColor.alpha() != selectedColor.alpha() && d->m_currentInnerColor.alpha() > 0 ) { + d->m_currentInnerColor.setAlpha( selectedColor.alpha() ); + d->setInnerColorActionIcon( d->m_currentInnerColor ); + } + + d->m_annotator->setAnnotationColor( d->m_currentColor ); +} + +void AnnotationActionHandler::slotSelectInnerColor() +{ + QColor selectedColor = QColorDialog::getColor(d->m_currentInnerColor, nullptr, i18n("Select annotation inner color") ); + if (!selectedColor.isValid()) + return; + + d->m_currentInnerColor = selectedColor; + + d->setInnerColorActionIcon( d->m_currentInnerColor ); + + d->m_annotator->setAnnotationInnerColor( d->m_currentInnerColor ); + + if ( selectedColor.alpha() > 0 && d->m_currentColor.alpha() != selectedColor.alpha() ) { + d->m_currentColor.setAlpha( selectedColor.alpha() ); + d->setColorActionIcon( d->m_currentColor ); + d->m_annotator->setAnnotationColor( d->m_currentColor ); + } +} + +void AnnotationActionHandler::slotSelectAnnotationFont() +{ + bool ok; + QFont selectedFont = QFontDialog::getFont(&ok, d->m_currentFont); + if (ok) { + d->m_currentFont = selectedFont; + d->m_annotator->setAnnotationFont( d->m_currentFont ); + } +} + +void AnnotationActionHandler::slotSelectAnnotationWidth( int widthNumber ) +{ + d->m_annotator->setAnnotationWidth( widthNumber / 2.0 ); +} + +void AnnotationActionHandler::slotSelectAnnotationOpacity( int opacity ) +{ + d->m_annotator->setAnnotationOpacity( opacity / 100.0 ); +} + +#include "moc_annotationactionhandler.cpp" diff --git a/ui/data/tools.xml b/ui/data/tools.xml --- a/ui/data/tools.xml +++ b/ui/data/tools.xml @@ -17,63 +17,84 @@ Geom --> - - - + + + 1 - - - + + + 2 - - - + + + 3 - - - + + + 4 - - - + + + + + + + + 5 - - - + + + 6 - - - + + + 7 - - - - + + + + - 8 - - - + + + - 9 - - - + + + + + + + + + + + + + + + + + + 8 + diff --git a/ui/pageview.h b/ui/pageview.h --- a/ui/pageview.h +++ b/ui/pageview.h @@ -249,13 +249,12 @@ void slotAutoFitToggled( bool ); void slotViewMode( QAction *action ); void slotContinuousToggled( bool ); - void slotSetMouseNormal(); + void slotMouseNormalToggled( bool ); void slotSetMouseZoom(); void slotSetMouseMagnifier(); void slotSetMouseSelect(); void slotSetMouseTextSelect(); void slotSetMouseTableSelect(); - void slotToggleAnnotator( bool ); void slotAutoScrollUp(); void slotAutoScrollDown(); void slotScrollUp( bool singleStep = false ); diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -219,7 +220,6 @@ QAction * aMouseTableSelect; QAction * aMouseMagnifier; KToggleAction * aTrimToSelection; - KToggleAction * aToggleAnnotator; KSelectAction * aZoom; QAction * aZoomIn; QAction * aZoomOut; @@ -345,7 +345,6 @@ d->aMouseNormal = nullptr; d->aMouseSelect = nullptr; d->aMouseTextSelect = nullptr; - d->aToggleAnnotator = nullptr; d->aZoomFitWidth = nullptr; d->aZoomFitPage = nullptr; d->aZoomAutoFit = nullptr; @@ -593,7 +592,7 @@ d->mouseModeActionGroup->setExclusive( true ); d->aMouseNormal = new QAction( QIcon::fromTheme( QStringLiteral("input-mouse") ), i18n( "&Browse Tool" ), this ); ac->addAction(QStringLiteral("mouse_drag"), d->aMouseNormal ); - connect( d->aMouseNormal, &QAction::triggered, this, &PageView::slotSetMouseNormal ); + connect( d->aMouseNormal, &QAction::toggled, this, &PageView::slotMouseNormalToggled ); d->aMouseNormal->setIconText( i18nc( "Browse Tool", "Browse" ) ); d->aMouseNormal->setCheckable( true ); ac->setDefaultShortcut(d->aMouseNormal, QKeySequence(Qt::CTRL + Qt::Key_1)); @@ -660,12 +659,6 @@ d->aMouseMagnifier->setActionGroup( d->mouseModeActionGroup ); d->aMouseMagnifier->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier ); - d->aToggleAnnotator = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-freehand") ), i18n("&Review"), this); - ac->addAction(QStringLiteral("mouse_toggle_annotate"), d->aToggleAnnotator ); - d->aToggleAnnotator->setCheckable( true ); - connect( d->aToggleAnnotator, &QAction::toggled, this, &PageView::slotToggleAnnotator ); - ac->setDefaultShortcut(d->aToggleAnnotator, Qt::Key_F6); - ToolAction *ta = new ToolAction( this ); ac->addAction( QStringLiteral("mouse_selecttools"), ta ); ta->addAction( d->aMouseSelect ); @@ -732,6 +725,12 @@ connect(d->document, &Okular::Document::canRedoChanged, kredo, &QAction::setEnabled); kundo->setEnabled(false); kredo->setEnabled(false); + + if( !d->annotator ) { + d->annotator = new PageViewAnnotator( this, d->document ); + connect( d->annotator, &PageViewAnnotator::toolSelected, d->aMouseNormal , &QAction::trigger ); + } + d->annotator->setupActions( ac ); } bool PageView::canFitPageWidth() const @@ -847,12 +846,7 @@ updatePageStep(); if ( d->annotator ) - { - d->annotator->setEnabled( false ); d->annotator->reparseConfig(); - if ( d->aToggleAnnotator->isChecked() ) - slotToggleAnnotator( true ); - } // Something like invert colors may have changed // As we don't have a way to find out the old value @@ -985,13 +979,8 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setupFlags ) { bool documentChanged = setupFlags & Okular::DocumentObserver::DocumentChanged; - const bool allownotes = d->document->isAllowed( Okular::AllowNotes ); const bool allowfillforms = d->document->isAllowed( Okular::AllowFillForms ); - // allownotes may have changed - if ( d->aToggleAnnotator ) - d->aToggleAnnotator->setEnabled( allownotes ); - // reuse current pages if nothing new if ( ( pageSet.count() == d->items.count() ) && !documentChanged && !( setupFlags & Okular::DocumentObserver::NewLayoutForPages ) ) { @@ -1222,14 +1211,6 @@ d->annotator->setToolsEnabled( allowTools ); d->annotator->setTextToolsEnabled( allowTools && d->document->supportsSearching() ); } - if ( d->aToggleAnnotator ) - { - if ( !allowAnnotations && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - } - d->aToggleAnnotator->setEnabled( allowAnnotations ); - } #ifdef HAVE_SPEECH if ( d->aSpeakDoc ) { @@ -5015,32 +4996,27 @@ } } -void PageView::slotSetMouseNormal() +void PageView::slotMouseNormalToggled( bool checked ) { - d->mouseMode = Okular::Settings::EnumMouseMode::Browse; - Okular::Settings::setMouseMode( d->mouseMode ); - // hide the messageWindow - d->messageWindow->hide(); - // 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(); - Okular::Settings::self()->save(); + if( checked ) { + d->mouseMode = Okular::Settings::EnumMouseMode::Browse; + Okular::Settings::setMouseMode( d->mouseMode ); + // hide the messageWindow + d->messageWindow->hide(); + // force an update of the cursor + updateCursor(); + Okular::Settings::self()->save(); + } else { + d->annotator->detachAnnotation(); + } } void PageView::slotSetMouseZoom() { d->mouseMode = Okular::Settings::EnumMouseMode::Zoom; Okular::Settings::setMouseMode( d->mouseMode ); // 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->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); @@ -5063,12 +5039,6 @@ Okular::Settings::setMouseMode( d->mouseMode ); // 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->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); @@ -5080,12 +5050,6 @@ Okular::Settings::setMouseMode( d->mouseMode ); // 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->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); @@ -5099,75 +5063,11 @@ d->messageWindow->display( i18n( "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->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); } -void PageView::slotToggleAnnotator( bool on ) -{ - // the 'inHere' trick is needed as the slotSetMouseZoom() calls this - static bool inHere = false; - if ( inHere ) - return; - inHere = true; - - // the annotator can be used in normal mouse mode only, so if asked for it, - // switch to normal mode - if ( on && d->mouseMode != Okular::Settings::EnumMouseMode::Browse ) - d->aMouseNormal->trigger(); - - // ask for Author's name if not already set - if ( Okular::Settings::identityAuthor().isEmpty() ) - { - // get default username from the kdelibs/kdecore/KUser - KUser currentUser; - QString userName = currentUser.property( KUser::FullName ).toString(); - // ask the user for confirmation/change - if ( userName.isEmpty() ) - { - bool ok = false; - userName = QInputDialog::getText(nullptr, - i18n( "Annotations author" ), - i18n( "Please insert your name or initials:" ), - QLineEdit::Normal, - QString(), - &ok ); - - if ( !ok ) - { - d->aToggleAnnotator->trigger(); - inHere = false; - return; - } - } - // save the name - Okular::Settings::setIdentityAuthor( userName ); - Okular::Settings::self()->save(); - } - - // create the annotator object if not present - if ( !d->annotator ) - { - d->annotator = new PageViewAnnotator( this, d->document ); - bool allowTools = d->document->pages() > 0 && d->document->isAllowed( Okular::AllowNotes ); - d->annotator->setToolsEnabled( allowTools ); - d->annotator->setTextToolsEnabled( allowTools && d->document->supportsSearching() ); - } - - // initialize/reset annotator (and show/hide toolbar) - d->annotator->setEnabled( on ); - d->annotator->setHidingForced( false ); - - inHere = false; -} - void PageView::slotAutoScrollUp() { if ( d->scrollIncrement < -9 ) @@ -5307,12 +5207,6 @@ d->mouseMode = Okular::Settings::EnumMouseMode::TrimSelect; // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Draw a rectangle around the page area you wish to keep visible" ), QString(), PageViewMessage::Info, -1 ); - // force hiding of annotator toolbar - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); } else { diff --git a/ui/pageviewannotator.h b/ui/pageviewannotator.h --- a/ui/pageviewannotator.h +++ b/ui/pageviewannotator.h @@ -10,24 +10,29 @@ #ifndef _OKULAR_PAGEVIEWANNOTATOR_H_ #define _OKULAR_PAGEVIEWANNOTATOR_H_ +#include #include #include #include +#include + #include "pageviewutils.h" #include "annotationtools.h" class QKeyEvent; class QMouseEvent; class QPainter; +class AnnotationActionHandler; namespace Okular { class Document; } // engines are defined and implemented in the cpp class AnnotatorEngine; +class AnnotationTools; class PageView; /** @@ -54,17 +59,16 @@ PageViewAnnotator( PageView * parent, Okular::Document * storage ); ~PageViewAnnotator(); + void setupActions( KActionCollection *ac ); + // called to show/hide the editing toolbar void setEnabled( bool enabled ); // called to toggle the usage of text annotating tools void setTextToolsEnabled( bool enabled ); void setToolsEnabled( bool enabled ); - void setHidingForced( bool forced ); - bool hidingWasForced() const; - // methods used when creating the annotation // @return Is a tool currently selected? bool active() const; @@ -88,25 +92,45 @@ static QString defaultToolName( const QDomElement &toolElement ); static QPixmap makeToolPixmap( const QDomElement &toolElement ); - private Q_SLOTS: - void slotToolSelected( int toolID ); - void slotSaveToolbarOrientation( int side ); - void slotToolDoubleClicked( int toolID ); + void saveAnnotationTools(); + + void selectTool( int toolID ); + int setFavoriteTool ( int toolID ); + void setAnnotationColor( QColor color ); + void setAnnotationInnerColor( QColor color ); + void setAnnotationOpacity( double opacity ); + void setAnnotationFont( QFont font ); + void setAnnotationWidth( double width ); + + QDomElement builtinTool( int toolID ); + QDomElement favoriteTool( int toolID ); + QDomElement currentEngineElement(); + QDomElement currentAnnotationElement(); + bool continuousMode(); - private: void detachAnnotation(); + public Q_SLOTS: + void setContinuousMode( bool enabled ); + void bookmarkAnnotation(); + void slotAdvancedSettings(); + + Q_SIGNALS: + void toolSelected(); + + private: + // global class pointers Okular::Document * m_document; PageView * m_pageView; - PageViewToolBar * m_toolBar; + AnnotationActionHandler * m_actionHandler; AnnotatorEngine * m_engine; - QDomElement m_toolsDefinition; + AnnotationTools * m_toolsDefinition; + AnnotationTools * m_favoriteToolsDefinition; QLinkedList m_items; bool m_textToolsEnabled; bool m_toolsEnabled; bool m_continuousMode; - bool m_hidingWasForced; // creation related variables int m_lastToolID; diff --git a/ui/pageviewannotator.cpp b/ui/pageviewannotator.cpp --- a/ui/pageviewannotator.cpp +++ b/ui/pageviewannotator.cpp @@ -33,10 +33,12 @@ #include // local includes +#include "conf/editannottooldialog.h" #include "core/area.h" #include "core/document.h" #include "core/page.h" #include "core/annotations.h" +#include "ui/annotationactionhandler.h" #include "settings.h" #include "annotationtools.h" #include "guiutils.h" @@ -162,10 +164,7 @@ // set font color if ( m_annotElement.hasAttribute( QStringLiteral("textColor") ) ) { - if ( inplaceIntent == Okular::TextAnnotation::TypeWriter ) - ta->setTextColor( m_annotElement.attribute( QStringLiteral("textColor") ) ); - else - ta->setTextColor( Qt::black ); + ta->setTextColor( m_annotElement.attribute( QStringLiteral("textColor") ) ); } //set width if ( m_annotElement.hasAttribute( QStringLiteral ( "width" ) ) ) @@ -654,64 +653,107 @@ QRect rect; }; +/** @short AnnotationTools*/ +class AnnotationTools +{ + public: + AnnotationTools() : m_toolsCount( 0 ) {} -/** PageViewAnnotator **/ + void setTools( QStringList tools ) + { + // Populate m_toolsDefinition + m_toolsCount = 0; + m_toolsDefinition.clear(); + QDomElement root = m_toolsDefinition.createElement( QStringLiteral( "root" ) ); + m_toolsDefinition.appendChild( root ); + foreach ( const QString &toolXml, tools ) + { + QDomDocument entryParser; + if ( entryParser.setContent( toolXml ) ) { + root.appendChild( m_toolsDefinition.importNode( entryParser.documentElement(), true ) ); + m_toolsCount++; + } else { + qCWarning(OkularUiDebug) << "Skipping malformed tool XML in AnnotationTools setting"; + } + } + } + QStringList toStringList() + { + QStringList tools; + QDomElement toolElement = m_toolsDefinition.documentElement().firstChildElement(); + QString str; + QTextStream stream(&str); + while( !toolElement.isNull() ) + { + str.clear(); + toolElement.save(stream, -1 /* indent disabled */); + tools << str; + toolElement = toolElement.nextSiblingElement(); + } + return tools; + } + + QDomElement tool( int toolID ) + { + QDomElement toolElement = m_toolsDefinition.documentElement().firstChildElement(); + while( !toolElement.isNull() && toolElement.attribute("id").toInt() != toolID ) { + toolElement = toolElement.nextSiblingElement(); + } + return toolElement; // can return a null element + } + + void appendTool( QDomElement toolElement ) + { + toolElement = toolElement.cloneNode().toElement(); + toolElement.setAttribute( QStringLiteral( "id" ), ++m_toolsCount ); + m_toolsDefinition.documentElement().appendChild( toolElement ); + } + + bool updateTool( QDomElement newToolElement, int toolID ) + { + newToolElement = newToolElement.cloneNode().toElement(); + newToolElement.setAttribute( QStringLiteral( "id" ), toolID ); + QDomElement toolElement = tool( toolID ); + QDomNode oldTool = m_toolsDefinition.documentElement().replaceChild( newToolElement, toolElement ); + bool success = !oldTool.isNull(); + return success; + } + + private: + QDomDocument m_toolsDefinition; + int m_toolsCount; + +}; + +/** PageViewAnnotator **/ PageViewAnnotator::PageViewAnnotator( PageView * parent, Okular::Document * storage ) : QObject( parent ), m_document( storage ), m_pageView( parent ), - m_toolBar( nullptr ), m_engine( nullptr ), m_textToolsEnabled( false ), m_toolsEnabled( false ), - m_continuousMode( false ), m_hidingWasForced( false ), m_lastToolID( -1 ), m_lockedItem( nullptr ) + m_actionHandler( nullptr ), m_engine( nullptr ), m_toolsDefinition( nullptr ), + m_favoriteToolsDefinition( nullptr ), m_continuousMode( true ), m_lastToolID( -1 ), + m_lockedItem( nullptr ) { reparseConfig(); } void PageViewAnnotator::reparseConfig() { - m_items.clear(); - // Read tool list from configuration. It's a list of XML elements - const QStringList userTools = Okular::Settings::annotationTools(); + if( !m_toolsDefinition ) + m_toolsDefinition = new AnnotationTools(); + m_toolsDefinition->setTools( Okular::Settings::annotationTools() ); - // Populate m_toolsDefinition - QDomDocument doc; - m_toolsDefinition = doc.createElement( QStringLiteral("annotatingTools") ); - foreach ( const QString &toolXml, userTools ) - { - QDomDocument entryParser; - if ( entryParser.setContent( toolXml ) ) - m_toolsDefinition.appendChild( doc.importNode( entryParser.documentElement(), true ) ); - else - qCWarning(OkularUiDebug) << "Skipping malformed tool XML in AnnotationTools setting"; - } + if( !m_favoriteToolsDefinition ) + m_favoriteToolsDefinition = new AnnotationTools(); + m_favoriteToolsDefinition->setTools( Okular::Settings::favoriteAnnotationTools() ); - // Create the AnnotationToolItems from the XML dom tree - QDomNode toolDescription = m_toolsDefinition.firstChild(); - while ( toolDescription.isElement() ) - { - QDomElement toolElement = toolDescription.toElement(); - if ( toolElement.tagName() == QLatin1String("tool") ) - { - AnnotationToolItem item; - item.id = toolElement.attribute(QStringLiteral("id")).toInt(); - if ( toolElement.hasAttribute( QStringLiteral("name") ) ) - item.text = toolElement.attribute( QStringLiteral("name") ); - else - item.text = defaultToolName( toolElement ); - item.pixmap = makeToolPixmap( toolElement ); - QDomNode shortcutNode = toolElement.elementsByTagName( QStringLiteral("shortcut") ).item( 0 ); - if ( shortcutNode.isElement() ) - item.shortcut = shortcutNode.toElement().text(); - QDomNodeList engineNodeList = toolElement.elementsByTagName( QStringLiteral("engine") ); - if ( engineNodeList.size() > 0 ) - { - QDomElement engineEl = engineNodeList.item( 0 ).toElement(); - if ( !engineEl.isNull() && engineEl.hasAttribute( QStringLiteral("type") ) ) - item.isText = engineEl.attribute( QStringLiteral("type") ) == QLatin1String( "TextSelector" ); - } - m_items.push_back( item ); - } - toolDescription = toolDescription.nextSibling(); - } + m_continuousMode = Okular::Settings::annotationContinuousMode(); + + if ( Okular::Settings::identityAuthor().isEmpty() ) + detachAnnotation(); + + if( m_actionHandler ) + m_actionHandler->reparseTools(); } PageViewAnnotator::~PageViewAnnotator() @@ -723,65 +765,27 @@ { if ( !on ) { - // remove toolBar - if ( m_toolBar ) - m_toolBar->hideAndDestroy(); - m_toolBar = nullptr; // deactivate the active tool, if any - slotToolSelected( -1 ); - return; - } - - // if no tools are defined, don't show the toolbar - if ( !m_toolsDefinition.hasChildNodes() ) + selectTool( -1 ); return; - - // create toolBar - if ( !m_toolBar ) - { - m_toolBar = new PageViewToolBar( m_pageView, m_pageView->viewport() ); - m_toolBar->setSide( (PageViewToolBar::Side)Okular::Settings::editToolBarPlacement() ); - m_toolBar->setItems( m_items ); - m_toolBar->setToolsEnabled( m_toolsEnabled ); - m_toolBar->setTextToolsEnabled( m_textToolsEnabled ); - connect(m_toolBar, &PageViewToolBar::toolSelected, this, &PageViewAnnotator::slotToolSelected); - connect(m_toolBar, &PageViewToolBar::orientationChanged, this, &PageViewAnnotator::slotSaveToolbarOrientation); - - connect(m_toolBar, &PageViewToolBar::buttonDoubleClicked, this, &PageViewAnnotator::slotToolDoubleClicked); - m_toolBar->setCursor(Qt::ArrowCursor); } - - // show the toolBar - m_toolBar->showAndAnimate(); } void PageViewAnnotator::setTextToolsEnabled( bool enabled ) { - m_textToolsEnabled = enabled; - if ( m_toolBar ) - m_toolBar->setTextToolsEnabled( m_textToolsEnabled ); + if ( m_actionHandler ) + m_actionHandler->setTextToolsEnabled( enabled ); } void PageViewAnnotator::setToolsEnabled( bool enabled ) { - m_toolsEnabled = enabled; - if ( m_toolBar ) - m_toolBar->setToolsEnabled( m_toolsEnabled ); -} - -void PageViewAnnotator::setHidingForced( bool forced ) -{ - m_hidingWasForced = forced; -} - -bool PageViewAnnotator::hidingWasForced() const -{ - return m_hidingWasForced; + if ( m_actionHandler ) + m_actionHandler->setToolsEnabled( enabled ); } bool PageViewAnnotator::active() const { - return m_engine && m_toolBar; + return m_engine != nullptr; } bool PageViewAnnotator::annotating() const @@ -864,7 +868,7 @@ } if ( m_continuousMode ) - slotToolSelected( m_lastToolID ); + selectTool( m_lastToolID ); else detachAnnotation(); } @@ -893,18 +897,6 @@ return QRect(); } - // We set all tablet events that take place over the annotations toolbar to ignore so that corresponding mouse - // events will be delivered to the toolbar. However, we still allow the annotations code to handle - // TabletMove and TabletRelease events in case the user is drawing an annotation onto the toolbar. - const QPoint toolBarPos = m_toolBar->mapFromGlobal( e->globalPos() ); - const QRect toolBarRect = m_toolBar->rect(); - if ( toolBarRect.contains( toolBarPos ) ) - { - e->ignore(); - if (e->type() == QEvent::TabletPress) - return QRect(); - } - AnnotatorEngine::EventType eventType; AnnotatorEngine::Button button; @@ -928,7 +920,7 @@ bool PageViewAnnotator::routePaints( const QRect & wantedRect ) const { - return m_engine && m_toolBar && wantedRect.intersects( m_lastDrawnRect ) && m_lockedItem; + return m_engine && wantedRect.intersects( m_lastDrawnRect ) && m_lockedItem; } void PageViewAnnotator::routePaint( QPainter * painter, const QRect & paintRect ) @@ -957,8 +949,38 @@ painter->restore(); } -void PageViewAnnotator::slotToolSelected( int toolID ) +void PageViewAnnotator::selectTool( int toolID ) { + + // ask for Author's name if not already set + if ( toolID > 0 && Okular::Settings::identityAuthor().isEmpty() ) + { + // get default username from the kdelibs/kdecore/KUser + KUser currentUser; + QString userName = currentUser.property( KUser::FullName ).toString(); + userName = ""; + // ask the user for confirmation/change + if ( userName.isEmpty() ) + { + bool ok = false; + userName = QInputDialog::getText(nullptr, + i18n( "Bookmark annotation" ), + i18n( "Insert a custom name for the annotation:" ), + QLineEdit::Normal, + QString(), + &ok ); + + if ( !ok ) + { + detachAnnotation(); + return; + } + } + // save the name + Okular::Settings::setIdentityAuthor( userName ); + Okular::Settings::self()->save(); + } + // terminate any previous operation if ( m_engine ) { @@ -972,7 +994,6 @@ m_lastDrawnRect = QRect(); } - if ( toolID != m_lastToolID ) m_continuousMode = false; // store current tool for later usage m_lastToolID = toolID; @@ -985,38 +1006,25 @@ } // for the selected tool create the Engine - QDomNode toolNode = m_toolsDefinition.firstChild(); - while ( toolNode.isElement() ) + QDomElement toolElement = m_toolsDefinition->tool( toolID ); + if ( !toolElement.isNull() ) { - QDomElement toolElement = toolNode.toElement(); - toolNode = toolNode.nextSibling(); - - // only find out the element describing selected tool - if ( toolElement.tagName() != QLatin1String("tool") || toolElement.attribute(QStringLiteral("id")).toInt() != toolID ) - continue; - // parse tool properties - QDomNode toolSubNode = toolElement.firstChild(); - while ( toolSubNode.isElement() ) + QDomElement engineElement = toolElement.firstChildElement( QStringLiteral("engine") ); + if ( !engineElement.isNull() ) { - QDomElement toolSubElement = toolSubNode.toElement(); - toolSubNode = toolSubNode.nextSibling(); - // create the AnnotatorEngine - if ( toolSubElement.tagName() == QLatin1String("engine") ) - { - QString type = toolSubElement.attribute( QStringLiteral("type") ); - if ( type == QLatin1String("SmoothLine") ) - m_engine = new SmoothPathEngine( toolSubElement ); - else if ( type == QLatin1String("PickPoint") ) - m_engine = new PickPointEngine( toolSubElement ); - else if ( type == QLatin1String("PolyLine") ) - m_engine = new PolyLineEngine( toolSubElement ); - else if ( type == QLatin1String("TextSelector") ) - m_engine = new TextSelectorEngine( toolSubElement, m_pageView ); - else - qCWarning(OkularUiDebug).nospace() << "tools.xml: engine type:'" << type << "' is not defined!"; - } + QString type = engineElement.attribute( QStringLiteral("type") ); + if ( type == QLatin1String("SmoothLine") ) + m_engine = new SmoothPathEngine( engineElement ); + else if ( type == QLatin1String("PickPoint") ) + m_engine = new PickPointEngine( engineElement ); + else if ( type == QLatin1String("PolyLine") ) + m_engine = new PolyLineEngine( engineElement ); + else if ( type == QLatin1String("TextSelector") ) + m_engine = new TextSelectorEngine( engineElement, m_pageView ); + else + qCWarning(OkularUiDebug).nospace() << "tools.xml: engine type:'" << type << "' is not defined!"; // display the tooltip const QString annotType = toolElement.attribute( QStringLiteral("type") ); @@ -1060,25 +1068,154 @@ } m_pageView->updateCursor(); - // stop after parsing selected tool's node - break; } + + if( toolID > 0 ) + emit toolSelected(); +} + +int PageViewAnnotator::setFavoriteTool( int favToolID ) +{ + QDomElement favToolElement = m_favoriteToolsDefinition->tool( favToolID ); + int toolID = favToolElement.attribute( QStringLiteral("sourceId") ).toInt(); + m_toolsDefinition->updateTool( favToolElement, toolID ); + saveAnnotationTools(); + return toolID; } -void PageViewAnnotator::slotSaveToolbarOrientation( int side ) +void PageViewAnnotator::bookmarkAnnotation() { - Okular::Settings::setEditToolBarPlacement( (int)side ); + QDomElement sourceToolElement = m_toolsDefinition->tool( m_lastToolID ); + if( sourceToolElement.isNull() ) + return; + + bool ok = false; + QString itemText = QInputDialog::getText(nullptr, + i18n( "Add favorite annotation" ), + i18n( "Custom annotation name:" ), + QLineEdit::Normal, + defaultToolName( sourceToolElement ), + &ok ); + if( !ok ) + return; + + QDomElement toolElement = sourceToolElement.cloneNode().toElement(); + // Store name attribute only if the user specified a customized name + if ( !itemText.isEmpty() ) + toolElement.setAttribute( QStringLiteral("name"), itemText ); + toolElement.setAttribute( QStringLiteral("sourceId"), sourceToolElement.attribute( QStringLiteral("id") ) ); + m_favoriteToolsDefinition->appendTool( toolElement ); + saveAnnotationTools(); +} + +void PageViewAnnotator::setContinuousMode( bool enabled ) +{ + m_continuousMode = enabled; + Okular::Settings::setAnnotationContinuousMode( enabled ); Okular::Settings::self()->save(); } -void PageViewAnnotator::slotToolDoubleClicked( int /*toolID*/ ) +void PageViewAnnotator::slotAdvancedSettings() +{ + QDomElement toolElement = m_toolsDefinition->tool( m_lastToolID ); + EditAnnotToolDialog t( nullptr, toolElement); + + if ( t.exec() != QDialog::Accepted ) + return; + + QDomElement toolElementUpdated = t.toolXml().documentElement(); + int toolID = toolElement.attribute( QStringLiteral("id") ).toInt(); + m_toolsDefinition->updateTool( toolElementUpdated, toolID ); + m_actionHandler->reparseTools(); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +QDomElement PageViewAnnotator::builtinTool( int toolID ) +{ + return m_toolsDefinition->tool( toolID ); +} + +QDomElement PageViewAnnotator::favoriteTool( int toolID ) +{ + return m_favoriteToolsDefinition->tool( toolID ); +} + +QDomElement PageViewAnnotator::currentEngineElement() { - m_continuousMode = true; + return m_toolsDefinition->tool( m_lastToolID ).firstChildElement( QStringLiteral("engine") ); +} + +QDomElement PageViewAnnotator::currentAnnotationElement() +{ + return currentEngineElement().firstChildElement( QStringLiteral("annotation") ); +} + +bool PageViewAnnotator::continuousMode() +{ + return m_continuousMode; +} + +void PageViewAnnotator::setAnnotationColor( QColor color ) +{ + currentEngineElement().setAttribute( QStringLiteral( "color" ), color.name(QColor::HexRgb ) ); + QDomElement annotationElement = currentAnnotationElement(); + QString annotType = annotationElement.attribute( QStringLiteral( "type" ) ); + if ( annotType == "Typewriter" || annotType == "FreeText" ) { + annotationElement.setAttribute( QStringLiteral( "textColor" ), color.name( QColor::HexRgb ) ); + } else { + annotationElement.setAttribute( QStringLiteral( "color" ), color.name( QColor::HexRgb ) ); + } + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationInnerColor( QColor color ) +{ + QDomElement annotationElement = currentAnnotationElement(); + QString annotType = annotationElement.attribute( QStringLiteral( "type" ) ); + if ( annotType == "FreeText" ) { + annotationElement.setAttribute( QStringLiteral( "color" ), color.name( QColor::HexRgb ) ); + } else { + annotationElement.setAttribute( QStringLiteral( "innerColor" ), color.name( QColor::HexRgb ) ); + } + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationOpacity( double opacity ) +{ + currentAnnotationElement().setAttribute( QStringLiteral( "opacity" ), opacity ); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationFont( QFont font ) +{ + currentAnnotationElement().setAttribute( QStringLiteral( "font" ), font.toString() ); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationWidth( double width) +{ + currentAnnotationElement().setAttribute( QStringLiteral( "width" ), width ); + saveAnnotationTools(); + selectTool( m_lastToolID ); } void PageViewAnnotator::detachAnnotation() { - m_toolBar->selectButton( -1 ); + selectTool( -1 ); + if( m_actionHandler ) + m_actionHandler->deselectAllAnnotationActions(); +} + +void PageViewAnnotator::saveAnnotationTools() +{ + Okular::Settings::setAnnotationTools( m_toolsDefinition->toStringList() ); + Okular::Settings::setFavoriteAnnotationTools( m_favoriteToolsDefinition->toStringList() ); + Okular::Settings::self()->save(); } QString PageViewAnnotator::defaultToolName( const QDomElement &toolElement ) @@ -1276,6 +1413,15 @@ return pixmap; } +void PageViewAnnotator::setupActions( KActionCollection * ac ) +{ + if ( !m_actionHandler ) + { + m_actionHandler = new AnnotationActionHandler( this, ac ); + connect( m_actionHandler, &AnnotationActionHandler::advancedSettingsTriggered, this, &PageViewAnnotator::slotAdvancedSettings ); + } +} + #include "moc_pageviewannotator.cpp" /* kate: replace-tabs on; indent-width 4; */ diff --git a/ui/pageviewutils.h b/ui/pageviewutils.h --- a/ui/pageviewutils.h +++ b/ui/pageviewutils.h @@ -17,13 +17,13 @@ #include #include #include -#include - #include "core/area.h" +class KActionCollection; class QAction; class QLabel; +class QMenu; class QTimer; class FormWidgetIface; class PageView; @@ -144,83 +144,4 @@ bool isText; }; -class ToolBarButton : public QToolButton -{ - Q_OBJECT - public: - static const int iconSize = 32; - static const int buttonSize = 40; - - ToolBarButton( QWidget * parent, const AnnotationToolItem &item ); - int buttonID() const { return m_id; } - bool isText() const { return m_isText; } - - Q_SIGNALS: - void buttonDoubleClicked( int buttonID ); - - protected: - void mouseDoubleClickEvent( QMouseEvent * event ) override; - - private: - int m_id; - bool m_isText; -}; - -/** - * @short A widget containing exclusive buttons, that slides in from a side. - * - * This is a shaped widget that slides in from a side of the 'anchor widget' - * it's attached to. It can be dragged and docked on {left,top,right,bottom} - * sides and contains toggable exclusive buttons. - * When a 'tool' of this 'toolBar' is selected, a signal is emitted. - */ -class PageViewToolBar : public QWidget -{ - Q_OBJECT - public: - PageViewToolBar( PageView * parent, QWidget * anchorWidget ); - ~PageViewToolBar(); - - // animated widget controls - enum Side { Left = 0, Top = 1, Right = 2, Bottom = 3 }; - - void setItems( const QLinkedList &items ); - void setSide( Side side ); - - void showAndAnimate(); - void hideAndDestroy(); - - void selectButton( int id ); - - void setToolsEnabled( bool on ); - void setTextToolsEnabled( bool on ); - - // query properties - - Q_SIGNALS: - // the tool 'toolID' has been selected - void toolSelected( int toolID ); - // orientation has been changed - void orientationChanged( int side ); - // a tool button of this toolbar has been double clicked - void buttonDoubleClicked( int buttonID ); - - protected: - // handle widget events { anchor_resize, paint, animation, drag } - bool eventFilter( QObject * o, QEvent * e ) override; - void paintEvent( QPaintEvent * ) override; - void mousePressEvent( QMouseEvent * e ) override; - void mouseMoveEvent( QMouseEvent * e ) override; - void mouseReleaseEvent( QMouseEvent * e ) override; - - private: - // private variables - friend class ToolBarPrivate; - class ToolBarPrivate * d; - - private Q_SLOTS: - void slotAnimate(); - void slotButtonClicked(); -}; - #endif diff --git a/ui/pageviewutils.cpp b/ui/pageviewutils.cpp --- a/ui/pageviewutils.cpp +++ b/ui/pageviewutils.cpp @@ -14,30 +14,14 @@ // qt/kde includes #include -#include -#include -#include -#include -#include +#include #include -#include -#include #include -#include -#include -#include -#include #include -#include - -// system includes -#include // local includes #include "formwidgets.h" -#include "pageview.h" #include "videowidget.h" -#include "core/movie.h" #include "core/page.h" #include "core/form.h" #include "settings.h" @@ -429,520 +413,4 @@ hide(); } - -/*********************/ -/** PageViewToolBar */ -/*********************/ - -ToolBarButton::ToolBarButton( QWidget * parent, const AnnotationToolItem &item ) - : QToolButton( parent ), m_id( item.id ), m_isText( item.isText ) -{ - setCheckable( true ); - setAutoRaise( true ); - resize( buttonSize, buttonSize ); - setIconSize( QSize( iconSize, iconSize ) ); - setIcon( QIcon( item.pixmap ) ); - // set shortcut if defined - if ( !item.shortcut.isEmpty() ) - setShortcut( QKeySequence( item.shortcut ) ); - else - KAcceleratorManager::setNoAccel( this ); - - // if accel is set display it along name - QString accelString = shortcut().toString( QKeySequence::NativeText ); - if ( !accelString.isEmpty() ) - setToolTip( QStringLiteral("%1 [%2]").arg( item.text, accelString ) ); - else - setToolTip( item.text ); -} - -void ToolBarButton::mouseDoubleClickEvent( QMouseEvent * /*event*/ ) -{ - emit buttonDoubleClicked( buttonID() ); -} - -/* PageViewToolBar */ - -static const int toolBarGridSize = 40; - -class ToolBarPrivate -{ -public: - ToolBarPrivate( PageViewToolBar * qq ) - : q( qq ) - { - } - - // rebuild contents and reposition then widget - void buildToolBar(); - void reposition(); - // compute the visible and hidden positions along current side - QPoint getInnerPoint() const; - QPoint getOuterPoint() const; - void selectButton( ToolBarButton * button ); - - PageViewToolBar * q; - - // anchored widget and side - QWidget * anchorWidget; - PageViewToolBar::Side anchorSide; - - // slide in/out stuff - QTimer * animTimer; - QPoint currentPosition; - QPoint endPosition; - bool hiding; - bool visible; - - // background pixmap and buttons - QPixmap backgroundPixmap; - QLinkedList< ToolBarButton * > buttons; -}; - -PageViewToolBar::PageViewToolBar( PageView * parent, QWidget * anchorWidget ) - : QWidget( parent ), d( new ToolBarPrivate( this ) ) -{ - // initialize values of the private data storage structure - d->anchorWidget = anchorWidget; - d->anchorSide = Left; - d->hiding = false; - d->visible = false; - - // create the animation timer - d->animTimer = new QTimer( this ); - connect( d->animTimer, &QTimer::timeout, this, &PageViewToolBar::slotAnimate ); - - // apply a filter to get notified when anchor changes geometry - d->anchorWidget->installEventFilter( this ); - - setContextMenuPolicy( Qt::ActionsContextMenu ); - addAction( parent->actionCollection()->action( QStringLiteral("options_configure_annotations") ) ); -} - -PageViewToolBar::~PageViewToolBar() -{ - // delete the private data storage structure - delete d; -} - -void PageViewToolBar::setItems( const QLinkedList &items ) -{ - // delete buttons if already present - if ( !d->buttons.isEmpty() ) - { - QLinkedList< ToolBarButton * >::iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; it != end; ++it ) - delete *it; - d->buttons.clear(); - } - - // create new buttons for given items - QLinkedList::const_iterator it = items.begin(), end = items.end(); - for ( ; it != end; ++it ) - { - ToolBarButton * button = new ToolBarButton( this, *it ); - connect(button, &ToolBarButton::clicked, this, &PageViewToolBar::slotButtonClicked); - connect(button, &ToolBarButton::buttonDoubleClicked, this, &PageViewToolBar::buttonDoubleClicked); - d->buttons.append( button ); - } - - // rebuild toolbar shape and contents - d->reposition(); -} - -void PageViewToolBar::setSide( Side side ) -{ - d->anchorSide = side; - - d->reposition(); -} - -void PageViewToolBar::showAndAnimate() -{ - // set parameters for sliding in - d->hiding = false; - - show(); - -#ifdef OKULAR_ANIMATE_REVIEW_TOOBAR - // start scrolling in - d->animTimer->start( 20 ); -#else - d->currentPosition = d->endPosition; - - move( d->currentPosition ); - - d->visible = true; -#endif -} - -void PageViewToolBar::hideAndDestroy() -{ - // set parameters for sliding out - d->hiding = true; - d->endPosition = d->getOuterPoint(); - -#ifdef OKULAR_ANIMATE_REVIEW_TOOBAR - // start scrolling out - d->animTimer->start( 20 ); -#else - d->currentPosition = d->endPosition; - - move( d->currentPosition ); - - d->visible = false; - deleteLater(); -#endif -} - -void PageViewToolBar::selectButton( int id ) -{ - ToolBarButton * button = nullptr; - if ( id >= 0 && id < d->buttons.count() ) - button = *(d->buttons.begin() + id); - else - { - QLinkedList< ToolBarButton * >::const_iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; !button && it != end; ++it ) - if ( (*it)->isChecked() ) - button = *it; - if ( button ) - button->setChecked( false ); - } - d->selectButton( button ); -} - -bool PageViewToolBar::eventFilter( QObject * obj, QEvent * e ) -{ - // if anchorWidget changed geometry reposition toolbar - if ( obj == d->anchorWidget && e->type() == QEvent::Resize ) - { - d->animTimer->stop(); - if ( d->hiding ) - deleteLater(); - else - d->reposition(); - } - - // don't block event - return false; -} - -void PageViewToolBar::paintEvent( QPaintEvent * e ) -{ - // paint the internal pixmap over the widget - QPainter p( this ); - p.drawImage( e->rect().topLeft(), d->backgroundPixmap.toImage(), e->rect() ); -} - -void PageViewToolBar::mousePressEvent( QMouseEvent * e ) -{ - // set 'dragging' cursor - if ( e->button() == Qt::LeftButton ) - setCursor( Qt::SizeAllCursor ); -} - -void PageViewToolBar::mouseMoveEvent( QMouseEvent * e ) -{ - if ( ( QApplication::mouseButtons() & Qt::LeftButton ) != Qt::LeftButton ) - return; - - // compute the nearest side to attach the widget to - QPoint parentPos = mapToParent( e->pos() ); - float nX = (float)parentPos.x() / (float)d->anchorWidget->width(), - nY = (float)parentPos.y() / (float)d->anchorWidget->height(); - if ( nX > 0.3 && nX < 0.7 && nY > 0.3 && nY < 0.7 ) - return; - bool LT = nX < (1.0 - nY); - bool LB = nX < (nY); - Side side = LT ? ( LB ? Left : Top ) : ( LB ? Bottom : Right ); - - // check if side changed - if ( side == d->anchorSide ) - return; - - d->anchorSide = side; - d->reposition(); - emit orientationChanged( (int)side ); -} - -void PageViewToolBar::mouseReleaseEvent( QMouseEvent * e ) -{ - // set normal cursor - if ( e->button() == Qt::LeftButton ) - setCursor( Qt::ArrowCursor ); -} - -void ToolBarPrivate::buildToolBar() -{ - int buttonsNumber = buttons.count(), - parentWidth = anchorWidget->width(), - parentHeight = anchorWidget->height(), - myCols = 1, - myRows = 1; - - // 1. find out columns and rows we're going to use - bool topLeft = anchorSide == PageViewToolBar::Left || anchorSide == PageViewToolBar::Top; - bool vertical = anchorSide == PageViewToolBar::Left || anchorSide == PageViewToolBar::Right; - if ( vertical ) - { - myCols = 1 + (buttonsNumber * toolBarGridSize) / - (parentHeight - toolBarGridSize); - myRows = (int)ceil( (float)buttonsNumber / (float)myCols ); - } - else - { - myRows = 1 + (buttonsNumber * toolBarGridSize) / - (parentWidth - toolBarGridSize); - myCols = (int)ceil( (float)buttonsNumber / (float)myRows ); - } - - // 2. compute widget size (from rows/cols) - int myWidth = myCols * toolBarGridSize, - myHeight = myRows * toolBarGridSize, - xOffset = (toolBarGridSize - ToolBarButton::buttonSize) / 2, - yOffset = (toolBarGridSize - ToolBarButton::buttonSize) / 2; - - if ( vertical ) - { - myHeight += 16; - myWidth += 4; - yOffset += 12; - if ( anchorSide == PageViewToolBar::Right ) - xOffset += 4; - } - else - { - myWidth += 16; - myHeight += 4; - xOffset += 12; - if ( anchorSide == PageViewToolBar::Bottom ) - yOffset += 4; - } - - bool prevUpdates = q->updatesEnabled(); - q->setUpdatesEnabled( false ); - - // 3. resize pixmap, mask and widget - QBitmap mask( myWidth + 1, myHeight + 1 ); - backgroundPixmap = QPixmap( myWidth + 1, myHeight + 1 ); - backgroundPixmap.fill(Qt::transparent); - q->resize( myWidth + 1, myHeight + 1 ); - - // 4. create and set transparency mask // 4. draw background - QPainter maskPainter( &mask); - mask.fill( Qt::white ); - maskPainter.setBrush( Qt::black ); - if ( vertical ) - maskPainter.drawRoundRect( topLeft ? -10 : 0, 0, myWidth + 11, myHeight, 2000 / (myWidth + 10), 2000 / myHeight ); - else - maskPainter.drawRoundRect( 0, topLeft ? -10 : 0, myWidth, myHeight + 11, 2000 / myWidth, 2000 / (myHeight + 10) ); - maskPainter.end(); - q->setMask( mask ); - - // 5. draw background - QPainter bufferPainter( &backgroundPixmap ); - bufferPainter.translate( 0.5, 0.5 ); - QPalette pal = q->palette(); - // 5.1. draw horizontal/vertical gradient - QLinearGradient grad; - switch ( anchorSide ) - { - case PageViewToolBar::Left: - grad = QLinearGradient( 0, 1, myWidth + 1, 1 ); - break; - case PageViewToolBar::Right: - grad = QLinearGradient( myWidth + 1, 1, 0, 1 ); - break; - case PageViewToolBar::Top: - grad = QLinearGradient( 1, 0, 1, myHeight + 1 ); - break; - case PageViewToolBar::Bottom: - grad = QLinearGradient( 1, myHeight + 1, 0, 1 ); - break; - } - grad.setColorAt( 0, pal.color( QPalette::Active, QPalette::Button ) ); - grad.setColorAt( 1, pal.color( QPalette::Active, QPalette::Light ) ); - bufferPainter.setBrush( QBrush( grad ) ); - // 5.2. draw rounded border - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Dark ).lighter( 140 ) ); - bufferPainter.setRenderHints( QPainter::Antialiasing ); - if ( vertical ) - bufferPainter.drawRoundRect( topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight ); - else - bufferPainter.drawRoundRect( 0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10) ); - // 5.3. draw handle - bufferPainter.translate( -0.5, -0.5 ); - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Mid ) ); - if ( vertical ) - { - int dx = anchorSide == PageViewToolBar::Left ? 2 : 4; - bufferPainter.drawLine( dx, 6, dx + myWidth - 8, 6 ); - bufferPainter.drawLine( dx, 9, dx + myWidth - 8, 9 ); - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Light ) ); - bufferPainter.drawLine( dx + 1, 7, dx + myWidth - 7, 7 ); - bufferPainter.drawLine( dx + 1, 10, dx + myWidth - 7, 10 ); - } - else - { - int dy = anchorSide == PageViewToolBar::Top ? 2 : 4; - bufferPainter.drawLine( 6, dy, 6, dy + myHeight - 8 ); - bufferPainter.drawLine( 9, dy, 9, dy + myHeight - 8 ); - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Light ) ); - bufferPainter.drawLine( 7, dy + 1, 7, dy + myHeight - 7 ); - bufferPainter.drawLine( 10, dy + 1, 10, dy + myHeight - 7 ); - } - bufferPainter.end(); - - // 6. reposition buttons (in rows/col grid) - int gridX = 0, - gridY = 0; - QLinkedList< ToolBarButton * >::const_iterator it = buttons.begin(), end = buttons.end(); - for ( ; it != end; ++it ) - { - ToolBarButton * button = *it; - button->move( gridX * toolBarGridSize + xOffset, - gridY * toolBarGridSize + yOffset ); - button->show(); - if ( ++gridX == myCols ) - { - gridX = 0; - gridY++; - } - } - - q->setUpdatesEnabled( prevUpdates ); -} - -void ToolBarPrivate::reposition() -{ - // note: hiding widget here will gives better gfx, but ends drag operation - // rebuild widget and move it to its final place - buildToolBar(); - if ( !visible ) - { - currentPosition = getOuterPoint(); - endPosition = getInnerPoint(); - } - else - { - currentPosition = getInnerPoint(); - endPosition = getOuterPoint(); - } - q->move( currentPosition ); - - // repaint all buttons (to update background) - QLinkedList< ToolBarButton * >::const_iterator it = buttons.begin(), end = buttons.end(); - for ( ; it != end; ++it ) - (*it)->update(); -} - -QPoint ToolBarPrivate::getInnerPoint() const -{ - // returns the final position of the widget - QPoint newPos; - switch ( anchorSide ) - { - case PageViewToolBar::Left: - newPos = QPoint( 0, ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Top: - newPos = QPoint( ( anchorWidget->width() - q->width() ) / 2, 0 ); - break; - case PageViewToolBar::Right: - newPos = QPoint( anchorWidget->width() - q->width(), ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Bottom: - newPos = QPoint( ( anchorWidget->width() - q->width()) / 2, anchorWidget->height() - q->height() ); - break; - } - return newPos + anchorWidget->pos(); -} - -QPoint ToolBarPrivate::getOuterPoint() const -{ - // returns the point from which the transition starts - QPoint newPos; - switch ( anchorSide ) - { - case PageViewToolBar::Left: - newPos = QPoint( -q->width(), ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Top: - newPos = QPoint( ( anchorWidget->width() - q->width() ) / 2, -q->height() ); - break; - case PageViewToolBar::Right: - newPos = QPoint( anchorWidget->width(), ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Bottom: - newPos = QPoint( ( anchorWidget->width() - q->width() ) / 2, anchorWidget->height() ); - break; - } - return newPos + anchorWidget->pos(); -} - -void PageViewToolBar::slotAnimate() -{ - // move currentPosition towards endPosition - int dX = d->endPosition.x() - d->currentPosition.x(), - dY = d->endPosition.y() - d->currentPosition.y(); - dX = dX / 6 + qMax( -1, qMin( 1, dX) ); - dY = dY / 6 + qMax( -1, qMin( 1, dY) ); - d->currentPosition.setX( d->currentPosition.x() + dX ); - d->currentPosition.setY( d->currentPosition.y() + dY ); - - // move the widget - move( d->currentPosition ); - - // handle arrival to the end - if ( d->currentPosition == d->endPosition ) - { - d->animTimer->stop(); - if ( d->hiding ) - { - d->visible = false; - deleteLater(); - } - else - { - d->visible = true; - } - } -} - -void PageViewToolBar::slotButtonClicked() -{ - ToolBarButton * button = qobject_cast( sender() ); - d->selectButton( button ); -} - -void ToolBarPrivate::selectButton( ToolBarButton * button ) -{ - if ( button ) - { - // deselect other buttons - QLinkedList< ToolBarButton * >::const_iterator it = buttons.begin(), end = buttons.end(); - for ( ; it != end; ++it ) - if ( *it != button ) - (*it)->setChecked( false ); - // emit signal (-1 if button has been unselected) - emit q->toolSelected( button->isChecked() ? button->buttonID() : -1 ); - } -} - -void PageViewToolBar::setToolsEnabled( bool on ) -{ - QLinkedList< ToolBarButton * >::const_iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; it != end; ++it ) - (*it)->setEnabled( on ); -} - -void PageViewToolBar::setTextToolsEnabled( bool on ) -{ - QLinkedList< ToolBarButton * >::const_iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; it != end; ++it ) - if ( (*it)->isText() ) - (*it)->setEnabled( on ); -} - #include "moc_pageviewutils.cpp" diff --git a/ui/toolaction.cpp b/ui/toolaction.cpp --- a/ui/toolaction.cpp +++ b/ui/toolaction.cpp @@ -49,7 +49,7 @@ button->setFocusPolicy( Qt::NoFocus ); button->setIconSize( toolBar->iconSize() ); button->setToolButtonStyle( toolBar->toolButtonStyle() ); - button->setPopupMode( QToolButton::DelayedPopup ); + button->setPopupMode( QToolButton::MenuButtonPopup ); button->setMenu( new QMenu( button ) ); button->setCheckable( true ); connect(toolBar, &QToolBar::iconSizeChanged, button, &QToolButton::setIconSize); @@ -68,7 +68,6 @@ if ( action->isChecked() ) button->setDefaultAction( action ); } - button->setToolTip( i18n("Click to use the current selection tool\nClick and hold to choose another selection tool") ); } return button; @@ -80,7 +79,6 @@ if ( button ) { button->setDefaultAction( action ); - button->setToolTip( i18n("Click to use the current selection tool\nClick and hold to choose another selection tool") ); } }