7#include <QGuiApplication>
8#include <QLoggingCategory>
11#include <QRegularExpression>
12#include <QStandardPaths>
13#include <QtPdf/private/qpdfdocument_p.h>
14#include <QtPdf/private/qtpdfglobal_p.h>
16Q_PDF_LOGGING_CATEGORY(qLcIm,
"qt.pdf.im")
20static const QRegularExpression WordDelimiter(QStringLiteral(
"\\s"));
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
45QQuickPdfSelection::QQuickPdfSelection(QQuickItem *parent)
49 setFlags(ItemIsFocusScope | ItemAcceptsInputMethod);
54
55
56QQuickPdfSelection::~QQuickPdfSelection() =
default;
59
60
61
62
63QQuickPdfDocument *QQuickPdfSelection::document()
const
68void QQuickPdfSelection::setDocument(QQuickPdfDocument *document)
70 if (m_document == document)
74 disconnect(m_document, &QQuickPdfDocument::sourceChanged,
75 this, &QQuickPdfSelection::resetPoints);
77 m_document = document;
78 emit documentChanged();
80 connect(m_document, &QQuickPdfDocument::sourceChanged,
81 this, &QQuickPdfSelection::resetPoints);
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118QList<QPolygonF> QQuickPdfSelection::geometry()
const
124
125
126
127
128void QQuickPdfSelection::clear()
130 m_hitPoint = QPointF();
133 m_heightAtAnchor = 0;
134 m_heightAtCursor = 0;
135 m_fromCharIndex = -1;
142 emit selectedAreaChanged();
143 QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
147
148
149
150
151void QQuickPdfSelection::selectAll()
155 QPdfSelection sel = m_document->document()->getAllText(m_page);
156 if (sel.text() != m_text) {
158 if (QGuiApplication::clipboard()->supportsSelection())
159 sel.copyToClipboard(QClipboard::Selection);
163 if (sel.bounds() != m_geometry) {
164 m_geometry = sel.bounds();
165 emit selectedAreaChanged();
168 m_fromCharIndex = sel.startIndex();
169 m_toCharIndex = sel.endIndex();
170 if (sel.bounds().isEmpty()) {
174 m_from = sel.bounds().first().boundingRect().topLeft() * m_renderScale;
175 m_to = sel.bounds().last().boundingRect().bottomRight() * m_renderScale - QPointF(0, m_heightAtCursor);
178 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
183void QQuickPdfSelection::keyReleaseEvent(QKeyEvent *ev)
185 qCDebug(qLcIm) <<
"release" << ev;
186 const auto &allText = pageText();
187 if (ev == QKeySequence::MoveToPreviousWord) {
192 int i = allText.lastIndexOf(WordDelimiter, m_fromCharIndex - allText.size());
197 auto sel = m_document->document()->getSelectionAtIndex(m_page, i, m_text.size() + m_fromCharIndex - i);
199 QGuiApplication::inputMethod()->update(Qt::ImAnchorRectangle);
200 }
else if (ev == QKeySequence::SelectNextWord) {
203 int i = allText.indexOf(WordDelimiter, m_toCharIndex);
206 auto sel = m_document->document()->getSelectionAtIndex(m_page, m_fromCharIndex, m_text.size() + i - m_toCharIndex);
208 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle);
209 }
else if (ev == QKeySequence::Copy) {
214void QQuickPdfSelection::inputMethodEvent(QInputMethodEvent *event)
216 for (
auto attr : event->attributes()) {
218 case QInputMethodEvent::Cursor:
219 qCDebug(qLcIm) <<
"QInputMethodEvent::Cursor: moved to" << attr.start <<
"len" << attr.length;
221 case QInputMethodEvent::Selection: {
224 auto sel = m_document->document()->getSelectionAtIndex(m_page, attr.start, attr.length);
226 qCDebug(qLcIm) <<
"QInputMethodEvent::Selection: from" << attr.start <<
"len" << attr.length
227 <<
"result:" << m_fromCharIndex <<
"->" << m_toCharIndex << sel.boundingRectangle();
229 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
232 case QInputMethodEvent::Language:
233 case QInputMethodEvent::Ruby:
234 case QInputMethodEvent::TextFormat:
235#if QT_VERSION >= QT_VERSION_CHECK(6
, 10
, 0
)
236 case QInputMethodEvent::MimeData:
243QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query,
const QVariant &argument)
const
245 if (!argument.isNull()) {
246 qCDebug(qLcIm) <<
"IM query" << query <<
"with arg" << argument;
247 if (query == Qt::ImCursorPosition) {
251 if (m_hitPoint == argument.toPointF())
252 return inputMethodQuery(query);
253 m_hitPoint = argument.toPointF();
254 auto tp = m_document->document()->d->hitTest(m_page, m_hitPoint / m_renderScale);
255 qCDebug(qLcIm) <<
"ImCursorPosition hit testing in px" << m_hitPoint <<
"pt" << (m_hitPoint / m_renderScale)
256 <<
"got char index" << tp.charIndex <<
"@" << tp.position <<
"pt," << tp.position * m_renderScale <<
"px";
257 if (tp.charIndex >= 0) {
258 m_toCharIndex = tp.charIndex;
259 m_to = tp.position * m_renderScale - QPointF(0, m_heightAtCursor);
260 m_heightAtCursor = tp.height * m_renderScale;
261 if (qFuzzyIsNull(m_heightAtAnchor))
262 m_heightAtAnchor = m_heightAtCursor;
266 return inputMethodQuery(query);
269QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query)
const
277 ret = QVariant(Qt::ImhMultiLine | Qt::ImhNoPredictiveText);
279 case Qt::ImInputItemClipRectangle:
280 ret = boundingRect();
282 case Qt::ImAnchorPosition:
283 ret = m_fromCharIndex;
285 case Qt::ImAbsolutePosition:
288 case Qt::ImCursorPosition:
291 case Qt::ImAnchorRectangle:
292 ret = QRectF(m_from, QSizeF(1, m_heightAtAnchor));
294 case Qt::ImCursorRectangle:
295 ret = QRectF(m_to, QSizeF(1, m_heightAtCursor));
297 case Qt::ImSurroundingText:
298 ret = QVariant(pageText());
300 case Qt::ImTextBeforeCursor:
301 ret = QVariant(pageText().mid(0, m_toCharIndex));
303 case Qt::ImTextAfterCursor:
304 ret = QVariant(pageText().mid(m_toCharIndex));
306 case Qt::ImCurrentSelection:
307 ret = QVariant(m_text);
309 case Qt::ImEnterKeyType:
312 QFont font = QGuiApplication::font();
313 font.setPointSizeF(m_heightAtCursor);
317 case Qt::ImMaximumTextLength:
319 case Qt::ImPreferredLanguage:
321 case Qt::ImPlatformData:
326 case Qt::ImQueryInput:
328 qWarning() <<
"unexpected composite query";
331 qCDebug(qLcIm) <<
"IM query" << query <<
"returns" << ret;
336const QString &QQuickPdfSelection::pageText()
const
338 if (m_pageTextDirty) {
341 m_pageText = m_document->document()->getAllText(m_page).text();
342 m_pageTextDirty =
false;
347void QQuickPdfSelection::resetPoints()
349 bool wasHolding = m_hold;
357
358
359
360
361
362
363int QQuickPdfSelection::page()
const
368void QQuickPdfSelection::setPage(
int page)
374 m_pageTextDirty =
true;
380
381
382
383
384
385
386
387qreal QQuickPdfSelection::renderScale()
const
389 return m_renderScale;
392void QQuickPdfSelection::setRenderScale(qreal scale)
394 if (qFuzzyIsNull(scale)) {
395 qWarning() <<
"PdfSelection.renderScale cannot be set to 0.";
399 if (qFuzzyCompare(scale, m_renderScale))
402 m_renderScale = scale;
403 emit renderScaleChanged();
408
409
410
411
412
413
414
415
416QPointF QQuickPdfSelection::from()
const
421void QQuickPdfSelection::setFrom(QPointF from)
423 if (m_hold || m_from == from)
432
433
434
435
436
437
438
439QPointF QQuickPdfSelection::to()
const
444void QQuickPdfSelection::setTo(QPointF to)
446 if (m_hold || m_to == to)
455
456
457
458
459
460
461
462bool QQuickPdfSelection::hold()
const
467void QQuickPdfSelection::setHold(
bool hold)
477
478
479
480
481
482QString QQuickPdfSelection::text()
const
487#if QT_CONFIG(clipboard)
489
490
491
492
493void QQuickPdfSelection::copyToClipboard()
const
495 QGuiApplication::clipboard()->setText(m_text);
499void QQuickPdfSelection::updateResults()
503 QPdfSelection sel = m_document->document()->getSelection(m_page,
504 m_from / m_renderScale, m_to / m_renderScale);
508void QQuickPdfSelection::update(
const QPdfSelection &sel,
bool textAndGeometryOnly)
510 if (sel.text() != m_text) {
512 if (QGuiApplication::clipboard()->supportsSelection())
513 sel.copyToClipboard(QClipboard::Selection);
517 if (sel.bounds() != m_geometry) {
518 m_geometry = sel.bounds();
519 emit selectedAreaChanged();
522 if (textAndGeometryOnly)
525 m_fromCharIndex = sel.startIndex();
526 m_toCharIndex = sel.endIndex();
527 if (sel.bounds().isEmpty()) {
528 m_from = sel.boundingRectangle().topLeft() * m_renderScale;
531 Qt::InputMethodQueries toUpdate = {};
532 QRectF firstLineBounds = sel.bounds().first().boundingRect();
533 m_from = firstLineBounds.topLeft() * m_renderScale;
534 if (!qFuzzyCompare(m_heightAtAnchor, firstLineBounds.height())) {
535 m_heightAtAnchor = firstLineBounds.height() * m_renderScale;
536 toUpdate.setFlag(Qt::ImAnchorRectangle);
538 QRectF lastLineBounds = sel.bounds().last().boundingRect();
539 if (!qFuzzyCompare(m_heightAtCursor, lastLineBounds.height())) {
540 m_heightAtCursor = lastLineBounds.height() * m_renderScale;
541 toUpdate.setFlag(Qt::ImCursorRectangle);
543 m_to = lastLineBounds.topRight() * m_renderScale;
545 QGuiApplication::inputMethod()->update(toUpdate);
551#include "moc_qquickpdfselection_p.cpp"