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