1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <QtWidget.hxx>
21 #include <QtWidget.moc>
23 #include <QtFrame.hxx>
24 #include <QtGraphics.hxx>
25 #include <QtInstance.hxx>
26 #include <QtMainWindow.hxx>
27 #include <QtSvpGraphics.hxx>
28 #include <QtTransferable.hxx>
29 #include <QtTools.hxx>
31 #include <QtCore/QMimeData>
32 #include <QtGui/QDrag>
33 #include <QtGui/QFocusEvent>
34 #include <QtGui/QGuiApplication>
35 #include <QtGui/QImage>
36 #include <QtGui/QKeyEvent>
37 #include <QtGui/QMouseEvent>
38 #include <QtGui/QPainter>
39 #include <QtGui/QPaintEvent>
40 #include <QtGui/QResizeEvent>
41 #include <QtGui/QShowEvent>
42 #include <QtGui/QTextCharFormat>
43 #include <QtGui/QWheelEvent>
44 #include <QtWidgets/QMainWindow>
45 #include <QtWidgets/QToolTip>
46 #include <QtWidgets/QWidget>
49 #include <vcl/commandevent.hxx>
50 #include <vcl/event.hxx>
51 #include <vcl/qt/QtUtils.hxx>
52 #include <vcl/toolkit/floatwin.hxx>
54 #include <comphelper/diagnose_ex.hxx>
56 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
57 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
59 #if CHECK_ANY_QT_USING_X11
61 #include <X11/keysymdef.h>
64 using namespace com::sun::star
;
66 void QtWidget::paintEvent(QPaintEvent
* pEvent
) { m_rFrame
.handlePaintEvent(pEvent
, this); }
68 void QtWidget::resizeEvent(QResizeEvent
* pEvent
) { m_rFrame
.handleResizeEvent(pEvent
); }
70 void QtWidget::fakeResize()
72 QResizeEvent
aEvent(size(), QSize());
76 void QtWidget::fillSalAbstractMouseEvent(const QtFrame
& rFrame
, const QInputEvent
* pQEvent
,
77 const QPoint
& rPos
, Qt::MouseButtons eButtons
, int nWidth
,
78 SalAbstractMouseEvent
& aSalEvent
)
80 const qreal fRatio
= rFrame
.devicePixelRatioF();
81 const Point aPos
= toPoint(rPos
* fRatio
);
83 aSalEvent
.mnX
= QGuiApplication::isLeftToRight() ? aPos
.X() : round(nWidth
* fRatio
) - aPos
.X();
84 aSalEvent
.mnY
= aPos
.Y();
85 aSalEvent
.mnTime
= pQEvent
->timestamp();
86 aSalEvent
.mnCode
= GetKeyModCode(pQEvent
->modifiers()) | GetMouseModCode(eButtons
);
89 #define FILL_SAME(rFrame, nWidth) \
90 fillSalAbstractMouseEvent(rFrame, pEvent, pEvent->pos(), pEvent->buttons(), nWidth, aEvent)
92 void QtWidget::handleMouseButtonEvent(const QtFrame
& rFrame
, const QMouseEvent
* pEvent
)
95 FILL_SAME(rFrame
, rFrame
.GetQWidget()->width());
97 switch (pEvent
->button())
100 aEvent
.mnButton
= MOUSE_LEFT
;
102 case Qt::MiddleButton
:
103 aEvent
.mnButton
= MOUSE_MIDDLE
;
105 case Qt::RightButton
:
106 aEvent
.mnButton
= MOUSE_RIGHT
;
113 if (pEvent
->type() == QEvent::MouseButtonPress
|| pEvent
->type() == QEvent::MouseButtonDblClick
)
114 nEventType
= SalEvent::MouseButtonDown
;
116 nEventType
= SalEvent::MouseButtonUp
;
117 rFrame
.CallCallback(nEventType
, &aEvent
);
120 void QtWidget::mousePressEvent(QMouseEvent
* pEvent
)
122 handleMouseButtonEvent(m_rFrame
, pEvent
);
123 if (m_rFrame
.isPopup()
124 && !geometry().translated(geometry().topLeft() * -1).contains(pEvent
->pos()))
128 void QtWidget::mouseReleaseEvent(QMouseEvent
* pEvent
) { handleMouseButtonEvent(m_rFrame
, pEvent
); }
130 void QtWidget::mouseMoveEvent(QMouseEvent
* pEvent
)
132 SalMouseEvent aEvent
;
133 FILL_SAME(m_rFrame
, width());
137 m_rFrame
.CallCallback(SalEvent::MouseMove
, &aEvent
);
141 void QtWidget::handleMouseEnterLeaveEvents(const QtFrame
& rFrame
, QEvent
* pQEvent
)
143 const qreal fRatio
= rFrame
.devicePixelRatioF();
144 const QWidget
* pWidget
= rFrame
.GetQWidget();
145 const Point aPos
= toPoint(pWidget
->mapFromGlobal(QCursor::pos()) * fRatio
);
147 SalMouseEvent aEvent
;
149 = QGuiApplication::isLeftToRight() ? aPos
.X() : round(pWidget
->width() * fRatio
) - aPos
.X();
150 aEvent
.mnY
= aPos
.Y();
153 aEvent
.mnCode
= GetKeyModCode(QGuiApplication::keyboardModifiers())
154 | GetMouseModCode(QGuiApplication::mouseButtons());
157 if (pQEvent
->type() == QEvent::Enter
)
158 nEventType
= SalEvent::MouseMove
;
160 nEventType
= SalEvent::MouseLeave
;
161 rFrame
.CallCallback(nEventType
, &aEvent
);
165 void QtWidget::leaveEvent(QEvent
* pEvent
) { handleMouseEnterLeaveEvents(m_rFrame
, pEvent
); }
167 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
168 void QtWidget::enterEvent(QEnterEvent
* pEvent
)
170 void QtWidget::enterEvent(QEvent
* pEvent
)
173 handleMouseEnterLeaveEvents(m_rFrame
, pEvent
);
176 void QtWidget::wheelEvent(QWheelEvent
* pEvent
)
178 SalWheelMouseEvent aEvent
;
179 fillSalAbstractMouseEvent(m_rFrame
, pEvent
, pEvent
->position().toPoint(), pEvent
->buttons(),
182 // mouse wheel ticks are 120, which we map to 3 lines.
183 // we have to accumulate for touch scroll to keep track of the absolute delta.
185 int nDelta
= pEvent
->angleDelta().y(), lines
;
186 aEvent
.mbHorz
= nDelta
== 0;
189 nDelta
= (QGuiApplication::isLeftToRight() ? 1 : -1) * pEvent
->angleDelta().x();
194 lines
= m_nDeltaX
/ 40;
195 m_nDeltaX
= m_nDeltaX
% 40;
200 lines
= m_nDeltaY
/ 40;
201 m_nDeltaY
= m_nDeltaY
% 40;
204 aEvent
.mnDelta
= nDelta
;
205 aEvent
.mnNotchDelta
= nDelta
< 0 ? -1 : 1;
206 aEvent
.mnScrollLines
= std::abs(lines
);
208 m_rFrame
.CallCallback(SalEvent::WheelMouse
, &aEvent
);
212 void QtWidget::dragEnterEvent(QDragEnterEvent
* event
)
214 if (qobject_cast
<const QtMimeData
*>(event
->mimeData()))
217 event
->acceptProposedAction();
220 // also called when a drop is rejected
221 void QtWidget::dragLeaveEvent(QDragLeaveEvent
*) { m_rFrame
.handleDragLeave(); }
223 void QtWidget::dragMoveEvent(QDragMoveEvent
* pEvent
) { m_rFrame
.handleDragMove(pEvent
); }
225 void QtWidget::dropEvent(QDropEvent
* pEvent
) { m_rFrame
.handleDrop(pEvent
); }
227 void QtWidget::moveEvent(QMoveEvent
* pEvent
)
229 // already handled by QtMainWindow::moveEvent
230 if (m_rFrame
.m_pTopLevel
)
233 m_rFrame
.handleMoveEvent(pEvent
);
236 void QtWidget::showEvent(QShowEvent
*)
238 QSize
aSize(m_rFrame
.GetQWidget()->size() * m_rFrame
.devicePixelRatioF());
239 // forcing an immediate update somehow interferes with the hide + show
240 // sequence from QtFrame::SetModal, if the frame was already set visible,
241 // resulting in a hidden / unmapped window
242 SalPaintEvent
aPaintEvt(0, 0, aSize
.width(), aSize
.height());
243 if (m_rFrame
.isPopup())
244 GetQtInstance().setActivePopup(&m_rFrame
);
245 m_rFrame
.CallCallback(SalEvent::Paint
, &aPaintEvt
);
248 void QtWidget::hideEvent(QHideEvent
* pEvent
)
250 if (m_rFrame
.isPopup())
252 if (GetQtInstance().activePopup() == &m_rFrame
)
253 GetQtInstance().setActivePopup(nullptr);
255 // destroy the QWindow as the popup would otherwise still show up
256 // as a top-level child of the app on the a11y level
257 // (Qt explicitly filters out widgets of type Qt::Popup, but
258 // Qt::ToolTip is currently used for popups to work around another
259 // issue, s. the QtFrame ctor)
260 if (!pEvent
->spontaneous())
265 void QtWidget::closeEvent(QCloseEvent
* /*pEvent*/)
267 m_rFrame
.CallCallback(SalEvent::Close
, nullptr);
270 static sal_uInt16
GetKeyCode(int keyval
, Qt::KeyboardModifiers modifiers
)
272 sal_uInt16 nCode
= 0;
273 if (keyval
>= Qt::Key_0
&& keyval
<= Qt::Key_9
)
274 nCode
= KEY_0
+ (keyval
- Qt::Key_0
);
275 else if (keyval
>= Qt::Key_A
&& keyval
<= Qt::Key_Z
)
276 nCode
= KEY_A
+ (keyval
- Qt::Key_A
);
277 else if (keyval
>= Qt::Key_F1
&& keyval
<= Qt::Key_F26
)
278 nCode
= KEY_F1
+ (keyval
- Qt::Key_F1
);
279 else if (modifiers
.testFlag(Qt::KeypadModifier
)
280 && (keyval
== Qt::Key_Period
|| keyval
== Qt::Key_Comma
))
281 // Qt doesn't use a special keyval for decimal separator ("," or ".")
282 // on numerical keypad, but sets Qt::KeypadModifier in addition
309 case Qt::Key_PageDown
:
310 nCode
= KEY_PAGEDOWN
;
320 // oddly enough, Qt doesn't send Shift-Tab event as 'Tab key pressed with Shift
321 // modifier' but as 'Backtab key pressed' (while its modifier bits are still
322 // set to Shift) -- so let's map both Key_Tab and Key_Backtab to VCL's KEY_TAB
323 case Qt::Key_Backtab
:
326 case Qt::Key_Backspace
:
327 nCode
= KEY_BACKSPACE
;
342 nCode
= KEY_SUBTRACT
;
344 case Qt::Key_Asterisk
:
345 nCode
= KEY_MULTIPLY
;
359 case Qt::Key_Greater
:
369 nCode
= KEY_CONTEXTMENU
;
383 case Qt::Key_AsciiTilde
:
386 case Qt::Key_QuoteLeft
:
387 nCode
= KEY_QUOTELEFT
;
389 case Qt::Key_BracketLeft
:
390 nCode
= KEY_BRACKETLEFT
;
392 case Qt::Key_BracketRight
:
393 nCode
= KEY_BRACKETRIGHT
;
395 case Qt::Key_NumberSign
:
396 nCode
= KEY_NUMBERSIGN
;
398 case Qt::Key_Forward
:
399 nCode
= KEY_XF86FORWARD
;
402 nCode
= KEY_XF86BACK
;
407 case Qt::Key_Semicolon
:
408 nCode
= KEY_SEMICOLON
;
428 void QtWidget::commitText(QtFrame
& rFrame
, const QString
& aText
)
430 SalExtTextInputEvent aInputEvent
;
431 aInputEvent
.mpTextAttr
= nullptr;
432 aInputEvent
.mnCursorFlags
= 0;
433 aInputEvent
.maText
= toOUString(aText
);
434 aInputEvent
.mnCursorPos
= aInputEvent
.maText
.getLength();
436 SolarMutexGuard aGuard
;
437 vcl::DeletionListener
aDel(&rFrame
);
438 rFrame
.CallCallback(SalEvent::ExtTextInput
, &aInputEvent
);
439 if (!aDel
.isDeleted())
440 rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
443 void QtWidget::deleteReplacementText(QtFrame
& rFrame
, int nReplacementStart
, int nReplacementLength
)
445 // get the surrounding text
446 SolarMutexGuard aGuard
;
447 SalSurroundingTextRequestEvent aSurroundingTextEvt
;
448 aSurroundingTextEvt
.maText
.clear();
449 aSurroundingTextEvt
.mnStart
= aSurroundingTextEvt
.mnEnd
= 0;
450 rFrame
.CallCallback(SalEvent::SurroundingTextRequest
, &aSurroundingTextEvt
);
452 // Turn nReplacementStart, nReplacementLength into a UTF-16 selection
453 const Selection aSelection
= SalFrame::CalcDeleteSurroundingSelection(
454 aSurroundingTextEvt
.maText
, aSurroundingTextEvt
.mnStart
, nReplacementStart
,
457 const Selection
aInvalid(SAL_MAX_UINT32
, SAL_MAX_UINT32
);
458 if (aSelection
== aInvalid
)
460 SAL_WARN("vcl.qt", "Invalid selection when deleting IM replacement text");
464 SalSurroundingTextSelectionChangeEvent aEvt
;
465 aEvt
.mnStart
= aSelection
.Min();
466 aEvt
.mnEnd
= aSelection
.Max();
467 rFrame
.CallCallback(SalEvent::DeleteSurroundingTextRequest
, &aEvt
);
470 bool QtWidget::handleGestureEvent(QtFrame
& rFrame
, QGestureEvent
* pGestureEvent
)
472 if (QGesture
* pGesture
= pGestureEvent
->gesture(Qt::PinchGesture
))
474 if (!pGesture
->hasHotSpot())
476 pGestureEvent
->ignore();
480 GestureEventZoomType eType
= GestureEventZoomType::Begin
;
481 switch (pGesture
->state())
483 case Qt::GestureStarted
:
484 eType
= GestureEventZoomType::Begin
;
486 case Qt::GestureUpdated
:
487 eType
= GestureEventZoomType::Update
;
489 case Qt::GestureFinished
:
490 eType
= GestureEventZoomType::End
;
493 case Qt::GestureCanceled
:
495 SAL_WARN("vcl.qt", "Unhandled pinch gesture state: " << pGesture
->state());
496 pGestureEvent
->ignore();
500 QPinchGesture
* pPinchGesture
= static_cast<QPinchGesture
*>(pGesture
);
501 const QPointF aHotspot
= pGesture
->hotSpot();
502 SalGestureZoomEvent aEvent
;
503 aEvent
.meEventType
= eType
;
504 aEvent
.mnX
= aHotspot
.x();
505 aEvent
.mnY
= aHotspot
.y();
506 aEvent
.mfScaleDelta
= 1 + pPinchGesture
->totalScaleFactor();
507 rFrame
.CallCallback(SalEvent::GestureZoom
, &aEvent
);
508 pGestureEvent
->accept();
512 pGestureEvent
->ignore();
516 bool QtWidget::handleKeyEvent(QtFrame
& rFrame
, const QWidget
& rWidget
, QKeyEvent
* pEvent
)
518 const bool bIsKeyPressed
519 = pEvent
->type() == QEvent::KeyPress
|| pEvent
->type() == QEvent::ShortcutOverride
;
520 sal_uInt16 nCode
= GetKeyCode(pEvent
->key(), pEvent
->modifiers());
521 if (bIsKeyPressed
&& nCode
== 0 && pEvent
->text().length() > 1
522 && rWidget
.testAttribute(Qt::WA_InputMethodEnabled
))
524 commitText(rFrame
, pEvent
->text());
529 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle
);
531 if (nCode
== 0 && pEvent
->text().isEmpty())
533 sal_uInt16 nModCode
= GetKeyModCode(pEvent
->modifiers());
534 SalKeyModEvent aModEvt
;
535 aModEvt
.mbDown
= bIsKeyPressed
;
536 aModEvt
.mnModKeyCode
= ModKeyFlags::NONE
;
538 #if CHECK_ANY_QT_USING_X11
539 if (QGuiApplication::platformName() == "xcb")
541 // pressing just the ctrl key leads to a keysym of XK_Control but
542 // the event state does not contain ControlMask. In the release
543 // event it's the other way round: it does contain the Control mask.
544 // The modifier mode therefore has to be adapted manually.
545 ModKeyFlags nExtModMask
= ModKeyFlags::NONE
;
546 sal_uInt16 nModMask
= 0;
547 switch (pEvent
->nativeVirtualKey())
550 nExtModMask
= ModKeyFlags::LeftMod1
;
554 nExtModMask
= ModKeyFlags::RightMod1
;
558 nExtModMask
= ModKeyFlags::LeftMod2
;
562 nExtModMask
= ModKeyFlags::RightMod2
;
566 nExtModMask
= ModKeyFlags::LeftShift
;
567 nModMask
= KEY_SHIFT
;
570 nExtModMask
= ModKeyFlags::RightShift
;
571 nModMask
= KEY_SHIFT
;
573 // Map Meta/Super keys to MOD3 modifier on all Unix systems
577 nExtModMask
= ModKeyFlags::LeftMod3
;
582 nExtModMask
= ModKeyFlags::RightMod3
;
589 // sending the old mnModKeyCode mask on release is needed to
590 // implement the writing direction switch with Ctrl + L/R-Shift
591 aModEvt
.mnModKeyCode
= rFrame
.m_nKeyModifiers
;
592 nModCode
&= ~nModMask
;
593 rFrame
.m_nKeyModifiers
&= ~nExtModMask
;
597 nModCode
|= nModMask
;
598 rFrame
.m_nKeyModifiers
|= nExtModMask
;
599 aModEvt
.mnModKeyCode
= rFrame
.m_nKeyModifiers
;
603 aModEvt
.mnCode
= nModCode
;
605 rFrame
.CallCallback(SalEvent::KeyModChange
, &aModEvt
);
609 #if CHECK_ANY_QT_USING_X11
610 // prevent interference of writing direction switch (Ctrl + L/R-Shift) with "normal" shortcuts
611 rFrame
.m_nKeyModifiers
= ModKeyFlags::NONE
;
615 aEvent
.mnCharCode
= (pEvent
->text().isEmpty() ? 0 : pEvent
->text().at(0).unicode());
617 aEvent
.mnCode
= nCode
;
618 aEvent
.mnCode
|= GetKeyModCode(pEvent
->modifiers());
620 bool bStopProcessingKey
;
622 bStopProcessingKey
= rFrame
.CallCallback(SalEvent::KeyInput
, &aEvent
);
624 bStopProcessingKey
= rFrame
.CallCallback(SalEvent::KeyUp
, &aEvent
);
625 if (bStopProcessingKey
)
627 return bStopProcessingKey
;
630 bool QtWidget::handleEvent(QtFrame
& rFrame
, QWidget
& rWidget
, QEvent
* pEvent
)
632 if (pEvent
->type() == QEvent::Gesture
)
634 QGestureEvent
* pGestureEvent
= static_cast<QGestureEvent
*>(pEvent
);
635 return handleGestureEvent(rFrame
, pGestureEvent
);
637 else if (pEvent
->type() == QEvent::ShortcutOverride
)
639 // ignore non-spontaneous QEvent::ShortcutOverride events,
640 // since such an extra event is sent e.g. with Orca screen reader enabled,
641 // so that two events of that kind (the "real one" and a non-spontaneous one)
642 // would otherwise be processed, resulting in duplicate input as 'handleKeyEvent'
643 // is called below (s. tdf#122053)
644 if (!pEvent
->spontaneous())
646 // accept event so shortcut action (from menu) isn't triggered in addition
647 // to the processing for the spontaneous event further below
652 // Accepted event disables shortcut activation,
653 // but enables keypress event.
654 // If event is not accepted and shortcut is successfully activated,
655 // KeyPress event is omitted.
657 // Instead of processing keyPressEvent, handle ShortcutOverride event,
658 // and if it's handled - disable the shortcut, it should have been activated.
659 // Don't process keyPressEvent generated after disabling shortcut since it was handled here.
660 // If event is not handled, don't accept it and let Qt activate related shortcut.
661 if (handleKeyEvent(rFrame
, rWidget
, static_cast<QKeyEvent
*>(pEvent
)))
664 else if (pEvent
->type() == QEvent::ToolTip
)
666 // Qt's POV on the active popup is wrong due to our fake popup, so check LO's state.
667 // Otherwise Qt will continue handling ToolTip events from the "parent" window.
668 const QtFrame
* pPopupFrame
= GetQtInstance().activePopup();
669 if (!rFrame
.m_aTooltipText
.isEmpty() && (!pPopupFrame
|| pPopupFrame
== &rFrame
))
671 // tdf#162297 Use a dummy style to ensure the tooltip is wrapped
672 QString
sTooltipText("<font font-weight=normal>");
673 sTooltipText
+= toQString(rFrame
.m_aTooltipText
);
674 sTooltipText
+= "</font>";
675 QToolTip::showText(QCursor::pos(), sTooltipText
, &rWidget
, rFrame
.m_aTooltipArea
);
679 QToolTip::hideText();
687 bool QtWidget::event(QEvent
* pEvent
)
689 return handleEvent(m_rFrame
, *this, pEvent
) || QWidget::event(pEvent
);
692 void QtWidget::keyReleaseEvent(QKeyEvent
* pEvent
)
694 if (!handleKeyReleaseEvent(m_rFrame
, *this, pEvent
))
695 QWidget::keyReleaseEvent(pEvent
);
698 void QtWidget::focusInEvent(QFocusEvent
*) { m_rFrame
.CallCallback(SalEvent::GetFocus
, nullptr); }
700 void QtWidget::closePopup()
702 VclPtr
<FloatingWindow
> pFirstFloat
= ImplGetSVData()->mpWinData
->mpFirstFloat
;
703 if (pFirstFloat
&& !(pFirstFloat
->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose
))
705 SolarMutexGuard aGuard
;
706 pFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
710 void QtWidget::focusOutEvent(QFocusEvent
*)
712 #if CHECK_ANY_QT_USING_X11
713 m_rFrame
.m_nKeyModifiers
= ModKeyFlags::NONE
;
716 m_rFrame
.CallCallback(SalEvent::LoseFocus
, nullptr);
720 QtWidget::QtWidget(QtFrame
& rFrame
, Qt::WindowFlags f
)
721 // if you try to set the QWidget parent via the QtFrame, instead of using nullptr, at
722 // least test Wayland popups; these horribly broke last time doing this (read commits)!
723 : QWidget(nullptr, f
)
725 , m_bNonEmptyIMPreeditSeen(false)
726 , m_bInInputMethodQueryCursorRectangle(false)
730 setAttribute(Qt::WA_TranslucentBackground
);
731 setAttribute(Qt::WA_OpaquePaintEvent
);
732 setAttribute(Qt::WA_NoSystemBackground
);
733 setMouseTracking(true);
734 if (!rFrame
.isPopup())
735 setFocusPolicy(Qt::StrongFocus
);
737 setFocusPolicy(Qt::ClickFocus
);
739 grabGesture(Qt::PinchGesture
);
742 static ExtTextInputAttr
lcl_MapUnderlineStyle(QTextCharFormat::UnderlineStyle us
)
746 case QTextCharFormat::NoUnderline
:
747 return ExtTextInputAttr::NONE
;
748 case QTextCharFormat::DotLine
:
749 return ExtTextInputAttr::DottedUnderline
;
750 case QTextCharFormat::DashDotDotLine
:
751 case QTextCharFormat::DashDotLine
:
752 return ExtTextInputAttr::DashDotUnderline
;
753 case QTextCharFormat::WaveUnderline
:
754 return ExtTextInputAttr::GrayWaveline
;
756 return ExtTextInputAttr::Underline
;
760 void QtWidget::inputMethodEvent(QInputMethodEvent
* pEvent
)
762 const bool bHasCommitText
= !pEvent
->commitString().isEmpty();
763 const int nReplacementLength
= pEvent
->replacementLength();
765 if (nReplacementLength
> 0 || bHasCommitText
)
767 if (nReplacementLength
> 0)
768 deleteReplacementText(m_rFrame
, pEvent
->replacementStart(), nReplacementLength
);
770 commitText(m_rFrame
, pEvent
->commitString());
774 SalExtTextInputEvent aInputEvent
;
775 aInputEvent
.mpTextAttr
= nullptr;
776 aInputEvent
.mnCursorFlags
= 0;
777 aInputEvent
.maText
= toOUString(pEvent
->preeditString());
778 aInputEvent
.mnCursorPos
= 0;
780 const sal_Int32 nLength
= aInputEvent
.maText
.getLength();
781 const QList
<QInputMethodEvent::Attribute
>& rAttrList
= pEvent
->attributes();
782 std::vector
<ExtTextInputAttr
> aTextAttrs(std::max(sal_Int32(1), nLength
),
783 ExtTextInputAttr::NONE
);
784 aInputEvent
.mpTextAttr
= aTextAttrs
.data();
786 for (const QInputMethodEvent::Attribute
& rAttr
: rAttrList
)
790 case QInputMethodEvent::TextFormat
:
792 QTextCharFormat aCharFormat
793 = qvariant_cast
<QTextFormat
>(rAttr
.value
).toCharFormat();
794 if (aCharFormat
.isValid())
796 ExtTextInputAttr aETIP
797 = lcl_MapUnderlineStyle(aCharFormat
.underlineStyle());
798 if (aCharFormat
.hasProperty(QTextFormat::BackgroundBrush
))
799 aETIP
|= ExtTextInputAttr::Highlight
;
800 if (aCharFormat
.fontStrikeOut())
801 aETIP
|= ExtTextInputAttr::RedText
;
802 for (int j
= rAttr
.start
; j
< rAttr
.start
+ rAttr
.length
; j
++)
804 SAL_WARN_IF(j
>= static_cast<int>(aTextAttrs
.size()), "vcl.qt",
805 "QInputMethodEvent::Attribute out of range. Broken range: "
806 << rAttr
.start
<< "," << rAttr
.start
+ rAttr
.length
807 << " Legal range: 0," << aTextAttrs
.size());
808 if (j
>= static_cast<int>(aTextAttrs
.size()))
810 aTextAttrs
[j
] = aETIP
;
815 case QInputMethodEvent::Cursor
:
817 aInputEvent
.mnCursorPos
= rAttr
.start
;
818 if (rAttr
.length
== 0)
819 aInputEvent
.mnCursorFlags
|= EXTTEXTINPUT_CURSOR_INVISIBLE
;
823 SAL_WARN("vcl.qt", "Unhandled QInputMethodEvent attribute: "
824 << static_cast<int>(rAttr
.type
));
829 const bool bIsEmpty
= aInputEvent
.maText
.isEmpty();
830 if (m_bNonEmptyIMPreeditSeen
|| !bIsEmpty
)
832 SolarMutexGuard aGuard
;
833 vcl::DeletionListener
aDel(&m_rFrame
);
834 m_rFrame
.CallCallback(SalEvent::ExtTextInput
, &aInputEvent
);
835 if (!aDel
.isDeleted() && bIsEmpty
)
836 m_rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
837 m_bNonEmptyIMPreeditSeen
= !bIsEmpty
;
844 static bool lcl_retrieveSurrounding(sal_Int32
& rPosition
, sal_Int32
& rAnchor
, QString
* pText
,
847 SolarMutexGuard aGuard
;
848 vcl::Window
* pFocusWin
= Application::GetFocusWindow();
852 uno::Reference
<accessibility::XAccessibleEditableText
> xText
;
855 uno::Reference
<accessibility::XAccessible
> xAccessible(pFocusWin
->GetAccessible());
856 if (xAccessible
.is())
857 xText
= FindFocusedEditableText(xAccessible
->getAccessibleContext());
859 catch (const uno::Exception
&)
861 TOOLS_WARN_EXCEPTION("vcl.qt", "Exception in getting input method surrounding text");
866 rPosition
= xText
->getCaretPosition();
870 *pText
= toQString(xText
->getText());
872 sal_Int32 nSelStart
= xText
->getSelectionStart();
873 sal_Int32 nSelEnd
= xText
->getSelectionEnd();
874 if (nSelStart
== nSelEnd
)
880 if (rPosition
== nSelStart
)
885 *pSelection
= toQString(xText
->getSelectedText());
894 QVariant
QtWidget::inputMethodQuery(Qt::InputMethodQuery property
) const
898 case Qt::ImSurroundingText
:
901 sal_Int32 nCursorPos
, nAnchor
;
902 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, &aText
, nullptr))
903 return QVariant(aText
);
906 case Qt::ImCursorPosition
:
908 sal_Int32 nCursorPos
, nAnchor
;
909 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, nullptr))
910 return QVariant(static_cast<int>(nCursorPos
));
913 case Qt::ImCursorRectangle
:
915 if (!m_bInInputMethodQueryCursorRectangle
)
917 m_bInInputMethodQueryCursorRectangle
= true;
918 SalExtTextInputPosEvent aPosEvent
;
919 m_rFrame
.CallCallback(SalEvent::ExtTextInputPos
, &aPosEvent
);
920 const qreal fRatio
= m_rFrame
.devicePixelRatioF();
921 m_aImCursorRectangle
.setRect(aPosEvent
.mnX
/ fRatio
, aPosEvent
.mnY
/ fRatio
,
922 aPosEvent
.mnWidth
/ fRatio
,
923 aPosEvent
.mnHeight
/ fRatio
);
924 m_bInInputMethodQueryCursorRectangle
= false;
926 return QVariant(m_aImCursorRectangle
);
928 case Qt::ImAnchorPosition
:
930 sal_Int32 nCursorPos
, nAnchor
;
931 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, nullptr))
932 return QVariant(static_cast<int>(nAnchor
));
935 case Qt::ImCurrentSelection
:
938 sal_Int32 nCursorPos
, nAnchor
;
939 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, &aSelection
))
940 return QVariant(aSelection
);
944 return QWidget::inputMethodQuery(property
);
948 void QtWidget::endExtTextInput()
950 if (m_bNonEmptyIMPreeditSeen
)
952 m_rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
953 m_bNonEmptyIMPreeditSeen
= false;
957 void QtWidget::changeEvent(QEvent
* pEvent
)
959 switch (pEvent
->type())
961 case QEvent::FontChange
:
963 case QEvent::PaletteChange
:
965 case QEvent::StyleChange
:
967 GetQtInstance().UpdateStyle(QEvent::FontChange
== pEvent
->type());
973 QWidget::changeEvent(pEvent
);
976 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */