From 33fa8b542dbdab7ab4d0321f36a69327278892ad Mon Sep 17 00:00:00 2001 From: Heiko Becker Date: Mon, 8 Nov 2021 20:03:08 +0100 Subject: [PATCH 1/3] GIT_SILENT Upgrade release service version to 21.11.80. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa3150ca6..d64f5b272 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16) # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "21") set (RELEASE_SERVICE_VERSION_MINOR "11") -set (RELEASE_SERVICE_VERSION_MICRO "70") +set (RELEASE_SERVICE_VERSION_MICRO "80") set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") project(okular VERSION ${RELEASE_SERVICE_VERSION}) From f686f4c6bf45b92784a6bf90a037199158ff0073 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 5 Nov 2021 00:22:30 +0100 Subject: [PATCH 2/3] Markdown: Don't crash on files with html images, alt text, inside links If we have code like and the img has alt text The old code did remove image insert alt text but when we remove the image, the qtextdocument says "i don't need the anymore since there's nothing inside" and then everything breaks because the layout has changed and we're iterating over "unexisting" text blocks So instead we just insert the alt text and since we have selected the image with the cursor it will replace it correctly BUGS: 444971 --- generators/markdown/converter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/generators/markdown/converter.cpp b/generators/markdown/converter.cpp index 2f7fd9ebf..353f4874f 100644 --- a/generators/markdown/converter.cpp +++ b/generators/markdown/converter.cpp @@ -206,7 +206,6 @@ void Converter::convertImages(const QTextBlock &parent, const QDir &dir, QTextDo cursor.insertImage(format); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) } else if ((!textCharFormat.toImageFormat().property(QTextFormat::ImageAltText).toString().isEmpty())) { - cursor.removeSelectedText(); cursor.insertText(textCharFormat.toImageFormat().property(QTextFormat::ImageAltText).toString()); #endif } From 7bd0f8f3991b6cea879f234a1e1356fb85d6f5c5 Mon Sep 17 00:00:00 2001 From: Felix Ernst Date: Tue, 9 Nov 2021 01:27:50 +0000 Subject: [PATCH 3/3] Add KHamburgerMenu --- part/part.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++------- part/part.h | 20 ++++- part/part.rc | 4 +- 3 files changed, 199 insertions(+), 28 deletions(-) diff --git a/part/part.cpp b/part/part.cpp index be205c16e..1f33ae246 100644 --- a/part/part.cpp +++ b/part/part.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -44,14 +45,19 @@ #include #include +#include "kconfigwidgets_version.h" // TODO KF 5.81 Remove this include, because the relevant section below will also be removed. #include #include #include #include #include #include +#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0) +#include +#endif #include #include +#include #include #include #include @@ -60,6 +66,7 @@ #include #include #include +#include #include #include #ifdef WITH_KWALLET @@ -110,7 +117,9 @@ #include "thumbnaillist.h" #include "toc.h" #include "xmlgui_helper.h" + #include +#include #ifdef OKULAR_KEEP_FILE_OPEN class FileKeeper @@ -288,7 +297,6 @@ Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList &args) , m_fileWasRemoved(false) , m_showMenuBarAction(nullptr) , m_showFullScreenAction(nullptr) - , m_actionsSearched(false) , m_cliPresentation(false) , m_cliPrint(false) , m_cliPrintAndExit(false) @@ -743,6 +751,8 @@ void Part::setupViewerActions() m_saveAs = nullptr; m_openContainingFolder = nullptr; + m_hamburgerMenuAction = nullptr; + QAction *prefs = KStandardAction::preferences(this, SLOT(slotPreferences()), ac); if (m_embedMode == NativeShellMode) { prefs->setText(i18n("Configure Okular...")); @@ -923,6 +933,16 @@ void Part::setupActions() connect(m_openContainingFolder, &QAction::triggered, this, &Part::slotOpenContainingFolder); m_openContainingFolder->setEnabled(false); +#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0) + if (m_embedMode == Okular::NativeShellMode) { // This hamburger menu is designed to be quite Okular-specific. + m_hamburgerMenuAction = KStandardAction::hamburgerMenu(nullptr, nullptr, ac); + if (auto *mainWindow = findMainWindow()) { + m_hamburgerMenuAction->setMenuBar(mainWindow->menuBar()); + } + connect(m_hamburgerMenuAction, &KHamburgerMenu::aboutToShowMenu, this, &Part::slotUpdateHamburgerMenu); + } +#endif + QAction *importPS = ac->addAction(QStringLiteral("import_ps")); importPS->setText(i18n("&Import PostScript as PDF...")); importPS->setIcon(QIcon::fromTheme(QStringLiteral("document-import"))); @@ -2934,26 +2954,11 @@ void Part::showMenu(const Okular::Page *page, const QPoint point, const QString bool reallyShow = false; const bool currentPage = page && page->number() == m_document->viewport().pageNumber; - if (!m_actionsSearched) { - // the quest for options_show_menubar - KActionCollection *ac; - QAction *act; - - if (factory()) { - const QList clients(factory()->clients()); - for (int i = 0; (!m_showMenuBarAction || !m_showFullScreenAction) && i < clients.size(); ++i) { - ac = clients.at(i)->actionCollection(); - // show_menubar - act = ac->action(QStringLiteral("options_show_menubar")); - if (act && qobject_cast(act)) - m_showMenuBarAction = qobject_cast(act); - // fullscreen - act = ac->action(QStringLiteral("fullscreen")); - if (act && qobject_cast(act)) - m_showFullScreenAction = qobject_cast(act); - } - } - m_actionsSearched = true; + if (!m_showMenuBarAction) { + m_showMenuBarAction = findActionInKPartHierarchy(KStandardAction::name(KStandardAction::ShowMenubar)); + } + if (!m_showFullScreenAction) { + m_showFullScreenAction = findActionInKPartHierarchy(KStandardAction::name(KStandardAction::FullScreen)); } QMenu *popup = new QMenu(widget()); @@ -2981,12 +2986,20 @@ void Part::showMenu(const Okular::Page *page, const QPoint point, const QString reallyShow = true; } - if ((m_showMenuBarAction && !m_showMenuBarAction->isChecked()) || (m_showFullScreenAction && m_showFullScreenAction->isChecked())) { - popup->addAction(new OKMenuTitle(popup, i18n("Tools"))); - if (m_showMenuBarAction && !m_showMenuBarAction->isChecked()) + const int amountOfActions = popup->actions().count(); + if (m_showMenuBarAction && !m_showMenuBarAction->isChecked()) { + if (m_hamburgerMenuAction) { +#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0) + m_hamburgerMenuAction->addToMenu(popup); +#endif + } else if (m_showMenuBarAction) { popup->addAction(m_showMenuBarAction); - if (m_showFullScreenAction && m_showFullScreenAction->isChecked()) - popup->addAction(m_showFullScreenAction); + } + } + if (m_showFullScreenAction && m_showFullScreenAction->isChecked()) + popup->addAction(m_showFullScreenAction); + if (popup->actions().count() > amountOfActions && popup->actions().constLast()->isVisible()) { + popup->insertAction(popup->actions().at(amountOfActions), new OKMenuTitle(popup, i18n("Tools"))); reallyShow = true; } @@ -3013,6 +3026,34 @@ void Part::showMenu(const Okular::Page *page, const QPoint point, const QString delete popup; } +template Action *Part::findActionInKPartHierarchy(const QString &actionName) +{ + static_assert(std::is_base_of::value, "Calling this method to find something other than an Action makes no sense."); + if (factory()) { + const QList clients(factory()->clients()); + for (auto client : clients) { + if (QAction *act = client->actionCollection()->action(actionName)) { + if (Action *castedAction = qobject_cast(act)) { + return castedAction; + } + } + } + } + return nullptr; +} + +KMainWindow *Part::findMainWindow() +{ + auto *potentialMainWindow = parent(); + while (potentialMainWindow) { + if (auto *mainWindow = qobject_cast(potentialMainWindow)) { + return mainWindow; + } + potentialMainWindow = potentialMainWindow->parent(); + } + return nullptr; +} + void Part::slotShowProperties() { PropertiesDialog *d = new PropertiesDialog(widget(), m_document); @@ -3040,6 +3081,116 @@ void Part::slotHidePresentation() delete (PresentationWidget *)m_presentationWidget; } +void Part::slotUpdateHamburgerMenu() +{ +#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0) + auto ac = actionCollection(); + + auto menu = m_hamburgerMenuAction->menu(); + if (!menu) { + menu = new QMenu(widget()); + m_hamburgerMenuAction->setMenu(menu); + if (!m_showMenuBarAction) { + m_showMenuBarAction = findActionInKPartHierarchy(KStandardAction::name(KStandardAction::ShowMenubar)); + } + m_hamburgerMenuAction->setShowMenuBarAction(m_showMenuBarAction); + } else { + menu->clear(); + } + + QToolBar *visibleMainToolbar = nullptr; + if (auto *mainWindow = findMainWindow()) { + visibleMainToolbar = mainWindow->toolBar(); + if (!visibleMainToolbar->isVisible()) { + visibleMainToolbar = nullptr; + } + const auto toolbars = mainWindow->toolBars(); + for (const auto &toolbar : toolbars) { + m_hamburgerMenuAction->hideActionsOf(toolbar); + } + + bool menuAvailable = false; // We already know the menu bar is hidden when this menu is opened. + // If no menu is available, we want to add actions to the hamburger menu to show them again. + // The hamburger menu serves as the fallback that is available through the right-click context menu. + if (visibleMainToolbar && visibleMainToolbar->actions().contains(m_hamburgerMenuAction)) { + menuAvailable = true; + } + if (!menuAvailable) { + menu->addAction(m_showMenuBarAction); + if (!visibleMainToolbar) { + menu->addAction(findActionInKPartHierarchy(QStringLiteral("mainToolBar"))); + } + menu->addSeparator(); + } + } + + // When changing actions, keep "Simple by default, powerful when needed" in mind. + // To retrieve an action, it is fastest to use a direct pointer if available (m_action), otherwise use + // ac->action(actionName) and if the action isn't in the actionCollection() of this part, + // use findActionInKPartHierarchy(actionName). + menu->addAction(findActionInKPartHierarchy(KStandardAction::name(KStandardAction::Open))); + menu->addAction(findActionInKPartHierarchy(KStandardAction::name(KStandardAction::OpenRecent))); + menu->addAction(m_save); + menu->addAction(m_saveAs); + menu->addSeparator(); + menu->addAction(ac->action(QStringLiteral("mouse_drag"))); + if (!visibleMainToolbar || (visibleMainToolbar && !visibleMainToolbar->actions().contains(ac->action(QStringLiteral("mouse_selecttools"))))) { + menu->addAction(ac->action(QStringLiteral("mouse_select"))); + } + menu->addAction(m_copy); + menu->addAction(m_find); + menu->addAction(m_showLeftPanel); + if (!visibleMainToolbar || (visibleMainToolbar && !visibleMainToolbar->actions().contains(ac->action(QStringLiteral("annotation_favorites"))))) { + menu->addAction(ac->action(QStringLiteral("mouse_toggle_annotate"))); + } + menu->addAction(ac->action(KStandardAction::name(KStandardAction::Undo))); + menu->addAction(ac->action(KStandardAction::name(KStandardAction::Redo))); + menu->addSeparator(); + + menu->addAction(findActionInKPartHierarchy(KStandardAction::name(KStandardAction::Print))); + menu->addAction(m_printPreview); + menu->addAction(m_showProperties); + menu->addAction(m_openContainingFolder); + menu->addAction(m_share); + menu->addSeparator(); + + menu->addAction(ac->action(QStringLiteral("zoom_to"))); + const QMenuBar *menuBar = m_hamburgerMenuAction->menuBar(); + if (menuBar && menuBar->actions().count() < 3) { // 3 to make sure none of the code below can crash. + menuBar = nullptr; + } + auto curatedViewMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("page-2sides")), menuBar ? menuBar->actions().at(1)->text() : QStringLiteral("View")); + if (!m_showFullScreenAction) { + m_showFullScreenAction = findActionInKPartHierarchy(KStandardAction::name(KStandardAction::FullScreen)); + } + curatedViewMenu->addAction(m_showFullScreenAction); + curatedViewMenu->addAction(m_showPresentation); + curatedViewMenu->addSeparator(); + curatedViewMenu->addAction(findActionInKPartHierarchy(QStringLiteral("view_render_mode"))); + if (auto *viewOrientationMenu = qobject_cast(factory()->container(QStringLiteral("view_orientation"), this))) { + curatedViewMenu->addAction(viewOrientationMenu->menuAction()); + } + curatedViewMenu->addAction(findActionInKPartHierarchy(QStringLiteral("view_trim_mode"))); + curatedViewMenu->addSeparator(); + curatedViewMenu->addAction(ac->action(QStringLiteral("view_toggle_forms"))); + m_hamburgerMenuAction->hideActionsOf(curatedViewMenu); + +#ifdef HAVE_SPEECH + auto speakMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("text-speak")), i18nc("@action:inmenu menu that contains actions to control text to speach", "Speak")); + speakMenu->addAction(ac->action(QStringLiteral("speak_document"))); + speakMenu->addAction(ac->action(QStringLiteral("speak_current_page"))); + speakMenu->addAction(ac->action(QStringLiteral("speak_stop_all"))); + speakMenu->addAction(ac->action(QStringLiteral("speak_pause_resume"))); + m_hamburgerMenuAction->hideActionsOf(speakMenu); +#endif + + // Add the "Settings" menu from the menu bar. + if (menuBar) { + menu->addAction(menuBar->actions().at(menuBar->actions().count() - 3)); + } +#endif +} + void Part::slotTogglePresentation() { if (m_document->isOpened()) { diff --git a/part/part.h b/part/part.h index fb579317b..0a7f81e49 100644 --- a/part/part.h +++ b/part/part.h @@ -44,6 +44,8 @@ class QMenu; class KConfigDialog; class KDirWatch; +class KHamburgerMenu; +class KMainWindow; class KToggleAction; class KToggleFullScreenAction; class QTemporaryFile; @@ -219,6 +221,14 @@ protected Q_SLOTS: void slotShowBottomBar(); void slotShowPresentation(); void slotHidePresentation(); + + /** + * Updates the menu that is by default at the right end of the toolbar. + * In true "simple by default" fashion, the menu only contains the most important actions + * which are needed to use all essential Okular features. More advanced actions can be + * discovered through a sub-menu (@see KConfigWidgets::KHamburgerMenu::setMenuBarAdvertised()). + */ + void slotUpdateHamburgerMenu(); void slotExportAs(QAction *); bool slotImportPSFile(); void slotAboutBackend(); @@ -257,6 +267,14 @@ public Q_SLOTS: private: bool aboutToShowContextMenu(QMenu *menu, QAction *action, QMenu *contextMenu); void showMenu(const Okular::Page *page, const QPoint point, const QString &bookmarkTitle = QString(), const Okular::DocumentViewport &vp = DocumentViewport(), bool showTOCActions = false); + /** + * Searches the actionCollections of all KXMLGUIClients that were created by the same factory() + * as this Part for a QAction that has both the specified name and the specified class. + * @return an action with class @p Action and name @p actionName. nullptr if no such action is found. + */ + template Action *findActionInKPartHierarchy(const QString &actionName); + /** @return the first KMainWindow among the ancestors of this part. nullptr if no KMainWindow is found. */ + KMainWindow *findMainWindow(); bool eventFilter(QObject *watched, QEvent *event) override; Document::OpenResult doOpenFile(const QMimeType &mime, const QString &fileNameToOpen, bool *isCompressedFile); bool openUrl(const QUrl &url, bool swapInsteadOfOpening); @@ -387,6 +405,7 @@ private: #endif QAction *m_showPresentation; QAction *m_openContainingFolder; + KHamburgerMenu *m_hamburgerMenuAction; KToggleAction *m_showMenuBarAction; KToggleAction *m_showLeftPanel; KToggleAction *m_showBottomBar; @@ -401,7 +420,6 @@ private: QAction *m_closeFindBar; DrawingToolActions *m_presentationDrawingActions; - bool m_actionsSearched; BrowserExtension *m_bExtension; QList m_exportFormats; diff --git a/part/part.rc b/part/part.rc index 28fe76af5..cf32895ec 100644 --- a/part/part.rc +++ b/part/part.rc @@ -1,6 +1,6 @@ - + &File @@ -109,6 +109,8 @@ + +