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 <QtFrame.hxx>
21 #include <QtFrame.moc>
24 #include <QtDragAndDrop.hxx>
25 #include <QtFontFace.hxx>
26 #include <QtGraphics.hxx>
27 #include <QtInstance.hxx>
28 #include <QtMainWindow.hxx>
30 #include <QtSvpGraphics.hxx>
31 #include <QtSystem.hxx>
32 #include <QtTools.hxx>
33 #include <QtTransferable.hxx>
34 #if CHECK_ANY_QT_USING_X11
35 #include <QtX11Support.hxx>
38 #include <QtCore/QMimeData>
39 #include <QtCore/QPoint>
40 #include <QtCore/QSize>
41 #include <QtCore/QThread>
42 #include <QtGui/QDragMoveEvent>
43 #include <QtGui/QDropEvent>
44 #include <QtGui/QIcon>
45 #include <QtGui/QWindow>
46 #include <QtGui/QScreen>
47 #include <QtWidgets/QStyle>
48 #include <QtWidgets/QToolTip>
49 #include <QtWidgets/QApplication>
50 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
51 #include <QtWidgets/QDesktopWidget>
53 #include <QtWidgets/QMenuBar>
54 #include <QtWidgets/QMainWindow>
55 #if CHECK_QT5_USING_X11
56 #include <QtX11Extras/QX11Info>
60 #include <vcl/syswin.hxx>
62 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
65 #include <headless/svpgdi.hxx>
67 #include <unx/fontmanager.hxx>
69 #if CHECK_QT5_USING_X11 && QT5_HAVE_XCB_ICCCM
70 static bool g_bNeedsWmHintsWindowGroup
= true;
73 static void SvpDamageHandler(void* handle
, sal_Int32 nExtentsX
, sal_Int32 nExtentsY
,
74 sal_Int32 nExtentsWidth
, sal_Int32 nExtentsHeight
)
76 QtFrame
* pThis
= static_cast<QtFrame
*>(handle
);
77 pThis
->Damage(nExtentsX
, nExtentsY
, nExtentsWidth
, nExtentsHeight
);
82 sal_Int32
screenNumber(const QScreen
* pScreen
)
84 const QList
<QScreen
*> screens
= QApplication::screens();
86 sal_Int32 nScreen
= 0;
88 for (const QScreen
* pCurScreen
: screens
)
90 if (pScreen
== pCurScreen
)
98 return bFound
? nScreen
: -1;
102 QtFrame::QtFrame(QtFrame
* pParent
, SalFrameStyleFlags nStyle
, bool bUseCairo
)
103 : m_pTopLevel(nullptr)
104 , m_bUseCairo(bUseCairo
)
105 , m_bNullRegion(true)
106 , m_bGraphicsInUse(false)
107 , m_ePointerStyle(PointerStyle::Arrow
)
108 , m_pDragSource(nullptr)
109 , m_pDropTarget(nullptr)
111 , m_bDefaultSize(true)
112 , m_bDefaultPos(true)
113 , m_bFullScreen(false)
114 , m_bFullScreenSpanAll(false)
115 #if CHECK_ANY_QT_USING_X11
116 , m_nKeyModifiers(ModKeyFlags::NONE
)
118 , m_nInputLanguage(LANGUAGE_DONTKNOW
)
120 QtInstance
* pInst
= GetQtInstance();
121 pInst
->insertFrame(this);
123 m_aDamageHandler
.handle
= this;
124 m_aDamageHandler
.damaged
= ::SvpDamageHandler
;
126 if (nStyle
& SalFrameStyleFlags::DEFAULT
) // ensure default style
128 nStyle
|= SalFrameStyleFlags::MOVEABLE
| SalFrameStyleFlags::SIZEABLE
129 | SalFrameStyleFlags::CLOSEABLE
;
130 nStyle
&= ~SalFrameStyleFlags::FLOAT
;
136 Qt::WindowFlags
aWinFlags(Qt::Widget
);
137 if (!(nStyle
& SalFrameStyleFlags::SYSTEMCHILD
))
139 if (nStyle
& SalFrameStyleFlags::INTRO
)
140 aWinFlags
= Qt::SplashScreen
;
141 // floating toolbars are frameless tool windows
142 // + they must be able to receive keyboard focus
143 else if ((nStyle
& SalFrameStyleFlags::FLOAT
)
144 && (nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
))
145 aWinFlags
= Qt::Tool
| Qt::FramelessWindowHint
;
146 else if (nStyle
& SalFrameStyleFlags::TOOLTIP
)
147 aWinFlags
= Qt::ToolTip
;
148 // Can't use Qt::Popup, because it grabs the input focus and generates a focus-out event,
149 // instantly auto-closing the LO's editable ComboBox popup.
150 // On X11, the alternative Qt::Window | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint
151 // seems to work well enough, but at least on Wayland and WASM, this results in problems.
152 // So while using Qt::ToolTip, the popups are wrongly advertised via accessibility, at least
153 // the GUI seems to work on all platforms... what a mess.
155 aWinFlags
= Qt::ToolTip
| Qt::FramelessWindowHint
;
156 else if (nStyle
& SalFrameStyleFlags::TOOLWINDOW
)
157 aWinFlags
= Qt::Tool
;
158 // top level windows can't be transient in Qt, so make them dialogs, if they have a parent. At least
159 // the plasma shell relies on this setting to skip dialogs in the window list. And Qt Xcb will just
160 // set transient for the types Dialog, Sheet, Tool, SplashScreen, ToolTip, Drawer and Popup.
161 else if (nStyle
& SalFrameStyleFlags::DIALOG
|| m_pParent
)
162 aWinFlags
= Qt::Dialog
;
164 aWinFlags
= Qt::Window
;
167 if (aWinFlags
== Qt::Window
)
169 m_pTopLevel
= new QtMainWindow(*this, aWinFlags
);
170 m_pQWidget
= new QtWidget(*this);
171 m_pTopLevel
->setCentralWidget(m_pQWidget
);
172 m_pTopLevel
->setFocusProxy(m_pQWidget
);
176 m_pQWidget
= new QtWidget(*this, aWinFlags
);
177 // from Qt's POV the popup window doesn't have the input focus, so we must force tooltips...
179 m_pQWidget
->setAttribute(Qt::WA_AlwaysShowToolTips
);
182 FillSystemEnvData(m_aSystemData
, reinterpret_cast<sal_IntPtr
>(this), m_pQWidget
);
184 QWindow
* pChildWindow
= windowHandle();
185 connect(pChildWindow
, &QWindow::screenChanged
, this, &QtFrame::screenChanged
);
187 if (pParent
&& !(pParent
->m_nStyle
& SalFrameStyleFlags::PLUG
))
189 QWindow
* pParentWindow
= pParent
->windowHandle();
190 if (pParentWindow
&& pChildWindow
&& (pParentWindow
!= pChildWindow
))
191 pChildWindow
->setTransientParent(pParentWindow
);
194 SetIcon(SV_ICON_ID_OFFICE
);
196 fixICCCMwindowGroup();
199 void QtFrame::screenChanged(QScreen
*) { m_pQWidget
->fakeResize(); }
201 void QtFrame::FillSystemEnvData(SystemEnvData
& rData
, sal_IntPtr pWindow
, QWidget
* pWidget
)
203 assert(rData
.platform
== SystemEnvData::Platform::Invalid
);
204 assert(rData
.toolkit
== SystemEnvData::Toolkit::Invalid
);
205 if (QGuiApplication::platformName() == "wayland")
206 rData
.platform
= SystemEnvData::Platform::Wayland
;
207 else if (QGuiApplication::platformName() == "xcb")
208 rData
.platform
= SystemEnvData::Platform::Xcb
;
209 else if (QGuiApplication::platformName() == "wasm")
210 rData
.platform
= SystemEnvData::Platform::WASM
;
213 // maybe add a SystemEnvData::Platform::Unsupported to avoid special cases and not abort?
215 "Unsupported qt VCL platform: " << toOUString(QGuiApplication::platformName()));
219 rData
.toolkit
= SystemEnvData::Toolkit::Qt
;
220 rData
.aShellWindow
= pWindow
;
221 rData
.pWidget
= pWidget
;
224 void QtFrame::fixICCCMwindowGroup()
226 #if CHECK_QT5_USING_X11 && QT5_HAVE_XCB_ICCCM
227 if (!g_bNeedsWmHintsWindowGroup
)
229 g_bNeedsWmHintsWindowGroup
= false;
231 assert(m_aSystemData
.platform
!= SystemEnvData::Platform::Invalid
);
232 if (m_aSystemData
.platform
!= SystemEnvData::Platform::Xcb
)
235 g_bNeedsWmHintsWindowGroup
= QtX11Support::fixICCCMwindowGroup(asChild()->winId());
237 (void)this; // avoid loplugin:staticmethods
243 QtInstance
* pInst
= GetQtInstance();
244 pInst
->eraseFrame(this);
246 m_aSystemData
.aShellWindow
= 0;
249 void QtFrame::Damage(sal_Int32 nExtentsX
, sal_Int32 nExtentsY
, sal_Int32 nExtentsWidth
,
250 sal_Int32 nExtentsHeight
) const
252 m_pQWidget
->update(scaledQRect(QRect(nExtentsX
, nExtentsY
, nExtentsWidth
, nExtentsHeight
),
253 1 / devicePixelRatioF()));
256 SalGraphics
* QtFrame::AcquireGraphics()
258 if (m_bGraphicsInUse
)
261 m_bGraphicsInUse
= true;
267 QSize aSize
= m_pQWidget
->size() * devicePixelRatioF();
268 m_pSvpGraphics
.reset(new QtSvpGraphics(this));
270 cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, aSize
.width(), aSize
.height()));
271 m_pSvpGraphics
->setSurface(m_pSurface
.get(),
272 basegfx::B2IVector(aSize
.width(), aSize
.height()));
273 cairo_surface_set_user_data(m_pSurface
.get(), QtSvpGraphics::getDamageKey(),
274 &m_aDamageHandler
, nullptr);
276 return m_pSvpGraphics
.get();
282 m_pQtGraphics
.reset(new QtGraphics(this));
284 new QImage(m_pQWidget
->size() * devicePixelRatioF(), Qt_DefaultFormat32
));
285 m_pQImage
->fill(Qt::transparent
);
286 m_pQtGraphics
->ChangeQImage(m_pQImage
.get());
288 return m_pQtGraphics
.get();
292 void QtFrame::ReleaseGraphics(SalGraphics
* pSalGraph
)
296 assert(pSalGraph
== m_pSvpGraphics
.get());
298 assert(pSalGraph
== m_pQtGraphics
.get());
299 m_bGraphicsInUse
= false;
302 bool QtFrame::PostEvent(std::unique_ptr
<ImplSVEvent
> pData
)
304 QtInstance
* pInst
= GetQtInstance();
305 pInst
->PostEvent(this, pData
.release(), SalEvent::UserEvent
);
309 QWidget
* QtFrame::asChild() const
316 qreal
QtFrame::devicePixelRatioF() const { return asChild()->devicePixelRatioF(); }
318 bool QtFrame::isWindow() const { return asChild()->isWindow(); }
320 QWindow
* QtFrame::windowHandle() const
322 // set attribute 'Qt::WA_NativeWindow' first to make sure a window handle actually exists
323 QWidget
* pChild
= asChild();
324 assert(pChild
->window() == pChild
);
325 switch (m_aSystemData
.platform
)
327 case SystemEnvData::Platform::Wayland
:
328 case SystemEnvData::Platform::Xcb
:
329 pChild
->setAttribute(Qt::WA_NativeWindow
);
331 case SystemEnvData::Platform::WASM
:
332 // no idea, why Qt::WA_NativeWindow breaks the menubar for EMSCRIPTEN
334 case SystemEnvData::Platform::Invalid
:
338 return pChild
->windowHandle();
341 QScreen
* QtFrame::screen() const
343 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
344 return asChild()->screen();
346 // QWidget::screen only available from Qt 5.14 on, call windowHandle(),
347 // with the indirect result that mouse move events on Wayland will not be
348 // emitted reliably, s. QTBUG-75766
349 QWindow
* const pWindow
= windowHandle();
350 return pWindow
? pWindow
->screen() : nullptr;
354 bool QtFrame::isMinimized() const { return asChild()->isMinimized(); }
356 bool QtFrame::isMaximized() const { return asChild()->isMaximized(); }
358 void QtFrame::SetWindowStateImpl(Qt::WindowStates eState
)
360 return asChild()->setWindowState(eState
);
363 void QtFrame::SetTitle(const OUString
& rTitle
)
365 m_pQWidget
->window()->setWindowTitle(toQString(rTitle
));
368 void QtFrame::SetIcon(sal_uInt16 nIcon
)
371 & (SalFrameStyleFlags::PLUG
| SalFrameStyleFlags::SYSTEMCHILD
372 | SalFrameStyleFlags::FLOAT
| SalFrameStyleFlags::INTRO
373 | SalFrameStyleFlags::OWNERDRAWDECORATION
)
379 if (nIcon
== SV_ICON_ID_TEXT
)
380 appicon
= "libreoffice-writer";
381 else if (nIcon
== SV_ICON_ID_SPREADSHEET
)
382 appicon
= "libreoffice-calc";
383 else if (nIcon
== SV_ICON_ID_DRAWING
)
384 appicon
= "libreoffice-draw";
385 else if (nIcon
== SV_ICON_ID_PRESENTATION
)
386 appicon
= "libreoffice-impress";
387 else if (nIcon
== SV_ICON_ID_DATABASE
)
388 appicon
= "libreoffice-base";
389 else if (nIcon
== SV_ICON_ID_FORMULA
)
390 appicon
= "libreoffice-math";
392 appicon
= "libreoffice-startcenter";
394 QIcon aIcon
= QIcon::fromTheme(appicon
);
395 m_pQWidget
->window()->setWindowIcon(aIcon
);
398 void QtFrame::SetMenu(SalMenu
*) {}
400 void QtFrame::SetExtendedFrameStyle(SalExtStyle
/*nExtStyle*/) { /* not needed */}
402 void QtFrame::Show(bool bVisible
, bool bNoActivate
)
405 if (bVisible
== asChild()->isVisible())
408 auto* pSalInst(GetQtInstance());
411 if (!bVisible
) // hide
413 pSalInst
->RunInMainThread([this]() { asChild()->setVisible(false); });
420 pSalInst
->RunInMainThread([this, bNoActivate
]() {
421 QWidget
* const pChild
= asChild();
422 pChild
->setVisible(true);
426 pChild
->activateWindow();
432 void QtFrame::SetMinClientSize(tools::Long nWidth
, tools::Long nHeight
)
436 const qreal fRatio
= devicePixelRatioF();
437 asChild()->setMinimumSize(round(nWidth
/ fRatio
), round(nHeight
/ fRatio
));
441 void QtFrame::SetMaxClientSize(tools::Long nWidth
, tools::Long nHeight
)
445 const qreal fRatio
= devicePixelRatioF();
446 asChild()->setMaximumSize(round(nWidth
/ fRatio
), round(nHeight
/ fRatio
));
450 int QtFrame::menuBarOffset() const
452 QtMainWindow
* pTopLevel
= m_pParent
->GetTopLevelWindow();
453 if (pTopLevel
&& pTopLevel
->menuBar() && pTopLevel
->menuBar()->isVisible())
454 return round(pTopLevel
->menuBar()->geometry().height() * devicePixelRatioF());
458 void QtFrame::SetDefaultPos()
466 const qreal fRatio
= devicePixelRatioF();
467 QWidget
* const pParentWin
= m_pParent
->asChild()->window();
468 QWidget
* const pChildWin
= asChild()->window();
469 QPoint aPos
= (pParentWin
->rect().center() - pChildWin
->rect().center()) * fRatio
;
470 aPos
.ry() -= menuBarOffset();
471 SetPosSize(aPos
.x(), aPos
.y(), 0, 0, SAL_FRAME_POSSIZE_X
| SAL_FRAME_POSSIZE_Y
);
472 assert(!m_bDefaultPos
);
475 m_bDefaultPos
= false;
478 Size
QtFrame::CalcDefaultSize()
485 const QScreen
* pScreen
= screen();
487 pScreen
= QGuiApplication::screens().at(0);
488 aSize
= bestmaxFrameSizeForScreenSize(toSize(pScreen
->size()));
492 if (!m_bFullScreenSpanAll
)
494 aSize
= toSize(QGuiApplication::screens().at(maGeometry
.screen())->size());
498 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
499 QScreen
* pScreen
= QGuiApplication::screenAt(QPoint(0, 0));
501 // QGuiApplication::screenAt was added in Qt 5.10, use deprecated QDesktopWidget
502 int nLeftScreen
= QApplication::desktop()->screenNumber(QPoint(0, 0));
503 QScreen
* pScreen
= QGuiApplication::screens()[nLeftScreen
];
505 aSize
= toSize(pScreen
->availableVirtualGeometry().size());
512 void QtFrame::SetDefaultSize()
517 Size aDefSize
= CalcDefaultSize();
518 SetPosSize(0, 0, aDefSize
.Width(), aDefSize
.Height(),
519 SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
);
520 assert(!m_bDefaultSize
);
523 void QtFrame::SetPosSize(tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
,
526 if (!isWindow() || isChild(true, false))
529 if (nFlags
& (SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
))
531 if (isChild(false) || !m_pQWidget
->isMaximized())
533 if (!(nFlags
& SAL_FRAME_POSSIZE_WIDTH
))
534 nWidth
= maGeometry
.width();
535 else if (!(nFlags
& SAL_FRAME_POSSIZE_HEIGHT
))
536 nHeight
= maGeometry
.height();
538 if (nWidth
> 0 && nHeight
> 0)
540 m_bDefaultSize
= false;
541 const int nNewWidth
= round(nWidth
/ devicePixelRatioF());
542 const int nNewHeight
= round(nHeight
/ devicePixelRatioF());
543 if (m_nStyle
& SalFrameStyleFlags::SIZEABLE
)
544 asChild()->resize(nNewWidth
, nNewHeight
);
546 asChild()->setFixedSize(nNewWidth
, nNewHeight
);
549 // assume the resize happened
550 // needed for calculations and will eventually be corrected by events
552 maGeometry
.setWidth(nWidth
);
554 maGeometry
.setHeight(nHeight
);
558 if (!(nFlags
& (SAL_FRAME_POSSIZE_X
| SAL_FRAME_POSSIZE_Y
)))
560 if (nFlags
& (SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
))
567 const SalFrameGeometry
& aParentGeometry
= m_pParent
->maGeometry
;
568 if (QGuiApplication::isRightToLeft())
569 nX
= aParentGeometry
.x() + aParentGeometry
.width() - nX
- maGeometry
.width() - 1;
571 nX
+= aParentGeometry
.x();
572 nY
+= aParentGeometry
.y() + menuBarOffset();
575 if (!(nFlags
& SAL_FRAME_POSSIZE_X
))
577 else if (!(nFlags
& SAL_FRAME_POSSIZE_Y
))
580 // assume the reposition happened
581 // needed for calculations and will eventually be corrected by events later
582 maGeometry
.setPos({ nX
, nY
});
584 m_bDefaultPos
= false;
585 asChild()->move(round(nX
/ devicePixelRatioF()), round(nY
/ devicePixelRatioF()));
588 void QtFrame::GetClientSize(tools::Long
& rWidth
, tools::Long
& rHeight
)
590 rWidth
= round(m_pQWidget
->width() * devicePixelRatioF());
591 rHeight
= round(m_pQWidget
->height() * devicePixelRatioF());
594 void QtFrame::GetWorkArea(tools::Rectangle
& rRect
)
598 QScreen
* pScreen
= screen();
602 QSize aSize
= pScreen
->availableVirtualSize() * devicePixelRatioF();
603 rRect
= tools::Rectangle(0, 0, aSize
.width(), aSize
.height());
606 SalFrame
* QtFrame::GetParent() const { return m_pParent
; }
608 void QtFrame::SetModal(bool bModal
)
610 if (!isWindow() || asChild()->isModal() == bModal
)
613 auto* pSalInst(GetQtInstance());
615 pSalInst
->RunInMainThread([this, bModal
]() {
617 QWidget
* const pChild
= asChild();
618 const bool bWasVisible
= pChild
->isVisible();
620 // modality change is only effective if the window is hidden
624 if (QGuiApplication::platformName() == "xcb")
626 SAL_WARN("vcl.qt", "SetModal called after Show - apply delay");
627 // tdf#152979 give QXcbConnection some time to avoid
628 // "qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window"
629 QThread::msleep(100);
633 pChild
->setWindowModality(bModal
? Qt::WindowModal
: Qt::NonModal
);
640 bool QtFrame::GetModal() const { return isWindow() && windowHandle()->isModal(); }
642 void QtFrame::SetWindowState(const vcl::WindowData
* pState
)
644 if (!isWindow() || !pState
|| isChild(true, false))
647 const vcl::WindowDataMask nMaxGeometryMask
648 = vcl::WindowDataMask::PosSize
| vcl::WindowDataMask::MaximizedX
649 | vcl::WindowDataMask::MaximizedY
| vcl::WindowDataMask::MaximizedWidth
650 | vcl::WindowDataMask::MaximizedHeight
;
652 if ((pState
->mask() & vcl::WindowDataMask::State
)
653 && (pState
->state() & vcl::WindowState::Maximized
) && !isMaximized()
654 && (pState
->mask() & nMaxGeometryMask
) == nMaxGeometryMask
)
656 const qreal fRatio
= devicePixelRatioF();
657 QWidget
* const pChild
= asChild();
658 pChild
->resize(ceil(pState
->width() / fRatio
), ceil(pState
->height() / fRatio
));
659 pChild
->move(ceil(pState
->x() / fRatio
), ceil(pState
->y() / fRatio
));
660 SetWindowStateImpl(Qt::WindowMaximized
);
662 else if (pState
->mask() & vcl::WindowDataMask::PosSize
)
664 sal_uInt16 nPosSizeFlags
= 0;
665 if (pState
->mask() & vcl::WindowDataMask::X
)
666 nPosSizeFlags
|= SAL_FRAME_POSSIZE_X
;
667 if (pState
->mask() & vcl::WindowDataMask::Y
)
668 nPosSizeFlags
|= SAL_FRAME_POSSIZE_Y
;
669 if (pState
->mask() & vcl::WindowDataMask::Width
)
670 nPosSizeFlags
|= SAL_FRAME_POSSIZE_WIDTH
;
671 if (pState
->mask() & vcl::WindowDataMask::Height
)
672 nPosSizeFlags
|= SAL_FRAME_POSSIZE_HEIGHT
;
673 SetPosSize(pState
->x(), pState
->y(), pState
->width(), pState
->height(), nPosSizeFlags
);
675 else if (pState
->mask() & vcl::WindowDataMask::State
&& !isChild())
677 if (pState
->state() & vcl::WindowState::Maximized
)
678 SetWindowStateImpl(Qt::WindowMaximized
);
679 else if (pState
->state() & vcl::WindowState::Minimized
)
680 SetWindowStateImpl(Qt::WindowMinimized
);
682 SetWindowStateImpl(Qt::WindowNoState
);
686 bool QtFrame::GetWindowState(vcl::WindowData
* pState
)
688 pState
->setState(vcl::WindowState::Normal
);
689 pState
->setMask(vcl::WindowDataMask::State
);
691 pState
->rState() |= vcl::WindowState::Minimized
;
692 else if (isMaximized())
693 pState
->rState() |= vcl::WindowState::Maximized
;
696 // we want the frame position and the client area size
697 QRect rect
= scaledQRect({ asChild()->pos(), asChild()->size() }, devicePixelRatioF());
698 pState
->setPosSize(toRectangle(rect
));
699 pState
->rMask() |= vcl::WindowDataMask::PosSize
;
705 void QtFrame::ShowFullScreen(bool bFullScreen
, sal_Int32 nScreen
)
707 // only top-level windows can go fullscreen
710 if (m_bFullScreen
== bFullScreen
)
713 m_bFullScreen
= bFullScreen
;
714 m_bFullScreenSpanAll
= m_bFullScreen
&& (nScreen
< 0);
716 // show it if it isn't shown yet
722 m_aRestoreGeometry
= m_pTopLevel
->geometry();
723 m_nRestoreScreen
= maGeometry
.screen();
724 SetScreenNumber(m_bFullScreenSpanAll
? m_nRestoreScreen
: nScreen
);
725 if (!m_bFullScreenSpanAll
)
726 windowHandle()->showFullScreen();
728 windowHandle()->showNormal();
732 SetScreenNumber(m_nRestoreScreen
);
733 windowHandle()->showNormal();
734 m_pTopLevel
->setGeometry(m_aRestoreGeometry
);
738 void QtFrame::StartPresentation(bool bStart
)
740 #if CHECK_ANY_QT_USING_X11
741 // meh - so there's no Qt platform independent solution
742 // https://forum.qt.io/topic/38504/solved-qdialog-in-fullscreen-disable-os-screensaver
743 assert(m_aSystemData
.platform
!= SystemEnvData::Platform::Invalid
);
744 const bool bIsX11
= m_aSystemData
.platform
== SystemEnvData::Platform::Xcb
;
745 std::optional
<unsigned int> aRootWindow
;
746 std::optional
<Display
*> aDisplay
;
748 #if CHECK_QT5_USING_X11
749 if (QX11Info::isPlatformX11())
751 aRootWindow
= QX11Info::appRootWindow();
752 aDisplay
= QX11Info::display();
756 m_ScreenSaverInhibitor
.inhibit(bStart
, u
"presentation", bIsX11
, aRootWindow
, aDisplay
);
762 void QtFrame::SetAlwaysOnTop(bool bOnTop
)
764 QWidget
* const pWidget
= asChild();
765 const Qt::WindowFlags flags
= pWidget
->windowFlags();
767 pWidget
->setWindowFlags(flags
| Qt::CustomizeWindowHint
| Qt::WindowStaysOnTopHint
);
769 pWidget
->setWindowFlags(flags
& ~(Qt::CustomizeWindowHint
| Qt::WindowStaysOnTopHint
));
772 void QtFrame::ToTop(SalFrameToTop nFlags
)
774 QWidget
* const pWidget
= asChild();
775 if (isWindow() && !(nFlags
& SalFrameToTop::GrabFocusOnly
))
777 if ((nFlags
& SalFrameToTop::RestoreWhenMin
) || (nFlags
& SalFrameToTop::ForegroundTask
))
779 if (nFlags
& SalFrameToTop::RestoreWhenMin
)
780 pWidget
->setWindowState(pWidget
->windowState() & ~Qt::WindowMinimized
);
781 pWidget
->activateWindow();
783 else if ((nFlags
& SalFrameToTop::GrabFocus
) || (nFlags
& SalFrameToTop::GrabFocusOnly
))
785 if (!(nFlags
& SalFrameToTop::GrabFocusOnly
))
786 pWidget
->activateWindow();
787 pWidget
->setFocus(Qt::OtherFocusReason
);
791 void QtFrame::SetPointer(PointerStyle ePointerStyle
)
793 if (ePointerStyle
== m_ePointerStyle
)
795 m_ePointerStyle
= ePointerStyle
;
797 m_pQWidget
->setCursor(GetQtData()->getCursor(ePointerStyle
));
800 void QtFrame::CaptureMouse(bool bMouse
)
802 static const char* pEnv
= getenv("SAL_NO_MOUSEGRABS");
807 m_pQWidget
->grabMouse();
809 m_pQWidget
->releaseMouse();
812 void QtFrame::SetPointerPos(tools::Long nX
, tools::Long nY
)
814 // some cursor already exists (and it has m_ePointerStyle shape)
815 // so here we just reposition it
816 QCursor::setPos(m_pQWidget
->mapToGlobal(QPoint(nX
, nY
) / devicePixelRatioF()));
819 void QtFrame::Flush()
821 // was: QGuiApplication::sync();
822 // but FIXME it causes too many issues, figure out sth better
824 // unclear if we need to also flush cairo surface - gtk3 backend
825 // does not do it. QPainter in QtWidget::paintEvent() is
826 // destroyed, so that state should be safely flushed.
829 bool QtFrame::ShowTooltip(const OUString
& rText
, const tools::Rectangle
& rHelpArea
)
831 QRect
aHelpArea(toQRect(rHelpArea
));
832 if (QGuiApplication::isRightToLeft())
833 aHelpArea
.moveLeft(maGeometry
.width() - aHelpArea
.width() - aHelpArea
.left() - 1);
834 m_aTooltipText
= rText
;
835 m_aTooltipArea
= aHelpArea
;
839 void QtFrame::SetInputContext(SalInputContext
* pContext
)
844 if (!(pContext
->mnOptions
& InputContextFlags::Text
))
847 m_pQWidget
->setAttribute(Qt::WA_InputMethodEnabled
);
850 void QtFrame::EndExtTextInput(EndExtTextInputFlags
/*nFlags*/)
853 m_pQWidget
->endExtTextInput();
856 OUString
QtFrame::GetKeyName(sal_uInt16 nKeyCode
)
858 vcl::KeyCode
vclKeyCode(nKeyCode
);
859 int nCode
= vclKeyCode
.GetCode();
862 if (nCode
>= KEY_0
&& nCode
<= KEY_9
)
863 nRetCode
= (nCode
- KEY_0
) + Qt::Key_0
;
864 else if (nCode
>= KEY_A
&& nCode
<= KEY_Z
)
865 nRetCode
= (nCode
- KEY_A
) + Qt::Key_A
;
866 else if (nCode
>= KEY_F1
&& nCode
<= KEY_F26
)
867 nRetCode
= (nCode
- KEY_F1
) + Qt::Key_F1
;
873 nRetCode
= Qt::Key_Down
;
876 nRetCode
= Qt::Key_Up
;
879 nRetCode
= Qt::Key_Left
;
882 nRetCode
= Qt::Key_Right
;
885 nRetCode
= Qt::Key_Home
;
888 nRetCode
= Qt::Key_End
;
891 nRetCode
= Qt::Key_PageUp
;
894 nRetCode
= Qt::Key_PageDown
;
897 nRetCode
= Qt::Key_Return
;
900 nRetCode
= Qt::Key_Escape
;
903 nRetCode
= Qt::Key_Tab
;
906 nRetCode
= Qt::Key_Backspace
;
909 nRetCode
= Qt::Key_Space
;
912 nRetCode
= Qt::Key_Insert
;
915 nRetCode
= Qt::Key_Delete
;
918 nRetCode
= Qt::Key_Plus
;
921 nRetCode
= Qt::Key_Minus
;
924 nRetCode
= Qt::Key_Asterisk
;
927 nRetCode
= Qt::Key_Slash
;
930 nRetCode
= Qt::Key_Period
;
933 nRetCode
= Qt::Key_Comma
;
936 nRetCode
= Qt::Key_Less
;
939 nRetCode
= Qt::Key_Greater
;
942 nRetCode
= Qt::Key_Equal
;
945 nRetCode
= Qt::Key_Find
;
947 case KEY_CONTEXTMENU
:
948 nRetCode
= Qt::Key_Menu
;
951 nRetCode
= Qt::Key_Help
;
954 nRetCode
= Qt::Key_Undo
;
957 nRetCode
= Qt::Key_Redo
;
960 nRetCode
= Qt::Key_AsciiTilde
;
963 nRetCode
= Qt::Key_QuoteLeft
;
965 case KEY_BRACKETLEFT
:
966 nRetCode
= Qt::Key_BracketLeft
;
968 case KEY_BRACKETRIGHT
:
969 nRetCode
= Qt::Key_BracketRight
;
972 nRetCode
= Qt::Key_NumberSign
;
975 nRetCode
= Qt::Key_Colon
;
978 nRetCode
= Qt::Key_Semicolon
;
983 nRetCode
= Qt::Key_Copy
;
986 nRetCode
= Qt::Key_Cut
;
989 nRetCode
= Qt::Key_Paste
;
992 nRetCode
= Qt::Key_Open
;
997 if (vclKeyCode
.IsShift())
998 nRetCode
+= Qt::SHIFT
;
999 if (vclKeyCode
.IsMod1())
1000 nRetCode
+= Qt::CTRL
;
1001 if (vclKeyCode
.IsMod2())
1002 nRetCode
+= Qt::ALT
;
1004 QKeySequence
keySeq(nRetCode
);
1005 OUString sKeyName
= toOUString(keySeq
.toString());
1010 bool QtFrame::MapUnicodeToKeyCode(sal_Unicode
/*aUnicode*/, LanguageType
/*aLangType*/,
1011 vcl::KeyCode
& /*rKeyCode*/)
1013 // not supported yet
1017 LanguageType
QtFrame::GetInputLanguage() { return m_nInputLanguage
; }
1019 void QtFrame::setInputLanguage(LanguageType nInputLanguage
)
1021 if (nInputLanguage
== m_nInputLanguage
)
1023 m_nInputLanguage
= nInputLanguage
;
1024 CallCallback(SalEvent::InputLanguageChange
, nullptr);
1027 static Color
toColor(const QColor
& rColor
)
1029 return Color(rColor
.red(), rColor
.green(), rColor
.blue());
1032 static bool toVclFont(const QFont
& rQFont
, const css::lang::Locale
& rLocale
, vcl::Font
& rVclFont
)
1034 psp::FastPrintFontInfo aInfo
;
1035 QFontInfo
qFontInfo(rQFont
);
1037 OUString sFamilyName
= toOUString(rQFont
.family());
1038 aInfo
.m_aFamilyName
= sFamilyName
;
1039 aInfo
.m_eItalic
= QtFontFace::toFontItalic(qFontInfo
.style());
1040 aInfo
.m_eWeight
= QtFontFace::toFontWeight(qFontInfo
.weight());
1041 aInfo
.m_eWidth
= QtFontFace::toFontWidth(rQFont
.stretch());
1043 psp::PrintFontManager::get().matchFont(aInfo
, rLocale
);
1044 SAL_INFO("vcl.qt", "font match result for '"
1045 << sFamilyName
<< "': "
1046 << (aInfo
.m_nID
!= 0 ? OUString::Concat("'") + aInfo
.m_aFamilyName
+ "'"
1047 : OUString("failed")));
1049 if (aInfo
.m_nID
== 0)
1052 int nPointHeight
= qFontInfo
.pointSize();
1053 if (nPointHeight
<= 0)
1054 nPointHeight
= rQFont
.pointSize();
1056 vcl::Font
aFont(aInfo
.m_aFamilyName
, Size(0, nPointHeight
));
1057 if (aInfo
.m_eWeight
!= WEIGHT_DONTKNOW
)
1058 aFont
.SetWeight(aInfo
.m_eWeight
);
1059 if (aInfo
.m_eWidth
!= WIDTH_DONTKNOW
)
1060 aFont
.SetWidthType(aInfo
.m_eWidth
);
1061 if (aInfo
.m_eItalic
!= ITALIC_DONTKNOW
)
1062 aFont
.SetItalic(aInfo
.m_eItalic
);
1063 if (aInfo
.m_ePitch
!= PITCH_DONTKNOW
)
1064 aFont
.SetPitch(aInfo
.m_ePitch
);
1070 void QtFrame::UpdateSettings(AllSettings
& rSettings
)
1072 if (QtData::noNativeControls())
1075 StyleSettings
style(rSettings
.GetStyleSettings());
1076 const css::lang::Locale aLocale
= rSettings
.GetUILanguageTag().getLocale();
1079 QPalette pal
= QApplication::palette();
1081 style
.SetToolbarIconSize(ToolbarIconSize::Large
);
1083 Color aFore
= toColor(pal
.color(QPalette::Active
, QPalette::WindowText
));
1084 Color aBack
= toColor(pal
.color(QPalette::Active
, QPalette::Window
));
1085 Color aText
= toColor(pal
.color(QPalette::Active
, QPalette::Text
));
1086 Color aBase
= toColor(pal
.color(QPalette::Active
, QPalette::Base
));
1087 Color aButn
= toColor(pal
.color(QPalette::Active
, QPalette::ButtonText
));
1088 Color aMid
= toColor(pal
.color(QPalette::Active
, QPalette::Mid
));
1089 Color aHigh
= toColor(pal
.color(QPalette::Active
, QPalette::Highlight
));
1090 Color aHighText
= toColor(pal
.color(QPalette::Active
, QPalette::HighlightedText
));
1091 Color aLink
= toColor(pal
.color(QPalette::Active
, QPalette::Link
));
1092 Color aVisitedLink
= toColor(pal
.color(QPalette::Active
, QPalette::LinkVisited
));
1094 style
.SetSkipDisabledInMenus(true);
1097 style
.SetRadioCheckTextColor(aFore
);
1098 style
.SetLabelTextColor(aFore
);
1099 style
.SetDialogTextColor(aFore
);
1100 style
.SetGroupTextColor(aFore
);
1103 style
.SetFieldTextColor(aText
);
1104 style
.SetFieldRolloverTextColor(aText
);
1105 style
.SetListBoxWindowTextColor(aText
);
1106 style
.SetWindowTextColor(aText
);
1107 style
.SetToolTextColor(aText
);
1110 style
.SetFieldColor(aBase
);
1111 style
.SetWindowColor(aBase
);
1112 style
.SetActiveTabColor(aBase
);
1113 style
.SetListBoxWindowBackgroundColor(aBase
);
1114 style
.SetAlternatingRowColor(toColor(pal
.color(QPalette::Active
, QPalette::AlternateBase
)));
1117 style
.SetDefaultButtonTextColor(aButn
);
1118 style
.SetButtonTextColor(aButn
);
1119 style
.SetDefaultActionButtonTextColor(aButn
);
1120 style
.SetActionButtonTextColor(aButn
);
1121 style
.SetFlatButtonTextColor(aButn
);
1122 style
.SetDefaultButtonRolloverTextColor(aButn
);
1123 style
.SetButtonRolloverTextColor(aButn
);
1124 style
.SetDefaultActionButtonRolloverTextColor(aButn
);
1125 style
.SetActionButtonRolloverTextColor(aButn
);
1126 style
.SetFlatButtonRolloverTextColor(aButn
);
1127 style
.SetDefaultButtonPressedRolloverTextColor(aButn
);
1128 style
.SetButtonPressedRolloverTextColor(aButn
);
1129 style
.SetDefaultActionButtonPressedRolloverTextColor(aButn
);
1130 style
.SetActionButtonPressedRolloverTextColor(aButn
);
1131 style
.SetFlatButtonPressedRolloverTextColor(aButn
);
1134 style
.SetTabTextColor(aButn
);
1135 style
.SetTabRolloverTextColor(aButn
);
1136 style
.SetTabHighlightTextColor(aButn
);
1139 style
.SetDisableColor(toColor(pal
.color(QPalette::Disabled
, QPalette::WindowText
)));
1142 style
.BatchSetBackgrounds(aBack
);
1143 style
.SetInactiveTabColor(aBack
);
1146 style
.SetWorkspaceColor(aMid
);
1149 style
.SetHighlightColor(aHigh
);
1150 style
.SetHighlightTextColor(aHighText
);
1151 style
.SetListBoxWindowHighlightColor(aHigh
);
1152 style
.SetListBoxWindowHighlightTextColor(aHighText
);
1153 style
.SetActiveColor(aHigh
);
1154 style
.SetActiveTextColor(aHighText
);
1157 style
.SetLinkColor(aLink
);
1158 style
.SetVisitedLinkColor(aVisitedLink
);
1161 style
.SetHelpColor(toColor(QToolTip::palette().color(QPalette::Active
, QPalette::ToolTipBase
)));
1162 style
.SetHelpTextColor(
1163 toColor(QToolTip::palette().color(QPalette::Active
, QPalette::ToolTipText
)));
1166 std::unique_ptr
<QMenuBar
> pMenuBar
= std::make_unique
<QMenuBar
>();
1167 QPalette qMenuCG
= pMenuBar
->palette();
1169 // Menu text and background color, theme specific
1170 Color aMenuFore
= toColor(qMenuCG
.color(QPalette::WindowText
));
1171 Color aMenuBack
= toColor(qMenuCG
.color(QPalette::Window
));
1173 style
.SetMenuTextColor(aMenuFore
);
1174 style
.SetMenuBarTextColor(style
.GetPersonaMenuBarTextColor().value_or(aMenuFore
));
1175 style
.SetMenuColor(aMenuBack
);
1176 style
.SetMenuBarColor(aMenuBack
);
1177 style
.SetMenuHighlightColor(toColor(qMenuCG
.color(QPalette::Highlight
)));
1178 style
.SetMenuHighlightTextColor(toColor(qMenuCG
.color(QPalette::HighlightedText
)));
1180 // set special menubar highlight text color
1181 if (QApplication::style()->inherits("HighContrastStyle"))
1182 ImplGetSVData()->maNWFData
.maMenuBarHighlightTextColor
1183 = toColor(qMenuCG
.color(QPalette::HighlightedText
));
1185 ImplGetSVData()->maNWFData
.maMenuBarHighlightTextColor
= aMenuFore
;
1187 // set menubar rollover color
1188 if (pMenuBar
->style()->styleHint(QStyle::SH_MenuBar_MouseTracking
))
1190 style
.SetMenuBarRolloverColor(toColor(qMenuCG
.color(QPalette::Highlight
)));
1191 style
.SetMenuBarRolloverTextColor(ImplGetSVData()->maNWFData
.maMenuBarHighlightTextColor
);
1195 style
.SetMenuBarRolloverColor(aMenuBack
);
1196 style
.SetMenuBarRolloverTextColor(aMenuFore
);
1198 style
.SetMenuBarHighlightTextColor(style
.GetMenuHighlightTextColor());
1202 if (toVclFont(QApplication::font(), aLocale
, aFont
))
1204 style
.BatchSetFonts(aFont
, aFont
);
1205 aFont
.SetWeight(WEIGHT_BOLD
);
1206 style
.SetTitleFont(aFont
);
1207 style
.SetFloatTitleFont(aFont
);
1211 if (toVclFont(QToolTip::font(), aLocale
, aFont
))
1212 style
.SetHelpFont(aFont
);
1215 if (toVclFont(pMenuBar
->font(), aLocale
, aFont
))
1216 style
.SetMenuFont(aFont
);
1219 style
.SetPreferredIconTheme(toOUString(QIcon::themeName()));
1222 style
.SetScrollBarSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent
));
1223 style
.SetMinThumbSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarSliderMin
));
1225 // These colors are used for the ruler text and marks
1226 style
.SetShadowColor(toColor(pal
.color(QPalette::Disabled
, QPalette::WindowText
)));
1227 style
.SetDarkShadowColor(toColor(pal
.color(QPalette::Inactive
, QPalette::WindowText
)));
1229 // Cursor blink interval
1230 int nFlashTime
= QApplication::cursorFlashTime();
1231 style
.SetCursorBlinkTime(nFlashTime
!= 0 ? nFlashTime
/ 2 : STYLE_CURSOR_NOBLINKTIME
);
1233 rSettings
.SetStyleSettings(style
);
1236 void QtFrame::Beep() { QApplication::beep(); }
1238 SalFrame::SalPointerState
QtFrame::GetPointerState()
1240 SalPointerState aState
;
1241 aState
.maPos
= toPoint(QCursor::pos() * devicePixelRatioF());
1242 aState
.maPos
.Move(-maGeometry
.x(), -maGeometry
.y());
1243 aState
.mnState
= GetMouseModCode(QGuiApplication::mouseButtons())
1244 | GetKeyModCode(QGuiApplication::keyboardModifiers());
1248 KeyIndicatorState
QtFrame::GetIndicatorState() { return KeyIndicatorState(); }
1250 void QtFrame::SimulateKeyPress(sal_uInt16 nKeyCode
)
1252 SAL_WARN("vcl.qt", "missing simulate keypress " << nKeyCode
);
1255 // don't set QWidget parents; this breaks popups on Wayland, like the LO ComboBox or ColorPicker!
1256 void QtFrame::SetParent(SalFrame
* pNewParent
) { m_pParent
= static_cast<QtFrame
*>(pNewParent
); }
1258 void QtFrame::SetPluginParent(SystemParentData
* /*pNewParent*/)
1260 //FIXME: no SetPluginParent impl. for qt5
1263 void QtFrame::ResetClipRegion() { m_bNullRegion
= true; }
1265 void QtFrame::BeginSetClipRegion(sal_uInt32
)
1267 m_aRegion
= QRegion(QRect(QPoint(0, 0), m_pQWidget
->size()));
1270 void QtFrame::UnionClipRegion(tools::Long nX
, tools::Long nY
, tools::Long nWidth
,
1271 tools::Long nHeight
)
1274 = m_aRegion
.united(scaledQRect(QRect(nX
, nY
, nWidth
, nHeight
), 1 / devicePixelRatioF()));
1277 void QtFrame::EndSetClipRegion() { m_bNullRegion
= false; }
1279 void QtFrame::SetScreenNumber(unsigned int nScreen
)
1284 QWindow
* const pWindow
= windowHandle();
1288 QList
<QScreen
*> screens
= QApplication::screens();
1289 if (static_cast<int>(nScreen
) < screens
.size() || m_bFullScreenSpanAll
)
1293 if (!m_bFullScreenSpanAll
)
1295 screenGeo
= QGuiApplication::screens().at(nScreen
)->geometry();
1296 pWindow
->setScreen(QApplication::screens()[nScreen
]);
1298 else // special case: fullscreen over all available screens
1300 assert(m_bFullScreen
);
1302 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
1303 QScreen
* pScreen
= QGuiApplication::screenAt(QPoint(0, 0));
1305 // QGuiApplication::screenAt was added in Qt 5.10, use deprecated QDesktopWidget
1306 int nLeftScreen
= QApplication::desktop()->screenNumber(QPoint(0, 0));
1307 QScreen
* pScreen
= QGuiApplication::screens()[nLeftScreen
];
1309 // entire virtual desktop
1310 screenGeo
= pScreen
->availableVirtualGeometry();
1311 pWindow
->setScreen(pScreen
);
1312 pWindow
->setGeometry(screenGeo
);
1313 nScreen
= screenNumber(pScreen
);
1316 // setScreen by itself has no effect, explicitly move the widget to
1318 asChild()->move(screenGeo
.topLeft());
1322 // index outta bounds, use primary screen
1323 QScreen
* primaryScreen
= QApplication::primaryScreen();
1324 pWindow
->setScreen(primaryScreen
);
1325 nScreen
= static_cast<sal_uInt32
>(screenNumber(primaryScreen
));
1328 maGeometry
.setScreen(nScreen
);
1331 void QtFrame::SetApplicationID(const OUString
& rWMClass
)
1333 #if CHECK_QT5_USING_X11
1334 assert(m_aSystemData
.platform
!= SystemEnvData::Platform::Invalid
);
1335 if (m_aSystemData
.platform
!= SystemEnvData::Platform::Xcb
|| !m_pTopLevel
)
1338 QtX11Support::setApplicationID(m_pTopLevel
->winId(), rWMClass
);
1344 void QtFrame::ResolveWindowHandle(SystemEnvData
& rData
) const
1348 assert(rData
.platform
!= SystemEnvData::Platform::Invalid
);
1349 if (rData
.platform
!= SystemEnvData::Platform::Wayland
)
1350 rData
.SetWindowHandle(static_cast<QWidget
*>(rData
.pWidget
)->winId());
1355 void QtFrame::registerDragSource(QtDragSource
* pDragSource
)
1357 assert(!m_pDragSource
);
1358 m_pDragSource
= pDragSource
;
1361 void QtFrame::deregisterDragSource(QtDragSource
const* pDragSource
)
1363 assert(m_pDragSource
== pDragSource
);
1365 m_pDragSource
= nullptr;
1368 void QtFrame::registerDropTarget(QtDropTarget
* pDropTarget
)
1370 assert(!m_pDropTarget
);
1371 m_pDropTarget
= pDropTarget
;
1372 m_pQWidget
->setAcceptDrops(true);
1375 void QtFrame::deregisterDropTarget(QtDropTarget
const* pDropTarget
)
1377 assert(m_pDropTarget
== pDropTarget
);
1379 m_pDropTarget
= nullptr;
1382 static css::uno::Reference
<css::datatransfer::XTransferable
>
1383 lcl_getXTransferable(const QMimeData
* pMimeData
)
1385 css::uno::Reference
<css::datatransfer::XTransferable
> xTransferable
;
1386 const QtMimeData
* pQtMimeData
= dynamic_cast<const QtMimeData
*>(pMimeData
);
1388 xTransferable
= new QtDnDTransferable(pMimeData
);
1390 xTransferable
= pQtMimeData
->xTransferable();
1391 return xTransferable
;
1394 static sal_Int8
lcl_getUserDropAction(const QDropEvent
* pEvent
, const sal_Int8 nSourceActions
,
1395 const QMimeData
* pMimeData
)
1397 // we completely ignore all proposals by the Qt event, as they don't
1398 // match at all with the preferred LO DnD actions.
1399 // check the key modifiers to detect a user-overridden DnD action
1400 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1401 const Qt::KeyboardModifiers eKeyMod
= pEvent
->modifiers();
1403 const Qt::KeyboardModifiers eKeyMod
= pEvent
->keyboardModifiers();
1405 sal_Int8 nUserDropAction
= 0;
1406 if ((eKeyMod
& Qt::ShiftModifier
) && !(eKeyMod
& Qt::ControlModifier
))
1407 nUserDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
1408 else if ((eKeyMod
& Qt::ControlModifier
) && !(eKeyMod
& Qt::ShiftModifier
))
1409 nUserDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
1410 else if ((eKeyMod
& Qt::ShiftModifier
) && (eKeyMod
& Qt::ControlModifier
))
1411 nUserDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_LINK
;
1412 nUserDropAction
&= nSourceActions
;
1414 // select the default DnD action, if there isn't a user preference
1415 if (0 == nUserDropAction
)
1417 // default LO internal action is move, but default external action is copy
1418 nUserDropAction
= dynamic_cast<const QtMimeData
*>(pMimeData
)
1419 ? css::datatransfer::dnd::DNDConstants::ACTION_MOVE
1420 : css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
1421 nUserDropAction
&= nSourceActions
;
1423 // if the default doesn't match any allowed source action, fall back to the
1424 // preferred of all allowed source actions
1425 if (0 == nUserDropAction
)
1426 nUserDropAction
= toVclDropAction(getPreferredDropAction(nSourceActions
));
1428 // this is "our" preference, but actually we would even prefer any default,
1430 nUserDropAction
|= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT
;
1432 return nUserDropAction
;
1435 void QtFrame::handleDragMove(QDragMoveEvent
* pEvent
)
1437 assert(m_pDropTarget
);
1439 // prepare our suggested drop action for the drop target
1440 const sal_Int8 nSourceActions
= toVclDropActions(pEvent
->possibleActions());
1441 const QMimeData
* pMimeData
= pEvent
->mimeData();
1442 const sal_Int8 nUserDropAction
= lcl_getUserDropAction(pEvent
, nSourceActions
, pMimeData
);
1444 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1445 const Point aPos
= toPoint(pEvent
->position().toPoint() * devicePixelRatioF());
1447 const Point aPos
= toPoint(pEvent
->pos() * devicePixelRatioF());
1450 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent
;
1451 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(m_pDropTarget
);
1452 aEvent
.Context
= static_cast<css::datatransfer::dnd::XDropTargetDragContext
*>(m_pDropTarget
);
1453 aEvent
.LocationX
= aPos
.X();
1454 aEvent
.LocationY
= aPos
.Y();
1455 aEvent
.DropAction
= nUserDropAction
;
1456 aEvent
.SourceActions
= nSourceActions
;
1458 // ask the drop target to accept our drop action
1461 aEvent
.SupportedDataFlavors
= lcl_getXTransferable(pMimeData
)->getTransferDataFlavors();
1462 m_pDropTarget
->fire_dragEnter(aEvent
);
1466 m_pDropTarget
->fire_dragOver(aEvent
);
1468 // the drop target accepted our drop action => inform Qt
1469 if (m_pDropTarget
->proposedDropAction() != 0)
1471 pEvent
->setDropAction(getPreferredDropAction(m_pDropTarget
->proposedDropAction()));
1474 else // or maybe someone else likes it?
1478 void QtFrame::handleDrop(QDropEvent
* pEvent
)
1480 assert(m_pDropTarget
);
1482 // prepare our suggested drop action for the drop target
1483 const sal_Int8 nSourceActions
= toVclDropActions(pEvent
->possibleActions());
1484 const sal_Int8 nUserDropAction
1485 = lcl_getUserDropAction(pEvent
, nSourceActions
, pEvent
->mimeData());
1487 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1488 const Point aPos
= toPoint(pEvent
->position().toPoint() * devicePixelRatioF());
1490 const Point aPos
= toPoint(pEvent
->pos() * devicePixelRatioF());
1493 css::datatransfer::dnd::DropTargetDropEvent aEvent
;
1494 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(m_pDropTarget
);
1495 aEvent
.Context
= static_cast<css::datatransfer::dnd::XDropTargetDropContext
*>(m_pDropTarget
);
1496 aEvent
.LocationX
= aPos
.X();
1497 aEvent
.LocationY
= aPos
.Y();
1498 aEvent
.SourceActions
= nSourceActions
;
1499 aEvent
.DropAction
= nUserDropAction
;
1500 aEvent
.Transferable
= lcl_getXTransferable(pEvent
->mimeData());
1502 // ask the drop target to accept our drop action
1503 m_pDropTarget
->fire_drop(aEvent
);
1506 const bool bDropSuccessful
= m_pDropTarget
->dropSuccessful();
1507 const sal_Int8 nDropAction
= m_pDropTarget
->proposedDropAction();
1509 // inform the drag source of the drag-origin frame of the drop result
1510 if (pEvent
->source())
1512 QtWidget
* pWidget
= dynamic_cast<QtWidget
*>(pEvent
->source());
1513 assert(pWidget
); // AFAIK there shouldn't be any non-Qt5Widget as source in LO itself
1515 pWidget
->frame().m_pDragSource
->fire_dragEnd(nDropAction
, bDropSuccessful
);
1518 // the drop target accepted our drop action => inform Qt
1519 if (bDropSuccessful
)
1521 pEvent
->setDropAction(getPreferredDropAction(nDropAction
));
1524 else // or maybe someone else likes it?
1528 void QtFrame::handleDragLeave()
1530 css::datatransfer::dnd::DropTargetEvent aEvent
;
1531 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(m_pDropTarget
);
1532 m_pDropTarget
->fire_dragExit(aEvent
);
1536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */