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