diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,7 +387,7 @@ ui/thumbnaillist.cpp ui/toc.cpp ui/tocmodel.cpp - ui/toolaction.cpp + ui/toggleactionmenu.cpp ui/videowidget.cpp ui/layers.cpp ui/signatureguiutils.cpp diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -73,7 +73,7 @@ #include "pageviewannotator.h" #include "pageviewmouseannotation.h" #include "priorities.h" -#include "toolaction.h" +#include "toggleactionmenu.h" #include "okmenutitle.h" #ifdef HAVE_SPEECH #include "tts.h" @@ -236,6 +236,7 @@ QAction * aSpeakStop; KActionCollection * actionCollection; QActionGroup * mouseModeActionGroup; + ToggleActionMenu * aMouseModeMenu; QAction * aFitWindowToPage; int setting_viewCols; @@ -654,18 +655,23 @@ d->aMouseMagnifier->setActionGroup( d->mouseModeActionGroup ); d->aMouseMagnifier->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier ); + // Mouse-Mode action menu + d->aMouseModeMenu = new ToggleActionMenu( QIcon(),QString(), this, + QToolButton::MenuButtonPopup, + ToggleActionMenu::ImplicitDefaultAction ); + d->aMouseModeMenu->addAction( d->aMouseSelect ); + d->aMouseModeMenu->addAction( d->aMouseTextSelect ); + d->aMouseModeMenu->addAction( d->aMouseTableSelect ); + d->aMouseModeMenu->suggestDefaultAction( d->aMouseTextSelect ); + d->aMouseModeMenu->setText( i18nc( "@action", "Selection Tools" ) ); + ac->addAction( QStringLiteral( "mouse_selecttools" ), d->aMouseModeMenu ); + 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->aMouseTextSelect ); - ta->addAction( d->aMouseSelect ); - ta->addAction( d->aMouseTableSelect ); - // speak actions #ifdef HAVE_SPEECH d->aSpeakDoc = new QAction( QIcon::fromTheme( QStringLiteral("text-speak") ), i18n( "Speak Whole Document" ), this ); @@ -1198,6 +1204,8 @@ if ( d->mouseModeActionGroup ) d->mouseModeActionGroup->setEnabled( haspages ); + if ( d->aMouseModeMenu ) + d->aMouseModeMenu->setEnabled( haspages ); if ( d->aRotateClockwise ) d->aRotateClockwise->setEnabled( haspages ); diff --git a/ui/toggleactionmenu.h b/ui/toggleactionmenu.h new file mode 100644 --- /dev/null +++ b/ui/toggleactionmenu.h @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hurka * + * * + * Inspired by and replacing toolaction.h by: * + * Copyright (C) 2004-2006 by Albert Astals Cid * + * * + * 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 TOGGLEACTIONMENU_H +#define TOGGLEACTIONMENU_H + +#include +#include +#include + +/** + * @brief A KActionMenu, with allows to set the default action of its toolbar buttons. + * + * Usually, a KActionMenu creates toolbar buttons which reflect its own action properties + * (icon, text, tooltip, checked state,...), as it is a QAction itself. + * + * ToggleActionMenu will use its own action properties only when plugged as submenu in another menu. + * The default action of the toolbar buttons can easily be changed with the slot setDefaultAction(). + * + * Naming: The user can *Toggle* the checked state of an *Action* by directly clicking the toolbar button, + * but can also open a *Menu*. + * + * @par Intention + * Setting the default action of the toolbar button can be useful for: + * * Providing the most propably needed entry of a menu directly on the menu button. + * * Showing the last used menu entry on the menu button, including its checked state. + * The advantage is that the user often does not need to open the menu, + * and that the toolbar button shows additional information + * like checked state or the user's last selection. + * + * This shall replace the former ToolAction in Okular, + * while beeing flexible enough for other (planned) action menus. + */ +class ToggleActionMenu : public KActionMenu +{ + Q_OBJECT + +public: + /** + * Defines how the menu behaves. + */ + enum MenuLogic { + DefaultLogic = 0x0, + /** + * Automatically makes the triggered action the default action, even if in a submenu. + * When a toolbar button is constructed, + * the default action is set to the default action set with setDefaultAction() before, + * otherwise to the first checked action in the menu, + * otherwise to the action suggested with suggestDefaultAction(). + */ + ImplicitDefaultAction = 0x1 + }; + + enum PopupMode { + DelayedPopup, + MenuButtonPopup + } + + explicit ToggleActionMenu( QObject *parent ); + ToggleActionMenu( const QString &text, QObject * parent ); + /** + * Constructs an empty ToggleActionMenu. + * + * @param icon The icon of this menu, when plugged into another menu. + * @param text The name of this menu, when plugged into another menu. + * @param parent Parent @c QOject. + * @param popupMode The popup mode of the toolbar buttons. + * You will want to use @c DelayedPopup or @c MenuButtonPopup, + * @c InstantPopup would make @c ToggleActionMenu pointless. + * @param logic To define special behaviour of @c ToggleActionMenu, + * to simplify the usage. + */ + ToggleActionMenu( const QIcon &icon, + const QString &text, + QObject *parent, + PopupMode popupMode = MenuButtonPopup, + MenuLogic logic = DefaultLogic + ); + + QWidget *createWidget( QWidget *parent ) override; + + /** + * Returns the current default action of the toolbar buttons. + * + * In ImplicitDefaultAction mode, + * when the default action was not yet set with setDefaultAction(), + * it will determine it from the first checked action in the menu, + * otherwise from the action set with suggestDefaultAction(). + */ + QAction *defaultAction(); + + /** + * Suggests a default action to be used as fallback. + * + * It will be used if the default action is not determined another way. + * This is useful for ImplicitDefaultAction mode, + * when you can not guarrantee that one action in the menu + * will be checked. + * + * @note + * In DefaultLogic mode, or when you already have called setDefaultAction(), + * you have to use setDefaultAction() instead. + */ + void suggestDefaultAction( QAction * action ); + +public slots: + /** + * Sets the default action of the toolbar buttons. + * + * This action will be triggered by clicking directly on the toolbar buttons. + * It will also set the text, icon, checked state, etc. of the toolbar buttons. + * + * @note + * The default action will not set the enabled state or popup mode of the menu buttons. + * These properties are still set by the corresponding properties of this ToggleActionMenu. + * + * @warning + * The action will not be added to the menu, + * it usually makes sense to addAction() it before to setDefaultAction() it. + * + * @see suggestDefaultAction() + */ + void setDefaultAction( QAction *action ); + +private: + QAction *m_defaultAction; + QAction *m_suggestedDefaultAction; + QList< QPointer< QToolButton > > m_buttons; + MenuLogic m_menuLogic; + + /** + * Returns the first checked action in @p menu and its submenus, + * or nullptr if no action is checked. + */ + QAction *checkedAction( QMenu *menu ) const; + +private slots: + /** + * Updates the toolbar buttons, using both the default action and properties of this menu itself. + * + * This ensures that the toolbar buttons reflect e. g. a disabled state of this menu. + */ + void updateButtons(); +}; + +#endif // TOGGLEACTIONMENU_H diff --git a/ui/toggleactionmenu.cpp b/ui/toggleactionmenu.cpp new file mode 100644 --- /dev/null +++ b/ui/toggleactionmenu.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hurka * + * * + * Inspired by and replacing toolaction.h by: * + * Copyright (C) 2004-2006 by Albert Astals Cid * + * * + * 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 "toggleactionmenu.h" + +#include +#include + +ToggleActionMenu::ToggleActionMenu( QObject * parent ) + : ToggleActionMenu( QIcon(), QString(), parent ) +{ +} + +ToggleActionMenu::ToggleActionMenu( const QString &text, QObject * parent ) + : ToggleActionMenu( QIcon(), text, parent ) +{ +} + +ToggleActionMenu::ToggleActionMenu( const QIcon &icon, + const QString &text, + QObject * parent, + PopupMode popupMode, + MenuLogic logic ) + : KActionMenu( icon, text, parent ), + m_defaultAction( nullptr ), + m_suggestedDefaultAction( nullptr ), + m_menuLogic( logic ) +{ + connect( this, &QAction::changed, this, &ToggleActionMenu::updateButtons ); + + if ( popupMode == DelayedPopup ) + { + setDelayed( true ); + } + else + { + setDelayed( false ); + } + setStickyMenu( false ); + + if ( logic & ImplicitDefaultAction ) + { + connect( menu(), &QMenu::triggered, this, &ToggleActionMenu::setDefaultAction ); + } +} + +QWidget * ToggleActionMenu::createWidget( QWidget * parent ) +{ + QToolButton * button = qobject_cast< QToolButton * >( KActionMenu::createWidget( parent ) ); + if ( !button ) { + // This function is used to add a button into the toolbar. + // KActionMenu will plug itself as QToolButton. + // So, if no QToolButton was returned, this was not called the intended way. + return button; + } + + // Remove this menu action from the button, + // so it doesn't compose a menu of this menu action and its own menu. + button->removeAction( this ); + // The button has lost the menu now, let it use the correct menu. + button->setMenu( menu() ); + + m_buttons.append( QPointer< QToolButton >( button ) ); + + // Apply other properties to the button. + updateButtons(); + + return button; +} + +void ToggleActionMenu::setDefaultAction( QAction *action ) +{ + m_defaultAction = action; + updateButtons(); +} + +void ToggleActionMenu::suggestDefaultAction( QAction *action ) +{ + m_suggestedDefaultAction = action; +} + +QAction * ToggleActionMenu::checkedAction( QMenu *menu ) const +{ + for ( QAction * a : menu->actions() ) + { + if ( a->isChecked() ) + { + return a; + } + else if ( a->menu() ) + { + QAction * b = checkedAction( a->menu() ); + if ( b ) + { + return b; + } + } + } + return nullptr; +} + +void ToggleActionMenu::updateButtons() +{ + for ( QPointer< QToolButton > button : qAsConst( m_buttons ) ) + { + if ( button ) + { + button->setDefaultAction( defaultAction() ); + + // Override some properties of the default action, + // where the property of this menu makes more sense. + button->setEnabled( isEnabled() ); + + if (delayed()) + { + button->setPopupMode(QToolButton::DelayedPopup); + } + else if (stickyMenu()) + { + button->setPopupMode(QToolButton::InstantPopup); + } + else + { + button->setPopupMode(QToolButton::MenuButtonPopup); + } + } + } +} + +QAction * ToggleActionMenu::defaultAction() +{ + if ( ( m_menuLogic & ImplicitDefaultAction ) && !m_defaultAction ) + { + m_defaultAction = checkedAction( menu() ); + } + if ( !m_defaultAction ) + { + m_defaultAction = m_suggestedDefaultAction; + } + return m_defaultAction; +} diff --git a/ui/toolaction.h b/ui/toolaction.h deleted file mode 100644 --- a/ui/toolaction.h +++ /dev/null @@ -1,41 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2004-2006 by Albert Astals Cid * - * * - * 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 TOOLACTION_H -#define TOOLACTION_H - -#include -#include - -#include - -class QToolButton; - -class ToolAction : public KSelectAction -{ - Q_OBJECT - - public: - explicit ToolAction( QObject *parent = nullptr ); - virtual ~ToolAction(); - - void addAction( QAction *action ); - - protected: - QWidget* createWidget( QWidget *parent ) override; - - private Q_SLOTS: - void slotNewDefaultAction( QAction *action ); - - private: - QList< QPointer< QToolButton > > m_buttons; - QList< QAction * > m_actions; -}; - -#endif diff --git a/ui/toolaction.cpp b/ui/toolaction.cpp deleted file mode 100644 --- a/ui/toolaction.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2004-2006 by Albert Astals Cid * - * * - * 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 "toolaction.h" - -#include -#include -#include - -#include - -ToolAction::ToolAction( QObject *parent ) - : KSelectAction( parent ) -{ - setText( i18n( "Selection Tools" ) ); -} - -ToolAction::~ToolAction() -{ -} - -void ToolAction::addAction( QAction *action ) -{ - bool setDefault = !m_buttons.isEmpty() ? m_buttons.first()->menu()->actions().isEmpty() : false; - foreach ( QToolButton *button, m_buttons ) - if ( button ) - { - button->menu()->addAction( action ); - if ( setDefault ) - button->setDefaultAction( action ); - } - m_actions.append( action ); -} - -QWidget* ToolAction::createWidget( QWidget *parent ) -{ - QToolBar *toolBar = qobject_cast< QToolBar * >( parent ); - if ( !toolBar ) - return nullptr; - - QToolButton *button = new QToolButton( toolBar ); - button->setAutoRaise( true ); - button->setFocusPolicy( Qt::NoFocus ); - button->setIconSize( toolBar->iconSize() ); - button->setToolButtonStyle( toolBar->toolButtonStyle() ); - button->setPopupMode( QToolButton::MenuButtonPopup ); - button->setMenu( new QMenu( button ) ); - button->setCheckable( true ); - connect(toolBar, &QToolBar::iconSizeChanged, button, &QToolButton::setIconSize); - connect(toolBar, &QToolBar::toolButtonStyleChanged, button, &QToolButton::setToolButtonStyle); - connect(button, &QToolButton::triggered, toolBar, &QToolBar::actionTriggered); - connect( button->menu(), &QMenu::triggered, this, &ToolAction::slotNewDefaultAction ); - - m_buttons.append( button ); - - if ( !m_actions.isEmpty() ) - { - button->setDefaultAction( m_actions.first() ); - foreach ( QAction *action, m_actions ) - { - button->menu()->addAction( action ); - if ( action->isChecked() ) - button->setDefaultAction( action ); - } - button->setToolTip( i18n("Click to use the current selection tool\nClick on the arrow to choose another selection tool") ); - } - - return button; -} - -void ToolAction::slotNewDefaultAction( QAction *action ) -{ - foreach ( QToolButton *button, m_buttons ) - if ( button ) - { - button->setDefaultAction( action ); - button->setToolTip( i18n("Click to use the current selection tool\nClick on the arrow to choose another selection tool") ); - } -} - -#include "moc_toolaction.cpp"