mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Tool for text selection (first part)
This commit is contained in:
@@ -227,3 +227,11 @@ qt_libraries.files = $$[QT_INSTALL_BINS]/Qt?Widgets$${SUFFIX}.dll \
|
||||
$$[QT_INSTALL_BINS]/Qt?Svg$${SUFFIX}.dll
|
||||
qt_libraries.path = $$DESTDIR/install
|
||||
INSTALLS += qt_libraries
|
||||
|
||||
qt_plugin_platform.files = $$[QT_INSTALL_PLUGINS]/platforms/qwindows$${SUFFIX}.dll
|
||||
qt_plugin_platform.path = $$DESTDIR/install/platforms
|
||||
INSTALLS += qt_plugin_platform
|
||||
|
||||
qt_plugin_iconengine.files = $$[QT_INSTALL_PLUGINS]/iconengines/qsvgicon$${SUFFIX}.dll
|
||||
qt_plugin_iconengine.path = $$DESTDIR/install/iconengines
|
||||
INSTALLS += qt_plugin_iconengine
|
||||
|
@@ -784,6 +784,31 @@ std::vector<PDFInteger> PDFDrawWidgetProxy::getPagesIntersectingRect(QRect rect)
|
||||
return pages;
|
||||
}
|
||||
|
||||
PDFInteger PDFDrawWidgetProxy::getPageUnderPoint(QPoint point, QPointF* pagePoint) const
|
||||
{
|
||||
// Iterate trough pages, place them and test, if they intersects with rectangle
|
||||
for (const LayoutItem& item : m_layout.items)
|
||||
{
|
||||
// The offsets m_horizontalOffset and m_verticalOffset are offsets to the
|
||||
// topleft point of the block. But block maybe doesn't start at (0, 0),
|
||||
// so we must also use translation from the block beginning.
|
||||
QRect placedRect = item.pageRect.translated(m_horizontalOffset - m_layout.blockRect.left(), m_verticalOffset - m_layout.blockRect.top());
|
||||
if (placedRect.contains(point))
|
||||
{
|
||||
if (pagePoint)
|
||||
{
|
||||
const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex);
|
||||
QMatrix matrix = PDFRenderer::createPagePointToDevicePointMatrix(page, placedRect).inverted();
|
||||
*pagePoint = matrix.map(point);
|
||||
}
|
||||
|
||||
return item.pageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
QRect PDFDrawWidgetProxy::getPagesIntersectingRectBoundingBox(QRect rect) const
|
||||
{
|
||||
QRect resultRect;
|
||||
|
@@ -35,7 +35,6 @@ namespace pdf
|
||||
class PDFProgress;
|
||||
class PDFWidget;
|
||||
class IDrawWidget;
|
||||
class PDFWidgetTool;
|
||||
class PDFCMSManager;
|
||||
class PDFTextLayoutGetter;
|
||||
class PDFAsynchronousPageCompiler;
|
||||
@@ -269,6 +268,13 @@ public:
|
||||
/// \param rect Rectangle to test
|
||||
std::vector<PDFInteger> getPagesIntersectingRect(QRect rect) const;
|
||||
|
||||
/// Returns page, under which is point. If no page is under the point,
|
||||
/// then -1 is returned. Point is in widget coordinates. If \p pagePoint
|
||||
/// is not nullptr, then point in page coordinate space is set here.
|
||||
/// \param point Point
|
||||
/// \param pagePoint Point in page coordinate system
|
||||
PDFInteger getPageUnderPoint(QPoint point, QPointF* pagePoint) const;
|
||||
|
||||
/// Returns bounding box of pages, which are intersecting rectangle (even partially)
|
||||
/// \param rect Rectangle to test
|
||||
QRect getPagesIntersectingRectBoundingBox(QRect rect) const;
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include "pdfdrawwidget.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfwidgettool.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QGridLayout>
|
||||
@@ -31,6 +32,7 @@ namespace pdf
|
||||
PDFWidget::PDFWidget(const PDFCMSManager* cmsManager, RendererEngine engine, int samplesCount, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
m_cmsManager(cmsManager),
|
||||
m_toolManager(nullptr),
|
||||
m_drawWidget(nullptr),
|
||||
m_horizontalScrollBar(nullptr),
|
||||
m_verticalScrollBar(nullptr),
|
||||
@@ -177,6 +179,7 @@ PDFDrawWidgetBase<BaseWidget>::PDFDrawWidgetBase(PDFWidget* widget, QWidget* par
|
||||
m_mouseOperation(MouseOperation::None)
|
||||
{
|
||||
this->setFocusPolicy(Qt::StrongFocus);
|
||||
this->setMouseTracking(true);
|
||||
}
|
||||
|
||||
template<typename BaseWidget>
|
||||
@@ -216,10 +219,21 @@ void PDFDrawWidgetBase<BaseWidget>::performMouseOperation(QPoint currentMousePos
|
||||
template<typename BaseWidget>
|
||||
void PDFDrawWidgetBase<BaseWidget>::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
QScrollBar* verticalScrollbar = m_widget->getVerticalScrollbar();
|
||||
event->ignore();
|
||||
|
||||
// Try to pass event to tool manager
|
||||
if (PDFToolManager* toolManager = getPDFWidget()->getToolManager())
|
||||
{
|
||||
toolManager->keyPressEvent(this, event);
|
||||
if (event->isAccepted())
|
||||
{
|
||||
updateCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical navigation
|
||||
QScrollBar* verticalScrollbar = m_widget->getVerticalScrollbar();
|
||||
if (verticalScrollbar->isVisible())
|
||||
{
|
||||
constexpr std::pair<QKeySequence::StandardKey, PDFDrawWidgetProxy::Operation> keyToOperations[] =
|
||||
@@ -241,24 +255,52 @@ void PDFDrawWidgetBase<BaseWidget>::keyPressEvent(QKeyEvent* event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
template<typename BaseWidget>
|
||||
void PDFDrawWidgetBase<BaseWidget>::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
// Try to pass event to tool manager
|
||||
if (PDFToolManager* toolManager = getPDFWidget()->getToolManager())
|
||||
{
|
||||
toolManager->mousePressEvent(this, event);
|
||||
if (event->isAccepted())
|
||||
{
|
||||
updateCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
m_mouseOperation = MouseOperation::Translate;
|
||||
m_lastMousePosition = event->pos();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
}
|
||||
|
||||
updateCursor();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
template<typename BaseWidget>
|
||||
void PDFDrawWidgetBase<BaseWidget>::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
// Try to pass event to tool manager
|
||||
if (PDFToolManager* toolManager = getPDFWidget()->getToolManager())
|
||||
{
|
||||
toolManager->mouseReleaseEvent(this, event);
|
||||
if (event->isAccepted())
|
||||
{
|
||||
updateCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
performMouseOperation(event->pos());
|
||||
|
||||
switch (m_mouseOperation)
|
||||
@@ -269,7 +311,6 @@ void PDFDrawWidgetBase<BaseWidget>::mouseReleaseEvent(QMouseEvent* event)
|
||||
case MouseOperation::Translate:
|
||||
{
|
||||
m_mouseOperation = MouseOperation::None;
|
||||
unsetCursor();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -277,19 +318,83 @@ void PDFDrawWidgetBase<BaseWidget>::mouseReleaseEvent(QMouseEvent* event)
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
updateCursor();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
template<typename BaseWidget>
|
||||
void PDFDrawWidgetBase<BaseWidget>::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
// Try to pass event to tool manager
|
||||
if (PDFToolManager* toolManager = getPDFWidget()->getToolManager())
|
||||
{
|
||||
toolManager->mouseMoveEvent(this, event);
|
||||
if (event->isAccepted())
|
||||
{
|
||||
updateCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
performMouseOperation(event->pos());
|
||||
updateCursor();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
template<typename BaseWidget>
|
||||
void PDFDrawWidgetBase<BaseWidget>::updateCursor()
|
||||
{
|
||||
std::optional<QCursor> cursor;
|
||||
if (PDFToolManager* toolManager = m_widget->getToolManager())
|
||||
{
|
||||
cursor = toolManager->getCursor();
|
||||
}
|
||||
|
||||
if (!cursor)
|
||||
{
|
||||
switch (m_mouseOperation)
|
||||
{
|
||||
case MouseOperation::None:
|
||||
cursor = QCursor(Qt::OpenHandCursor);
|
||||
break;
|
||||
|
||||
case MouseOperation::Translate:
|
||||
cursor = QCursor(Qt::ClosedHandCursor);
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
this->setCursor(*cursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->unsetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BaseWidget>
|
||||
void PDFDrawWidgetBase<BaseWidget>::wheelEvent(QWheelEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
// Try to pass event to tool manager
|
||||
if (PDFToolManager* toolManager = getPDFWidget()->getToolManager())
|
||||
{
|
||||
toolManager->wheelEvent(this, event);
|
||||
if (event->isAccepted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers keyboardModifiers = QApplication::keyboardModifiers();
|
||||
|
||||
PDFDrawWidgetProxy* proxy = m_widget->getDrawWidgetProxy();
|
||||
|
@@ -29,6 +29,7 @@ namespace pdf
|
||||
{
|
||||
class PDFDocument;
|
||||
class PDFCMSManager;
|
||||
class PDFToolManager;
|
||||
class PDFDrawWidget;
|
||||
class PDFDrawWidgetProxy;
|
||||
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
void updateCacheLimits(int compiledPageCacheLimit, int thumbnailsCacheLimit, int fontCacheLimit, int instancedFontCacheLimit);
|
||||
|
||||
const PDFCMSManager* getCMSManager() const { return m_cmsManager; }
|
||||
PDFToolManager* getToolManager() const { return m_toolManager; }
|
||||
IDrawWidget* getDrawWidget() const { return m_drawWidget; }
|
||||
QScrollBar* getHorizontalScrollbar() const { return m_horizontalScrollBar; }
|
||||
QScrollBar* getVerticalScrollbar() const { return m_verticalScrollBar; }
|
||||
@@ -84,6 +86,8 @@ public:
|
||||
const PageRenderingErrors* getPageRenderingErrors() const { return &m_pageRenderingErrors; }
|
||||
int getPageRenderingErrorCount() const;
|
||||
|
||||
void setToolManager(PDFToolManager* toolManager) { m_toolManager = toolManager; }
|
||||
|
||||
signals:
|
||||
void pageRenderingErrorsChanged(PDFInteger pageIndex, int errorsCount);
|
||||
|
||||
@@ -95,6 +99,7 @@ private:
|
||||
IDrawWidget* createDrawWidget(RendererEngine rendererEngine, int samplesCount);
|
||||
|
||||
const PDFCMSManager* m_cmsManager;
|
||||
PDFToolManager* m_toolManager;
|
||||
IDrawWidget* m_drawWidget;
|
||||
QScrollBar* m_horizontalScrollBar;
|
||||
QScrollBar* m_verticalScrollBar;
|
||||
@@ -125,6 +130,8 @@ protected:
|
||||
PDFWidget* getPDFWidget() const { return m_widget; }
|
||||
|
||||
private:
|
||||
void updateCursor();
|
||||
|
||||
enum class MouseOperation
|
||||
{
|
||||
None,
|
||||
|
@@ -349,6 +349,19 @@ qint64 PDFTextLayout::getMemoryConsumptionEstimate() const
|
||||
return estimate;
|
||||
}
|
||||
|
||||
bool PDFTextLayout::isHoveringOverTextBlock(const QPointF& point) const
|
||||
{
|
||||
for (const PDFTextBlock& block : m_blocks)
|
||||
{
|
||||
if (block.getBoundingBox().contains(point))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, PDFTextLayout& layout)
|
||||
{
|
||||
stream >> layout.m_characters;
|
||||
|
@@ -200,6 +200,9 @@ struct PDFTextSelectionColoredItem
|
||||
|
||||
}
|
||||
|
||||
bool operator==(const PDFTextSelectionColoredItem&) const = default;
|
||||
bool operator!=(const PDFTextSelectionColoredItem&) const = default;
|
||||
|
||||
inline bool operator<(const PDFTextSelectionColoredItem& other) const { return std::tie(start, end) < std::tie(other.start, other.end); }
|
||||
|
||||
PDFCharacterPointer start;
|
||||
@@ -217,6 +220,9 @@ public:
|
||||
|
||||
using iterator = PDFTextSelectionColoredItems::const_iterator;
|
||||
|
||||
bool operator==(const PDFTextSelection&) const = default;
|
||||
bool operator!=(const PDFTextSelection&) const = default;
|
||||
|
||||
/// Adds text selection items to selection
|
||||
/// \param items Items
|
||||
/// \param color Color for items (must include alpha channel)
|
||||
@@ -232,6 +238,9 @@ public:
|
||||
/// Returns iterator to end of page range
|
||||
iterator end(PDFInteger pageIndex) const;
|
||||
|
||||
/// Returns true, if text selection is empty
|
||||
bool isEmpty() const { return m_items.empty(); }
|
||||
|
||||
private:
|
||||
PDFTextSelectionColoredItems m_items;
|
||||
};
|
||||
@@ -327,6 +336,9 @@ public:
|
||||
/// Returns recognized text blocks
|
||||
const PDFTextBlocks& getTextBlocks() const { return m_blocks; }
|
||||
|
||||
/// Returns true, if given point is pointing to some text block
|
||||
bool isHoveringOverTextBlock(const QPointF& point) const;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const PDFTextLayout& layout);
|
||||
friend QDataStream& operator>>(QDataStream& stream, PDFTextLayout& layout);
|
||||
|
||||
|
@@ -25,6 +25,9 @@
|
||||
#include <QLineEdit>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
@@ -33,6 +36,17 @@ PDFWidgetTool::PDFWidgetTool(PDFDrawWidgetProxy* proxy, QObject* parent) :
|
||||
BaseClass(parent),
|
||||
m_active(false),
|
||||
m_document(nullptr),
|
||||
m_action(nullptr),
|
||||
m_proxy(proxy)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PDFWidgetTool::PDFWidgetTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent) :
|
||||
BaseClass(parent),
|
||||
m_active(false),
|
||||
m_document(nullptr),
|
||||
m_action(action),
|
||||
m_proxy(proxy)
|
||||
{
|
||||
|
||||
@@ -62,6 +76,7 @@ void PDFWidgetTool::setDocument(const PDFDocument* document)
|
||||
// We must turn off the tool, if we are changing the document
|
||||
setActive(false);
|
||||
m_document = document;
|
||||
updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,17 +96,57 @@ void PDFWidgetTool::setActive(bool active)
|
||||
}
|
||||
|
||||
setActiveImpl(active);
|
||||
updateActions();
|
||||
|
||||
m_proxy->repaintNeeded();
|
||||
emit toolActivityChanged(active);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFWidgetTool::keyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::wheelEvent(QWidget* widget, QWheelEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::setActiveImpl(bool active)
|
||||
{
|
||||
Q_UNUSED(active);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::updateActions()
|
||||
{
|
||||
if (m_action)
|
||||
{
|
||||
m_action->setChecked(isActive());
|
||||
m_action->setEnabled(m_document);
|
||||
}
|
||||
}
|
||||
|
||||
PDFFindTextTool::PDFFindTextTool(PDFDrawWidgetProxy* proxy, QAction* prevAction, QAction* nextAction, QObject* parent, QWidget* parentDialog) :
|
||||
BaseClass(proxy, parent),
|
||||
m_prevAction(prevAction),
|
||||
@@ -143,7 +198,7 @@ void PDFFindTextTool::setActiveImpl(bool active)
|
||||
getProxy()->getTextLayoutCompiler()->makeTextLayout();
|
||||
|
||||
// Create dialog
|
||||
m_dialog = new QDialog(m_parentDialog, Qt::Tool);
|
||||
m_dialog = new QDialog(m_parentDialog, Qt::Popup | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
|
||||
m_dialog->setWindowTitle(tr("Find"));
|
||||
|
||||
QGridLayout* layout = new QGridLayout(m_dialog);
|
||||
@@ -164,6 +219,9 @@ void PDFFindTextTool::setActiveImpl(bool active)
|
||||
m_previousButton->setDefault(false);
|
||||
m_nextButton->setDefault(false);
|
||||
|
||||
m_previousButton->setShortcut(m_prevAction->shortcut());
|
||||
m_nextButton->setShortcut(m_nextAction->shortcut());
|
||||
|
||||
connect(m_previousButton, &QPushButton::clicked, m_prevAction, &QAction::trigger);
|
||||
connect(m_nextButton, &QPushButton::clicked, m_nextAction, &QAction::trigger);
|
||||
connect(m_findTextEdit, &QLineEdit::editingFinished, this, &PDFFindTextTool::onSearchText);
|
||||
@@ -184,6 +242,8 @@ void PDFFindTextTool::setActiveImpl(bool active)
|
||||
|
||||
m_dialog->show();
|
||||
m_dialog->move(topRightParent - QPoint(m_dialog->width() * 1.1, 0));
|
||||
m_dialog->setFocus();
|
||||
m_findTextEdit->setFocus();
|
||||
connect(m_dialog, &QDialog::rejected, this, [this] { setActive(false); });
|
||||
}
|
||||
else
|
||||
@@ -329,6 +389,8 @@ void PDFFindTextTool::performSearch()
|
||||
|
||||
void PDFFindTextTool::updateActions()
|
||||
{
|
||||
BaseClass::updateActions();
|
||||
|
||||
const bool isActive = this->isActive();
|
||||
const bool hasResults = !m_findResults.empty();
|
||||
const bool enablePrevious = isActive && hasResults;
|
||||
@@ -384,15 +446,161 @@ PDFTextSelection PDFFindTextTool::getTextSelectionImpl() const
|
||||
return result;
|
||||
}
|
||||
|
||||
PDFToolManager::PDFToolManager(PDFDrawWidgetProxy* proxy, QAction* findPreviousAction, QAction* findNextAction, QObject* parent, QWidget* parentDialog) :
|
||||
PDFSelectTextTool::PDFSelectTextTool(PDFDrawWidgetProxy* proxy, QAction* action, QAction* selectAllAction, QAction* deselectAction, QObject* parent) :
|
||||
BaseClass(proxy, action, parent),
|
||||
m_selectAllAction(selectAllAction),
|
||||
m_deselectAction(deselectAction),
|
||||
m_isCursorOverText(false)
|
||||
{
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
QPointF pagePoint;
|
||||
const PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
if (pageIndex != -1)
|
||||
{
|
||||
m_selectionInfo.pageIndex = pageIndex;
|
||||
m_selectionInfo.selectionStartPoint = pagePoint;
|
||||
event->accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_selectionInfo = SelectionInfo();
|
||||
}
|
||||
|
||||
setSelection(pdf::PDFTextSelection());
|
||||
updateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
if (m_selectionInfo.pageIndex != -1)
|
||||
{
|
||||
QPointF pagePoint;
|
||||
const PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
|
||||
if (m_selectionInfo.pageIndex == pageIndex)
|
||||
{
|
||||
// Jakub Melka: handle the selection
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelection(pdf::PDFTextSelection());
|
||||
}
|
||||
|
||||
m_selectionInfo = SelectionInfo();
|
||||
event->accept();
|
||||
updateCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
|
||||
QPointF pagePoint;
|
||||
const PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
PDFTextLayout textLayout = getProxy()->getTextLayoutCompiler()->getTextLayoutLazy(pageIndex);
|
||||
m_isCursorOverText = textLayout.isHoveringOverTextBlock(pagePoint);
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
if (m_selectionInfo.pageIndex != -1)
|
||||
{
|
||||
if (m_selectionInfo.pageIndex == pageIndex)
|
||||
{
|
||||
// Jakub Melka: handle the selection
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelection(pdf::PDFTextSelection());
|
||||
}
|
||||
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::setActiveImpl(bool active)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
pdf::PDFAsynchronousTextLayoutCompiler* compiler = getProxy()->getTextLayoutCompiler();
|
||||
if (!compiler->isTextLayoutReady())
|
||||
{
|
||||
compiler->makeTextLayout();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just clear the text selection
|
||||
setSelection(PDFTextSelection());
|
||||
}
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::updateActions()
|
||||
{
|
||||
BaseClass::updateActions();
|
||||
|
||||
m_selectAllAction->setEnabled(isActive());
|
||||
m_deselectAction->setEnabled(isActive() && !m_textSelection.isEmpty());
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::updateCursor()
|
||||
{
|
||||
if (isActive())
|
||||
{
|
||||
if (m_isCursorOverText)
|
||||
{
|
||||
setCursor(QCursor(Qt::IBeamCursor));
|
||||
}
|
||||
else
|
||||
{
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFSelectTextTool::setSelection(PDFTextSelection&& textSelection)
|
||||
{
|
||||
if (m_textSelection != textSelection)
|
||||
{
|
||||
m_textSelection = qMove(textSelection);
|
||||
getProxy()->repaintNeeded();
|
||||
updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
PDFToolManager::PDFToolManager(PDFDrawWidgetProxy* proxy, Actions actions, QObject* parent, QWidget* parentDialog) :
|
||||
BaseClass(parent),
|
||||
m_predefinedTools()
|
||||
{
|
||||
m_predefinedTools[FindTextTool] = new PDFFindTextTool(proxy, findPreviousAction, findNextAction, this, parentDialog);
|
||||
m_predefinedTools[FindTextTool] = new PDFFindTextTool(proxy, actions.findPrevAction, actions.findNextAction, this, parentDialog);
|
||||
m_predefinedTools[SelectTextTool] = new PDFSelectTextTool(proxy, actions.selectTextToolAction, actions.selectAllAction, actions.deselectAction, this);
|
||||
|
||||
for (PDFWidgetTool* tool : m_predefinedTools)
|
||||
{
|
||||
m_tools.insert(tool);
|
||||
|
||||
if (QAction* action = tool->getAction())
|
||||
{
|
||||
m_actionsToTools[action] = tool;
|
||||
connect(action, &QAction::triggered, this, &PDFToolManager::onToolActionTriggered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,6 +612,22 @@ void PDFToolManager::setDocument(const PDFDocument* document)
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::setActiveTool(PDFWidgetTool* tool)
|
||||
{
|
||||
PDFWidgetTool* activeTool = getActiveTool();
|
||||
if (activeTool && activeTool != tool)
|
||||
{
|
||||
activeTool->setActive(false);
|
||||
}
|
||||
|
||||
Q_ASSERT(!getActiveTool());
|
||||
|
||||
if (tool)
|
||||
{
|
||||
tool->setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
PDFWidgetTool* PDFToolManager::getActiveTool() const
|
||||
{
|
||||
for (PDFWidgetTool* tool : m_tools)
|
||||
@@ -422,4 +646,88 @@ PDFFindTextTool* PDFToolManager::getFindTextTool() const
|
||||
return qobject_cast<PDFFindTextTool*>(m_predefinedTools[FindTextTool]);
|
||||
}
|
||||
|
||||
void PDFToolManager::keyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
// Escape key cancels current tool
|
||||
PDFWidgetTool* activeTool = getActiveTool();
|
||||
if (event->key() == Qt::Key_Escape && activeTool)
|
||||
{
|
||||
activeTool->setActive(false);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeTool)
|
||||
{
|
||||
activeTool->keyPressEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
if (PDFWidgetTool* activeTool = getActiveTool())
|
||||
{
|
||||
activeTool->mousePressEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
if (PDFWidgetTool* activeTool = getActiveTool())
|
||||
{
|
||||
activeTool->mouseReleaseEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
if (PDFWidgetTool* activeTool = getActiveTool())
|
||||
{
|
||||
activeTool->mouseMoveEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::wheelEvent(QWidget* widget, QWheelEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
if (PDFWidgetTool* activeTool = getActiveTool())
|
||||
{
|
||||
activeTool->wheelEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
const std::optional<QCursor>& PDFToolManager::getCursor() const
|
||||
{
|
||||
if (PDFWidgetTool* tool = getActiveTool())
|
||||
{
|
||||
return tool->getCursor();
|
||||
}
|
||||
|
||||
static const std::optional<QCursor> dummy;
|
||||
return dummy;
|
||||
}
|
||||
|
||||
void PDFToolManager::onToolActionTriggered(bool checked)
|
||||
{
|
||||
PDFWidgetTool* tool = m_actionsToTools.at(qobject_cast<QAction*>(sender()));
|
||||
if (checked)
|
||||
{
|
||||
setActiveTool(tool);
|
||||
}
|
||||
else
|
||||
{
|
||||
tool->setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace pdf
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "pdftextlayout.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QCursor>
|
||||
|
||||
class QCheckBox;
|
||||
|
||||
@@ -40,6 +41,7 @@ private:
|
||||
|
||||
public:
|
||||
explicit PDFWidgetTool(PDFDrawWidgetProxy* proxy, QObject* parent);
|
||||
explicit PDFWidgetTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
|
||||
virtual ~PDFWidgetTool();
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
@@ -61,24 +63,62 @@ public:
|
||||
/// Returns true, if tool is active
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
/// Returns action for activating/deactivating this tool
|
||||
QAction* getAction() const { return m_action; }
|
||||
|
||||
/// Handles key press event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event);
|
||||
|
||||
/// Handles mouse press event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse release event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse move event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse wheel event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void wheelEvent(QWidget* widget, QWheelEvent* event);
|
||||
|
||||
/// Returns actual cursor defined by the tool. Cursor can be undefined,
|
||||
/// in this case, optional will be set to nullopt.
|
||||
const std::optional<QCursor>& getCursor() const { return m_cursor; }
|
||||
|
||||
signals:
|
||||
void toolActivityChanged(bool active);
|
||||
|
||||
protected:
|
||||
virtual void setActiveImpl(bool active);
|
||||
virtual void updateActions();
|
||||
|
||||
PDFDrawWidgetProxy* getProxy() const { return m_proxy; }
|
||||
|
||||
inline void setCursor(QCursor cursor) { m_cursor = qMove(cursor); }
|
||||
inline void unsetCursor() { m_cursor = std::nullopt; }
|
||||
|
||||
private:
|
||||
bool m_active;
|
||||
const PDFDocument* m_document;
|
||||
QAction* m_action;
|
||||
PDFDrawWidgetProxy* m_proxy;
|
||||
std::vector<PDFWidgetTool*> m_toolStack;
|
||||
std::optional<QCursor> m_cursor;
|
||||
};
|
||||
|
||||
/// Simple tool for find text in PDF document. It is much simpler than advanced
|
||||
/// search and can't search using regular expressions.
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFFindTextTool : public PDFWidgetTool
|
||||
class PDFFindTextTool : public PDFWidgetTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -102,6 +142,7 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void setActiveImpl(bool active) override;
|
||||
virtual void updateActions() override;
|
||||
|
||||
private:
|
||||
void onSearchText();
|
||||
@@ -109,7 +150,6 @@ private:
|
||||
void onActionNext();
|
||||
|
||||
void performSearch();
|
||||
void updateActions();
|
||||
void updateResultsUI();
|
||||
void updateTitle();
|
||||
void clearResults();
|
||||
@@ -142,6 +182,45 @@ private:
|
||||
mutable pdf::PDFCachedItem<pdf::PDFTextSelection> m_textSelection;
|
||||
};
|
||||
|
||||
/// Tool for selection of text in document
|
||||
class PDFSelectTextTool : public PDFWidgetTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFWidgetTool;
|
||||
|
||||
public:
|
||||
/// Construct new text selection tool
|
||||
/// \param proxy Draw widget proxy
|
||||
/// \param parent Parent object
|
||||
explicit PDFSelectTextTool(PDFDrawWidgetProxy* proxy, QAction* action, QAction* selectAllAction,QAction* deselectAction, QObject* parent);
|
||||
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
|
||||
protected:
|
||||
virtual void setActiveImpl(bool active) override;
|
||||
virtual void updateActions() override;
|
||||
|
||||
private:
|
||||
void updateCursor();
|
||||
void setSelection(pdf::PDFTextSelection&& textSelection);
|
||||
|
||||
struct SelectionInfo
|
||||
{
|
||||
PDFInteger pageIndex = -1;
|
||||
QPointF selectionStartPoint;
|
||||
};
|
||||
|
||||
QAction* m_selectAllAction;
|
||||
QAction* m_deselectAction;
|
||||
pdf::PDFTextSelection m_textSelection;
|
||||
SelectionInfo m_selectionInfo;
|
||||
bool m_isCursorOverText;
|
||||
};
|
||||
|
||||
/// Manager used for managing tools, their activity, availability
|
||||
/// and other settings. It also defines a predefined set of tools,
|
||||
/// available for various purposes (text searching, magnifier tool etc.)
|
||||
@@ -153,13 +232,21 @@ private:
|
||||
using BaseClass = QObject;
|
||||
|
||||
public:
|
||||
struct Actions
|
||||
{
|
||||
QAction* findPrevAction = nullptr; ///< Action for navigating to previous result
|
||||
QAction* findNextAction = nullptr; ///< Action for navigating to next result
|
||||
QAction* selectTextToolAction = nullptr;
|
||||
QAction* selectAllAction = nullptr;
|
||||
QAction* deselectAction = nullptr;
|
||||
};
|
||||
|
||||
/// Construct new text search tool
|
||||
/// \param proxy Draw widget proxy
|
||||
/// \param prevAction Action for navigating to previous result
|
||||
/// \param nextAction Action for navigating to next result
|
||||
/// \param actions Actions
|
||||
/// \param parent Parent object
|
||||
/// \param parentDialog Paret dialog for tool dialog
|
||||
explicit PDFToolManager(PDFDrawWidgetProxy* proxy, QAction* findPreviousAction, QAction* findNextAction, QObject* parent, QWidget* parentDialog);
|
||||
explicit PDFToolManager(PDFDrawWidgetProxy* proxy, Actions actions, QObject* parent, QWidget* parentDialog);
|
||||
|
||||
/// Sets document
|
||||
/// \param document Document
|
||||
@@ -168,9 +255,13 @@ public:
|
||||
enum PredefinedTools
|
||||
{
|
||||
FindTextTool,
|
||||
SelectTextTool,
|
||||
ToolEnd
|
||||
};
|
||||
|
||||
/// Sets active tool
|
||||
void setActiveTool(PDFWidgetTool* tool);
|
||||
|
||||
/// Returns first active tool from tool set. If no tool is active,
|
||||
/// then nullptr is returned.
|
||||
PDFWidgetTool* getActiveTool() const;
|
||||
@@ -178,9 +269,41 @@ public:
|
||||
/// Returns find text tool
|
||||
PDFFindTextTool* getFindTextTool() const;
|
||||
|
||||
/// Handles key press event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
void keyPressEvent(QWidget* widget, QKeyEvent* event);
|
||||
|
||||
/// Handles mouse press event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
void mousePressEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse release event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
void mouseReleaseEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse move event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
void mouseMoveEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse wheel event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
void wheelEvent(QWidget* widget, QWheelEvent* event);
|
||||
|
||||
/// Returns actual cursor defined by the tool. Cursor can be undefined,
|
||||
/// in this case, optional will be set to nullopt.
|
||||
const std::optional<QCursor>& getCursor() const;
|
||||
|
||||
private:
|
||||
void onToolActionTriggered(bool checked);
|
||||
|
||||
std::set<PDFWidgetTool*> m_tools;
|
||||
std::array<PDFWidgetTool*, ToolEnd> m_predefinedTools;
|
||||
std::map<QAction*, PDFWidgetTool*> m_actionsToTools;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
Reference in New Issue
Block a user