diff --git a/Pdf4QtViewer/CMakeLists.txt b/Pdf4QtViewer/CMakeLists.txt index e15e348..e470d80 100644 --- a/Pdf4QtViewer/CMakeLists.txt +++ b/Pdf4QtViewer/CMakeLists.txt @@ -73,6 +73,8 @@ add_library(Pdf4QtViewer SHARED pdfbookmarkmanager.cpp pdfbookmarkui.h pdfbookmarkui.cpp + pdfactioncombobox.h + pdfactioncombobox.cpp ) add_compile_definitions(QT_INSTALL_DIRECTORY="${QT6_INSTALL_PREFIX}") diff --git a/Pdf4QtViewer/pdfactioncombobox.cpp b/Pdf4QtViewer/pdfactioncombobox.cpp new file mode 100644 index 0000000..e3cc285 --- /dev/null +++ b/Pdf4QtViewer/pdfactioncombobox.cpp @@ -0,0 +1,211 @@ +// Copyright (C) 2023 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "pdfactioncombobox.h" +#include "pdfwidgetutils.h" + +#include +#include +#include +#include +#include +#include + +namespace pdfviewer +{ + +PDFActionComboBox::PDFActionComboBox(QWidget* parent) : + BaseClass(parent), + m_model(nullptr) +{ + setPlaceholderText(tr("Find action...")); + setClearButtonEnabled(true); + setMinimumWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, DEFAULT_WIDTH)); + + m_model = new QStandardItemModel(this); + + QCompleter* completer = new QCompleter(m_model, this); + setFocusPolicy(Qt::StrongFocus); + setCompleter(completer); + + completer->setCompletionMode(QCompleter::PopupCompletion); + completer->setCompletionColumn(0); + completer->setCompletionRole(Qt::DisplayRole); + completer->setFilterMode(Qt::MatchContains); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setWrapAround(false); + completer->setMaxVisibleItems(20); + + connect(this, &QLineEdit::editingFinished, this, &PDFActionComboBox::performExecuteAction, Qt::QueuedConnection); +} + +QSize PDFActionComboBox::sizeHint() const +{ + QSize sizeHint = BaseClass::sizeHint(); + sizeHint.setWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, DEFAULT_WIDTH)); + return sizeHint; +} + +QSize PDFActionComboBox::minimumSizeHint() const +{ + QSize sizeHint = BaseClass::minimumSizeHint(); + sizeHint.setWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, DEFAULT_WIDTH)); + return sizeHint; +} + +void PDFActionComboBox::addQuickFindAction(QAction* action) +{ + if (std::find(m_actions.begin(), m_actions.end(), action) == m_actions.end()) + { + m_actions.push_back(action); + connect(action, &QAction::changed, this, &PDFActionComboBox::onActionChanged); + updateAction(action); + } +} + +bool PDFActionComboBox::event(QEvent* event) +{ + if (event->type() == QEvent::ShortcutOverride) + { + QKeyEvent* keyEvent = dynamic_cast(event); + switch (keyEvent->key()) + { + case Qt::Key_Down: + case Qt::Key_Up: + event->accept(); + return true; + } + } + + if (event->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = dynamic_cast(event); + + // Redirect up and down arrows to the completer + switch (keyEvent->key()) + { + case Qt::Key_Down: + case Qt::Key_Up: + { + if (completer()) + { + if (completer()->popup()->isVisible()) + { + QCoreApplication::sendEvent(completer()->popup(), event); + } + else + { + completer()->complete(); + } + } + return true; + } + + case Qt::Key_Enter: + case Qt::Key_Return: + clearFocus(); + return true; + + default: + break; + } + } + + return BaseClass::event(event); +} + +void PDFActionComboBox::onActionChanged() +{ + QAction* action = qobject_cast(sender()); + updateAction(action); +} + +void PDFActionComboBox::performExecuteAction() +{ + QString text = this->text(); + + QAction* action = nullptr; + for (QAction* currentAction : m_actions) + { + if (currentAction->text() == text) + { + action = currentAction; + } + } + + clear(); + completer()->setCompletionPrefix(QString()); + + if (action) + { + action->trigger(); + } +} + +void PDFActionComboBox::updateAction(QAction* action) +{ + if (!action) + { + return; + } + + int actionIndex = findAction(action); + if (action->isEnabled()) + { + if (actionIndex == -1) + { + QStandardItem* item = new QStandardItem(action->icon(), action->text()); + item->setData(QVariant::fromValue(action), Qt::UserRole); + m_model->appendRow(item); + } + else + { + QStandardItem* item = m_model->item(actionIndex); + item->setIcon(action->icon()); + item->setText(action->text()); + } + } + else + { + // Remove action from the model + if (actionIndex != -1) + { + m_model->removeRow(actionIndex); + } + } +} + +int PDFActionComboBox::findAction(QAction* action) +{ + const int rowCount = m_model->rowCount(); + + for (int i = 0; i < rowCount; ++i) + { + QModelIndex index = m_model->index(i, 0); + QAction* currentAction = index.data(Qt::UserRole).value(); + + if (currentAction == action) + { + return i; + } + } + + return -1; +} + +} // namespace pdfviewer diff --git a/Pdf4QtViewer/pdfactioncombobox.h b/Pdf4QtViewer/pdfactioncombobox.h new file mode 100644 index 0000000..7c3a545 --- /dev/null +++ b/Pdf4QtViewer/pdfactioncombobox.h @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef PDFACTIONCOMBOBOX_H +#define PDFACTIONCOMBOBOX_H + +#include "pdfwidgetsglobal.h" + +#include +#include +#include + +class QStandardItemModel; +class QSortFilterProxyModel; + +namespace pdfviewer +{ + +class PDFActionComboBox : public QLineEdit +{ + Q_OBJECT + +private: + using BaseClass = QLineEdit; + +public: + PDFActionComboBox(QWidget* parent); + + virtual QSize sizeHint() const override; + virtual QSize minimumSizeHint() const override; + virtual bool event(QEvent* event) override; + + void addQuickFindAction(QAction* action); + +private: + static constexpr int DEFAULT_WIDTH = 220; + + void onActionChanged(); + void performExecuteAction(); + + void updateAction(QAction* action); + int findAction(QAction* action); + + std::vector m_actions; + QStandardItemModel* m_model; +}; + +} // namespace pdfviewer + +#endif // PDFACTIONCOMBOBOX_H diff --git a/Pdf4QtViewer/pdfprogramcontroller.cpp b/Pdf4QtViewer/pdfprogramcontroller.cpp index fdd8059..b6bfd96 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.cpp +++ b/Pdf4QtViewer/pdfprogramcontroller.cpp @@ -43,6 +43,7 @@ #include "pdfencryptionsettingsdialog.h" #include "pdfwidgetannotation.h" #include "pdfwidgetformmanager.h" +#include "pdfactioncombobox.h" #include #include @@ -56,6 +57,8 @@ #include #include #include +#include +#include #include "pdfdbgheap.h" @@ -366,6 +369,7 @@ PDFProgramController::PDFProgramController(QObject* parent) : m_annotationManager(nullptr), m_formManager(nullptr), m_bookmarkManager(nullptr), + m_actionComboBox(nullptr), m_isBusy(false), m_isFactorySettingsBeingRestored(false), m_progress(nullptr) @@ -673,6 +677,24 @@ void PDFProgramController::initialize(Features features, } } +void PDFProgramController::initActionComboBox(PDFActionComboBox* comboBox) +{ + m_actionComboBox = comboBox; + + if (m_actionComboBox) + { + bool updatesEnabled = m_actionComboBox->updatesEnabled(); + m_actionComboBox->setUpdatesEnabled(false); + + for (QAction* action : m_actionManager->getActions()) + { + m_actionComboBox->addQuickFindAction(action); + } + + m_actionComboBox->setUpdatesEnabled(updatesEnabled); + } +} + void PDFProgramController::finishInitialization() { readSettings(Settings(WindowSettings | ActionSettings)); diff --git a/Pdf4QtViewer/pdfprogramcontroller.h b/Pdf4QtViewer/pdfprogramcontroller.h index 76507c5..fb382d1 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.h +++ b/Pdf4QtViewer/pdfprogramcontroller.h @@ -35,6 +35,7 @@ #include class QMainWindow; +class QComboBox; class QToolBar; namespace pdf @@ -53,6 +54,7 @@ class PDFViewerSettings; class PDFUndoRedoManager; class PDFRecentFileManager; class PDFTextToSpeech; +class PDFActionComboBox; class IMainWindow { @@ -291,6 +293,7 @@ public: IMainWindow* mainWindowInterface, PDFActionManager* actionManager, pdf::PDFProgress* progress); + void initActionComboBox(PDFActionComboBox* comboBox); void finishInitialization(); void writeSettings(); void resetSettings(); @@ -440,6 +443,7 @@ private: pdf::PDFWidgetAnnotationManager* m_annotationManager; pdf::PDFWidgetFormManager* m_formManager; PDFBookmarkManager* m_bookmarkManager; + PDFActionComboBox* m_actionComboBox; PDFFileInfo m_fileInfo; QFileSystemWatcher m_fileWatcher; diff --git a/Pdf4QtViewer/pdfviewermainwindow.cpp b/Pdf4QtViewer/pdfviewermainwindow.cpp index e4fbcc1..9634b97 100644 --- a/Pdf4QtViewer/pdfviewermainwindow.cpp +++ b/Pdf4QtViewer/pdfviewermainwindow.cpp @@ -41,6 +41,7 @@ #include "pdfsignaturehandler.h" #include "pdfadvancedtools.h" #include "pdfwidgetutils.h" +#include "pdfactioncombobox.h" #include #include @@ -310,6 +311,9 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : connect(m_progress, &pdf::PDFProgress::progressStep, this, &PDFViewerMainWindow::onProgressStep); connect(m_progress, &pdf::PDFProgress::progressFinished, this, &PDFViewerMainWindow::onProgressFinished); + PDFActionComboBox* actionComboBox = new PDFActionComboBox(this); + menuBar()->setCornerWidget(actionComboBox); + m_programController->finishInitialization(); updateDeveloperMenu(); @@ -319,6 +323,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : } m_actionManager->styleActions(); + m_programController->initActionComboBox(actionComboBox); } PDFViewerMainWindow::~PDFViewerMainWindow() diff --git a/Pdf4QtViewer/pdfviewermainwindowlite.cpp b/Pdf4QtViewer/pdfviewermainwindowlite.cpp index 0eb6576..385318d 100644 --- a/Pdf4QtViewer/pdfviewermainwindowlite.cpp +++ b/Pdf4QtViewer/pdfviewermainwindowlite.cpp @@ -41,6 +41,7 @@ #include "pdfsignaturehandler.h" #include "pdfadvancedtools.h" #include "pdfwidgetutils.h" +#include "pdfactioncombobox.h" #include #include @@ -228,6 +229,8 @@ PDFViewerMainWindowLite::PDFViewerMainWindowLite(QWidget* parent) : connect(m_progress, &pdf::PDFProgress::progressStep, this, &PDFViewerMainWindowLite::onProgressStep); connect(m_progress, &pdf::PDFProgress::progressFinished, this, &PDFViewerMainWindowLite::onProgressFinished); + PDFActionComboBox* actionComboBox = new PDFActionComboBox(this); + menuBar()->setCornerWidget(actionComboBox); m_programController->finishInitialization(); if (pdf::PDFToolManager* toolManager = m_programController->getToolManager()) @@ -236,6 +239,7 @@ PDFViewerMainWindowLite::PDFViewerMainWindowLite(QWidget* parent) : } m_actionManager->styleActions(); + m_programController->initActionComboBox(actionComboBox); } PDFViewerMainWindowLite::~PDFViewerMainWindowLite() diff --git a/RELEASES.txt b/RELEASES.txt index 08eb77f..d970570 100644 --- a/RELEASES.txt +++ b/RELEASES.txt @@ -1,4 +1,5 @@ CURRENT: + - Issue #134: Add search bar for actions - Issue #129: Cannot compile with lcms 2.16 - Issue #128: Create list of markup annotations - Issue #126: Remove include from main headers