1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/toolkit/floatwin.hxx>
53 #include <comphelper/diagnose_ex.hxx>
55 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
56 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
58 #if CHECK_ANY_QT_USING_X11
60 #include <X11/keysymdef.h>
63 using namespace com::sun::star
;
65 void QtWidget::paintEvent(QPaintEvent
* pEvent
)
68 if (!m_rFrame
.m_bNullRegion
)
69 p
.setClipRegion(m_rFrame
.m_aRegion
);
72 if (m_rFrame
.m_bUseCairo
)
74 cairo_surface_t
* pSurface
= m_rFrame
.m_pSurface
.get();
75 cairo_surface_flush(pSurface
);
77 aImage
= QImage(cairo_image_surface_get_data(pSurface
),
78 cairo_image_surface_get_width(pSurface
),
79 cairo_image_surface_get_height(pSurface
), Qt_DefaultFormat32
);
82 aImage
= *m_rFrame
.m_pQImage
;
84 const qreal fRatio
= m_rFrame
.devicePixelRatioF();
85 aImage
.setDevicePixelRatio(fRatio
);
86 QRectF
source(pEvent
->rect().topLeft() * fRatio
, pEvent
->rect().size() * fRatio
);
87 p
.drawImage(pEvent
->rect(), aImage
, source
);
90 void QtWidget::resizeEvent(QResizeEvent
* pEvent
)
92 const qreal fRatio
= m_rFrame
.devicePixelRatioF();
93 const int nWidth
= ceil(pEvent
->size().width() * fRatio
);
94 const int nHeight
= ceil(pEvent
->size().height() * fRatio
);
96 m_rFrame
.maGeometry
.setSize({ nWidth
, nHeight
});
98 if (m_rFrame
.m_bUseCairo
)
100 if (m_rFrame
.m_pSurface
)
102 const int nOldWidth
= cairo_image_surface_get_width(m_rFrame
.m_pSurface
.get());
103 const int nOldHeight
= cairo_image_surface_get_height(m_rFrame
.m_pSurface
.get());
104 if (nOldWidth
!= nWidth
|| nOldHeight
!= nHeight
)
106 cairo_surface_t
* pSurface
107 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, nWidth
, nHeight
);
108 cairo_surface_set_user_data(pSurface
, SvpSalGraphics::getDamageKey(),
109 &m_rFrame
.m_aDamageHandler
, nullptr);
110 m_rFrame
.m_pSvpGraphics
->setSurface(pSurface
, basegfx::B2IVector(nWidth
, nHeight
));
111 UniqueCairoSurface
old_surface(m_rFrame
.m_pSurface
.release());
112 m_rFrame
.m_pSurface
.reset(pSurface
);
114 const int nMinWidth
= qMin(nOldWidth
, nWidth
);
115 const int nMinHeight
= qMin(nOldHeight
, nHeight
);
116 SalTwoRect
rect(0, 0, nMinWidth
, nMinHeight
, 0, 0, nMinWidth
, nMinHeight
);
117 m_rFrame
.m_pSvpGraphics
->copySource(rect
, old_surface
.get());
123 if (m_rFrame
.m_pQImage
&& m_rFrame
.m_pQImage
->size() != QSize(nWidth
, nHeight
))
125 QImage
* pImage
= new QImage(m_rFrame
.m_pQImage
->copy(0, 0, nWidth
, nHeight
));
126 m_rFrame
.m_pQtGraphics
->ChangeQImage(pImage
);
127 m_rFrame
.m_pQImage
.reset(pImage
);
131 m_rFrame
.CallCallback(SalEvent::Resize
, nullptr);
134 void QtWidget::fakeResize()
136 QResizeEvent
aEvent(size(), QSize());
137 resizeEvent(&aEvent
);
140 void QtWidget::fillSalAbstractMouseEvent(const QtFrame
& rFrame
, const QInputEvent
* pQEvent
,
141 const QPoint
& rPos
, Qt::MouseButtons eButtons
, int nWidth
,
142 SalAbstractMouseEvent
& aSalEvent
)
144 const qreal fRatio
= rFrame
.devicePixelRatioF();
145 const Point aPos
= toPoint(rPos
* fRatio
);
147 aSalEvent
.mnX
= QGuiApplication::isLeftToRight() ? aPos
.X() : round(nWidth
* fRatio
) - aPos
.X();
148 aSalEvent
.mnY
= aPos
.Y();
149 aSalEvent
.mnTime
= pQEvent
->timestamp();
150 aSalEvent
.mnCode
= GetKeyModCode(pQEvent
->modifiers()) | GetMouseModCode(eButtons
);
153 #define FILL_SAME(rFrame, nWidth) \
154 fillSalAbstractMouseEvent(rFrame, pEvent, pEvent->pos(), pEvent->buttons(), nWidth, aEvent)
156 void QtWidget::handleMouseButtonEvent(const QtFrame
& rFrame
, const QMouseEvent
* pEvent
)
158 SalMouseEvent aEvent
;
159 FILL_SAME(rFrame
, rFrame
.GetQWidget()->width());
161 switch (pEvent
->button())
164 aEvent
.mnButton
= MOUSE_LEFT
;
166 case Qt::MiddleButton
:
167 aEvent
.mnButton
= MOUSE_MIDDLE
;
169 case Qt::RightButton
:
170 aEvent
.mnButton
= MOUSE_RIGHT
;
177 if (pEvent
->type() == QEvent::MouseButtonPress
|| pEvent
->type() == QEvent::MouseButtonDblClick
)
178 nEventType
= SalEvent::MouseButtonDown
;
180 nEventType
= SalEvent::MouseButtonUp
;
181 rFrame
.CallCallback(nEventType
, &aEvent
);
184 void QtWidget::mousePressEvent(QMouseEvent
* pEvent
)
186 handleMouseButtonEvent(m_rFrame
, pEvent
);
187 if (m_rFrame
.isPopup()
188 && !geometry().translated(geometry().topLeft() * -1).contains(pEvent
->pos()))
192 void QtWidget::mouseReleaseEvent(QMouseEvent
* pEvent
) { handleMouseButtonEvent(m_rFrame
, pEvent
); }
194 void QtWidget::mouseMoveEvent(QMouseEvent
* pEvent
)
196 SalMouseEvent aEvent
;
197 FILL_SAME(m_rFrame
, width());
201 m_rFrame
.CallCallback(SalEvent::MouseMove
, &aEvent
);
205 void QtWidget::handleMouseEnterLeaveEvents(const QtFrame
& rFrame
, QEvent
* pQEvent
)
207 const qreal fRatio
= rFrame
.devicePixelRatioF();
208 const QWidget
* pWidget
= rFrame
.GetQWidget();
209 const Point aPos
= toPoint(pWidget
->mapFromGlobal(QCursor::pos()) * fRatio
);
211 SalMouseEvent aEvent
;
213 = QGuiApplication::isLeftToRight() ? aPos
.X() : round(pWidget
->width() * fRatio
) - aPos
.X();
214 aEvent
.mnY
= aPos
.Y();
217 aEvent
.mnCode
= GetKeyModCode(QGuiApplication::keyboardModifiers())
218 | GetMouseModCode(QGuiApplication::mouseButtons());
221 if (pQEvent
->type() == QEvent::Enter
)
222 nEventType
= SalEvent::MouseMove
;
224 nEventType
= SalEvent::MouseLeave
;
225 rFrame
.CallCallback(nEventType
, &aEvent
);
229 void QtWidget::leaveEvent(QEvent
* pEvent
) { handleMouseEnterLeaveEvents(m_rFrame
, pEvent
); }
231 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
232 void QtWidget::enterEvent(QEnterEvent
* pEvent
)
234 void QtWidget::enterEvent(QEvent
* pEvent
)
237 handleMouseEnterLeaveEvents(m_rFrame
, pEvent
);
240 void QtWidget::wheelEvent(QWheelEvent
* pEvent
)
242 SalWheelMouseEvent aEvent
;
243 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
244 fillSalAbstractMouseEvent(m_rFrame
, pEvent
, pEvent
->position().toPoint(), pEvent
->buttons(),
247 fillSalAbstractMouseEvent(m_rFrame
, pEvent
, pEvent
->pos(), pEvent
->buttons(), width(), aEvent
);
250 // mouse wheel ticks are 120, which we map to 3 lines.
251 // we have to accumulate for touch scroll to keep track of the absolute delta.
253 int nDelta
= pEvent
->angleDelta().y(), lines
;
254 aEvent
.mbHorz
= nDelta
== 0;
257 nDelta
= (QGuiApplication::isLeftToRight() ? 1 : -1) * pEvent
->angleDelta().x();
262 lines
= m_nDeltaX
/ 40;
263 m_nDeltaX
= m_nDeltaX
% 40;
268 lines
= m_nDeltaY
/ 40;
269 m_nDeltaY
= m_nDeltaY
% 40;
272 aEvent
.mnDelta
= nDelta
;
273 aEvent
.mnNotchDelta
= nDelta
< 0 ? -1 : 1;
274 aEvent
.mnScrollLines
= std::abs(lines
);
276 m_rFrame
.CallCallback(SalEvent::WheelMouse
, &aEvent
);
280 void QtWidget::dragEnterEvent(QDragEnterEvent
* event
)
282 if (dynamic_cast<const QtMimeData
*>(event
->mimeData()))
285 event
->acceptProposedAction();
288 // also called when a drop is rejected
289 void QtWidget::dragLeaveEvent(QDragLeaveEvent
*) { m_rFrame
.handleDragLeave(); }
291 void QtWidget::dragMoveEvent(QDragMoveEvent
* pEvent
) { m_rFrame
.handleDragMove(pEvent
); }
293 void QtWidget::dropEvent(QDropEvent
* pEvent
) { m_rFrame
.handleDrop(pEvent
); }
295 void QtWidget::moveEvent(QMoveEvent
* pEvent
)
297 // already handled by QtMainWindow::moveEvent
298 if (m_rFrame
.m_pTopLevel
)
301 m_rFrame
.maGeometry
.setPos(toPoint(pEvent
->pos() * m_rFrame
.devicePixelRatioF()));
302 m_rFrame
.CallCallback(SalEvent::Move
, nullptr);
305 void QtWidget::showEvent(QShowEvent
*)
307 QSize
aSize(m_rFrame
.GetQWidget()->size() * m_rFrame
.devicePixelRatioF());
308 // forcing an immediate update somehow interferes with the hide + show
309 // sequence from QtFrame::SetModal, if the frame was already set visible,
310 // resulting in a hidden / unmapped window
311 SalPaintEvent
aPaintEvt(0, 0, aSize
.width(), aSize
.height());
312 if (m_rFrame
.isPopup())
313 GetQtInstance()->setActivePopup(&m_rFrame
);
314 m_rFrame
.CallCallback(SalEvent::Paint
, &aPaintEvt
);
317 void QtWidget::hideEvent(QHideEvent
*)
319 if (m_rFrame
.isPopup() && GetQtInstance()->activePopup() == &m_rFrame
)
320 GetQtInstance()->setActivePopup(nullptr);
323 void QtWidget::closeEvent(QCloseEvent
* /*pEvent*/)
325 m_rFrame
.CallCallback(SalEvent::Close
, nullptr);
328 static sal_uInt16
GetKeyCode(int keyval
, Qt::KeyboardModifiers modifiers
)
330 sal_uInt16 nCode
= 0;
331 if (keyval
>= Qt::Key_0
&& keyval
<= Qt::Key_9
)
332 nCode
= KEY_0
+ (keyval
- Qt::Key_0
);
333 else if (keyval
>= Qt::Key_A
&& keyval
<= Qt::Key_Z
)
334 nCode
= KEY_A
+ (keyval
- Qt::Key_A
);
335 else if (keyval
>= Qt::Key_F1
&& keyval
<= Qt::Key_F26
)
336 nCode
= KEY_F1
+ (keyval
- Qt::Key_F1
);
337 else if (modifiers
.testFlag(Qt::KeypadModifier
)
338 && (keyval
== Qt::Key_Period
|| keyval
== Qt::Key_Comma
))
339 // Qt doesn't use a special keyval for decimal separator ("," or ".")
340 // on numerical keypad, but sets Qt::KeypadModifier in addition
367 case Qt::Key_PageDown
:
368 nCode
= KEY_PAGEDOWN
;
378 // oddly enough, Qt doesn't send Shift-Tab event as 'Tab key pressed with Shift
379 // modifier' but as 'Backtab key pressed' (while its modifier bits are still
380 // set to Shift) -- so let's map both Key_Tab and Key_Backtab to VCL's KEY_TAB
381 case Qt::Key_Backtab
:
384 case Qt::Key_Backspace
:
385 nCode
= KEY_BACKSPACE
;
400 nCode
= KEY_SUBTRACT
;
402 case Qt::Key_Asterisk
:
403 nCode
= KEY_MULTIPLY
;
417 case Qt::Key_Greater
:
427 nCode
= KEY_CONTEXTMENU
;
441 case Qt::Key_AsciiTilde
:
444 case Qt::Key_QuoteLeft
:
445 nCode
= KEY_QUOTELEFT
;
447 case Qt::Key_BracketLeft
:
448 nCode
= KEY_BRACKETLEFT
;
450 case Qt::Key_BracketRight
:
451 nCode
= KEY_BRACKETRIGHT
;
453 case Qt::Key_NumberSign
:
454 nCode
= KEY_NUMBERSIGN
;
459 case Qt::Key_Semicolon
:
460 nCode
= KEY_SEMICOLON
;
480 void QtWidget::commitText(QtFrame
& rFrame
, const QString
& aText
)
482 SalExtTextInputEvent aInputEvent
;
483 aInputEvent
.mpTextAttr
= nullptr;
484 aInputEvent
.mnCursorFlags
= 0;
485 aInputEvent
.maText
= toOUString(aText
);
486 aInputEvent
.mnCursorPos
= aInputEvent
.maText
.getLength();
488 SolarMutexGuard aGuard
;
489 vcl::DeletionListener
aDel(&rFrame
);
490 rFrame
.CallCallback(SalEvent::ExtTextInput
, &aInputEvent
);
491 if (!aDel
.isDeleted())
492 rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
495 void QtWidget::deleteReplacementText(QtFrame
& rFrame
, int nReplacementStart
, int nReplacementLength
)
497 // get the surrounding text
498 SolarMutexGuard aGuard
;
499 SalSurroundingTextRequestEvent aSurroundingTextEvt
;
500 aSurroundingTextEvt
.maText
.clear();
501 aSurroundingTextEvt
.mnStart
= aSurroundingTextEvt
.mnEnd
= 0;
502 rFrame
.CallCallback(SalEvent::SurroundingTextRequest
, &aSurroundingTextEvt
);
504 // Turn nReplacementStart, nReplacementLength into a UTF-16 selection
505 const Selection aSelection
= SalFrame::CalcDeleteSurroundingSelection(
506 aSurroundingTextEvt
.maText
, aSurroundingTextEvt
.mnStart
, nReplacementStart
,
509 const Selection
aInvalid(SAL_MAX_UINT32
, SAL_MAX_UINT32
);
510 if (aSelection
== aInvalid
)
512 SAL_WARN("vcl.qt", "Invalid selection when deleting IM replacement text");
516 SalSurroundingTextSelectionChangeEvent aEvt
;
517 aEvt
.mnStart
= aSelection
.Min();
518 aEvt
.mnEnd
= aSelection
.Max();
519 rFrame
.CallCallback(SalEvent::DeleteSurroundingTextRequest
, &aEvt
);
522 bool QtWidget::handleKeyEvent(QtFrame
& rFrame
, const QWidget
& rWidget
, QKeyEvent
* pEvent
)
524 const bool bIsKeyPressed
525 = pEvent
->type() == QEvent::KeyPress
|| pEvent
->type() == QEvent::ShortcutOverride
;
526 sal_uInt16 nCode
= GetKeyCode(pEvent
->key(), pEvent
->modifiers());
527 if (bIsKeyPressed
&& nCode
== 0 && pEvent
->text().length() > 1
528 && rWidget
.testAttribute(Qt::WA_InputMethodEnabled
))
530 commitText(rFrame
, pEvent
->text());
535 if (nCode
== 0 && pEvent
->text().isEmpty())
537 sal_uInt16 nModCode
= GetKeyModCode(pEvent
->modifiers());
538 SalKeyModEvent aModEvt
;
539 aModEvt
.mbDown
= bIsKeyPressed
;
540 aModEvt
.mnModKeyCode
= ModKeyFlags::NONE
;
542 #if CHECK_ANY_QT_USING_X11
543 if (QGuiApplication::platformName() == "xcb")
545 // pressing just the ctrl key leads to a keysym of XK_Control but
546 // the event state does not contain ControlMask. In the release
547 // event it's the other way round: it does contain the Control mask.
548 // The modifier mode therefore has to be adapted manually.
549 ModKeyFlags nExtModMask
= ModKeyFlags::NONE
;
550 sal_uInt16 nModMask
= 0;
551 switch (pEvent
->nativeVirtualKey())
554 nExtModMask
= ModKeyFlags::LeftMod1
;
558 nExtModMask
= ModKeyFlags::RightMod1
;
562 nExtModMask
= ModKeyFlags::LeftMod2
;
566 nExtModMask
= ModKeyFlags::RightMod2
;
570 nExtModMask
= ModKeyFlags::LeftShift
;
571 nModMask
= KEY_SHIFT
;
574 nExtModMask
= ModKeyFlags::RightShift
;
575 nModMask
= KEY_SHIFT
;
577 // Map Meta/Super keys to MOD3 modifier on all Unix systems
581 nExtModMask
= ModKeyFlags::LeftMod3
;
586 nExtModMask
= ModKeyFlags::RightMod3
;
593 // sending the old mnModKeyCode mask on release is needed to
594 // implement the writing direction switch with Ctrl + L/R-Shift
595 aModEvt
.mnModKeyCode
= rFrame
.m_nKeyModifiers
;
596 nModCode
&= ~nModMask
;
597 rFrame
.m_nKeyModifiers
&= ~nExtModMask
;
601 nModCode
|= nModMask
;
602 rFrame
.m_nKeyModifiers
|= nExtModMask
;
603 aModEvt
.mnModKeyCode
= rFrame
.m_nKeyModifiers
;
607 aModEvt
.mnCode
= nModCode
;
609 rFrame
.CallCallback(SalEvent::KeyModChange
, &aModEvt
);
613 #if CHECK_ANY_QT_USING_X11
614 // prevent interference of writing direction switch (Ctrl + L/R-Shift) with "normal" shortcuts
615 rFrame
.m_nKeyModifiers
= ModKeyFlags::NONE
;
619 aEvent
.mnCharCode
= (pEvent
->text().isEmpty() ? 0 : pEvent
->text().at(0).unicode());
621 aEvent
.mnCode
= nCode
;
622 aEvent
.mnCode
|= GetKeyModCode(pEvent
->modifiers());
624 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle
);
626 bool bStopProcessingKey
;
628 bStopProcessingKey
= rFrame
.CallCallback(SalEvent::KeyInput
, &aEvent
);
630 bStopProcessingKey
= rFrame
.CallCallback(SalEvent::KeyUp
, &aEvent
);
631 if (bStopProcessingKey
)
633 return bStopProcessingKey
;
636 bool QtWidget::handleEvent(QtFrame
& rFrame
, QWidget
& rWidget
, QEvent
* pEvent
)
638 if (pEvent
->type() == QEvent::ShortcutOverride
)
640 // ignore non-spontaneous QEvent::ShortcutOverride events,
641 // since such an extra event is sent e.g. with Orca screen reader enabled,
642 // so that two events of that kind (the "real one" and a non-spontaneous one)
643 // would otherwise be processed, resulting in duplicate input as 'handleKeyEvent'
644 // is called below (s. tdf#122053)
645 if (!pEvent
->spontaneous())
650 // Accepted event disables shortcut activation,
651 // but enables keypress event.
652 // If event is not accepted and shortcut is successfully activated,
653 // KeyPress event is omitted.
655 // Instead of processing keyPressEvent, handle ShortcutOverride event,
656 // and if it's handled - disable the shortcut, it should have been activated.
657 // Don't process keyPressEvent generated after disabling shortcut since it was handled here.
658 // If event is not handled, don't accept it and let Qt activate related shortcut.
659 if (handleKeyEvent(rFrame
, rWidget
, static_cast<QKeyEvent
*>(pEvent
)))
662 else if (pEvent
->type() == QEvent::ToolTip
)
664 // Qt's POV on the active popup is wrong due to our fake popup, so check LO's state.
665 // Otherwise Qt will continue handling ToolTip events from the "parent" window.
666 const QtFrame
* pPopupFrame
= GetQtInstance()->activePopup();
667 if (!rFrame
.m_aTooltipText
.isEmpty() && (!pPopupFrame
|| pPopupFrame
== &rFrame
))
668 QToolTip::showText(QCursor::pos(), toQString(rFrame
.m_aTooltipText
), &rWidget
,
669 rFrame
.m_aTooltipArea
);
672 QToolTip::hideText();
680 bool QtWidget::event(QEvent
* pEvent
)
682 return handleEvent(m_rFrame
, *this, pEvent
) || QWidget::event(pEvent
);
685 void QtWidget::keyReleaseEvent(QKeyEvent
* pEvent
)
687 if (!handleKeyReleaseEvent(m_rFrame
, *this, pEvent
))
688 QWidget::keyReleaseEvent(pEvent
);
691 void QtWidget::focusInEvent(QFocusEvent
*) { m_rFrame
.CallCallback(SalEvent::GetFocus
, nullptr); }
693 void QtWidget::closePopup()
695 VclPtr
<FloatingWindow
> pFirstFloat
= ImplGetSVData()->mpWinData
->mpFirstFloat
;
696 if (pFirstFloat
&& !(pFirstFloat
->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose
))
698 SolarMutexGuard aGuard
;
699 pFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
703 void QtWidget::focusOutEvent(QFocusEvent
*)
705 #if CHECK_ANY_QT_USING_X11
706 m_rFrame
.m_nKeyModifiers
= ModKeyFlags::NONE
;
709 m_rFrame
.CallCallback(SalEvent::LoseFocus
, nullptr);
713 QtWidget::QtWidget(QtFrame
& rFrame
, Qt::WindowFlags f
)
714 // if you try to set the QWidget parent via the QtFrame, instead of using the Q_NULLPTR, at
715 // least test Wayland popups; these horribly broke last time doing this (read commits)!
716 : QWidget(Q_NULLPTR
, f
)
718 , m_bNonEmptyIMPreeditSeen(false)
719 , m_bInInputMethodQueryCursorRectangle(false)
723 setAttribute(Qt::WA_TranslucentBackground
);
724 setAttribute(Qt::WA_OpaquePaintEvent
);
725 setAttribute(Qt::WA_NoSystemBackground
);
726 setMouseTracking(true);
727 if (!rFrame
.isPopup())
728 setFocusPolicy(Qt::StrongFocus
);
730 setFocusPolicy(Qt::ClickFocus
);
733 static ExtTextInputAttr
lcl_MapUnderlineStyle(QTextCharFormat::UnderlineStyle us
)
737 case QTextCharFormat::NoUnderline
:
738 return ExtTextInputAttr::NONE
;
739 case QTextCharFormat::DotLine
:
740 return ExtTextInputAttr::DottedUnderline
;
741 case QTextCharFormat::DashDotDotLine
:
742 case QTextCharFormat::DashDotLine
:
743 return ExtTextInputAttr::DashDotUnderline
;
744 case QTextCharFormat::WaveUnderline
:
745 return ExtTextInputAttr::GrayWaveline
;
747 return ExtTextInputAttr::Underline
;
751 void QtWidget::inputMethodEvent(QInputMethodEvent
* pEvent
)
753 const bool bHasCommitText
= !pEvent
->commitString().isEmpty();
754 const int nReplacementLength
= pEvent
->replacementLength();
756 if (nReplacementLength
> 0 || bHasCommitText
)
758 if (nReplacementLength
> 0)
759 deleteReplacementText(m_rFrame
, pEvent
->replacementStart(), nReplacementLength
);
761 commitText(m_rFrame
, pEvent
->commitString());
765 SalExtTextInputEvent aInputEvent
;
766 aInputEvent
.mpTextAttr
= nullptr;
767 aInputEvent
.mnCursorFlags
= 0;
768 aInputEvent
.maText
= toOUString(pEvent
->preeditString());
769 aInputEvent
.mnCursorPos
= 0;
771 const sal_Int32 nLength
= aInputEvent
.maText
.getLength();
772 const QList
<QInputMethodEvent::Attribute
>& rAttrList
= pEvent
->attributes();
773 std::vector
<ExtTextInputAttr
> aTextAttrs(std::max(sal_Int32(1), nLength
),
774 ExtTextInputAttr::NONE
);
775 aInputEvent
.mpTextAttr
= aTextAttrs
.data();
777 for (const QInputMethodEvent::Attribute
& rAttr
: rAttrList
)
781 case QInputMethodEvent::TextFormat
:
783 QTextCharFormat aCharFormat
784 = qvariant_cast
<QTextFormat
>(rAttr
.value
).toCharFormat();
785 if (aCharFormat
.isValid())
787 ExtTextInputAttr aETIP
788 = lcl_MapUnderlineStyle(aCharFormat
.underlineStyle());
789 if (aCharFormat
.hasProperty(QTextFormat::BackgroundBrush
))
790 aETIP
|= ExtTextInputAttr::Highlight
;
791 if (aCharFormat
.fontStrikeOut())
792 aETIP
|= ExtTextInputAttr::RedText
;
793 for (int j
= rAttr
.start
; j
< rAttr
.start
+ rAttr
.length
; j
++)
795 SAL_WARN_IF(j
>= static_cast<int>(aTextAttrs
.size()), "vcl.qt",
796 "QInputMethodEvent::Attribute out of range. Broken range: "
797 << rAttr
.start
<< "," << rAttr
.start
+ rAttr
.length
798 << " Legal range: 0," << aTextAttrs
.size());
799 if (j
>= static_cast<int>(aTextAttrs
.size()))
801 aTextAttrs
[j
] = aETIP
;
806 case QInputMethodEvent::Cursor
:
808 aInputEvent
.mnCursorPos
= rAttr
.start
;
809 if (rAttr
.length
== 0)
810 aInputEvent
.mnCursorFlags
|= EXTTEXTINPUT_CURSOR_INVISIBLE
;
814 SAL_WARN("vcl.qt", "Unhandled QInputMethodEvent attribute: "
815 << static_cast<int>(rAttr
.type
));
820 const bool bIsEmpty
= aInputEvent
.maText
.isEmpty();
821 if (m_bNonEmptyIMPreeditSeen
|| !bIsEmpty
)
823 SolarMutexGuard aGuard
;
824 vcl::DeletionListener
aDel(&m_rFrame
);
825 m_rFrame
.CallCallback(SalEvent::ExtTextInput
, &aInputEvent
);
826 if (!aDel
.isDeleted() && bIsEmpty
)
827 m_rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
828 m_bNonEmptyIMPreeditSeen
= !bIsEmpty
;
835 static bool lcl_retrieveSurrounding(sal_Int32
& rPosition
, sal_Int32
& rAnchor
, QString
* pText
,
838 SolarMutexGuard aGuard
;
839 vcl::Window
* pFocusWin
= Application::GetFocusWindow();
843 uno::Reference
<accessibility::XAccessibleEditableText
> xText
;
846 uno::Reference
<accessibility::XAccessible
> xAccessible(pFocusWin
->GetAccessible());
847 if (xAccessible
.is())
848 xText
= FindFocusedEditableText(xAccessible
->getAccessibleContext());
850 catch (const uno::Exception
&)
852 TOOLS_WARN_EXCEPTION("vcl.qt", "Exception in getting input method surrounding text");
857 rPosition
= xText
->getCaretPosition();
861 *pText
= toQString(xText
->getText());
863 sal_Int32 nSelStart
= xText
->getSelectionStart();
864 sal_Int32 nSelEnd
= xText
->getSelectionEnd();
865 if (nSelStart
== nSelEnd
)
871 if (rPosition
== nSelStart
)
876 *pSelection
= toQString(xText
->getSelectedText());
885 QVariant
QtWidget::inputMethodQuery(Qt::InputMethodQuery property
) const
889 case Qt::ImSurroundingText
:
892 sal_Int32 nCursorPos
, nAnchor
;
893 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, &aText
, nullptr))
894 return QVariant(aText
);
897 case Qt::ImCursorPosition
:
899 sal_Int32 nCursorPos
, nAnchor
;
900 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, nullptr))
901 return QVariant(static_cast<int>(nCursorPos
));
904 case Qt::ImCursorRectangle
:
906 if (!m_bInInputMethodQueryCursorRectangle
)
908 m_bInInputMethodQueryCursorRectangle
= true;
909 SalExtTextInputPosEvent aPosEvent
;
910 m_rFrame
.CallCallback(SalEvent::ExtTextInputPos
, &aPosEvent
);
911 const qreal fRatio
= m_rFrame
.devicePixelRatioF();
912 m_aImCursorRectangle
.setRect(aPosEvent
.mnX
/ fRatio
, aPosEvent
.mnY
/ fRatio
,
913 aPosEvent
.mnWidth
/ fRatio
,
914 aPosEvent
.mnHeight
/ fRatio
);
915 m_bInInputMethodQueryCursorRectangle
= false;
917 return QVariant(m_aImCursorRectangle
);
919 case Qt::ImAnchorPosition
:
921 sal_Int32 nCursorPos
, nAnchor
;
922 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, nullptr))
923 return QVariant(static_cast<int>(nAnchor
));
926 case Qt::ImCurrentSelection
:
929 sal_Int32 nCursorPos
, nAnchor
;
930 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, &aSelection
))
931 return QVariant(aSelection
);
935 return QWidget::inputMethodQuery(property
);
939 void QtWidget::endExtTextInput()
941 if (m_bNonEmptyIMPreeditSeen
)
943 m_rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
944 m_bNonEmptyIMPreeditSeen
= false;
948 void QtWidget::changeEvent(QEvent
* pEvent
)
950 switch (pEvent
->type())
952 case QEvent::FontChange
:
954 case QEvent::PaletteChange
:
956 case QEvent::StyleChange
:
958 auto* pSalInst(GetQtInstance());
960 pSalInst
->UpdateStyle(QEvent::FontChange
== pEvent
->type());
966 QWidget::changeEvent(pEvent
);
969 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */