nss: upgrade to release 3.73
[LibreOffice.git] / vcl / qt5 / Qt5Frame.cxx
blobd39d1729c812a83068881d41d4b83215569411ea
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <Qt5Frame.hxx>
21 #include <Qt5Frame.moc>
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>
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>
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
60 #include <saldatabasic.hxx>
61 #include <window.h>
62 #include <vcl/layout.hxx>
63 #include <vcl/syswin.hxx>
65 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
67 #include <cairo.h>
68 #include <headless/svpgdi.hxx>
70 #if QT5_USING_X11 && QT5_HAVE_XCB_ICCCM
71 static bool g_bNeedsWmHintsWindowGroup = true;
72 static xcb_atom_t g_aXcbClientLeaderAtom = 0;
73 #endif
75 static void SvpDamageHandler(void* handle, sal_Int32 nExtentsX, sal_Int32 nExtentsY,
76 sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight)
78 Qt5Frame* pThis = static_cast<Qt5Frame*>(handle);
79 pThis->Damage(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight);
82 namespace
84 sal_Int32 screenNumber(const QScreen* pScreen)
86 const QList<QScreen*> screens = QApplication::screens();
88 sal_Int32 nScreen = 0;
89 bool bFound = false;
90 for (const QScreen* pCurScreen : screens)
92 if (pScreen == pCurScreen)
94 bFound = true;
95 break;
97 nScreen++;
100 return bFound ? nScreen : -1;
104 Qt5Frame::Qt5Frame(Qt5Frame* pParent, SalFrameStyleFlags nStyle, bool bUseCairo)
105 : m_pTopLevel(nullptr)
106 , m_bUseCairo(bUseCairo)
107 , m_bNullRegion(true)
108 , m_bGraphicsInUse(false)
109 , m_ePointerStyle(PointerStyle::Arrow)
110 , m_pDragSource(nullptr)
111 , m_pDropTarget(nullptr)
112 , m_bInDrag(false)
113 , m_bDefaultSize(true)
114 , m_bDefaultPos(true)
115 , m_bFullScreen(false)
116 , m_bFullScreenSpanAll(false)
118 Qt5Instance* pInst = static_cast<Qt5Instance*>(GetSalData()->m_pInstance);
119 pInst->insertFrame(this);
121 m_aDamageHandler.handle = this;
122 m_aDamageHandler.damaged = ::SvpDamageHandler;
124 if (nStyle & SalFrameStyleFlags::DEFAULT) // ensure default style
126 nStyle |= SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE
127 | SalFrameStyleFlags::CLOSEABLE;
128 nStyle &= ~SalFrameStyleFlags::FLOAT;
131 m_nStyle = nStyle;
132 m_pParent = pParent;
134 Qt::WindowFlags aWinFlags;
135 if (!(nStyle & SalFrameStyleFlags::SYSTEMCHILD))
137 if (nStyle & SalFrameStyleFlags::INTRO)
138 aWinFlags |= Qt::SplashScreen;
139 // floating toolbars are frameless tool windows
140 // + they must be able to receive keyboard focus
141 else if ((nStyle & SalFrameStyleFlags::FLOAT)
142 && (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION))
143 aWinFlags |= Qt::Tool | Qt::FramelessWindowHint;
144 else if (nStyle & (SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::TOOLTIP))
145 aWinFlags |= Qt::ToolTip;
146 else if ((nStyle & SalFrameStyleFlags::FLOAT)
147 && !(nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION))
148 aWinFlags |= Qt::Popup;
149 else if (nStyle & SalFrameStyleFlags::TOOLWINDOW)
150 aWinFlags |= Qt::Tool;
151 // top level windows can't be transient in Qt, so make them dialogs, if they have a parent. At least
152 // the plasma shell relies on this setting to skip dialogs in the window list. And Qt Xcb will just
153 // set transient for the types Dialog, Sheet, Tool, SplashScreen, ToolTip, Drawer and Popup.
154 else if (nStyle & SalFrameStyleFlags::DIALOG || m_pParent)
155 aWinFlags |= Qt::Dialog;
156 else
157 aWinFlags |= Qt::Window;
160 if (aWinFlags == Qt::Window)
162 m_pTopLevel = new Qt5MainWindow(*this, aWinFlags);
163 m_pQWidget = new Qt5Widget(*this, aWinFlags);
164 m_pTopLevel->setCentralWidget(m_pQWidget);
165 m_pTopLevel->setFocusProxy(m_pQWidget);
167 else
168 m_pQWidget = new Qt5Widget(*this, aWinFlags);
170 if (pParent && !(pParent->m_nStyle & SalFrameStyleFlags::PLUG))
172 QWindow* pParentWindow = pParent->GetQWidget()->window()->windowHandle();
173 QWindow* pChildWindow = asChild()->window()->windowHandle();
174 if (pParentWindow && pChildWindow && (pParentWindow != pChildWindow))
175 pChildWindow->setTransientParent(pParentWindow);
178 // Calling 'QWidget::winId()' implicitly enables native windows to be used
179 // rather than "alien widgets" that are unknown to the windowing system,
180 // s. https://doc.qt.io/qt-5/qwidget.html#native-widgets-vs-alien-widgets
181 // Avoid this on Wayland due to problems with missing 'mouseMoveEvent's,
182 // s. tdf#122293/QTBUG-75766
183 const bool bWayland = QGuiApplication::platformName() == "wayland";
184 if (!bWayland)
185 m_aSystemData.SetWindowHandle(m_pQWidget->winId());
186 else
188 // TODO implement as needed for Wayland,
189 // s.a. commit c0d4f3ad3307c which did this for gtk3
190 // QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
191 // m_aSystemData.pDisplay = native->nativeResourceForWindow("display", nullptr);
192 // m_aSystemData.aWindow = reinterpret_cast<unsigned long>(
193 // native->nativeResourceForWindow("surface", m_pQWidget->windowHandle()));
196 m_aSystemData.aShellWindow = reinterpret_cast<sal_IntPtr>(this);
197 //m_aSystemData.pSalFrame = this;
198 m_aSystemData.pWidget = m_pQWidget;
199 //m_aSystemData.nScreen = m_nXScreen.getXScreen();
200 m_aSystemData.toolkit = SystemEnvData::Toolkit::Qt5;
201 if (!bWayland)
202 m_aSystemData.platform = SystemEnvData::Platform::Xcb;
203 else
204 m_aSystemData.platform = SystemEnvData::Platform::Wayland;
206 SetIcon(SV_ICON_ID_OFFICE);
208 fixICCCMwindowGroup();
211 void Qt5Frame::fixICCCMwindowGroup()
213 #if QT5_USING_X11 && QT5_HAVE_XCB_ICCCM
214 // older Qt5 just sets WM_CLIENT_LEADER, but not the XCB_ICCCM_WM_HINT_WINDOW_GROUP
215 // see Qt commit 0de4b326d8 ("xcb: fix issue with dialogs hidden by other windows")
216 // or QTBUG-46626. So LO has to set this itself to help some WMs.
217 if (!g_bNeedsWmHintsWindowGroup)
218 return;
219 g_bNeedsWmHintsWindowGroup = false;
221 if (QGuiApplication::platformName() != "xcb")
222 return;
223 if (QVersionNumber::fromString(qVersion()) >= QVersionNumber(5, 12))
224 return;
226 xcb_connection_t* conn = QX11Info::connection();
227 xcb_window_t win = asChild()->winId();
229 xcb_icccm_wm_hints_t hints;
231 xcb_get_property_cookie_t prop_cookie = xcb_icccm_get_wm_hints_unchecked(conn, win);
232 if (!xcb_icccm_get_wm_hints_reply(conn, prop_cookie, &hints, nullptr))
233 return;
235 if (hints.flags & XCB_ICCCM_WM_HINT_WINDOW_GROUP)
236 return;
238 if (g_aXcbClientLeaderAtom == 0)
240 const char* const leader_name = "WM_CLIENT_LEADER\0";
241 xcb_intern_atom_cookie_t atom_cookie
242 = xcb_intern_atom(conn, 1, strlen(leader_name), leader_name);
243 xcb_intern_atom_reply_t* atom_reply = xcb_intern_atom_reply(conn, atom_cookie, nullptr);
244 if (!atom_reply)
245 return;
246 g_aXcbClientLeaderAtom = atom_reply->atom;
247 free(atom_reply);
250 g_bNeedsWmHintsWindowGroup = true;
252 prop_cookie = xcb_get_property(conn, 0, win, g_aXcbClientLeaderAtom, XCB_ATOM_WINDOW, 0, 1);
253 xcb_get_property_reply_t* prop_reply = xcb_get_property_reply(conn, prop_cookie, nullptr);
254 if (!prop_reply)
255 return;
257 if (xcb_get_property_value_length(prop_reply) != 4)
259 free(prop_reply);
260 return;
263 xcb_window_t leader = *static_cast<xcb_window_t*>(xcb_get_property_value(prop_reply));
264 free(prop_reply);
266 hints.flags |= XCB_ICCCM_WM_HINT_WINDOW_GROUP;
267 hints.window_group = leader;
268 xcb_icccm_set_wm_hints(conn, win, &hints);
269 #else
270 (void)this; // avoid loplugin:staticmethods
271 #endif
274 Qt5Frame::~Qt5Frame()
276 Qt5Instance* pInst = static_cast<Qt5Instance*>(GetSalData()->m_pInstance);
277 pInst->eraseFrame(this);
278 delete asChild();
279 m_aSystemData.aShellWindow = 0;
282 void Qt5Frame::Damage(sal_Int32 nExtentsX, sal_Int32 nExtentsY, sal_Int32 nExtentsWidth,
283 sal_Int32 nExtentsHeight) const
285 m_pQWidget->update(scaledQRect(QRect(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight),
286 1 / devicePixelRatioF()));
289 SalGraphics* Qt5Frame::AcquireGraphics()
291 if (m_bGraphicsInUse)
292 return nullptr;
294 m_bGraphicsInUse = true;
296 if (m_bUseCairo)
298 if (!m_pSvpGraphics)
300 QSize aSize = m_pQWidget->size() * devicePixelRatioF();
301 m_pSvpGraphics.reset(new Qt5SvpGraphics(this));
302 m_pSurface.reset(
303 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, aSize.width(), aSize.height()));
304 m_pSvpGraphics->setSurface(m_pSurface.get(),
305 basegfx::B2IVector(aSize.width(), aSize.height()));
306 cairo_surface_set_user_data(m_pSurface.get(), Qt5SvpGraphics::getDamageKey(),
307 &m_aDamageHandler, nullptr);
309 return m_pSvpGraphics.get();
311 else
313 if (!m_pQt5Graphics)
315 m_pQt5Graphics.reset(new Qt5Graphics(this));
316 m_pQImage.reset(
317 new QImage(m_pQWidget->size() * devicePixelRatioF(), Qt5_DefaultFormat32));
318 m_pQImage->fill(Qt::transparent);
319 m_pQt5Graphics->ChangeQImage(m_pQImage.get());
321 return m_pQt5Graphics.get();
325 void Qt5Frame::ReleaseGraphics(SalGraphics* pSalGraph)
327 (void)pSalGraph;
328 if (m_bUseCairo)
329 assert(pSalGraph == m_pSvpGraphics.get());
330 else
331 assert(pSalGraph == m_pQt5Graphics.get());
332 m_bGraphicsInUse = false;
335 bool Qt5Frame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
337 Qt5Instance* pInst = static_cast<Qt5Instance*>(GetSalData()->m_pInstance);
338 pInst->PostEvent(this, pData.release(), SalEvent::UserEvent);
339 return true;
342 QWidget* Qt5Frame::asChild() const { return m_pTopLevel ? m_pTopLevel : m_pQWidget; }
344 qreal Qt5Frame::devicePixelRatioF() const { return asChild()->devicePixelRatioF(); }
346 bool Qt5Frame::isWindow() const { return asChild()->isWindow(); }
348 QWindow* Qt5Frame::windowHandle() const
350 // set attribute 'Qt::WA_NativeWindow' first to make sure a window handle actually exists
351 QWidget* pChild = asChild();
352 pChild->setAttribute(Qt::WA_NativeWindow);
353 return pChild->windowHandle();
356 QScreen* Qt5Frame::screen() const
358 QWindow* const pWindow = windowHandle();
359 return pWindow ? pWindow->screen() : nullptr;
362 bool Qt5Frame::isMinimized() const { return asChild()->isMinimized(); }
364 bool Qt5Frame::isMaximized() const { return asChild()->isMaximized(); }
366 void Qt5Frame::SetWindowStateImpl(Qt::WindowStates eState)
368 return asChild()->setWindowState(eState);
371 void Qt5Frame::SetTitle(const OUString& rTitle)
373 m_pQWidget->window()->setWindowTitle(toQString(rTitle));
376 void Qt5Frame::SetIcon(sal_uInt16 nIcon)
378 if (m_nStyle
379 & (SalFrameStyleFlags::PLUG | SalFrameStyleFlags::SYSTEMCHILD
380 | SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::INTRO
381 | SalFrameStyleFlags::OWNERDRAWDECORATION)
382 || !isWindow())
383 return;
385 QString appicon;
387 if (nIcon == SV_ICON_ID_TEXT)
388 appicon = "libreoffice-writer";
389 else if (nIcon == SV_ICON_ID_SPREADSHEET)
390 appicon = "libreoffice-calc";
391 else if (nIcon == SV_ICON_ID_DRAWING)
392 appicon = "libreoffice-draw";
393 else if (nIcon == SV_ICON_ID_PRESENTATION)
394 appicon = "libreoffice-impress";
395 else if (nIcon == SV_ICON_ID_DATABASE)
396 appicon = "libreoffice-base";
397 else if (nIcon == SV_ICON_ID_FORMULA)
398 appicon = "libreoffice-math";
399 else
400 appicon = "libreoffice-startcenter";
402 QIcon aIcon = QIcon::fromTheme(appicon);
403 m_pQWidget->window()->setWindowIcon(aIcon);
406 void Qt5Frame::SetMenu(SalMenu*) {}
408 void Qt5Frame::DrawMenuBar() { /* not needed */}
410 void Qt5Frame::SetExtendedFrameStyle(SalExtStyle /*nExtStyle*/) { /* not needed */}
412 void Qt5Frame::Show(bool bVisible, bool /*bNoActivate*/)
414 assert(m_pQWidget);
416 SetDefaultSize();
417 SetDefaultPos();
419 auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
420 assert(pSalInst);
421 pSalInst->RunInMainThread([this, bVisible]() { asChild()->setVisible(bVisible); });
424 void Qt5Frame::SetMinClientSize(tools::Long nWidth, tools::Long nHeight)
426 if (!isChild())
428 const qreal fRatio = devicePixelRatioF();
429 asChild()->setMinimumSize(round(nWidth / fRatio), round(nHeight / fRatio));
433 void Qt5Frame::SetMaxClientSize(tools::Long nWidth, tools::Long nHeight)
435 if (!isChild())
437 const qreal fRatio = devicePixelRatioF();
438 asChild()->setMaximumSize(round(nWidth / fRatio), round(nHeight / fRatio));
442 void Qt5Frame::SetDefaultPos()
444 if (!m_bDefaultPos)
445 return;
447 // center on parent
448 if (m_pParent)
450 const qreal fRatio = devicePixelRatioF();
451 QWidget* const pWindow = m_pParent->GetQWidget()->window();
452 QWidget* const pWidget = asChild();
453 QPoint aPos = pWindow->rect().center() - pWidget->rect().center();
454 SetPosSize(round(aPos.x() * fRatio), round(aPos.y() * fRatio), 0, 0,
455 SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y);
456 assert(!m_bDefaultPos);
458 else
459 m_bDefaultPos = false;
462 Size Qt5Frame::CalcDefaultSize()
464 assert(isWindow());
466 Size aSize;
467 if (!m_bFullScreen)
469 const QScreen* pScreen = screen();
470 SAL_WNODEPRECATED_DECLARATIONS_PUSH
471 aSize = bestmaxFrameSizeForScreenSize(
472 toSize(pScreen ? pScreen->size() : QApplication::desktop()->screenGeometry(0).size()));
473 SAL_WNODEPRECATED_DECLARATIONS_POP
475 else
477 if (!m_bFullScreenSpanAll)
479 SAL_WNODEPRECATED_DECLARATIONS_PUSH
480 aSize = toSize(
481 QApplication::desktop()->screenGeometry(maGeometry.nDisplayScreenNumber).size());
482 SAL_WNODEPRECATED_DECLARATIONS_POP
484 else
486 SAL_WNODEPRECATED_DECLARATIONS_PUSH
487 int nLeftScreen = QApplication::desktop()->screenNumber(QPoint(0, 0));
488 SAL_WNODEPRECATED_DECLARATIONS_POP
489 aSize = toSize(QApplication::screens()[nLeftScreen]->availableVirtualGeometry().size());
493 return aSize;
496 void Qt5Frame::SetDefaultSize()
498 if (!m_bDefaultSize)
499 return;
501 Size aDefSize = CalcDefaultSize();
502 SetPosSize(0, 0, aDefSize.Width(), aDefSize.Height(),
503 SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT);
504 assert(!m_bDefaultSize);
507 void Qt5Frame::SetPosSize(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
508 sal_uInt16 nFlags)
510 if (!isWindow() || isChild(true, false))
511 return;
513 if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
515 if (isChild(false) || !m_pQWidget->isMaximized())
517 if (!(nFlags & SAL_FRAME_POSSIZE_WIDTH))
518 nWidth = maGeometry.nWidth;
519 else if (!(nFlags & SAL_FRAME_POSSIZE_HEIGHT))
520 nHeight = maGeometry.nHeight;
522 if (nWidth > 0 && nHeight > 0)
524 m_bDefaultSize = false;
525 const int nNewWidth = round(nWidth / devicePixelRatioF());
526 const int nNewHeight = round(nHeight / devicePixelRatioF());
527 if (m_nStyle & SalFrameStyleFlags::SIZEABLE)
528 asChild()->resize(nNewWidth, nNewHeight);
529 else
530 asChild()->setFixedSize(nNewWidth, nNewHeight);
533 // assume the resize happened
534 // needed for calculations and will eventually be corrected by events
535 if (nWidth > 0)
536 maGeometry.nWidth = nWidth;
537 if (nHeight > 0)
538 maGeometry.nHeight = nHeight;
542 if (!(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)))
543 return;
545 if (m_pParent)
547 const SalFrameGeometry& aParentGeometry = m_pParent->maGeometry;
548 if (QGuiApplication::isRightToLeft())
549 nX = aParentGeometry.nX + aParentGeometry.nWidth - nX - maGeometry.nWidth - 1;
550 else
551 nX += aParentGeometry.nX;
552 nY += aParentGeometry.nY;
554 Qt5MainWindow* pTopLevel = m_pParent->GetTopLevelWindow();
555 if (pTopLevel && pTopLevel->menuBar() && pTopLevel->menuBar()->isVisible())
556 nY += round(pTopLevel->menuBar()->geometry().height() * devicePixelRatioF());
559 if (!(nFlags & SAL_FRAME_POSSIZE_X))
560 nX = maGeometry.nX;
561 else if (!(nFlags & SAL_FRAME_POSSIZE_Y))
562 nY = maGeometry.nY;
564 // assume the reposition happened
565 // needed for calculations and will eventually be corrected by events later
566 maGeometry.nX = nX;
567 maGeometry.nY = nY;
569 m_bDefaultPos = false;
570 asChild()->move(round(nX / devicePixelRatioF()), round(nY / devicePixelRatioF()));
573 void Qt5Frame::GetClientSize(tools::Long& rWidth, tools::Long& rHeight)
575 rWidth = round(m_pQWidget->width() * devicePixelRatioF());
576 rHeight = round(m_pQWidget->height() * devicePixelRatioF());
579 void Qt5Frame::GetWorkArea(tools::Rectangle& rRect)
581 if (!isWindow())
582 return;
583 QScreen* pScreen = screen();
584 if (!pScreen)
585 return;
587 QSize aSize = pScreen->availableVirtualSize() * devicePixelRatioF();
588 rRect = tools::Rectangle(0, 0, aSize.width(), aSize.height());
591 SalFrame* Qt5Frame::GetParent() const { return m_pParent; }
593 void Qt5Frame::SetModal(bool bModal)
595 if (!isWindow())
596 return;
598 auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
599 assert(pSalInst);
600 pSalInst->RunInMainThread([this, bModal]() {
602 QWidget* const pChild = asChild();
603 const bool bWasVisible = pChild->isVisible();
605 // modality change is only effective if the window is hidden
606 if (bWasVisible)
607 pChild->hide();
609 pChild->setWindowModality(bModal ? Qt::WindowModal : Qt::NonModal);
611 if (bWasVisible)
612 pChild->show();
616 bool Qt5Frame::GetModal() const { return isWindow() && windowHandle()->isModal(); }
618 void Qt5Frame::SetWindowState(const SalFrameState* pState)
620 if (!isWindow() || !pState || isChild(true, false))
621 return;
623 const WindowStateMask nMaxGeometryMask
624 = WindowStateMask::X | WindowStateMask::Y | WindowStateMask::Width | WindowStateMask::Height
625 | WindowStateMask::MaximizedX | WindowStateMask::MaximizedY
626 | WindowStateMask::MaximizedWidth | WindowStateMask::MaximizedHeight;
628 if ((pState->mnMask & WindowStateMask::State) && (pState->mnState & WindowStateState::Maximized)
629 && !isMaximized() && (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask)
631 const qreal fRatio = devicePixelRatioF();
632 QWidget* const pChild = asChild();
633 pChild->resize(ceil(pState->mnWidth / fRatio), ceil(pState->mnHeight / fRatio));
634 pChild->move(ceil(pState->mnX / fRatio), ceil(pState->mnY / fRatio));
635 SetWindowStateImpl(Qt::WindowMaximized);
637 else if (pState->mnMask
638 & (WindowStateMask::X | WindowStateMask::Y | WindowStateMask::Width
639 | WindowStateMask::Height))
641 sal_uInt16 nPosSizeFlags = 0;
642 if (pState->mnMask & WindowStateMask::X)
643 nPosSizeFlags |= SAL_FRAME_POSSIZE_X;
644 if (pState->mnMask & WindowStateMask::Y)
645 nPosSizeFlags |= SAL_FRAME_POSSIZE_Y;
646 if (pState->mnMask & WindowStateMask::Width)
647 nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH;
648 if (pState->mnMask & WindowStateMask::Height)
649 nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT;
650 SetPosSize(pState->mnX, pState->mnY, pState->mnWidth, pState->mnHeight, nPosSizeFlags);
652 else if (pState->mnMask & WindowStateMask::State && !isChild())
654 if (pState->mnState & WindowStateState::Maximized)
655 SetWindowStateImpl(Qt::WindowMaximized);
656 else if (pState->mnState & WindowStateState::Minimized)
657 SetWindowStateImpl(Qt::WindowMinimized);
658 else
659 SetWindowStateImpl(Qt::WindowNoState);
663 bool Qt5Frame::GetWindowState(SalFrameState* pState)
665 pState->mnState = WindowStateState::Normal;
666 pState->mnMask = WindowStateMask::State;
667 if (isMinimized() /*|| !windowHandle()*/)
668 pState->mnState |= WindowStateState::Minimized;
669 else if (isMaximized())
671 pState->mnState |= WindowStateState::Maximized;
673 else
675 // geometry() is the drawable area, which is wanted here
676 QRect rect = scaledQRect(asChild()->geometry(), devicePixelRatioF());
677 pState->mnX = rect.x();
678 pState->mnY = rect.y();
679 pState->mnWidth = rect.width();
680 pState->mnHeight = rect.height();
681 pState->mnMask |= WindowStateMask::X | WindowStateMask::Y | WindowStateMask::Width
682 | WindowStateMask::Height;
685 return true;
688 void Qt5Frame::ShowFullScreen(bool bFullScreen, sal_Int32 nScreen)
690 // only top-level windows can go fullscreen
691 assert(m_pTopLevel);
693 if (m_bFullScreen == bFullScreen)
694 return;
696 m_bFullScreen = bFullScreen;
697 m_bFullScreenSpanAll = m_bFullScreen && (nScreen < 0);
699 // show it if it isn't shown yet
700 if (!isWindow())
701 m_pTopLevel->show();
703 if (m_bFullScreen)
705 m_aRestoreGeometry = m_pTopLevel->geometry();
706 m_nRestoreScreen = maGeometry.nDisplayScreenNumber;
707 SetScreenNumber(m_bFullScreenSpanAll ? m_nRestoreScreen : nScreen);
708 if (!m_bFullScreenSpanAll)
709 windowHandle()->showFullScreen();
710 else
711 windowHandle()->showNormal();
713 else
715 SetScreenNumber(m_nRestoreScreen);
716 windowHandle()->showNormal();
717 m_pTopLevel->setGeometry(m_aRestoreGeometry);
721 void Qt5Frame::StartPresentation(bool bStart)
723 // meh - so there's no Qt platform independent solution
724 // https://forum.qt.io/topic/38504/solved-qdialog-in-fullscreen-disable-os-screensaver
725 #if QT5_USING_X11
726 std::optional<unsigned int> aRootWindow;
727 std::optional<Display*> aDisplay;
729 if (QX11Info::isPlatformX11())
731 aRootWindow = QX11Info::appRootWindow();
732 aDisplay = QX11Info::display();
735 m_ScreenSaverInhibitor.inhibit(bStart, "presentation", QX11Info::isPlatformX11(), aRootWindow,
736 aDisplay);
737 #else
738 (void)bStart;
739 #endif
742 void Qt5Frame::SetAlwaysOnTop(bool bOnTop)
744 QWidget* const pWidget = asChild();
745 const Qt::WindowFlags flags = pWidget->windowFlags();
746 if (bOnTop)
747 pWidget->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
748 else
749 pWidget->setWindowFlags(flags & ~(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
752 void Qt5Frame::ToTop(SalFrameToTop nFlags)
754 QWidget* const pWidget = asChild();
755 if (isWindow() && !(nFlags & SalFrameToTop::GrabFocusOnly))
756 pWidget->raise();
757 if ((nFlags & SalFrameToTop::RestoreWhenMin) || (nFlags & SalFrameToTop::ForegroundTask))
758 pWidget->activateWindow();
759 else if ((nFlags & SalFrameToTop::GrabFocus) || (nFlags & SalFrameToTop::GrabFocusOnly))
761 pWidget->activateWindow();
762 pWidget->setFocus();
766 void Qt5Frame::SetPointer(PointerStyle ePointerStyle)
768 QWindow* pWindow = m_pQWidget->window()->windowHandle();
769 if (!pWindow)
770 return;
771 if (ePointerStyle == m_ePointerStyle)
772 return;
773 m_ePointerStyle = ePointerStyle;
775 pWindow->setCursor(static_cast<Qt5Data*>(GetSalData())->getCursor(ePointerStyle));
778 void Qt5Frame::CaptureMouse(bool bMouse)
780 static const char* pEnv = getenv("SAL_NO_MOUSEGRABS");
781 if (pEnv && *pEnv)
782 return;
784 if (bMouse)
785 m_pQWidget->grabMouse();
786 else
787 m_pQWidget->releaseMouse();
790 void Qt5Frame::SetPointerPos(tools::Long nX, tools::Long nY)
792 // some cursor already exists (and it has m_ePointerStyle shape)
793 // so here we just reposition it
794 QCursor::setPos(m_pQWidget->mapToGlobal(QPoint(nX, nY)));
797 void Qt5Frame::Flush()
799 // was: QGuiApplication::sync();
800 // but FIXME it causes too many issues, figure out sth better
802 // unclear if we need to also flush cairo surface - gtk3 backend
803 // does not do it. QPainter in Qt5Widget::paintEvent() is
804 // destroyed, so that state should be safely flushed.
807 bool Qt5Frame::ShowTooltip(const OUString& rText, const tools::Rectangle& rHelpArea)
809 QRect aHelpArea(toQRect(rHelpArea));
810 if (QGuiApplication::isRightToLeft())
811 aHelpArea.moveLeft(maGeometry.nWidth - aHelpArea.width() - aHelpArea.left() - 1);
812 QToolTip::showText(QCursor::pos(), toQString(rText), m_pQWidget, aHelpArea);
813 return true;
816 void Qt5Frame::SetInputContext(SalInputContext* pContext)
818 if (!pContext)
819 return;
821 if (!(pContext->mnOptions & InputContextFlags::Text))
822 return;
824 m_pQWidget->setAttribute(Qt::WA_InputMethodEnabled);
827 void Qt5Frame::EndExtTextInput(EndExtTextInputFlags /*nFlags*/)
829 Qt5Widget* pQt5Widget = static_cast<Qt5Widget*>(m_pQWidget);
830 if (pQt5Widget)
831 pQt5Widget->endExtTextInput();
834 OUString Qt5Frame::GetKeyName(sal_uInt16 nKeyCode)
836 vcl::KeyCode vclKeyCode(nKeyCode);
837 int nCode = vclKeyCode.GetCode();
838 int nRetCode = 0;
840 if (nCode >= KEY_0 && nCode <= KEY_9)
841 nRetCode = (nCode - KEY_0) + Qt::Key_0;
842 else if (nCode >= KEY_A && nCode <= KEY_Z)
843 nRetCode = (nCode - KEY_A) + Qt::Key_A;
844 else if (nCode >= KEY_F1 && nCode <= KEY_F26)
845 nRetCode = (nCode - KEY_F1) + Qt::Key_F1;
846 else
848 switch (nCode)
850 case KEY_DOWN:
851 nRetCode = Qt::Key_Down;
852 break;
853 case KEY_UP:
854 nRetCode = Qt::Key_Up;
855 break;
856 case KEY_LEFT:
857 nRetCode = Qt::Key_Left;
858 break;
859 case KEY_RIGHT:
860 nRetCode = Qt::Key_Right;
861 break;
862 case KEY_HOME:
863 nRetCode = Qt::Key_Home;
864 break;
865 case KEY_END:
866 nRetCode = Qt::Key_End;
867 break;
868 case KEY_PAGEUP:
869 nRetCode = Qt::Key_PageUp;
870 break;
871 case KEY_PAGEDOWN:
872 nRetCode = Qt::Key_PageDown;
873 break;
874 case KEY_RETURN:
875 nRetCode = Qt::Key_Return;
876 break;
877 case KEY_ESCAPE:
878 nRetCode = Qt::Key_Escape;
879 break;
880 case KEY_TAB:
881 nRetCode = Qt::Key_Tab;
882 break;
883 case KEY_BACKSPACE:
884 nRetCode = Qt::Key_Backspace;
885 break;
886 case KEY_SPACE:
887 nRetCode = Qt::Key_Space;
888 break;
889 case KEY_INSERT:
890 nRetCode = Qt::Key_Insert;
891 break;
892 case KEY_DELETE:
893 nRetCode = Qt::Key_Delete;
894 break;
895 case KEY_ADD:
896 nRetCode = Qt::Key_Plus;
897 break;
898 case KEY_SUBTRACT:
899 nRetCode = Qt::Key_Minus;
900 break;
901 case KEY_MULTIPLY:
902 nRetCode = Qt::Key_Asterisk;
903 break;
904 case KEY_DIVIDE:
905 nRetCode = Qt::Key_Slash;
906 break;
907 case KEY_POINT:
908 nRetCode = Qt::Key_Period;
909 break;
910 case KEY_COMMA:
911 nRetCode = Qt::Key_Comma;
912 break;
913 case KEY_LESS:
914 nRetCode = Qt::Key_Less;
915 break;
916 case KEY_GREATER:
917 nRetCode = Qt::Key_Greater;
918 break;
919 case KEY_EQUAL:
920 nRetCode = Qt::Key_Equal;
921 break;
922 case KEY_FIND:
923 nRetCode = Qt::Key_Find;
924 break;
925 case KEY_CONTEXTMENU:
926 nRetCode = Qt::Key_Menu;
927 break;
928 case KEY_HELP:
929 nRetCode = Qt::Key_Help;
930 break;
931 case KEY_UNDO:
932 nRetCode = Qt::Key_Undo;
933 break;
934 case KEY_REPEAT:
935 nRetCode = Qt::Key_Redo;
936 break;
937 case KEY_TILDE:
938 nRetCode = Qt::Key_AsciiTilde;
939 break;
940 case KEY_QUOTELEFT:
941 nRetCode = Qt::Key_QuoteLeft;
942 break;
943 case KEY_BRACKETLEFT:
944 nRetCode = Qt::Key_BracketLeft;
945 break;
946 case KEY_BRACKETRIGHT:
947 nRetCode = Qt::Key_BracketRight;
948 break;
949 case KEY_SEMICOLON:
950 nRetCode = Qt::Key_Semicolon;
951 break;
953 // Special cases
954 case KEY_COPY:
955 nRetCode = Qt::Key_Copy;
956 break;
957 case KEY_CUT:
958 nRetCode = Qt::Key_Cut;
959 break;
960 case KEY_PASTE:
961 nRetCode = Qt::Key_Paste;
962 break;
963 case KEY_OPEN:
964 nRetCode = Qt::Key_Open;
965 break;
969 if (vclKeyCode.IsShift())
970 nRetCode += Qt::SHIFT;
971 if (vclKeyCode.IsMod1())
972 nRetCode += Qt::CTRL;
973 if (vclKeyCode.IsMod2())
974 nRetCode += Qt::ALT;
976 QKeySequence keySeq(nRetCode);
977 OUString sKeyName = toOUString(keySeq.toString());
979 return sKeyName;
982 bool Qt5Frame::MapUnicodeToKeyCode(sal_Unicode /*aUnicode*/, LanguageType /*aLangType*/,
983 vcl::KeyCode& /*rKeyCode*/)
985 // not supported yet
986 return false;
989 LanguageType Qt5Frame::GetInputLanguage()
991 // fallback
992 return LANGUAGE_DONTKNOW;
995 static Color toColor(const QColor& rColor)
997 return Color(rColor.red(), rColor.green(), rColor.blue());
1000 void Qt5Frame::UpdateSettings(AllSettings& rSettings)
1002 if (Qt5Data::noNativeControls())
1003 return;
1005 StyleSettings style(rSettings.GetStyleSettings());
1007 // General settings
1008 QPalette pal = QApplication::palette();
1010 style.SetToolbarIconSize(ToolbarIconSize::Large);
1012 Color aFore = toColor(pal.color(QPalette::Active, QPalette::WindowText));
1013 Color aBack = toColor(pal.color(QPalette::Active, QPalette::Window));
1014 Color aText = toColor(pal.color(QPalette::Active, QPalette::Text));
1015 Color aBase = toColor(pal.color(QPalette::Active, QPalette::Base));
1016 Color aButn = toColor(pal.color(QPalette::Active, QPalette::ButtonText));
1017 Color aMid = toColor(pal.color(QPalette::Active, QPalette::Mid));
1018 Color aHigh = toColor(pal.color(QPalette::Active, QPalette::Highlight));
1019 Color aHighText = toColor(pal.color(QPalette::Active, QPalette::HighlightedText));
1020 Color aLink = toColor(pal.color(QPalette::Active, QPalette::Link));
1021 Color aVisitedLink = toColor(pal.color(QPalette::Active, QPalette::LinkVisited));
1023 style.SetSkipDisabledInMenus(true);
1025 // Foreground
1026 style.SetRadioCheckTextColor(aFore);
1027 style.SetLabelTextColor(aFore);
1028 style.SetDialogTextColor(aFore);
1029 style.SetGroupTextColor(aFore);
1031 // Text
1032 style.SetFieldTextColor(aText);
1033 style.SetFieldRolloverTextColor(aText);
1034 style.SetWindowTextColor(aText);
1035 style.SetToolTextColor(aText);
1037 // Base
1038 style.SetFieldColor(aBase);
1039 style.SetWindowColor(aBase);
1040 style.SetActiveTabColor(aBase);
1041 style.SetAlternatingRowColor(toColor(pal.color(QPalette::Active, QPalette::AlternateBase)));
1043 // Buttons
1044 style.SetDefaultButtonTextColor(aButn);
1045 style.SetButtonTextColor(aButn);
1046 style.SetDefaultActionButtonTextColor(aButn);
1047 style.SetActionButtonTextColor(aButn);
1048 style.SetFlatButtonTextColor(aButn);
1049 style.SetDefaultButtonRolloverTextColor(aButn);
1050 style.SetButtonRolloverTextColor(aButn);
1051 style.SetDefaultActionButtonRolloverTextColor(aButn);
1052 style.SetActionButtonRolloverTextColor(aButn);
1053 style.SetFlatButtonRolloverTextColor(aButn);
1054 style.SetDefaultButtonPressedRolloverTextColor(aButn);
1055 style.SetButtonPressedRolloverTextColor(aButn);
1056 style.SetDefaultActionButtonPressedRolloverTextColor(aButn);
1057 style.SetActionButtonPressedRolloverTextColor(aButn);
1058 style.SetFlatButtonPressedRolloverTextColor(aButn);
1060 // Tabs
1061 style.SetTabTextColor(aButn);
1062 style.SetTabRolloverTextColor(aButn);
1063 style.SetTabHighlightTextColor(aButn);
1065 // Disable color
1066 style.SetDisableColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1068 // Background
1069 style.BatchSetBackgrounds(aBack);
1070 style.SetInactiveTabColor(aBack);
1072 // Workspace
1073 style.SetWorkspaceColor(aMid);
1075 // Selection
1076 style.SetHighlightColor(aHigh);
1077 style.SetHighlightTextColor(aHighText);
1078 style.SetActiveColor(aHigh);
1079 style.SetActiveTextColor(aHighText);
1081 // Links
1082 style.SetLinkColor(aLink);
1083 style.SetVisitedLinkColor(aVisitedLink);
1085 // Tooltip
1086 style.SetHelpColor(toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipBase)));
1087 style.SetHelpTextColor(
1088 toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipText)));
1090 const int flash_time = QApplication::cursorFlashTime();
1091 style.SetCursorBlinkTime(flash_time != 0 ? flash_time / 2 : STYLE_CURSOR_NOBLINKTIME);
1093 // Menu
1094 std::unique_ptr<QMenuBar> pMenuBar = std::make_unique<QMenuBar>();
1095 QPalette qMenuCG = pMenuBar->palette();
1097 // Menu text and background color, theme specific
1098 Color aMenuFore = toColor(qMenuCG.color(QPalette::WindowText));
1099 Color aMenuBack = toColor(qMenuCG.color(QPalette::Window));
1101 style.SetMenuTextColor(aMenuFore);
1102 style.SetMenuBarTextColor(style.GetPersonaMenuBarTextColor().value_or(aMenuFore));
1103 style.SetMenuColor(aMenuBack);
1104 style.SetMenuBarColor(aMenuBack);
1105 style.SetMenuHighlightColor(toColor(qMenuCG.color(QPalette::Highlight)));
1106 style.SetMenuHighlightTextColor(toColor(qMenuCG.color(QPalette::HighlightedText)));
1108 // set special menubar highlight text color
1109 if (QApplication::style()->inherits("HighContrastStyle"))
1110 ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor
1111 = toColor(qMenuCG.color(QPalette::HighlightedText));
1112 else
1113 ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor = aMenuFore;
1115 // set menubar rollover color
1116 if (pMenuBar->style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
1118 style.SetMenuBarRolloverColor(toColor(qMenuCG.color(QPalette::Highlight)));
1119 style.SetMenuBarRolloverTextColor(ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor);
1121 else
1123 style.SetMenuBarRolloverColor(aMenuBack);
1124 style.SetMenuBarRolloverTextColor(aMenuFore);
1126 style.SetMenuBarHighlightTextColor(style.GetMenuHighlightTextColor());
1128 // Icon theme
1129 style.SetPreferredIconTheme(toOUString(QIcon::themeName()));
1131 // Scroll bar size
1132 style.SetScrollBarSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent));
1133 style.SetMinThumbSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarSliderMin));
1135 // These colors are used for the ruler text and marks
1136 style.SetShadowColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1137 style.SetDarkShadowColor(toColor(pal.color(QPalette::Inactive, QPalette::WindowText)));
1139 rSettings.SetStyleSettings(style);
1142 void Qt5Frame::Beep() { QApplication::beep(); }
1144 SalFrame::SalPointerState Qt5Frame::GetPointerState()
1146 SalPointerState aState;
1147 aState.maPos = toPoint(QCursor::pos() * devicePixelRatioF());
1148 aState.maPos.Move(-maGeometry.nX, -maGeometry.nY);
1149 aState.mnState = GetMouseModCode(QGuiApplication::mouseButtons())
1150 | GetKeyModCode(QGuiApplication::keyboardModifiers());
1151 return aState;
1154 KeyIndicatorState Qt5Frame::GetIndicatorState() { return KeyIndicatorState(); }
1156 void Qt5Frame::SimulateKeyPress(sal_uInt16 nKeyCode)
1158 SAL_WARN("vcl.qt5", "missing simulate keypress " << nKeyCode);
1161 void Qt5Frame::SetParent(SalFrame* pNewParent) { m_pParent = static_cast<Qt5Frame*>(pNewParent); }
1163 bool Qt5Frame::SetPluginParent(SystemParentData* /*pNewParent*/)
1165 //FIXME: no SetPluginParent impl. for qt5
1166 return false;
1169 void Qt5Frame::ResetClipRegion() { m_bNullRegion = true; }
1171 void Qt5Frame::BeginSetClipRegion(sal_uInt32)
1173 m_aRegion = QRegion(QRect(QPoint(0, 0), m_pQWidget->size()));
1176 void Qt5Frame::UnionClipRegion(tools::Long nX, tools::Long nY, tools::Long nWidth,
1177 tools::Long nHeight)
1179 m_aRegion
1180 = m_aRegion.united(scaledQRect(QRect(nX, nY, nWidth, nHeight), 1 / devicePixelRatioF()));
1183 void Qt5Frame::EndSetClipRegion() { m_bNullRegion = false; }
1185 void Qt5Frame::SetScreenNumber(unsigned int nScreen)
1187 if (!isWindow())
1188 return;
1190 QWindow* const pWindow = windowHandle();
1191 if (!pWindow)
1192 return;
1194 QList<QScreen*> screens = QApplication::screens();
1195 if (static_cast<int>(nScreen) < screens.size() || m_bFullScreenSpanAll)
1197 QRect screenGeo;
1199 if (!m_bFullScreenSpanAll)
1201 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1202 screenGeo = QApplication::desktop()->screenGeometry(nScreen);
1203 SAL_WNODEPRECATED_DECLARATIONS_POP
1204 pWindow->setScreen(QApplication::screens()[nScreen]);
1206 else // special case: fullscreen over all available screens
1208 assert(m_bFullScreen);
1209 // left-most screen
1210 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1211 int nLeftScreen = QApplication::desktop()->screenNumber(QPoint(0, 0));
1212 SAL_WNODEPRECATED_DECLARATIONS_POP
1213 // entire virtual desktop
1214 screenGeo = QApplication::screens()[nLeftScreen]->availableVirtualGeometry();
1215 pWindow->setScreen(QApplication::screens()[nLeftScreen]);
1216 pWindow->setGeometry(screenGeo);
1217 nScreen = nLeftScreen;
1220 // setScreen by itself has no effect, explicitly move the widget to
1221 // the new screen
1222 asChild()->move(screenGeo.topLeft());
1224 else
1226 // index outta bounds, use primary screen
1227 QScreen* primaryScreen = QApplication::primaryScreen();
1228 pWindow->setScreen(primaryScreen);
1229 nScreen = static_cast<sal_uInt32>(screenNumber(primaryScreen));
1232 maGeometry.nDisplayScreenNumber = nScreen;
1235 void Qt5Frame::SetApplicationID(const OUString& rWMClass)
1237 #if QT5_USING_X11
1238 if (QGuiApplication::platformName() != "xcb" || !m_pTopLevel)
1239 return;
1241 OString aResClass = OUStringToOString(rWMClass, RTL_TEXTENCODING_ASCII_US);
1242 const char* pResClass
1243 = !aResClass.isEmpty() ? aResClass.getStr() : SalGenericSystem::getFrameClassName();
1244 OString aResName = SalGenericSystem::getFrameResName();
1246 // the WM_CLASS data consists of two concatenated cstrings, including the terminating '\0' chars
1247 const uint32_t data_len = aResName.getLength() + 1 + strlen(pResClass) + 1;
1248 char* data = new char[data_len];
1249 memcpy(data, aResName.getStr(), aResName.getLength() + 1);
1250 memcpy(data + aResName.getLength() + 1, pResClass, strlen(pResClass) + 1);
1252 xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, m_pTopLevel->winId(),
1253 XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, data_len, data);
1254 delete[] data;
1255 #else
1256 (void)rWMClass;
1257 #endif
1260 // Drag'n'drop foo
1262 void Qt5Frame::registerDragSource(Qt5DragSource* pDragSource)
1264 assert(!m_pDragSource);
1265 m_pDragSource = pDragSource;
1268 void Qt5Frame::deregisterDragSource(Qt5DragSource const* pDragSource)
1270 assert(m_pDragSource == pDragSource);
1271 (void)pDragSource;
1272 m_pDragSource = nullptr;
1275 void Qt5Frame::registerDropTarget(Qt5DropTarget* pDropTarget)
1277 assert(!m_pDropTarget);
1278 m_pDropTarget = pDropTarget;
1279 m_pQWidget->setAcceptDrops(true);
1282 void Qt5Frame::deregisterDropTarget(Qt5DropTarget const* pDropTarget)
1284 assert(m_pDropTarget == pDropTarget);
1285 (void)pDropTarget;
1286 m_pDropTarget = nullptr;
1289 static css::uno::Reference<css::datatransfer::XTransferable>
1290 lcl_getXTransferable(const QMimeData* pMimeData)
1292 css::uno::Reference<css::datatransfer::XTransferable> xTransferable;
1293 const Qt5MimeData* pQt5MimeData = dynamic_cast<const Qt5MimeData*>(pMimeData);
1294 if (!pQt5MimeData)
1295 xTransferable = new Qt5DnDTransferable(pMimeData);
1296 else
1297 xTransferable = pQt5MimeData->xTransferable();
1298 return xTransferable;
1301 static sal_Int8 lcl_getUserDropAction(const QDropEvent* pEvent, const sal_Int8 nSourceActions,
1302 const QMimeData* pMimeData)
1304 // we completely ignore all proposals by the Qt event, as they don't
1305 // match at all with the preferred LO DnD actions.
1307 // check the key modifiers to detect a user-overridden DnD action
1308 const Qt::KeyboardModifiers eKeyMod = pEvent->keyboardModifiers();
1309 sal_Int8 nUserDropAction = 0;
1310 if ((eKeyMod & Qt::ShiftModifier) && !(eKeyMod & Qt::ControlModifier))
1311 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
1312 else if ((eKeyMod & Qt::ControlModifier) && !(eKeyMod & Qt::ShiftModifier))
1313 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1314 else if ((eKeyMod & Qt::ShiftModifier) && (eKeyMod & Qt::ControlModifier))
1315 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_LINK;
1316 nUserDropAction &= nSourceActions;
1318 // select the default DnD action, if there isn't a user preference
1319 if (0 == nUserDropAction)
1321 // default LO internal action is move, but default external action is copy
1322 nUserDropAction = dynamic_cast<const Qt5MimeData*>(pMimeData)
1323 ? css::datatransfer::dnd::DNDConstants::ACTION_MOVE
1324 : css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1325 nUserDropAction &= nSourceActions;
1327 // if the default doesn't match any allowed source action, fall back to the
1328 // preferred of all allowed source actions
1329 if (0 == nUserDropAction)
1330 nUserDropAction = toVclDropAction(getPreferredDropAction(nSourceActions));
1332 // this is "our" preference, but actually we would even prefer any default,
1333 // if there is any
1334 nUserDropAction |= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT;
1336 return nUserDropAction;
1339 void Qt5Frame::handleDragMove(QDragMoveEvent* pEvent)
1341 assert(m_pDropTarget);
1343 // prepare our suggested drop action for the drop target
1344 const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1345 const QMimeData* pMimeData = pEvent->mimeData();
1346 const sal_Int8 nUserDropAction = lcl_getUserDropAction(pEvent, nSourceActions, pMimeData);
1347 const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1349 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent;
1350 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1351 aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDragContext*>(m_pDropTarget);
1352 aEvent.LocationX = aPos.X();
1353 aEvent.LocationY = aPos.Y();
1354 aEvent.DropAction = nUserDropAction;
1355 aEvent.SourceActions = nSourceActions;
1357 // ask the drop target to accept our drop action
1358 if (!m_bInDrag)
1360 aEvent.SupportedDataFlavors = lcl_getXTransferable(pMimeData)->getTransferDataFlavors();
1361 m_pDropTarget->fire_dragEnter(aEvent);
1362 m_bInDrag = true;
1364 else
1365 m_pDropTarget->fire_dragOver(aEvent);
1367 // the drop target accepted our drop action => inform Qt
1368 if (m_pDropTarget->proposedDropAction() != 0)
1370 pEvent->setDropAction(getPreferredDropAction(m_pDropTarget->proposedDropAction()));
1371 pEvent->accept();
1373 else // or maybe someone else likes it?
1374 pEvent->ignore();
1377 void Qt5Frame::handleDrop(QDropEvent* pEvent)
1379 assert(m_pDropTarget);
1381 // prepare our suggested drop action for the drop target
1382 const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1383 const sal_Int8 nUserDropAction
1384 = lcl_getUserDropAction(pEvent, nSourceActions, pEvent->mimeData());
1385 const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1387 css::datatransfer::dnd::DropTargetDropEvent aEvent;
1388 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1389 aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDropContext*>(m_pDropTarget);
1390 aEvent.LocationX = aPos.X();
1391 aEvent.LocationY = aPos.Y();
1392 aEvent.SourceActions = nSourceActions;
1393 aEvent.DropAction = nUserDropAction;
1394 aEvent.Transferable = lcl_getXTransferable(pEvent->mimeData());
1396 // ask the drop target to accept our drop action
1397 m_pDropTarget->fire_drop(aEvent);
1398 m_bInDrag = false;
1400 const bool bDropSuccessful = m_pDropTarget->dropSuccessful();
1401 const sal_Int8 nDropAction = m_pDropTarget->proposedDropAction();
1403 // inform the drag source of the drag-origin frame of the drop result
1404 if (pEvent->source())
1406 Qt5Widget* pWidget = dynamic_cast<Qt5Widget*>(pEvent->source());
1407 assert(pWidget); // AFAIK there shouldn't be any non-Qt5Widget as source in LO itself
1408 if (pWidget)
1409 pWidget->frame().m_pDragSource->fire_dragEnd(nDropAction, bDropSuccessful);
1412 // the drop target accepted our drop action => inform Qt
1413 if (bDropSuccessful)
1415 pEvent->setDropAction(getPreferredDropAction(nDropAction));
1416 pEvent->accept();
1418 else // or maybe someone else likes it?
1419 pEvent->ignore();
1422 void Qt5Frame::handleDragLeave()
1424 css::datatransfer::dnd::DropTargetEvent aEvent;
1425 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1426 m_pDropTarget->fire_dragExit(aEvent);
1427 m_bInDrag = false;
1430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */