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
;
456 case Qt::Key_Semicolon
:
457 nCode
= KEY_SEMICOLON
;
477 void QtWidget::commitText(QtFrame
& rFrame
, const QString
& aText
)
479 SalExtTextInputEvent aInputEvent
;
480 aInputEvent
.mpTextAttr
= nullptr;
481 aInputEvent
.mnCursorFlags
= 0;
482 aInputEvent
.maText
= toOUString(aText
);
483 aInputEvent
.mnCursorPos
= aInputEvent
.maText
.getLength();
485 SolarMutexGuard aGuard
;
486 vcl::DeletionListener
aDel(&rFrame
);
487 rFrame
.CallCallback(SalEvent::ExtTextInput
, &aInputEvent
);
488 if (!aDel
.isDeleted())
489 rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
492 void QtWidget::deleteReplacementText(QtFrame
& rFrame
, int nReplacementStart
, int nReplacementLength
)
494 // get the surrounding text
495 SolarMutexGuard aGuard
;
496 SalSurroundingTextRequestEvent aSurroundingTextEvt
;
497 aSurroundingTextEvt
.maText
.clear();
498 aSurroundingTextEvt
.mnStart
= aSurroundingTextEvt
.mnEnd
= 0;
499 rFrame
.CallCallback(SalEvent::SurroundingTextRequest
, &aSurroundingTextEvt
);
501 // Turn nReplacementStart, nReplacementLength into a UTF-16 selection
502 const Selection aSelection
= SalFrame::CalcDeleteSurroundingSelection(
503 aSurroundingTextEvt
.maText
, aSurroundingTextEvt
.mnStart
, nReplacementStart
,
506 const Selection
aInvalid(SAL_MAX_UINT32
, SAL_MAX_UINT32
);
507 if (aSelection
== aInvalid
)
509 SAL_WARN("vcl.qt", "Invalid selection when deleting IM replacement text");
513 SalSurroundingTextSelectionChangeEvent aEvt
;
514 aEvt
.mnStart
= aSelection
.Min();
515 aEvt
.mnEnd
= aSelection
.Max();
516 rFrame
.CallCallback(SalEvent::DeleteSurroundingTextRequest
, &aEvt
);
519 bool QtWidget::handleKeyEvent(QtFrame
& rFrame
, const QWidget
& rWidget
, QKeyEvent
* pEvent
)
521 const bool bIsKeyPressed
522 = pEvent
->type() == QEvent::KeyPress
|| pEvent
->type() == QEvent::ShortcutOverride
;
523 sal_uInt16 nCode
= GetKeyCode(pEvent
->key(), pEvent
->modifiers());
524 if (bIsKeyPressed
&& nCode
== 0 && pEvent
->text().length() > 1
525 && rWidget
.testAttribute(Qt::WA_InputMethodEnabled
))
527 commitText(rFrame
, pEvent
->text());
532 if (nCode
== 0 && pEvent
->text().isEmpty())
534 sal_uInt16 nModCode
= GetKeyModCode(pEvent
->modifiers());
535 SalKeyModEvent aModEvt
;
536 aModEvt
.mbDown
= bIsKeyPressed
;
537 aModEvt
.mnModKeyCode
= ModKeyFlags::NONE
;
539 #if CHECK_ANY_QT_USING_X11
540 if (QGuiApplication::platformName() == "xcb")
542 // pressing just the ctrl key leads to a keysym of XK_Control but
543 // the event state does not contain ControlMask. In the release
544 // event it's the other way round: it does contain the Control mask.
545 // The modifier mode therefore has to be adapted manually.
546 ModKeyFlags nExtModMask
= ModKeyFlags::NONE
;
547 sal_uInt16 nModMask
= 0;
548 switch (pEvent
->nativeVirtualKey())
551 nExtModMask
= ModKeyFlags::LeftMod1
;
555 nExtModMask
= ModKeyFlags::RightMod1
;
559 nExtModMask
= ModKeyFlags::LeftMod2
;
563 nExtModMask
= ModKeyFlags::RightMod2
;
567 nExtModMask
= ModKeyFlags::LeftShift
;
568 nModMask
= KEY_SHIFT
;
571 nExtModMask
= ModKeyFlags::RightShift
;
572 nModMask
= KEY_SHIFT
;
574 // Map Meta/Super keys to MOD3 modifier on all Unix systems
578 nExtModMask
= ModKeyFlags::LeftMod3
;
583 nExtModMask
= ModKeyFlags::RightMod3
;
590 // sending the old mnModKeyCode mask on release is needed to
591 // implement the writing direction switch with Ctrl + L/R-Shift
592 aModEvt
.mnModKeyCode
= rFrame
.m_nKeyModifiers
;
593 nModCode
&= ~nModMask
;
594 rFrame
.m_nKeyModifiers
&= ~nExtModMask
;
598 nModCode
|= nModMask
;
599 rFrame
.m_nKeyModifiers
|= nExtModMask
;
600 aModEvt
.mnModKeyCode
= rFrame
.m_nKeyModifiers
;
604 aModEvt
.mnCode
= nModCode
;
606 rFrame
.CallCallback(SalEvent::KeyModChange
, &aModEvt
);
610 #if CHECK_ANY_QT_USING_X11
611 // prevent interference of writing direction switch (Ctrl + L/R-Shift) with "normal" shortcuts
612 rFrame
.m_nKeyModifiers
= ModKeyFlags::NONE
;
616 aEvent
.mnCharCode
= (pEvent
->text().isEmpty() ? 0 : pEvent
->text().at(0).unicode());
618 aEvent
.mnCode
= nCode
;
619 aEvent
.mnCode
|= GetKeyModCode(pEvent
->modifiers());
621 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle
);
623 bool bStopProcessingKey
;
625 bStopProcessingKey
= rFrame
.CallCallback(SalEvent::KeyInput
, &aEvent
);
627 bStopProcessingKey
= rFrame
.CallCallback(SalEvent::KeyUp
, &aEvent
);
628 if (bStopProcessingKey
)
630 return bStopProcessingKey
;
633 bool QtWidget::handleEvent(QtFrame
& rFrame
, QWidget
& rWidget
, QEvent
* pEvent
)
635 if (pEvent
->type() == QEvent::ShortcutOverride
)
637 // ignore non-spontaneous QEvent::ShortcutOverride events,
638 // since such an extra event is sent e.g. with Orca screen reader enabled,
639 // so that two events of that kind (the "real one" and a non-spontaneous one)
640 // would otherwise be processed, resulting in duplicate input as 'handleKeyEvent'
641 // is called below (s. tdf#122053)
642 if (!pEvent
->spontaneous())
647 // Accepted event disables shortcut activation,
648 // but enables keypress event.
649 // If event is not accepted and shortcut is successfully activated,
650 // KeyPress event is omitted.
652 // Instead of processing keyPressEvent, handle ShortcutOverride event,
653 // and if it's handled - disable the shortcut, it should have been activated.
654 // Don't process keyPressEvent generated after disabling shortcut since it was handled here.
655 // If event is not handled, don't accept it and let Qt activate related shortcut.
656 if (handleKeyEvent(rFrame
, rWidget
, static_cast<QKeyEvent
*>(pEvent
)))
659 else if (pEvent
->type() == QEvent::ToolTip
)
661 // Qt's POV on the active popup is wrong due to our fake popup, so check LO's state.
662 // Otherwise Qt will continue handling ToolTip events from the "parent" window.
663 const QtFrame
* pPopupFrame
= GetQtInstance()->activePopup();
664 if (!rFrame
.m_aTooltipText
.isEmpty() && (!pPopupFrame
|| pPopupFrame
== &rFrame
))
665 QToolTip::showText(QCursor::pos(), toQString(rFrame
.m_aTooltipText
), &rWidget
,
666 rFrame
.m_aTooltipArea
);
669 QToolTip::hideText();
677 bool QtWidget::event(QEvent
* pEvent
)
679 return handleEvent(m_rFrame
, *this, pEvent
) || QWidget::event(pEvent
);
682 void QtWidget::keyReleaseEvent(QKeyEvent
* pEvent
)
684 if (!handleKeyReleaseEvent(m_rFrame
, *this, pEvent
))
685 QWidget::keyReleaseEvent(pEvent
);
688 void QtWidget::focusInEvent(QFocusEvent
*) { m_rFrame
.CallCallback(SalEvent::GetFocus
, nullptr); }
690 void QtWidget::closePopup()
692 VclPtr
<FloatingWindow
> pFirstFloat
= ImplGetSVData()->mpWinData
->mpFirstFloat
;
693 if (pFirstFloat
&& !(pFirstFloat
->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose
))
695 SolarMutexGuard aGuard
;
696 pFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
700 void QtWidget::focusOutEvent(QFocusEvent
*)
702 #if CHECK_ANY_QT_USING_X11
703 m_rFrame
.m_nKeyModifiers
= ModKeyFlags::NONE
;
706 m_rFrame
.CallCallback(SalEvent::LoseFocus
, nullptr);
710 QtWidget::QtWidget(QtFrame
& rFrame
, Qt::WindowFlags f
)
711 // if you try to set the QWidget parent via the QtFrame, instead of using the Q_NULLPTR, at
712 // least test Wayland popups; these horribly broke last time doing this (read commits)!
713 : QWidget(Q_NULLPTR
, f
)
715 , m_bNonEmptyIMPreeditSeen(false)
716 , m_bInInputMethodQueryCursorRectangle(false)
720 setAttribute(Qt::WA_TranslucentBackground
);
721 setAttribute(Qt::WA_OpaquePaintEvent
);
722 setAttribute(Qt::WA_NoSystemBackground
);
723 setMouseTracking(true);
724 if (!rFrame
.isPopup())
725 setFocusPolicy(Qt::StrongFocus
);
727 setFocusPolicy(Qt::ClickFocus
);
730 static ExtTextInputAttr
lcl_MapUnderlineStyle(QTextCharFormat::UnderlineStyle us
)
734 case QTextCharFormat::NoUnderline
:
735 return ExtTextInputAttr::NONE
;
736 case QTextCharFormat::DotLine
:
737 return ExtTextInputAttr::DottedUnderline
;
738 case QTextCharFormat::DashDotDotLine
:
739 case QTextCharFormat::DashDotLine
:
740 return ExtTextInputAttr::DashDotUnderline
;
741 case QTextCharFormat::WaveUnderline
:
742 return ExtTextInputAttr::GrayWaveline
;
744 return ExtTextInputAttr::Underline
;
748 void QtWidget::inputMethodEvent(QInputMethodEvent
* pEvent
)
750 const bool bHasCommitText
= !pEvent
->commitString().isEmpty();
751 const int nReplacementLength
= pEvent
->replacementLength();
753 if (nReplacementLength
> 0 || bHasCommitText
)
755 if (nReplacementLength
> 0)
756 deleteReplacementText(m_rFrame
, pEvent
->replacementStart(), nReplacementLength
);
758 commitText(m_rFrame
, pEvent
->commitString());
762 SalExtTextInputEvent aInputEvent
;
763 aInputEvent
.mpTextAttr
= nullptr;
764 aInputEvent
.mnCursorFlags
= 0;
765 aInputEvent
.maText
= toOUString(pEvent
->preeditString());
766 aInputEvent
.mnCursorPos
= 0;
768 const sal_Int32 nLength
= aInputEvent
.maText
.getLength();
769 const QList
<QInputMethodEvent::Attribute
>& rAttrList
= pEvent
->attributes();
770 std::vector
<ExtTextInputAttr
> aTextAttrs(std::max(sal_Int32(1), nLength
),
771 ExtTextInputAttr::NONE
);
772 aInputEvent
.mpTextAttr
= aTextAttrs
.data();
774 for (const QInputMethodEvent::Attribute
& rAttr
: rAttrList
)
778 case QInputMethodEvent::TextFormat
:
780 QTextCharFormat aCharFormat
781 = qvariant_cast
<QTextFormat
>(rAttr
.value
).toCharFormat();
782 if (aCharFormat
.isValid())
784 ExtTextInputAttr aETIP
785 = lcl_MapUnderlineStyle(aCharFormat
.underlineStyle());
786 if (aCharFormat
.hasProperty(QTextFormat::BackgroundBrush
))
787 aETIP
|= ExtTextInputAttr::Highlight
;
788 if (aCharFormat
.fontStrikeOut())
789 aETIP
|= ExtTextInputAttr::RedText
;
790 for (int j
= rAttr
.start
; j
< rAttr
.start
+ rAttr
.length
; j
++)
792 SAL_WARN_IF(j
>= static_cast<int>(aTextAttrs
.size()), "vcl.qt",
793 "QInputMethodEvent::Attribute out of range. Broken range: "
794 << rAttr
.start
<< "," << rAttr
.start
+ rAttr
.length
795 << " Legal range: 0," << aTextAttrs
.size());
796 if (j
>= static_cast<int>(aTextAttrs
.size()))
798 aTextAttrs
[j
] = aETIP
;
803 case QInputMethodEvent::Cursor
:
805 aInputEvent
.mnCursorPos
= rAttr
.start
;
806 if (rAttr
.length
== 0)
807 aInputEvent
.mnCursorFlags
|= EXTTEXTINPUT_CURSOR_INVISIBLE
;
811 SAL_WARN("vcl.qt", "Unhandled QInputMethodEvent attribute: "
812 << static_cast<int>(rAttr
.type
));
817 const bool bIsEmpty
= aInputEvent
.maText
.isEmpty();
818 if (m_bNonEmptyIMPreeditSeen
|| !bIsEmpty
)
820 SolarMutexGuard aGuard
;
821 vcl::DeletionListener
aDel(&m_rFrame
);
822 m_rFrame
.CallCallback(SalEvent::ExtTextInput
, &aInputEvent
);
823 if (!aDel
.isDeleted() && bIsEmpty
)
824 m_rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
825 m_bNonEmptyIMPreeditSeen
= !bIsEmpty
;
832 static bool lcl_retrieveSurrounding(sal_Int32
& rPosition
, sal_Int32
& rAnchor
, QString
* pText
,
835 SolarMutexGuard aGuard
;
836 vcl::Window
* pFocusWin
= Application::GetFocusWindow();
840 uno::Reference
<accessibility::XAccessibleEditableText
> xText
;
843 uno::Reference
<accessibility::XAccessible
> xAccessible(pFocusWin
->GetAccessible());
844 if (xAccessible
.is())
845 xText
= FindFocusedEditableText(xAccessible
->getAccessibleContext());
847 catch (const uno::Exception
&)
849 TOOLS_WARN_EXCEPTION("vcl.qt", "Exception in getting input method surrounding text");
854 rPosition
= xText
->getCaretPosition();
858 *pText
= toQString(xText
->getText());
860 sal_Int32 nSelStart
= xText
->getSelectionStart();
861 sal_Int32 nSelEnd
= xText
->getSelectionEnd();
862 if (nSelStart
== nSelEnd
)
868 if (rPosition
== nSelStart
)
873 *pSelection
= toQString(xText
->getSelectedText());
882 QVariant
QtWidget::inputMethodQuery(Qt::InputMethodQuery property
) const
886 case Qt::ImSurroundingText
:
889 sal_Int32 nCursorPos
, nAnchor
;
890 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, &aText
, nullptr))
891 return QVariant(aText
);
894 case Qt::ImCursorPosition
:
896 sal_Int32 nCursorPos
, nAnchor
;
897 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, nullptr))
898 return QVariant(static_cast<int>(nCursorPos
));
901 case Qt::ImCursorRectangle
:
903 if (!m_bInInputMethodQueryCursorRectangle
)
905 m_bInInputMethodQueryCursorRectangle
= true;
906 SalExtTextInputPosEvent aPosEvent
;
907 m_rFrame
.CallCallback(SalEvent::ExtTextInputPos
, &aPosEvent
);
908 const qreal fRatio
= m_rFrame
.devicePixelRatioF();
909 m_aImCursorRectangle
.setRect(aPosEvent
.mnX
/ fRatio
, aPosEvent
.mnY
/ fRatio
,
910 aPosEvent
.mnWidth
/ fRatio
,
911 aPosEvent
.mnHeight
/ fRatio
);
912 m_bInInputMethodQueryCursorRectangle
= false;
914 return QVariant(m_aImCursorRectangle
);
916 case Qt::ImAnchorPosition
:
918 sal_Int32 nCursorPos
, nAnchor
;
919 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, nullptr))
920 return QVariant(static_cast<int>(nAnchor
));
923 case Qt::ImCurrentSelection
:
926 sal_Int32 nCursorPos
, nAnchor
;
927 if (lcl_retrieveSurrounding(nCursorPos
, nAnchor
, nullptr, &aSelection
))
928 return QVariant(aSelection
);
932 return QWidget::inputMethodQuery(property
);
936 void QtWidget::endExtTextInput()
938 if (m_bNonEmptyIMPreeditSeen
)
940 m_rFrame
.CallCallback(SalEvent::EndExtTextInput
, nullptr);
941 m_bNonEmptyIMPreeditSeen
= false;
945 void QtWidget::changeEvent(QEvent
* pEvent
)
947 switch (pEvent
->type())
949 case QEvent::FontChange
:
951 case QEvent::PaletteChange
:
953 case QEvent::StyleChange
:
955 auto* pSalInst(GetQtInstance());
957 pSalInst
->UpdateStyle(QEvent::FontChange
== pEvent
->type());
963 QWidget::changeEvent(pEvent
);
966 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */