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..b94fc9b --- /dev/null +++ b/Pdf4QtViewer/pdfactioncombobox.cpp @@ -0,0 +1,191 @@ +// 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 + +namespace pdfviewer +{ + +PDFActionComboBox::PDFActionComboBox(QWidget* parent) : + BaseClass(parent), + m_model(nullptr) +{ + setEditable(true); + lineEdit()->setPlaceholderText(tr("Find action...")); + lineEdit()->setClearButtonEnabled(true); + setMinimumWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, DEFAULT_WIDTH)); + + m_model = new QStandardItemModel(this); + m_proxyModel = new QSortFilterProxyModel(this); + + m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_proxyModel->setDynamicSortFilter(true); + m_proxyModel->setFilterKeyColumn(0); + m_proxyModel->setFilterRole(Qt::DisplayRole); + m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + m_proxyModel->setSortLocaleAware(true); + m_proxyModel->setSortRole(Qt::DisplayRole); + m_proxyModel->setSourceModel(m_model); + + setFocusPolicy(Qt::StrongFocus); + setCompleter(nullptr); + setInsertPolicy(QComboBox::NoInsert); +/* + completer()->setCompletionMode(QCompleter::PopupCompletion); + completer()->setCompletionColumn(-1); + completer()->setFilterMode(Qt::MatchContains | Qt::MatchWildcard); + completer()->setCaseSensitivity(Qt::CaseInsensitive); + completer()->setModelSorting(QCompleter::UnsortedModel);*/ + + connect(this, &PDFActionComboBox::activated, this, &PDFActionComboBox::onActionActivated); + connect(this, &PDFActionComboBox::editTextChanged, this, &PDFActionComboBox::onEditTextChanged, Qt::QueuedConnection); + + setModel(m_proxyModel); +} + +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); + } +} + +void PDFActionComboBox::onActionChanged() +{ + QAction* action = qobject_cast(sender()); + updateAction(action); +} + +void PDFActionComboBox::onActionActivated(int index) +{ + QVariant actionData = itemData(index, Qt::UserRole); + QAction* action = actionData.value(); + + lineEdit()->clear(); + setCurrentIndex(-1); + + if (action && action->isEnabled()) + { + action->trigger(); + } +} + +void PDFActionComboBox::onEditTextChanged(const QString& text) +{ + if (text.isEmpty()) + { + m_proxyModel->setFilterFixedString(QString()); + } + else if (text.contains(QChar('*')) || text.contains(QChar('?'))) + { + m_proxyModel->setFilterWildcard(text); + } + else + { + m_proxyModel->setFilterFixedString(text); + } + + if (!text.isEmpty()) + { + showPopup(); + } + else + { + hidePopup(); + } + + lineEdit()->setFocus(); + lineEdit()->setCursorPosition(text.size()); +} + +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); + } + } + + setCurrentIndex(-1); +} + +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..32709c7 --- /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 + +class QStandardItemModel; +class QSortFilterProxyModel; + +namespace pdfviewer +{ + +class PDFActionComboBox : public QComboBox +{ + Q_OBJECT + +private: + using BaseClass = QComboBox; + +public: + PDFActionComboBox(QWidget* parent); + + virtual QSize sizeHint() const override; + virtual QSize minimumSizeHint() const override; + + void addQuickFindAction(QAction* action); + +private: + static constexpr int DEFAULT_WIDTH = 220; + + void onActionChanged(); + void onActionActivated(int index); + void onEditTextChanged(const QString& text); + + void updateAction(QAction* action); + int findAction(QAction* action); + + std::vector m_actions; + QStandardItemModel* m_model; + QSortFilterProxyModel* m_proxyModel; +}; + +} // 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()