1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <Qt5Frame.hxx>
21 #include <Qt5Frame.moc>
22 
23 #include <Qt5Data.hxx>
24 #include <Qt5DragAndDrop.hxx>
25 #include <Qt5Graphics.hxx>
26 #include <Qt5Instance.hxx>
27 #include <Qt5MainWindow.hxx>
28 #include <Qt5Menu.hxx>
29 #include <Qt5SvpGraphics.hxx>
30 #include <Qt5System.hxx>
31 #include <Qt5Tools.hxx>
32 #include <Qt5Transferable.hxx>
33 #include <Qt5Widget.hxx>
34 
35 #include <QtCore/QMimeData>
36 #include <QtCore/QPoint>
37 #include <QtCore/QSize>
38 #include <QtCore/QThread>
39 #include <QtCore/QVersionNumber>
40 #include <QtGui/QDragMoveEvent>
41 #include <QtGui/QDropEvent>
42 #include <QtGui/QIcon>
43 #include <QtGui/QWindow>
44 #include <QtGui/QScreen>
45 #include <QtWidgets/QStyle>
46 #include <QtWidgets/QToolTip>
47 #include <QtWidgets/QApplication>
48 #include <QtWidgets/QDesktopWidget>
49 #include <QtWidgets/QMenuBar>
50 #include <QtWidgets/QMainWindow>
51 
52 #if QT5_USING_X11
53 #include <QtX11Extras/QX11Info>
54 #include <xcb/xproto.h>
55 #if QT5_HAVE_XCB_ICCCM
56 #include <xcb/xcb_icccm.h>
57 #endif
58 #endif
59 
60 #include <saldatabasic.hxx>
61 #include <window.h>
62 #include <vcl/syswin.hxx>
63 
64 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
65 
66 #include <cairo.h>
67 #include <headless/svpgdi.hxx>
68 
69 #if QT5_USING_X11 && QT5_HAVE_XCB_ICCCM
70 static bool g_bNeedsWmHintsWindowGroup = true;
71 static xcb_atom_t g_aXcbClientLeaderAtom = 0;
72 #endif
73 
SvpDamageHandler(void * handle,sal_Int32 nExtentsX,sal_Int32 nExtentsY,sal_Int32 nExtentsWidth,sal_Int32 nExtentsHeight)74 static void SvpDamageHandler(void* handle, sal_Int32 nExtentsX, sal_Int32 nExtentsY,
75                              sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight)
76 {
77     Qt5Frame* pThis = static_cast<Qt5Frame*>(handle);
78     pThis->Damage(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight);
79 }
80 
81 namespace
82 {
screenNumber(const QScreen * pScreen)83 sal_Int32 screenNumber(const QScreen* pScreen)
84 {
85     const QList<QScreen*> screens = QApplication::screens();
86 
87     sal_Int32 nScreen = 0;
88     bool bFound = false;
89     for (const QScreen* pCurScreen : screens)
90     {
91         if (pScreen == pCurScreen)
92         {
93             bFound = true;
94             break;
95         }
96         nScreen++;
97     }
98 
99     return bFound ? nScreen : -1;
100 }
101 }
102 
Qt5Frame(Qt5Frame * pParent,SalFrameStyleFlags nStyle,bool bUseCairo)103 Qt5Frame::Qt5Frame(Qt5Frame* pParent, SalFrameStyleFlags nStyle, bool bUseCairo)
104     : m_pTopLevel(nullptr)
105     , m_bUseCairo(bUseCairo)
106     , m_bNullRegion(true)
107     , m_bGraphicsInUse(false)
108     , m_ePointerStyle(PointerStyle::Arrow)
109     , m_pDragSource(nullptr)
110     , m_pDropTarget(nullptr)
111     , m_bInDrag(false)
112     , m_bDefaultSize(true)
113     , m_bDefaultPos(true)
114     , m_bFullScreen(false)
115     , m_bFullScreenSpanAll(false)
116 #if QT5_USING_X11
117     , m_nKeyModifiers(ModKeyFlags::NONE)
118 #endif
119     , m_nInputLanguage(LANGUAGE_DONTKNOW)
120 {
121     Qt5Instance* pInst = static_cast<Qt5Instance*>(GetSalData()->m_pInstance);
122     pInst->insertFrame(this);
123 
124     m_aDamageHandler.handle = this;
125     m_aDamageHandler.damaged = ::SvpDamageHandler;
126 
127     if (nStyle & SalFrameStyleFlags::DEFAULT) // ensure default style
128     {
129         nStyle |= SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE
130                   | SalFrameStyleFlags::CLOSEABLE;
131         nStyle &= ~SalFrameStyleFlags::FLOAT;
132     }
133 
134     m_nStyle = nStyle;
135     m_pParent = pParent;
136 
137     Qt::WindowFlags aWinFlags;
138     if (!(nStyle & SalFrameStyleFlags::SYSTEMCHILD))
139     {
140         if (nStyle & SalFrameStyleFlags::INTRO)
141             aWinFlags |= Qt::SplashScreen;
142         // floating toolbars are frameless tool windows
143         // + they must be able to receive keyboard focus
144         else if ((nStyle & SalFrameStyleFlags::FLOAT)
145                  && (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION))
146             aWinFlags |= Qt::Tool | Qt::FramelessWindowHint;
147         else if (nStyle & (SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::TOOLTIP))
148             aWinFlags |= Qt::ToolTip;
149         else if ((nStyle & SalFrameStyleFlags::FLOAT)
150                  && !(nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION))
151             aWinFlags |= Qt::Popup;
152         else if (nStyle & SalFrameStyleFlags::TOOLWINDOW)
153             aWinFlags |= Qt::Tool;
154         // top level windows can't be transient in Qt, so make them dialogs, if they have a parent. At least
155         // the plasma shell relies on this setting to skip dialogs in the window list. And Qt Xcb will just
156         // set transient for the types Dialog, Sheet, Tool, SplashScreen, ToolTip, Drawer and Popup.
157         else if (nStyle & SalFrameStyleFlags::DIALOG || m_pParent)
158             aWinFlags |= Qt::Dialog;
159         else
160             aWinFlags |= Qt::Window;
161     }
162 
163     if (aWinFlags == Qt::Window)
164     {
165         m_pTopLevel = new Qt5MainWindow(*this, aWinFlags);
166         m_pQWidget = new Qt5Widget(*this, aWinFlags);
167         m_pTopLevel->setCentralWidget(m_pQWidget);
168         m_pTopLevel->setFocusProxy(m_pQWidget);
169     }
170     else
171         m_pQWidget = new Qt5Widget(*this, aWinFlags);
172 
173     if (pParent && !(pParent->m_nStyle & SalFrameStyleFlags::PLUG))
174     {
175         QWindow* pParentWindow = pParent->GetQWidget()->window()->windowHandle();
176         QWindow* pChildWindow = asChild()->window()->windowHandle();
177         if (pParentWindow && pChildWindow && (pParentWindow != pChildWindow))
178             pChildWindow->setTransientParent(pParentWindow);
179     }
180 
181     // Calling 'QWidget::winId()' implicitly enables native windows to be used
182     // rather than "alien widgets" that are unknown to the windowing system,
183     // s. https://doc.qt.io/qt-5/qwidget.html#native-widgets-vs-alien-widgets
184     // Avoid this on Wayland due to problems with missing 'mouseMoveEvent's,
185     // s. tdf#122293/QTBUG-75766
186     const bool bWayland = QGuiApplication::platformName() == "wayland";
187     if (!bWayland)
188         m_aSystemData.SetWindowHandle(m_pQWidget->winId());
189     else
190     {
191         // TODO implement as needed for Wayland,
192         // s.a. commit c0d4f3ad3307c which did this for gtk3
193         // QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
194         // m_aSystemData.pDisplay = native->nativeResourceForWindow("display", nullptr);
195         // m_aSystemData.aWindow = reinterpret_cast<unsigned long>(
196         //     native->nativeResourceForWindow("surface", m_pQWidget->windowHandle()));
197     }
198 
199     m_aSystemData.aShellWindow = reinterpret_cast<sal_IntPtr>(this);
200     //m_aSystemData.pSalFrame = this;
201     m_aSystemData.pWidget = m_pQWidget;
202     //m_aSystemData.nScreen = m_nXScreen.getXScreen();
203     m_aSystemData.toolkit = SystemEnvData::Toolkit::Qt5;
204     if (!bWayland)
205         m_aSystemData.platform = SystemEnvData::Platform::Xcb;
206     else
207         m_aSystemData.platform = SystemEnvData::Platform::Wayland;
208 
209     SetIcon(SV_ICON_ID_OFFICE);
210 
211     fixICCCMwindowGroup();
212 }
213 
fixICCCMwindowGroup()214 void Qt5Frame::fixICCCMwindowGroup()
215 {
216 #if QT5_USING_X11 && QT5_HAVE_XCB_ICCCM
217     // older Qt5 just sets WM_CLIENT_LEADER, but not the XCB_ICCCM_WM_HINT_WINDOW_GROUP
218     // see Qt commit 0de4b326d8 ("xcb: fix issue with dialogs hidden by other windows")
219     // or QTBUG-46626. So LO has to set this itself to help some WMs.
220     if (!g_bNeedsWmHintsWindowGroup)
221         return;
222     g_bNeedsWmHintsWindowGroup = false;
223 
224     if (QGuiApplication::platformName() != "xcb")
225         return;
226     if (QVersionNumber::fromString(qVersion()) >= QVersionNumber(5, 12))
227         return;
228 
229     xcb_connection_t* conn = QX11Info::connection();
230     xcb_window_t win = asChild()->winId();
231 
232     xcb_icccm_wm_hints_t hints;
233 
234     xcb_get_property_cookie_t prop_cookie = xcb_icccm_get_wm_hints_unchecked(conn, win);
235     if (!xcb_icccm_get_wm_hints_reply(conn, prop_cookie, &hints, nullptr))
236         return;
237 
238     if (hints.flags & XCB_ICCCM_WM_HINT_WINDOW_GROUP)
239         return;
240 
241     if (g_aXcbClientLeaderAtom == 0)
242     {
243         const char* const leader_name = "WM_CLIENT_LEADER\0";
244         xcb_intern_atom_cookie_t atom_cookie
245             = xcb_intern_atom(conn, 1, strlen(leader_name), leader_name);
246         xcb_intern_atom_reply_t* atom_reply = xcb_intern_atom_reply(conn, atom_cookie, nullptr);
247         if (!atom_reply)
248             return;
249         g_aXcbClientLeaderAtom = atom_reply->atom;
250         free(atom_reply);
251     }
252 
253     g_bNeedsWmHintsWindowGroup = true;
254 
255     prop_cookie = xcb_get_property(conn, 0, win, g_aXcbClientLeaderAtom, XCB_ATOM_WINDOW, 0, 1);
256     xcb_get_property_reply_t* prop_reply = xcb_get_property_reply(conn, prop_cookie, nullptr);
257     if (!prop_reply)
258         return;
259 
260     if (xcb_get_property_value_length(prop_reply) != 4)
261     {
262         free(prop_reply);
263         return;
264     }
265 
266     xcb_window_t leader = *static_cast<xcb_window_t*>(xcb_get_property_value(prop_reply));
267     free(prop_reply);
268 
269     hints.flags |= XCB_ICCCM_WM_HINT_WINDOW_GROUP;
270     hints.window_group = leader;
271     xcb_icccm_set_wm_hints(conn, win, &hints);
272 #else
273     (void)this; // avoid loplugin:staticmethods
274 #endif
275 }
276 
~Qt5Frame()277 Qt5Frame::~Qt5Frame()
278 {
279     Qt5Instance* pInst = static_cast<Qt5Instance*>(GetSalData()->m_pInstance);
280     pInst->eraseFrame(this);
281     delete asChild();
282     m_aSystemData.aShellWindow = 0;
283 }
284 
Damage(sal_Int32 nExtentsX,sal_Int32 nExtentsY,sal_Int32 nExtentsWidth,sal_Int32 nExtentsHeight) const285 void Qt5Frame::Damage(sal_Int32 nExtentsX, sal_Int32 nExtentsY, sal_Int32 nExtentsWidth,
286                       sal_Int32 nExtentsHeight) const
287 {
288     m_pQWidget->update(scaledQRect(QRect(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight),
289                                    1 / devicePixelRatioF()));
290 }
291 
AcquireGraphics()292 SalGraphics* Qt5Frame::AcquireGraphics()
293 {
294     if (m_bGraphicsInUse)
295         return nullptr;
296 
297     m_bGraphicsInUse = true;
298 
299     if (m_bUseCairo)
300     {
301         if (!m_pSvpGraphics)
302         {
303             QSize aSize = m_pQWidget->size() * devicePixelRatioF();
304             m_pSvpGraphics.reset(new Qt5SvpGraphics(this));
305             m_pSurface.reset(
306                 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, aSize.width(), aSize.height()));
307             m_pSvpGraphics->setSurface(m_pSurface.get(),
308                                        basegfx::B2IVector(aSize.width(), aSize.height()));
309             cairo_surface_set_user_data(m_pSurface.get(), Qt5SvpGraphics::getDamageKey(),
310                                         &m_aDamageHandler, nullptr);
311         }
312         return m_pSvpGraphics.get();
313     }
314     else
315     {
316         if (!m_pQt5Graphics)
317         {
318             m_pQt5Graphics.reset(new Qt5Graphics(this));
319             m_pQImage.reset(
320                 new QImage(m_pQWidget->size() * devicePixelRatioF(), Qt5_DefaultFormat32));
321             m_pQImage->fill(Qt::transparent);
322             m_pQt5Graphics->ChangeQImage(m_pQImage.get());
323         }
324         return m_pQt5Graphics.get();
325     }
326 }
327 
ReleaseGraphics(SalGraphics * pSalGraph)328 void Qt5Frame::ReleaseGraphics(SalGraphics* pSalGraph)
329 {
330     (void)pSalGraph;
331     if (m_bUseCairo)
332         assert(pSalGraph == m_pSvpGraphics.get());
333     else
334         assert(pSalGraph == m_pQt5Graphics.get());
335     m_bGraphicsInUse = false;
336 }
337 
PostEvent(std::unique_ptr<ImplSVEvent> pData)338 bool Qt5Frame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
339 {
340     Qt5Instance* pInst = static_cast<Qt5Instance*>(GetSalData()->m_pInstance);
341     pInst->PostEvent(this, pData.release(), SalEvent::UserEvent);
342     return true;
343 }
344 
asChild() const345 QWidget* Qt5Frame::asChild() const { return m_pTopLevel ? m_pTopLevel : m_pQWidget; }
346 
devicePixelRatioF() const347 qreal Qt5Frame::devicePixelRatioF() const { return asChild()->devicePixelRatioF(); }
348 
isWindow() const349 bool Qt5Frame::isWindow() const { return asChild()->isWindow(); }
350 
windowHandle() const351 QWindow* Qt5Frame::windowHandle() const
352 {
353     // set attribute 'Qt::WA_NativeWindow' first to make sure a window handle actually exists
354     QWidget* pChild = asChild();
355     pChild->setAttribute(Qt::WA_NativeWindow);
356     return pChild->windowHandle();
357 }
358 
screen() const359 QScreen* Qt5Frame::screen() const
360 {
361     QWindow* const pWindow = windowHandle();
362     return pWindow ? pWindow->screen() : nullptr;
363 }
364 
isMinimized() const365 bool Qt5Frame::isMinimized() const { return asChild()->isMinimized(); }
366 
isMaximized() const367 bool Qt5Frame::isMaximized() const { return asChild()->isMaximized(); }
368 
SetWindowStateImpl(Qt::WindowStates eState)369 void Qt5Frame::SetWindowStateImpl(Qt::WindowStates eState)
370 {
371     return asChild()->setWindowState(eState);
372 }
373 
SetTitle(const OUString & rTitle)374 void Qt5Frame::SetTitle(const OUString& rTitle)
375 {
376     m_pQWidget->window()->setWindowTitle(toQString(rTitle));
377 }
378 
SetIcon(sal_uInt16 nIcon)379 void Qt5Frame::SetIcon(sal_uInt16 nIcon)
380 {
381     if (m_nStyle
382             & (SalFrameStyleFlags::PLUG | SalFrameStyleFlags::SYSTEMCHILD
383                | SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::INTRO
384                | SalFrameStyleFlags::OWNERDRAWDECORATION)
385         || !isWindow())
386         return;
387 
388     QString appicon;
389 
390     if (nIcon == SV_ICON_ID_TEXT)
391         appicon = "libreoffice-writer";
392     else if (nIcon == SV_ICON_ID_SPREADSHEET)
393         appicon = "libreoffice-calc";
394     else if (nIcon == SV_ICON_ID_DRAWING)
395         appicon = "libreoffice-draw";
396     else if (nIcon == SV_ICON_ID_PRESENTATION)
397         appicon = "libreoffice-impress";
398     else if (nIcon == SV_ICON_ID_DATABASE)
399         appicon = "libreoffice-base";
400     else if (nIcon == SV_ICON_ID_FORMULA)
401         appicon = "libreoffice-math";
402     else
403         appicon = "libreoffice-startcenter";
404 
405     QIcon aIcon = QIcon::fromTheme(appicon);
406     m_pQWidget->window()->setWindowIcon(aIcon);
407 }
408 
SetMenu(SalMenu *)409 void Qt5Frame::SetMenu(SalMenu*) {}
410 
DrawMenuBar()411 void Qt5Frame::DrawMenuBar() { /* not needed */}
412 
SetExtendedFrameStyle(SalExtStyle)413 void Qt5Frame::SetExtendedFrameStyle(SalExtStyle /*nExtStyle*/) { /* not needed */}
414 
Show(bool bVisible,bool bNoActivate)415 void Qt5Frame::Show(bool bVisible, bool bNoActivate)
416 {
417     assert(m_pQWidget);
418     if (bVisible == asChild()->isVisible())
419         return;
420 
421     SetDefaultSize();
422     SetDefaultPos();
423 
424     auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
425     assert(pSalInst);
426     pSalInst->RunInMainThread([this, bVisible, bNoActivate]() {
427         asChild()->setVisible(bVisible);
428         asChild()->raise();
429         if (!bNoActivate)
430         {
431             asChild()->activateWindow();
432             asChild()->setFocus();
433         }
434     });
435 }
436 
SetMinClientSize(tools::Long nWidth,tools::Long nHeight)437 void Qt5Frame::SetMinClientSize(tools::Long nWidth, tools::Long nHeight)
438 {
439     if (!isChild())
440     {
441         const qreal fRatio = devicePixelRatioF();
442         asChild()->setMinimumSize(round(nWidth / fRatio), round(nHeight / fRatio));
443     }
444 }
445 
SetMaxClientSize(tools::Long nWidth,tools::Long nHeight)446 void Qt5Frame::SetMaxClientSize(tools::Long nWidth, tools::Long nHeight)
447 {
448     if (!isChild())
449     {
450         const qreal fRatio = devicePixelRatioF();
451         asChild()->setMaximumSize(round(nWidth / fRatio), round(nHeight / fRatio));
452     }
453 }
454 
SetDefaultPos()455 void Qt5Frame::SetDefaultPos()
456 {
457     if (!m_bDefaultPos)
458         return;
459 
460     // center on parent
461     if (m_pParent)
462     {
463         const qreal fRatio = devicePixelRatioF();
464         QWidget* const pWindow = m_pParent->GetQWidget()->window();
465         QWidget* const pWidget = asChild();
466         QPoint aPos = pWindow->rect().center() - pWidget->rect().center();
467         SetPosSize(round(aPos.x() * fRatio), round(aPos.y() * fRatio), 0, 0,
468                    SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y);
469         assert(!m_bDefaultPos);
470     }
471     else
472         m_bDefaultPos = false;
473 }
474 
CalcDefaultSize()475 Size Qt5Frame::CalcDefaultSize()
476 {
477     assert(isWindow());
478 
479     Size aSize;
480     if (!m_bFullScreen)
481     {
482         const QScreen* pScreen = screen();
483         SAL_WNODEPRECATED_DECLARATIONS_PUSH
484         aSize = bestmaxFrameSizeForScreenSize(
485             toSize(pScreen ? pScreen->size() : QApplication::desktop()->screenGeometry(0).size()));
486         SAL_WNODEPRECATED_DECLARATIONS_POP
487     }
488     else
489     {
490         if (!m_bFullScreenSpanAll)
491         {
492             SAL_WNODEPRECATED_DECLARATIONS_PUSH
493             aSize = toSize(
494                 QApplication::desktop()->screenGeometry(maGeometry.nDisplayScreenNumber).size());
495             SAL_WNODEPRECATED_DECLARATIONS_POP
496         }
497         else
498         {
499             SAL_WNODEPRECATED_DECLARATIONS_PUSH
500             int nLeftScreen = QApplication::desktop()->screenNumber(QPoint(0, 0));
501             SAL_WNODEPRECATED_DECLARATIONS_POP
502             aSize = toSize(QApplication::screens()[nLeftScreen]->availableVirtualGeometry().size());
503         }
504     }
505 
506     return aSize;
507 }
508 
SetDefaultSize()509 void Qt5Frame::SetDefaultSize()
510 {
511     if (!m_bDefaultSize)
512         return;
513 
514     Size aDefSize = CalcDefaultSize();
515     SetPosSize(0, 0, aDefSize.Width(), aDefSize.Height(),
516                SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT);
517     assert(!m_bDefaultSize);
518 }
519 
SetPosSize(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,sal_uInt16 nFlags)520 void Qt5Frame::SetPosSize(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
521                           sal_uInt16 nFlags)
522 {
523     if (!isWindow() || isChild(true, false))
524         return;
525 
526     if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
527     {
528         if (isChild(false) || !m_pQWidget->isMaximized())
529         {
530             if (!(nFlags & SAL_FRAME_POSSIZE_WIDTH))
531                 nWidth = maGeometry.nWidth;
532             else if (!(nFlags & SAL_FRAME_POSSIZE_HEIGHT))
533                 nHeight = maGeometry.nHeight;
534 
535             if (nWidth > 0 && nHeight > 0)
536             {
537                 m_bDefaultSize = false;
538                 const int nNewWidth = round(nWidth / devicePixelRatioF());
539                 const int nNewHeight = round(nHeight / devicePixelRatioF());
540                 if (m_nStyle & SalFrameStyleFlags::SIZEABLE)
541                     asChild()->resize(nNewWidth, nNewHeight);
542                 else
543                     asChild()->setFixedSize(nNewWidth, nNewHeight);
544             }
545 
546             // assume the resize happened
547             // needed for calculations and will eventually be corrected by events
548             if (nWidth > 0)
549                 maGeometry.nWidth = nWidth;
550             if (nHeight > 0)
551                 maGeometry.nHeight = nHeight;
552         }
553     }
554 
555     if (!(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)))
556         return;
557 
558     if (m_pParent)
559     {
560         const SalFrameGeometry& aParentGeometry = m_pParent->maGeometry;
561         if (QGuiApplication::isRightToLeft())
562             nX = aParentGeometry.nX + aParentGeometry.nWidth - nX - maGeometry.nWidth - 1;
563         else
564             nX += aParentGeometry.nX;
565         nY += aParentGeometry.nY;
566 
567         Qt5MainWindow* pTopLevel = m_pParent->GetTopLevelWindow();
568         if (pTopLevel && pTopLevel->menuBar() && pTopLevel->menuBar()->isVisible())
569             nY += round(pTopLevel->menuBar()->geometry().height() * devicePixelRatioF());
570     }
571 
572     if (!(nFlags & SAL_FRAME_POSSIZE_X))
573         nX = maGeometry.nX;
574     else if (!(nFlags & SAL_FRAME_POSSIZE_Y))
575         nY = maGeometry.nY;
576 
577     // assume the reposition happened
578     // needed for calculations and will eventually be corrected by events later
579     maGeometry.nX = nX;
580     maGeometry.nY = nY;
581 
582     m_bDefaultPos = false;
583     asChild()->move(round(nX / devicePixelRatioF()), round(nY / devicePixelRatioF()));
584 }
585 
GetClientSize(tools::Long & rWidth,tools::Long & rHeight)586 void Qt5Frame::GetClientSize(tools::Long& rWidth, tools::Long& rHeight)
587 {
588     rWidth = round(m_pQWidget->width() * devicePixelRatioF());
589     rHeight = round(m_pQWidget->height() * devicePixelRatioF());
590 }
591 
GetWorkArea(tools::Rectangle & rRect)592 void Qt5Frame::GetWorkArea(tools::Rectangle& rRect)
593 {
594     if (!isWindow())
595         return;
596     QScreen* pScreen = screen();
597     if (!pScreen)
598         return;
599 
600     QSize aSize = pScreen->availableVirtualSize() * devicePixelRatioF();
601     rRect = tools::Rectangle(0, 0, aSize.width(), aSize.height());
602 }
603 
GetParent() const604 SalFrame* Qt5Frame::GetParent() const { return m_pParent; }
605 
SetModal(bool bModal)606 void Qt5Frame::SetModal(bool bModal)
607 {
608     if (!isWindow())
609         return;
610 
611     auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
612     assert(pSalInst);
613     pSalInst->RunInMainThread([this, bModal]() {
614 
615         QWidget* const pChild = asChild();
616         const bool bWasVisible = pChild->isVisible();
617 
618         // modality change is only effective if the window is hidden
619         if (bWasVisible)
620             pChild->hide();
621 
622         pChild->setWindowModality(bModal ? Qt::WindowModal : Qt::NonModal);
623 
624         if (bWasVisible)
625             pChild->show();
626     });
627 }
628 
GetModal() const629 bool Qt5Frame::GetModal() const { return isWindow() && windowHandle()->isModal(); }
630 
SetWindowState(const SalFrameState * pState)631 void Qt5Frame::SetWindowState(const SalFrameState* pState)
632 {
633     if (!isWindow() || !pState || isChild(true, false))
634         return;
635 
636     const WindowStateMask nMaxGeometryMask
637         = WindowStateMask::X | WindowStateMask::Y | WindowStateMask::Width | WindowStateMask::Height
638           | WindowStateMask::MaximizedX | WindowStateMask::MaximizedY
639           | WindowStateMask::MaximizedWidth | WindowStateMask::MaximizedHeight;
640 
641     if ((pState->mnMask & WindowStateMask::State) && (pState->mnState & WindowStateState::Maximized)
642         && !isMaximized() && (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask)
643     {
644         const qreal fRatio = devicePixelRatioF();
645         QWidget* const pChild = asChild();
646         pChild->resize(ceil(pState->mnWidth / fRatio), ceil(pState->mnHeight / fRatio));
647         pChild->move(ceil(pState->mnX / fRatio), ceil(pState->mnY / fRatio));
648         SetWindowStateImpl(Qt::WindowMaximized);
649     }
650     else if (pState->mnMask
651              & (WindowStateMask::X | WindowStateMask::Y | WindowStateMask::Width
652                 | WindowStateMask::Height))
653     {
654         sal_uInt16 nPosSizeFlags = 0;
655         if (pState->mnMask & WindowStateMask::X)
656             nPosSizeFlags |= SAL_FRAME_POSSIZE_X;
657         if (pState->mnMask & WindowStateMask::Y)
658             nPosSizeFlags |= SAL_FRAME_POSSIZE_Y;
659         if (pState->mnMask & WindowStateMask::Width)
660             nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH;
661         if (pState->mnMask & WindowStateMask::Height)
662             nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT;
663         SetPosSize(pState->mnX, pState->mnY, pState->mnWidth, pState->mnHeight, nPosSizeFlags);
664     }
665     else if (pState->mnMask & WindowStateMask::State && !isChild())
666     {
667         if (pState->mnState & WindowStateState::Maximized)
668             SetWindowStateImpl(Qt::WindowMaximized);
669         else if (pState->mnState & WindowStateState::Minimized)
670             SetWindowStateImpl(Qt::WindowMinimized);
671         else
672             SetWindowStateImpl(Qt::WindowNoState);
673     }
674 }
675 
GetWindowState(SalFrameState * pState)676 bool Qt5Frame::GetWindowState(SalFrameState* pState)
677 {
678     pState->mnState = WindowStateState::Normal;
679     pState->mnMask = WindowStateMask::State;
680     if (isMinimized() /*|| !windowHandle()*/)
681         pState->mnState |= WindowStateState::Minimized;
682     else if (isMaximized())
683     {
684         pState->mnState |= WindowStateState::Maximized;
685     }
686     else
687     {
688         // geometry() is the drawable area, which is wanted here
689         QRect rect = scaledQRect(asChild()->geometry(), devicePixelRatioF());
690         pState->mnX = rect.x();
691         pState->mnY = rect.y();
692         pState->mnWidth = rect.width();
693         pState->mnHeight = rect.height();
694         pState->mnMask |= WindowStateMask::X | WindowStateMask::Y | WindowStateMask::Width
695                           | WindowStateMask::Height;
696     }
697 
698     return true;
699 }
700 
ShowFullScreen(bool bFullScreen,sal_Int32 nScreen)701 void Qt5Frame::ShowFullScreen(bool bFullScreen, sal_Int32 nScreen)
702 {
703     // only top-level windows can go fullscreen
704     assert(m_pTopLevel);
705 
706     if (m_bFullScreen == bFullScreen)
707         return;
708 
709     m_bFullScreen = bFullScreen;
710     m_bFullScreenSpanAll = m_bFullScreen && (nScreen < 0);
711 
712     // show it if it isn't shown yet
713     if (!isWindow())
714         m_pTopLevel->show();
715 
716     if (m_bFullScreen)
717     {
718         m_aRestoreGeometry = m_pTopLevel->geometry();
719         m_nRestoreScreen = maGeometry.nDisplayScreenNumber;
720         SetScreenNumber(m_bFullScreenSpanAll ? m_nRestoreScreen : nScreen);
721         if (!m_bFullScreenSpanAll)
722             windowHandle()->showFullScreen();
723         else
724             windowHandle()->showNormal();
725     }
726     else
727     {
728         SetScreenNumber(m_nRestoreScreen);
729         windowHandle()->showNormal();
730         m_pTopLevel->setGeometry(m_aRestoreGeometry);
731     }
732 }
733 
StartPresentation(bool bStart)734 void Qt5Frame::StartPresentation(bool bStart)
735 {
736 // meh - so there's no Qt platform independent solution
737 // https://forum.qt.io/topic/38504/solved-qdialog-in-fullscreen-disable-os-screensaver
738 #if QT5_USING_X11
739     std::optional<unsigned int> aRootWindow;
740     std::optional<Display*> aDisplay;
741 
742     if (QX11Info::isPlatformX11())
743     {
744         aRootWindow = QX11Info::appRootWindow();
745         aDisplay = QX11Info::display();
746     }
747 
748     m_ScreenSaverInhibitor.inhibit(bStart, u"presentation", QX11Info::isPlatformX11(), aRootWindow,
749                                    aDisplay);
750 #else
751     (void)bStart;
752 #endif
753 }
754 
SetAlwaysOnTop(bool bOnTop)755 void Qt5Frame::SetAlwaysOnTop(bool bOnTop)
756 {
757     QWidget* const pWidget = asChild();
758     const Qt::WindowFlags flags = pWidget->windowFlags();
759     if (bOnTop)
760         pWidget->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
761     else
762         pWidget->setWindowFlags(flags & ~(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
763 }
764 
ToTop(SalFrameToTop nFlags)765 void Qt5Frame::ToTop(SalFrameToTop nFlags)
766 {
767     QWidget* const pWidget = asChild();
768     if (isWindow() && !(nFlags & SalFrameToTop::GrabFocusOnly))
769         pWidget->raise();
770     if ((nFlags & SalFrameToTop::RestoreWhenMin) || (nFlags & SalFrameToTop::ForegroundTask))
771         pWidget->activateWindow();
772     else if ((nFlags & SalFrameToTop::GrabFocus) || (nFlags & SalFrameToTop::GrabFocusOnly))
773     {
774         pWidget->activateWindow();
775         pWidget->setFocus();
776     }
777 }
778 
SetPointer(PointerStyle ePointerStyle)779 void Qt5Frame::SetPointer(PointerStyle ePointerStyle)
780 {
781     QWindow* pWindow = m_pQWidget->window()->windowHandle();
782     if (!pWindow)
783         return;
784     if (ePointerStyle == m_ePointerStyle)
785         return;
786     m_ePointerStyle = ePointerStyle;
787 
788     pWindow->setCursor(static_cast<Qt5Data*>(GetSalData())->getCursor(ePointerStyle));
789 }
790 
CaptureMouse(bool bMouse)791 void Qt5Frame::CaptureMouse(bool bMouse)
792 {
793     static const char* pEnv = getenv("SAL_NO_MOUSEGRABS");
794     if (pEnv && *pEnv)
795         return;
796 
797     if (bMouse)
798         m_pQWidget->grabMouse();
799     else
800         m_pQWidget->releaseMouse();
801 }
802 
SetPointerPos(tools::Long nX,tools::Long nY)803 void Qt5Frame::SetPointerPos(tools::Long nX, tools::Long nY)
804 {
805     // some cursor already exists (and it has m_ePointerStyle shape)
806     // so here we just reposition it
807     QCursor::setPos(m_pQWidget->mapToGlobal(QPoint(nX, nY)));
808 }
809 
Flush()810 void Qt5Frame::Flush()
811 {
812     // was: QGuiApplication::sync();
813     // but FIXME it causes too many issues, figure out sth better
814 
815     // unclear if we need to also flush cairo surface - gtk3 backend
816     // does not do it. QPainter in Qt5Widget::paintEvent() is
817     // destroyed, so that state should be safely flushed.
818 }
819 
ShowTooltip(const OUString & rText,const tools::Rectangle & rHelpArea)820 bool Qt5Frame::ShowTooltip(const OUString& rText, const tools::Rectangle& rHelpArea)
821 {
822     QRect aHelpArea(toQRect(rHelpArea));
823     if (QGuiApplication::isRightToLeft())
824         aHelpArea.moveLeft(maGeometry.nWidth - aHelpArea.width() - aHelpArea.left() - 1);
825     QToolTip::showText(QCursor::pos(), toQString(rText), m_pQWidget, aHelpArea);
826     return true;
827 }
828 
SetInputContext(SalInputContext * pContext)829 void Qt5Frame::SetInputContext(SalInputContext* pContext)
830 {
831     if (!pContext)
832         return;
833 
834     if (!(pContext->mnOptions & InputContextFlags::Text))
835         return;
836 
837     m_pQWidget->setAttribute(Qt::WA_InputMethodEnabled);
838 }
839 
EndExtTextInput(EndExtTextInputFlags)840 void Qt5Frame::EndExtTextInput(EndExtTextInputFlags /*nFlags*/)
841 {
842     Qt5Widget* pQt5Widget = static_cast<Qt5Widget*>(m_pQWidget);
843     if (pQt5Widget)
844         pQt5Widget->endExtTextInput();
845 }
846 
GetKeyName(sal_uInt16 nKeyCode)847 OUString Qt5Frame::GetKeyName(sal_uInt16 nKeyCode)
848 {
849     vcl::KeyCode vclKeyCode(nKeyCode);
850     int nCode = vclKeyCode.GetCode();
851     int nRetCode = 0;
852 
853     if (nCode >= KEY_0 && nCode <= KEY_9)
854         nRetCode = (nCode - KEY_0) + Qt::Key_0;
855     else if (nCode >= KEY_A && nCode <= KEY_Z)
856         nRetCode = (nCode - KEY_A) + Qt::Key_A;
857     else if (nCode >= KEY_F1 && nCode <= KEY_F26)
858         nRetCode = (nCode - KEY_F1) + Qt::Key_F1;
859     else
860     {
861         switch (nCode)
862         {
863             case KEY_DOWN:
864                 nRetCode = Qt::Key_Down;
865                 break;
866             case KEY_UP:
867                 nRetCode = Qt::Key_Up;
868                 break;
869             case KEY_LEFT:
870                 nRetCode = Qt::Key_Left;
871                 break;
872             case KEY_RIGHT:
873                 nRetCode = Qt::Key_Right;
874                 break;
875             case KEY_HOME:
876                 nRetCode = Qt::Key_Home;
877                 break;
878             case KEY_END:
879                 nRetCode = Qt::Key_End;
880                 break;
881             case KEY_PAGEUP:
882                 nRetCode = Qt::Key_PageUp;
883                 break;
884             case KEY_PAGEDOWN:
885                 nRetCode = Qt::Key_PageDown;
886                 break;
887             case KEY_RETURN:
888                 nRetCode = Qt::Key_Return;
889                 break;
890             case KEY_ESCAPE:
891                 nRetCode = Qt::Key_Escape;
892                 break;
893             case KEY_TAB:
894                 nRetCode = Qt::Key_Tab;
895                 break;
896             case KEY_BACKSPACE:
897                 nRetCode = Qt::Key_Backspace;
898                 break;
899             case KEY_SPACE:
900                 nRetCode = Qt::Key_Space;
901                 break;
902             case KEY_INSERT:
903                 nRetCode = Qt::Key_Insert;
904                 break;
905             case KEY_DELETE:
906                 nRetCode = Qt::Key_Delete;
907                 break;
908             case KEY_ADD:
909                 nRetCode = Qt::Key_Plus;
910                 break;
911             case KEY_SUBTRACT:
912                 nRetCode = Qt::Key_Minus;
913                 break;
914             case KEY_MULTIPLY:
915                 nRetCode = Qt::Key_Asterisk;
916                 break;
917             case KEY_DIVIDE:
918                 nRetCode = Qt::Key_Slash;
919                 break;
920             case KEY_POINT:
921                 nRetCode = Qt::Key_Period;
922                 break;
923             case KEY_COMMA:
924                 nRetCode = Qt::Key_Comma;
925                 break;
926             case KEY_LESS:
927                 nRetCode = Qt::Key_Less;
928                 break;
929             case KEY_GREATER:
930                 nRetCode = Qt::Key_Greater;
931                 break;
932             case KEY_EQUAL:
933                 nRetCode = Qt::Key_Equal;
934                 break;
935             case KEY_FIND:
936                 nRetCode = Qt::Key_Find;
937                 break;
938             case KEY_CONTEXTMENU:
939                 nRetCode = Qt::Key_Menu;
940                 break;
941             case KEY_HELP:
942                 nRetCode = Qt::Key_Help;
943                 break;
944             case KEY_UNDO:
945                 nRetCode = Qt::Key_Undo;
946                 break;
947             case KEY_REPEAT:
948                 nRetCode = Qt::Key_Redo;
949                 break;
950             case KEY_TILDE:
951                 nRetCode = Qt::Key_AsciiTilde;
952                 break;
953             case KEY_QUOTELEFT:
954                 nRetCode = Qt::Key_QuoteLeft;
955                 break;
956             case KEY_BRACKETLEFT:
957                 nRetCode = Qt::Key_BracketLeft;
958                 break;
959             case KEY_BRACKETRIGHT:
960                 nRetCode = Qt::Key_BracketRight;
961                 break;
962             case KEY_SEMICOLON:
963                 nRetCode = Qt::Key_Semicolon;
964                 break;
965 
966             // Special cases
967             case KEY_COPY:
968                 nRetCode = Qt::Key_Copy;
969                 break;
970             case KEY_CUT:
971                 nRetCode = Qt::Key_Cut;
972                 break;
973             case KEY_PASTE:
974                 nRetCode = Qt::Key_Paste;
975                 break;
976             case KEY_OPEN:
977                 nRetCode = Qt::Key_Open;
978                 break;
979         }
980     }
981 
982     if (vclKeyCode.IsShift())
983         nRetCode += Qt::SHIFT;
984     if (vclKeyCode.IsMod1())
985         nRetCode += Qt::CTRL;
986     if (vclKeyCode.IsMod2())
987         nRetCode += Qt::ALT;
988 
989     QKeySequence keySeq(nRetCode);
990     OUString sKeyName = toOUString(keySeq.toString());
991 
992     return sKeyName;
993 }
994 
MapUnicodeToKeyCode(sal_Unicode,LanguageType,vcl::KeyCode &)995 bool Qt5Frame::MapUnicodeToKeyCode(sal_Unicode /*aUnicode*/, LanguageType /*aLangType*/,
996                                    vcl::KeyCode& /*rKeyCode*/)
997 {
998     // not supported yet
999     return false;
1000 }
1001 
GetInputLanguage()1002 LanguageType Qt5Frame::GetInputLanguage() { return m_nInputLanguage; }
1003 
setInputLanguage(LanguageType nInputLanguage)1004 void Qt5Frame::setInputLanguage(LanguageType nInputLanguage)
1005 {
1006     if (nInputLanguage == m_nInputLanguage)
1007         return;
1008     m_nInputLanguage = nInputLanguage;
1009     CallCallback(SalEvent::InputLanguageChange, nullptr);
1010 }
1011 
toColor(const QColor & rColor)1012 static Color toColor(const QColor& rColor)
1013 {
1014     return Color(rColor.red(), rColor.green(), rColor.blue());
1015 }
1016 
UpdateSettings(AllSettings & rSettings)1017 void Qt5Frame::UpdateSettings(AllSettings& rSettings)
1018 {
1019     if (Qt5Data::noNativeControls())
1020         return;
1021 
1022     StyleSettings style(rSettings.GetStyleSettings());
1023 
1024     // General settings
1025     QPalette pal = QApplication::palette();
1026 
1027     style.SetToolbarIconSize(ToolbarIconSize::Large);
1028 
1029     Color aFore = toColor(pal.color(QPalette::Active, QPalette::WindowText));
1030     Color aBack = toColor(pal.color(QPalette::Active, QPalette::Window));
1031     Color aText = toColor(pal.color(QPalette::Active, QPalette::Text));
1032     Color aBase = toColor(pal.color(QPalette::Active, QPalette::Base));
1033     Color aButn = toColor(pal.color(QPalette::Active, QPalette::ButtonText));
1034     Color aMid = toColor(pal.color(QPalette::Active, QPalette::Mid));
1035     Color aHigh = toColor(pal.color(QPalette::Active, QPalette::Highlight));
1036     Color aHighText = toColor(pal.color(QPalette::Active, QPalette::HighlightedText));
1037     Color aLink = toColor(pal.color(QPalette::Active, QPalette::Link));
1038     Color aVisitedLink = toColor(pal.color(QPalette::Active, QPalette::LinkVisited));
1039 
1040     style.SetSkipDisabledInMenus(true);
1041 
1042     // Foreground
1043     style.SetRadioCheckTextColor(aFore);
1044     style.SetLabelTextColor(aFore);
1045     style.SetDialogTextColor(aFore);
1046     style.SetGroupTextColor(aFore);
1047 
1048     // Text
1049     style.SetFieldTextColor(aText);
1050     style.SetFieldRolloverTextColor(aText);
1051     style.SetWindowTextColor(aText);
1052     style.SetToolTextColor(aText);
1053 
1054     // Base
1055     style.SetFieldColor(aBase);
1056     style.SetWindowColor(aBase);
1057     style.SetActiveTabColor(aBase);
1058     style.SetAlternatingRowColor(toColor(pal.color(QPalette::Active, QPalette::AlternateBase)));
1059 
1060     // Buttons
1061     style.SetDefaultButtonTextColor(aButn);
1062     style.SetButtonTextColor(aButn);
1063     style.SetDefaultActionButtonTextColor(aButn);
1064     style.SetActionButtonTextColor(aButn);
1065     style.SetFlatButtonTextColor(aButn);
1066     style.SetDefaultButtonRolloverTextColor(aButn);
1067     style.SetButtonRolloverTextColor(aButn);
1068     style.SetDefaultActionButtonRolloverTextColor(aButn);
1069     style.SetActionButtonRolloverTextColor(aButn);
1070     style.SetFlatButtonRolloverTextColor(aButn);
1071     style.SetDefaultButtonPressedRolloverTextColor(aButn);
1072     style.SetButtonPressedRolloverTextColor(aButn);
1073     style.SetDefaultActionButtonPressedRolloverTextColor(aButn);
1074     style.SetActionButtonPressedRolloverTextColor(aButn);
1075     style.SetFlatButtonPressedRolloverTextColor(aButn);
1076 
1077     // Tabs
1078     style.SetTabTextColor(aButn);
1079     style.SetTabRolloverTextColor(aButn);
1080     style.SetTabHighlightTextColor(aButn);
1081 
1082     // Disable color
1083     style.SetDisableColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1084 
1085     // Background
1086     style.BatchSetBackgrounds(aBack);
1087     style.SetInactiveTabColor(aBack);
1088 
1089     // Workspace
1090     style.SetWorkspaceColor(aMid);
1091 
1092     // Selection
1093     style.SetHighlightColor(aHigh);
1094     style.SetHighlightTextColor(aHighText);
1095     style.SetActiveColor(aHigh);
1096     style.SetActiveTextColor(aHighText);
1097 
1098     // Links
1099     style.SetLinkColor(aLink);
1100     style.SetVisitedLinkColor(aVisitedLink);
1101 
1102     // Tooltip
1103     style.SetHelpColor(toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipBase)));
1104     style.SetHelpTextColor(
1105         toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipText)));
1106 
1107     const int flash_time = QApplication::cursorFlashTime();
1108     style.SetCursorBlinkTime(flash_time != 0 ? flash_time / 2 : STYLE_CURSOR_NOBLINKTIME);
1109 
1110     // Menu
1111     std::unique_ptr<QMenuBar> pMenuBar = std::make_unique<QMenuBar>();
1112     QPalette qMenuCG = pMenuBar->palette();
1113 
1114     // Menu text and background color, theme specific
1115     Color aMenuFore = toColor(qMenuCG.color(QPalette::WindowText));
1116     Color aMenuBack = toColor(qMenuCG.color(QPalette::Window));
1117 
1118     style.SetMenuTextColor(aMenuFore);
1119     style.SetMenuBarTextColor(style.GetPersonaMenuBarTextColor().value_or(aMenuFore));
1120     style.SetMenuColor(aMenuBack);
1121     style.SetMenuBarColor(aMenuBack);
1122     style.SetMenuHighlightColor(toColor(qMenuCG.color(QPalette::Highlight)));
1123     style.SetMenuHighlightTextColor(toColor(qMenuCG.color(QPalette::HighlightedText)));
1124 
1125     // set special menubar highlight text color
1126     if (QApplication::style()->inherits("HighContrastStyle"))
1127         ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor
1128             = toColor(qMenuCG.color(QPalette::HighlightedText));
1129     else
1130         ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor = aMenuFore;
1131 
1132     // set menubar rollover color
1133     if (pMenuBar->style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
1134     {
1135         style.SetMenuBarRolloverColor(toColor(qMenuCG.color(QPalette::Highlight)));
1136         style.SetMenuBarRolloverTextColor(ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor);
1137     }
1138     else
1139     {
1140         style.SetMenuBarRolloverColor(aMenuBack);
1141         style.SetMenuBarRolloverTextColor(aMenuFore);
1142     }
1143     style.SetMenuBarHighlightTextColor(style.GetMenuHighlightTextColor());
1144 
1145     // Icon theme
1146     style.SetPreferredIconTheme(toOUString(QIcon::themeName()));
1147 
1148     // Scroll bar size
1149     style.SetScrollBarSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent));
1150     style.SetMinThumbSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarSliderMin));
1151 
1152     // These colors are used for the ruler text and marks
1153     style.SetShadowColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1154     style.SetDarkShadowColor(toColor(pal.color(QPalette::Inactive, QPalette::WindowText)));
1155 
1156     rSettings.SetStyleSettings(style);
1157 }
1158 
Beep()1159 void Qt5Frame::Beep() { QApplication::beep(); }
1160 
GetPointerState()1161 SalFrame::SalPointerState Qt5Frame::GetPointerState()
1162 {
1163     SalPointerState aState;
1164     aState.maPos = toPoint(QCursor::pos() * devicePixelRatioF());
1165     aState.maPos.Move(-maGeometry.nX, -maGeometry.nY);
1166     aState.mnState = GetMouseModCode(QGuiApplication::mouseButtons())
1167                      | GetKeyModCode(QGuiApplication::keyboardModifiers());
1168     return aState;
1169 }
1170 
GetIndicatorState()1171 KeyIndicatorState Qt5Frame::GetIndicatorState() { return KeyIndicatorState(); }
1172 
SimulateKeyPress(sal_uInt16 nKeyCode)1173 void Qt5Frame::SimulateKeyPress(sal_uInt16 nKeyCode)
1174 {
1175     SAL_WARN("vcl.qt5", "missing simulate keypress " << nKeyCode);
1176 }
1177 
SetParent(SalFrame * pNewParent)1178 void Qt5Frame::SetParent(SalFrame* pNewParent) { m_pParent = static_cast<Qt5Frame*>(pNewParent); }
1179 
SetPluginParent(SystemParentData *)1180 void Qt5Frame::SetPluginParent(SystemParentData* /*pNewParent*/)
1181 {
1182     //FIXME: no SetPluginParent impl. for qt5
1183 }
1184 
ResetClipRegion()1185 void Qt5Frame::ResetClipRegion() { m_bNullRegion = true; }
1186 
BeginSetClipRegion(sal_uInt32)1187 void Qt5Frame::BeginSetClipRegion(sal_uInt32)
1188 {
1189     m_aRegion = QRegion(QRect(QPoint(0, 0), m_pQWidget->size()));
1190 }
1191 
UnionClipRegion(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight)1192 void Qt5Frame::UnionClipRegion(tools::Long nX, tools::Long nY, tools::Long nWidth,
1193                                tools::Long nHeight)
1194 {
1195     m_aRegion
1196         = m_aRegion.united(scaledQRect(QRect(nX, nY, nWidth, nHeight), 1 / devicePixelRatioF()));
1197 }
1198 
EndSetClipRegion()1199 void Qt5Frame::EndSetClipRegion() { m_bNullRegion = false; }
1200 
SetScreenNumber(unsigned int nScreen)1201 void Qt5Frame::SetScreenNumber(unsigned int nScreen)
1202 {
1203     if (!isWindow())
1204         return;
1205 
1206     QWindow* const pWindow = windowHandle();
1207     if (!pWindow)
1208         return;
1209 
1210     QList<QScreen*> screens = QApplication::screens();
1211     if (static_cast<int>(nScreen) < screens.size() || m_bFullScreenSpanAll)
1212     {
1213         QRect screenGeo;
1214 
1215         if (!m_bFullScreenSpanAll)
1216         {
1217             SAL_WNODEPRECATED_DECLARATIONS_PUSH
1218             screenGeo = QApplication::desktop()->screenGeometry(nScreen);
1219             SAL_WNODEPRECATED_DECLARATIONS_POP
1220             pWindow->setScreen(QApplication::screens()[nScreen]);
1221         }
1222         else // special case: fullscreen over all available screens
1223         {
1224             assert(m_bFullScreen);
1225             // left-most screen
1226             SAL_WNODEPRECATED_DECLARATIONS_PUSH
1227             int nLeftScreen = QApplication::desktop()->screenNumber(QPoint(0, 0));
1228             SAL_WNODEPRECATED_DECLARATIONS_POP
1229             // entire virtual desktop
1230             screenGeo = QApplication::screens()[nLeftScreen]->availableVirtualGeometry();
1231             pWindow->setScreen(QApplication::screens()[nLeftScreen]);
1232             pWindow->setGeometry(screenGeo);
1233             nScreen = nLeftScreen;
1234         }
1235 
1236         // setScreen by itself has no effect, explicitly move the widget to
1237         // the new screen
1238         asChild()->move(screenGeo.topLeft());
1239     }
1240     else
1241     {
1242         // index outta bounds, use primary screen
1243         QScreen* primaryScreen = QApplication::primaryScreen();
1244         pWindow->setScreen(primaryScreen);
1245         nScreen = static_cast<sal_uInt32>(screenNumber(primaryScreen));
1246     }
1247 
1248     maGeometry.nDisplayScreenNumber = nScreen;
1249 }
1250 
SetApplicationID(const OUString & rWMClass)1251 void Qt5Frame::SetApplicationID(const OUString& rWMClass)
1252 {
1253 #if QT5_USING_X11
1254     if (QGuiApplication::platformName() != "xcb" || !m_pTopLevel)
1255         return;
1256 
1257     OString aResClass = OUStringToOString(rWMClass, RTL_TEXTENCODING_ASCII_US);
1258     const char* pResClass
1259         = !aResClass.isEmpty() ? aResClass.getStr() : SalGenericSystem::getFrameClassName();
1260     OString aResName = SalGenericSystem::getFrameResName();
1261 
1262     // the WM_CLASS data consists of two concatenated cstrings, including the terminating '\0' chars
1263     const uint32_t data_len = aResName.getLength() + 1 + strlen(pResClass) + 1;
1264     char* data = new char[data_len];
1265     memcpy(data, aResName.getStr(), aResName.getLength() + 1);
1266     memcpy(data + aResName.getLength() + 1, pResClass, strlen(pResClass) + 1);
1267 
1268     xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, m_pTopLevel->winId(),
1269                         XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, data_len, data);
1270     delete[] data;
1271 #else
1272     (void)rWMClass;
1273 #endif
1274 }
1275 
1276 // Drag'n'drop foo
1277 
registerDragSource(Qt5DragSource * pDragSource)1278 void Qt5Frame::registerDragSource(Qt5DragSource* pDragSource)
1279 {
1280     assert(!m_pDragSource);
1281     m_pDragSource = pDragSource;
1282 }
1283 
deregisterDragSource(Qt5DragSource const * pDragSource)1284 void Qt5Frame::deregisterDragSource(Qt5DragSource const* pDragSource)
1285 {
1286     assert(m_pDragSource == pDragSource);
1287     (void)pDragSource;
1288     m_pDragSource = nullptr;
1289 }
1290 
registerDropTarget(Qt5DropTarget * pDropTarget)1291 void Qt5Frame::registerDropTarget(Qt5DropTarget* pDropTarget)
1292 {
1293     assert(!m_pDropTarget);
1294     m_pDropTarget = pDropTarget;
1295     m_pQWidget->setAcceptDrops(true);
1296 }
1297 
deregisterDropTarget(Qt5DropTarget const * pDropTarget)1298 void Qt5Frame::deregisterDropTarget(Qt5DropTarget const* pDropTarget)
1299 {
1300     assert(m_pDropTarget == pDropTarget);
1301     (void)pDropTarget;
1302     m_pDropTarget = nullptr;
1303 }
1304 
1305 static css::uno::Reference<css::datatransfer::XTransferable>
lcl_getXTransferable(const QMimeData * pMimeData)1306 lcl_getXTransferable(const QMimeData* pMimeData)
1307 {
1308     css::uno::Reference<css::datatransfer::XTransferable> xTransferable;
1309     const Qt5MimeData* pQt5MimeData = dynamic_cast<const Qt5MimeData*>(pMimeData);
1310     if (!pQt5MimeData)
1311         xTransferable = new Qt5DnDTransferable(pMimeData);
1312     else
1313         xTransferable = pQt5MimeData->xTransferable();
1314     return xTransferable;
1315 }
1316 
lcl_getUserDropAction(const QDropEvent * pEvent,const sal_Int8 nSourceActions,const QMimeData * pMimeData)1317 static sal_Int8 lcl_getUserDropAction(const QDropEvent* pEvent, const sal_Int8 nSourceActions,
1318                                       const QMimeData* pMimeData)
1319 {
1320     // we completely ignore all proposals by the Qt event, as they don't
1321     // match at all with the preferred LO DnD actions.
1322 
1323     // check the key modifiers to detect a user-overridden DnD action
1324     const Qt::KeyboardModifiers eKeyMod = pEvent->keyboardModifiers();
1325     sal_Int8 nUserDropAction = 0;
1326     if ((eKeyMod & Qt::ShiftModifier) && !(eKeyMod & Qt::ControlModifier))
1327         nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
1328     else if ((eKeyMod & Qt::ControlModifier) && !(eKeyMod & Qt::ShiftModifier))
1329         nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1330     else if ((eKeyMod & Qt::ShiftModifier) && (eKeyMod & Qt::ControlModifier))
1331         nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_LINK;
1332     nUserDropAction &= nSourceActions;
1333 
1334     // select the default DnD action, if there isn't a user preference
1335     if (0 == nUserDropAction)
1336     {
1337         // default LO internal action is move, but default external action is copy
1338         nUserDropAction = dynamic_cast<const Qt5MimeData*>(pMimeData)
1339                               ? css::datatransfer::dnd::DNDConstants::ACTION_MOVE
1340                               : css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1341         nUserDropAction &= nSourceActions;
1342 
1343         // if the default doesn't match any allowed source action, fall back to the
1344         // preferred of all allowed source actions
1345         if (0 == nUserDropAction)
1346             nUserDropAction = toVclDropAction(getPreferredDropAction(nSourceActions));
1347 
1348         // this is "our" preference, but actually we would even prefer any default,
1349         // if there is any
1350         nUserDropAction |= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT;
1351     }
1352     return nUserDropAction;
1353 }
1354 
handleDragMove(QDragMoveEvent * pEvent)1355 void Qt5Frame::handleDragMove(QDragMoveEvent* pEvent)
1356 {
1357     assert(m_pDropTarget);
1358 
1359     // prepare our suggested drop action for the drop target
1360     const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1361     const QMimeData* pMimeData = pEvent->mimeData();
1362     const sal_Int8 nUserDropAction = lcl_getUserDropAction(pEvent, nSourceActions, pMimeData);
1363     const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1364 
1365     css::datatransfer::dnd::DropTargetDragEnterEvent aEvent;
1366     aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1367     aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDragContext*>(m_pDropTarget);
1368     aEvent.LocationX = aPos.X();
1369     aEvent.LocationY = aPos.Y();
1370     aEvent.DropAction = nUserDropAction;
1371     aEvent.SourceActions = nSourceActions;
1372 
1373     // ask the drop target to accept our drop action
1374     if (!m_bInDrag)
1375     {
1376         aEvent.SupportedDataFlavors = lcl_getXTransferable(pMimeData)->getTransferDataFlavors();
1377         m_pDropTarget->fire_dragEnter(aEvent);
1378         m_bInDrag = true;
1379     }
1380     else
1381         m_pDropTarget->fire_dragOver(aEvent);
1382 
1383     // the drop target accepted our drop action => inform Qt
1384     if (m_pDropTarget->proposedDropAction() != 0)
1385     {
1386         pEvent->setDropAction(getPreferredDropAction(m_pDropTarget->proposedDropAction()));
1387         pEvent->accept();
1388     }
1389     else // or maybe someone else likes it?
1390         pEvent->ignore();
1391 }
1392 
handleDrop(QDropEvent * pEvent)1393 void Qt5Frame::handleDrop(QDropEvent* pEvent)
1394 {
1395     assert(m_pDropTarget);
1396 
1397     // prepare our suggested drop action for the drop target
1398     const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1399     const sal_Int8 nUserDropAction
1400         = lcl_getUserDropAction(pEvent, nSourceActions, pEvent->mimeData());
1401     const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1402 
1403     css::datatransfer::dnd::DropTargetDropEvent aEvent;
1404     aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1405     aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDropContext*>(m_pDropTarget);
1406     aEvent.LocationX = aPos.X();
1407     aEvent.LocationY = aPos.Y();
1408     aEvent.SourceActions = nSourceActions;
1409     aEvent.DropAction = nUserDropAction;
1410     aEvent.Transferable = lcl_getXTransferable(pEvent->mimeData());
1411 
1412     // ask the drop target to accept our drop action
1413     m_pDropTarget->fire_drop(aEvent);
1414     m_bInDrag = false;
1415 
1416     const bool bDropSuccessful = m_pDropTarget->dropSuccessful();
1417     const sal_Int8 nDropAction = m_pDropTarget->proposedDropAction();
1418 
1419     // inform the drag source of the drag-origin frame of the drop result
1420     if (pEvent->source())
1421     {
1422         Qt5Widget* pWidget = dynamic_cast<Qt5Widget*>(pEvent->source());
1423         assert(pWidget); // AFAIK there shouldn't be any non-Qt5Widget as source in LO itself
1424         if (pWidget)
1425             pWidget->frame().m_pDragSource->fire_dragEnd(nDropAction, bDropSuccessful);
1426     }
1427 
1428     // the drop target accepted our drop action => inform Qt
1429     if (bDropSuccessful)
1430     {
1431         pEvent->setDropAction(getPreferredDropAction(nDropAction));
1432         pEvent->accept();
1433     }
1434     else // or maybe someone else likes it?
1435         pEvent->ignore();
1436 }
1437 
handleDragLeave()1438 void Qt5Frame::handleDragLeave()
1439 {
1440     css::datatransfer::dnd::DropTargetEvent aEvent;
1441     aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1442     m_pDropTarget->fire_dragExit(aEvent);
1443     m_bInDrag = false;
1444 }
1445 
1446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1447