| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file is part of the Collabora Office project. |
| * |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| * |
| * This file incorporates work covered by the following license notice: |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
| */ |
| |
| #include <svdata.hxx> |
| #include <brdwin.hxx> |
| #include <window.h> |
| #include <salframe.hxx> |
| #include <helpwin.hxx> |
| |
| #include <comphelper/lok.hxx> |
| #include <sal/log.hxx> |
| #include <vcl/layout.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vcl/wrkwin.hxx> |
| #include <vcl/event.hxx> |
| #include <vcl/toolbox.hxx> |
| #include <vcl/toolkit/floatwin.hxx> |
| #include <vcl/settings.hxx> |
| #include <vcl/IDialogRenderable.hxx> |
| |
| class FloatingWindow::ImplData |
| { |
| public: |
| ImplData(); |
| |
| VclPtr<ToolBox> mpBox; |
| AbsoluteScreenPixelRectangle maItemEdgeClipRect; // used to clip the common edge between a toolbar item and the border of this window |
| Point maPos; // position of the floating window wrt. parent |
| Point maKitTwipsPos; ///< absolute position of the floating window in the document - in twips (for toplevel floating windows). |
| }; |
| |
| FloatingWindow::ImplData::ImplData() |
| { |
| mpBox = nullptr; |
| } |
| |
| const AbsoluteScreenPixelRectangle & FloatingWindow::ImplGetItemEdgeClipRect() |
| { |
| return mpImplData->maItemEdgeClipRect; |
| } |
| |
| void FloatingWindow::ImplInitFloating( vcl::Window* pParent, WinBits nStyle ) |
| { |
| mpImplData.reset(new ImplData); |
| |
| mpWindowImpl->mbFloatWin = true; |
| mbInCleanUp = false; |
| mbGrabFocus = false; |
| |
| SAL_WARN_IF(!pParent, "vcl", "FloatWindow::FloatingWindow(): - pParent == NULL!"); |
| |
| if (!pParent) |
| pParent = ImplGetSVData()->maFrameData.mpAppWin; |
| |
| SAL_WARN_IF(!pParent, "vcl", "FloatWindow::FloatingWindow(): - pParent == NULL and no AppWindow exists"); |
| |
| // no Border, then we don't need a border window |
| if (!nStyle) |
| { |
| mpWindowImpl->mbOverlapWin = true; |
| nStyle |= WB_DIALOGCONTROL; |
| ImplInit(pParent, nStyle, nullptr); |
| } |
| else |
| { |
| if (!(nStyle & WB_NODIALOGCONTROL)) |
| nStyle |= WB_DIALOGCONTROL; |
| |
| if (nStyle & (WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE | WB_STANDALONE) |
| && !(nStyle & WB_OWNERDRAWDECORATION)) |
| { |
| WinBits nFloatWinStyle = nStyle; |
| // #99154# floaters are not closeable by default anymore, eg fullscreen floater |
| // nFloatWinStyle |= WB_CLOSEABLE; |
| mpWindowImpl->mbFrame = true; |
| mpWindowImpl->mbOverlapWin = true; |
| ImplInit(pParent, nFloatWinStyle & ~WB_BORDER, nullptr); |
| } |
| else |
| { |
| VclPtr<ImplBorderWindow> pBorderWin; |
| BorderWindowStyle nBorderStyle = BorderWindowStyle::Float; |
| |
| if (nStyle & WB_OWNERDRAWDECORATION) |
| nBorderStyle |= BorderWindowStyle::Frame; |
| else |
| nBorderStyle |= BorderWindowStyle::Overlap; |
| |
| if ((nStyle & WB_SYSTEMWINDOW) && !(nStyle & (WB_MOVEABLE | WB_SIZEABLE))) |
| { |
| nBorderStyle |= BorderWindowStyle::Frame; |
| nStyle |= WB_CLOSEABLE; // make undecorated floaters closeable |
| } |
| pBorderWin = VclPtr<ImplBorderWindow>::Create(pParent, nStyle, nBorderStyle); |
| ImplInit(pBorderWin, nStyle & ~WB_BORDER, nullptr); |
| pBorderWin->mpWindowImpl->mpClientWindow = this; |
| pBorderWin->GetBorder(mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, |
| mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder); |
| pBorderWin->SetDisplayActive(true); |
| mpWindowImpl->mpBorderWindow = pBorderWin; |
| mpWindowImpl->mpRealParent = pParent; |
| } |
| } |
| SetActivateMode( ActivateModeFlags::NONE ); |
| |
| mpNextFloat = nullptr; |
| mpFirstPopupModeWin = nullptr; |
| mnPostId = nullptr; |
| mnTitle = (nStyle & (WB_MOVEABLE | WB_POPUP)) ? FloatWinTitleType::Normal : FloatWinTitleType::NONE; |
| mnOldTitle = mnTitle; |
| mnPopupModeFlags = FloatWinPopupFlags::NONE; |
| mbInPopupMode = false; |
| mbPopupMode = false; |
| mbPopupModeCanceled = false; |
| mbPopupModeTearOff = false; |
| mbMouseDown = false; |
| |
| ImplInitSettings(); |
| } |
| |
| void FloatingWindow::ImplInitSettings() |
| { |
| const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); |
| |
| Color aColor; |
| if (IsControlBackground()) |
| aColor = GetControlBackground(); |
| else if (Window::GetStyle() & WB_3DLOOK) |
| aColor = rStyleSettings.GetFaceColor(); |
| else |
| aColor = rStyleSettings.GetWindowColor(); |
| SetBackground(aColor); |
| } |
| |
| FloatingWindow::FloatingWindow(vcl::Window* pParent, WinBits nStyle) : |
| SystemWindow(WindowType::FLOATINGWINDOW, "vcl::FloatingWindow maLayoutIdle") |
| { |
| ImplInitFloating(pParent, nStyle); |
| } |
| |
| FloatingWindow::FloatingWindow(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame) |
| : SystemWindow(WindowType::FLOATINGWINDOW, "vcl::FloatingWindow maLayoutIdle") |
| , mpNextFloat(nullptr) |
| , mpFirstPopupModeWin(nullptr) |
| , mnPostId(nullptr) |
| , mnPopupModeFlags(FloatWinPopupFlags::NONE) |
| , mnTitle(FloatWinTitleType::Unknown) |
| , mnOldTitle(FloatWinTitleType::Unknown) |
| , mbInPopupMode(false) |
| , mbPopupMode(false) |
| , mbPopupModeCanceled(false) |
| , mbPopupModeTearOff(false) |
| , mbMouseDown(false) |
| , mbGrabFocus(false) |
| , mbInCleanUp(false) |
| { |
| loadUI(pParent, rID, rUIXMLDescription, rFrame); |
| } |
| |
| void FloatingWindow::ImplDeferredInit(vcl::Window* pParent, WinBits nBits) |
| { |
| ImplInitFloating(pParent, nBits); |
| } |
| |
| void FloatingWindow::ApplySettings(vcl::RenderContext& rRenderContext) |
| { |
| const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); |
| |
| Color aColor; |
| if (Window::GetStyle() & WB_3DLOOK) |
| aColor = rStyleSettings.GetFaceColor(); |
| else |
| aColor = rStyleSettings.GetWindowColor(); |
| |
| ApplyControlBackground(rRenderContext, aColor); |
| } |
| |
| FloatingWindow::~FloatingWindow() |
| { |
| disposeOnce(); |
| assert (!mnPostId); |
| } |
| |
| void FloatingWindow::dispose() |
| { |
| ReleaseKitNotifier(); |
| |
| if (mpImplData) |
| { |
| if( mbPopupModeCanceled ) |
| // indicates that ESC key was pressed |
| // will be handled in Window::ImplGrabFocus() |
| SetDialogControlFlags( GetDialogControlFlags() | DialogControlFlags::FloatWinPopupModeEndCancel ); |
| |
| if ( IsInPopupMode() ) |
| EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll | FloatWinPopupEndFlags::DontCallHdl ); |
| |
| if ( mnPostId ) |
| Application::RemoveUserEvent( mnPostId ); |
| mnPostId = nullptr; |
| } |
| |
| mpImplData.reset(); |
| |
| mpNextFloat.reset(); |
| mpFirstPopupModeWin.reset(); |
| mxPrevFocusWin.reset(); |
| SystemWindow::dispose(); |
| } |
| |
| Point FloatingWindow::ImplCalcPos(vcl::Window* pWindow, |
| const tools::Rectangle& rRect, FloatWinPopupFlags nFlags, |
| sal_uInt16& rArrangeIndex, Point* pKitTwipsPos) |
| { |
| // get window position |
| AbsoluteScreenPixelPoint aPos; |
| Size aSize = ::isLayoutEnabled(pWindow) ? pWindow->get_preferred_size() : pWindow->GetSizePixel(); |
| AbsoluteScreenPixelRectangle aScreenRect = pWindow->ImplGetFrameWindow()->GetDesktopRectPixel(); |
| FloatingWindow *pFloatingWindow = dynamic_cast<FloatingWindow*>( pWindow ); |
| |
| // convert... |
| vcl::Window* pW = pWindow; |
| if ( pW->mpWindowImpl->mpRealParent ) |
| pW = pW->mpWindowImpl->mpRealParent; |
| |
| tools::Rectangle normRect( rRect ); // rRect is already relative to top-level window |
| normRect.SetPos( pW->ScreenToOutputPixel( normRect.TopLeft() ) ); |
| |
| bool bRTL = AllSettings::GetLayoutRTL(); |
| |
| AbsoluteScreenPixelRectangle devRect( pW->OutputToAbsoluteScreenPixel( normRect.TopLeft() ), |
| pW->OutputToAbsoluteScreenPixel( normRect.BottomRight() ) ); |
| |
| AbsoluteScreenPixelRectangle devRectRTL( devRect ); |
| if( bRTL ) |
| // create a rect that can be compared to desktop coordinates |
| devRectRTL = pW->ImplOutputToUnmirroredAbsoluteScreenPixel( normRect ); |
| if( Application::GetScreenCount() > 1 ) |
| aScreenRect = Application::GetScreenPosSizePixel( |
| Application::GetBestScreen( bRTL ? devRectRTL : devRect ) ); |
| |
| FloatWinPopupFlags nArrangeAry[5]; |
| sal_uInt16 nArrangeAttempts = 5; |
| AbsoluteScreenPixelPoint e1,e2; // the common edge between the item rect and the floating window |
| |
| if ( nFlags & FloatWinPopupFlags::Left ) |
| { |
| nArrangeAry[0] = FloatWinPopupFlags::Left; |
| nArrangeAry[1] = FloatWinPopupFlags::Right; |
| nArrangeAry[2] = FloatWinPopupFlags::Up; |
| nArrangeAry[3] = FloatWinPopupFlags::Down; |
| nArrangeAry[4] = FloatWinPopupFlags::Left; |
| } |
| else if ( nFlags & FloatWinPopupFlags::Right ) |
| { |
| nArrangeAry[0] = FloatWinPopupFlags::Right; |
| nArrangeAry[1] = FloatWinPopupFlags::Left; |
| nArrangeAry[2] = FloatWinPopupFlags::Up; |
| nArrangeAry[3] = FloatWinPopupFlags::Down; |
| nArrangeAry[4] = FloatWinPopupFlags::Right; |
| } |
| else if ( nFlags & FloatWinPopupFlags::Up ) |
| { |
| nArrangeAry[0] = FloatWinPopupFlags::Up; |
| nArrangeAry[1] = FloatWinPopupFlags::Down; |
| if (nFlags & FloatWinPopupFlags::NoHorzPlacement) |
| { |
| nArrangeAry[2] = FloatWinPopupFlags::Up; |
| nArrangeAttempts = 3; |
| } |
| else |
| { |
| nArrangeAry[2] = FloatWinPopupFlags::Right; |
| nArrangeAry[3] = FloatWinPopupFlags::Left; |
| nArrangeAry[4] = FloatWinPopupFlags::Up; |
| } |
| } |
| else |
| { |
| nArrangeAry[0] = FloatWinPopupFlags::Down; |
| nArrangeAry[1] = FloatWinPopupFlags::Up; |
| if (nFlags & FloatWinPopupFlags::NoHorzPlacement) |
| { |
| nArrangeAry[2] = FloatWinPopupFlags::Down; |
| nArrangeAttempts = 3; |
| } |
| else |
| { |
| nArrangeAry[2] = FloatWinPopupFlags::Right; |
| nArrangeAry[3] = FloatWinPopupFlags::Left; |
| nArrangeAry[4] = FloatWinPopupFlags::Down; |
| } |
| } |
| |
| sal_uInt16 nArrangeIndex = 0; |
| const bool bLOKActive = comphelper::COKit::isActive(); |
| |
| for ( ; nArrangeIndex < nArrangeAttempts; nArrangeIndex++ ) |
| { |
| bool bBreak = true; |
| switch ( nArrangeAry[nArrangeIndex] ) |
| { |
| |
| case FloatWinPopupFlags::Left: |
| aPos.setX( devRect.Left()-aSize.Width()+1 ); |
| aPos.setY( devRect.Top() ); |
| aPos.AdjustY( -(pWindow->mpWindowImpl->mnTopBorder) ); |
| if( bRTL ) |
| { |
| if( (devRectRTL.Right()+aSize.Width()) > aScreenRect.Right() ) |
| bBreak = false; |
| } |
| else |
| { |
| if ( aPos.X() < aScreenRect.Left() ) |
| bBreak = false; |
| } |
| if (bBreak || bLOKActive) |
| { |
| e1 = devRect.TopLeft(); |
| e2 = devRect.BottomLeft(); |
| // set non-zero width |
| e2.AdjustX( 1 ); |
| // don't clip corners |
| e1.AdjustY( 1 ); |
| e2.AdjustY( -1 ); |
| } |
| break; |
| case FloatWinPopupFlags::Right: |
| aPos = devRect.TopRight(); |
| aPos.AdjustY( -(pWindow->mpWindowImpl->mnTopBorder) ); |
| if( bRTL ) |
| { |
| if( (devRectRTL.Left() - aSize.Width()) < aScreenRect.Left() ) |
| bBreak = false; |
| } |
| else |
| { |
| if ( aPos.X()+aSize.Width() > aScreenRect.Right() ) |
| bBreak = false; |
| } |
| if (bBreak || bLOKActive) |
| { |
| e1 = devRect.TopRight(); |
| e2 = devRect.BottomRight(); |
| // set non-zero width |
| e2.AdjustX( 1 ); |
| // don't clip corners |
| e1.AdjustY( 1 ); |
| e2.AdjustY( -1 ); |
| } |
| break; |
| case FloatWinPopupFlags::Up: |
| aPos.setX( devRect.Left() ); |
| aPos.setY( devRect.Top()-aSize.Height()+1 ); |
| if ( aPos.Y() < aScreenRect.Top() ) |
| bBreak = false; |
| if (bBreak || bLOKActive) |
| { |
| e1 = devRect.TopLeft(); |
| e2 = devRect.TopRight(); |
| // set non-zero height |
| e2.AdjustY( 1 ); |
| // don't clip corners |
| e1.AdjustX( 1 ); |
| e2.AdjustX( -1 ); |
| } |
| break; |
| case FloatWinPopupFlags::Down: |
| aPos = devRect.BottomLeft(); |
| if ( aPos.Y()+aSize.Height() > aScreenRect.Bottom() ) |
| bBreak = false; |
| if (bBreak || bLOKActive) |
| { |
| e1 = devRect.BottomLeft(); |
| e2 = devRect.BottomRight(); |
| // set non-zero height |
| e2.AdjustY( 1 ); |
| // don't clip corners |
| e1.AdjustX( 1 ); |
| e2.AdjustX( -1 ); |
| } |
| break; |
| default: break; |
| } |
| |
| // no further adjustment for COKit |
| if (bLOKActive) |
| break; |
| |
| // set bBreak true on last attempt |
| if (nArrangeIndex + 1 == nArrangeAttempts) |
| bBreak = true; |
| |
| // adjust if necessary |
| if (bBreak) |
| { |
| if (aPos.Y() + aSize.Height() > aScreenRect.Bottom()) |
| { |
| aPos.setY(devRect.Bottom() - aSize.Height() + 1); |
| if (aPos.Y() < aScreenRect.Top()) |
| aPos.setY(aScreenRect.Top()); |
| // move to the right or left of the parent if possible |
| if (devRect.Right() + 4 + aSize.Width() < aScreenRect.Right()) |
| aPos.setX(devRect.Right() + 4); |
| else if (devRect.Left() - 4 - aSize.Width() > aScreenRect.Left()) |
| aPos.setX(devRect.Left() - 4 - aSize.Width()); |
| } |
| else |
| { |
| if( bRTL ) |
| { |
| if( devRectRTL.Right()-aSize.Width()+1 < aScreenRect.Left() ) |
| aPos.AdjustX( -(aScreenRect.Left() - devRectRTL.Right() + aSize.Width() - 1) ); |
| } |
| else if ( aPos.X()+aSize.Width() > aScreenRect.Right() ) |
| { |
| aPos.setX( devRect.Right()-aSize.Width()+1 ); |
| if ( aPos.X() < aScreenRect.Left() ) |
| aPos.setX( aScreenRect.Left() ); |
| } |
| } |
| } |
| |
| if ( bBreak ) |
| break; |
| } |
| |
| rArrangeIndex = nArrangeIndex; |
| |
| Point aPosOut = pW->AbsoluteScreenToOutputPixel( aPos ); |
| |
| // store a cliprect that can be used to clip the common edge of the itemrect and the floating window |
| if( pFloatingWindow && pFloatingWindow->mpImplData->mpBox ) |
| { |
| pFloatingWindow->mpImplData->maItemEdgeClipRect = |
| AbsoluteScreenPixelRectangle( e1, e2 ); |
| } |
| |
| if (bLOKActive && pKitTwipsPos) |
| { |
| if (pW->IsMapModeEnabled() || pW->GetMapMode().GetMapUnit() == MapUnit::MapPixel) |
| { |
| // if we use pW->LogicToLogic(aPos, pW->GetMapMode(), MapMode(MapUnit::MapTwip)), |
| // for pixel conversions when map mode is not enabled, we get |
| // a 20 twips per pixel conversion since LogicToLogic uses |
| // a fixed 72 dpi value, instead of a correctly computed output |
| // device dpi or at least the most commonly used 96 dpi value; |
| // and anyway the following is what we already do in |
| // ScGridWindow::LogicInvalidate when map mode is not enabled. |
| |
| *pKitTwipsPos = pW->PixelToLogic(aPosOut, MapMode(MapUnit::MapTwip)); |
| } |
| else |
| { |
| *pKitTwipsPos = OutputDevice::LogicToLogic(aPosOut, pW->GetMapMode(), MapMode(MapUnit::MapTwip)); |
| } |
| } |
| |
| // caller expects coordinates relative to top-level win |
| return pW->OutputToScreenPixel( aPosOut ); |
| } |
| |
| AbsoluteScreenPixelPoint FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const Point& rPos) |
| { |
| const OutputDevice *pWindowOutDev = pReference->GetOutDev(); |
| |
| // compare coordinates in absolute screen coordinates |
| if ( pWindowOutDev->HasMirroredGraphics() && !comphelper::COKit::isActive() ) |
| { |
| Point aTmp(rPos); |
| if(!pReference->IsRTLEnabled() ) |
| pWindowOutDev->ReMirror( aTmp ); |
| |
| tools::Rectangle aRect( pReference->ScreenToOutputPixel(aTmp), Size(1,1) ) ; |
| aRect = tools::Rectangle(pReference->ImplOutputToUnmirroredAbsoluteScreenPixel( aRect )); |
| return AbsoluteScreenPixelPoint(aRect.TopLeft()); |
| } |
| else |
| return pReference->OutputToAbsoluteScreenPixel( |
| pReference->ScreenToOutputPixel(rPos) ); |
| } |
| |
| AbsoluteScreenPixelRectangle FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const tools::Rectangle& rRect) |
| { |
| AbsoluteScreenPixelRectangle aFloatRect; |
| |
| const OutputDevice *pParentWinOutDev = pReference->GetOutDev(); |
| |
| // compare coordinates in absolute screen coordinates |
| // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509 |
| if( pParentWinOutDev->HasMirroredGraphics() && !comphelper::COKit::isActive() ) |
| { |
| tools::Rectangle aScreenRect(rRect); |
| if(!pReference->IsRTLEnabled() ) |
| pParentWinOutDev->ReMirror(aScreenRect); |
| |
| tools::Rectangle aOutRect(pReference->ScreenToOutputPixel(aScreenRect.TopLeft()), aScreenRect.GetSize()); |
| aFloatRect = pReference->ImplOutputToUnmirroredAbsoluteScreenPixel(aOutRect); |
| } |
| else |
| aFloatRect = AbsoluteScreenPixelRectangle( |
| pReference->OutputToAbsoluteScreenPixel(pReference->ScreenToOutputPixel(rRect.TopLeft())), |
| rRect.GetSize()); |
| |
| return aFloatRect; |
| } |
| |
| tools::Rectangle FloatingWindow::ImplConvertToRelPos(vcl::Window* pReference, const AbsoluteScreenPixelRectangle& rRect) |
| { |
| tools::Rectangle aFloatRect; |
| |
| const OutputDevice *pParentWinOutDev = pReference->GetOutDev(); |
| |
| // compare coordinates in absolute screen coordinates |
| // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509 |
| if( pParentWinOutDev->HasMirroredGraphics() ) |
| { |
| aFloatRect = pReference->ImplUnmirroredAbsoluteScreenToOutputPixel(rRect); |
| aFloatRect.SetPos(pReference->OutputToScreenPixel(aFloatRect.TopLeft())); |
| |
| if(!pReference->IsRTLEnabled() ) |
| pParentWinOutDev->ReMirror(aFloatRect); |
| } |
| else |
| aFloatRect = tools::Rectangle(pReference->OutputToScreenPixel(pReference->AbsoluteScreenToOutputPixel(rRect.TopLeft())), |
| rRect.GetSize()); |
| |
| return aFloatRect; |
| } |
| |
| FloatingWindow* FloatingWindow::ImplFloatHitTest( vcl::Window* pReference, const Point& rPos, bool& rbHitTestInsideRect ) |
| { |
| FloatingWindow* pWin = this; |
| rbHitTestInsideRect = false; |
| |
| AbsoluteScreenPixelPoint aAbsolute(FloatingWindow::ImplConvertToAbsPos(pReference, rPos)); |
| |
| do |
| { |
| // compute the floating window's size in absolute screen coordinates |
| |
| // use the border window to have the exact position |
| vcl::Window *pBorderWin = pWin->GetWindow( GetWindowType::Border ); |
| if (!pBorderWin) |
| break; |
| |
| // the top-left corner in output coordinates ie (0,0) |
| AbsoluteScreenPixelRectangle devRect( pBorderWin->ImplOutputToUnmirroredAbsoluteScreenPixel( tools::Rectangle( Point(), pBorderWin->GetSizePixel()) ) ) ; |
| if ( devRect.Contains( aAbsolute ) ) |
| { |
| // inside the window |
| return pWin; |
| } |
| |
| // test, if mouse is in rectangle, (this is typically the rect of the active |
| // toolbox item or similar) |
| // note: maFloatRect is set in FloatingWindow::StartPopupMode() and |
| // is already in absolute device coordinates |
| if ( pWin->maFloatRect.Contains( aAbsolute ) ) |
| { |
| rbHitTestInsideRect = true; |
| return pWin; |
| } |
| |
| pWin = pWin->mpNextFloat; |
| } |
| while ( pWin ); |
| |
| return nullptr; |
| } |
| |
| FloatingWindow* FloatingWindow::ImplFindLastLevelFloat() |
| { |
| FloatingWindow* pWin = this; |
| FloatingWindow* pLastFoundWin = pWin; |
| |
| do |
| { |
| if ( pWin->GetPopupModeFlags() & FloatWinPopupFlags::NewLevel ) |
| pLastFoundWin = pWin; |
| |
| pWin = pWin->mpNextFloat; |
| } |
| while ( pWin ); |
| |
| return pLastFoundWin; |
| } |
| |
| bool FloatingWindow::ImplIsFloatPopupModeWindow( const vcl::Window* pWindow ) |
| { |
| FloatingWindow* pWin = this; |
| |
| do |
| { |
| if ( pWin->mpFirstPopupModeWin == pWindow ) |
| return true; |
| |
| pWin = pWin->mpNextFloat; |
| } |
| while ( pWin ); |
| |
| return false; |
| } |
| |
| IMPL_LINK_NOARG(FloatingWindow, ImplEndPopupModeHdl, void*, void) |
| { |
| VclPtr<FloatingWindow> pThis(this); |
| mnPostId = nullptr; |
| mnPopupModeFlags = FloatWinPopupFlags::NONE; |
| mbPopupMode = false; |
| PopupModeEnd(); |
| } |
| |
| bool FloatingWindow::EventNotify( NotifyEvent& rNEvt ) |
| { |
| // call Base Class first for tab control |
| bool bRet = SystemWindow::EventNotify( rNEvt ); |
| if ( !bRet ) |
| { |
| if ( rNEvt.GetType() == NotifyEventType::KEYINPUT ) |
| { |
| const KeyEvent* pKEvt = rNEvt.GetKeyEvent(); |
| vcl::KeyCode aKeyCode = pKEvt->GetKeyCode(); |
| sal_uInt16 nKeyCode = aKeyCode.GetCode(); |
| |
| if ( (nKeyCode == KEY_ESCAPE) && (GetStyle() & WB_CLOSEABLE) ) |
| { |
| Close(); |
| return true; |
| } |
| } |
| } |
| |
| return bRet; |
| } |
| |
| void FloatingWindow::PixelInvalidate(const tools::Rectangle* /*pRectangle*/) |
| { |
| if (VclPtr<vcl::Window> pParent = GetParentWithKitNotifier()) |
| { |
| const tools::Rectangle aRect(Point(0,0), Size(GetSizePixel().Width()+1, GetSizePixel().Height()+1)); |
| std::vector<vcl::KitPayloadItem> aPayload |
| { |
| std::make_pair("rectangle"_ostr, aRect.toString()) |
| }; |
| const vcl::ICOKitNotifier* pNotifier = pParent->GetKitNotifier(); |
| pNotifier->notifyWindow(GetKitWindowId(), u"invalidate"_ustr, aPayload); |
| } |
| } |
| |
| void FloatingWindow::StateChanged( StateChangedType nType ) |
| { |
| if (nType == StateChangedType::InitShow) |
| { |
| DoInitialLayout(); |
| } |
| |
| SystemWindow::StateChanged( nType ); |
| |
| VclPtr<vcl::Window> pParent = GetParentWithKitNotifier(); |
| if (pParent) |
| { |
| if (nType == StateChangedType::InitShow) |
| { |
| std::vector<vcl::KitPayloadItem> aItems; |
| if (pParent == this) |
| { |
| // we are a toplevel window, let's so far pretend to be a |
| // dialog - but maybe we'll need a separate type for this |
| // later |
| if (mbInPopupMode) |
| aItems.emplace_back("type", "dropdown"); |
| else |
| aItems.emplace_back("type", "dialog"); |
| aItems.emplace_back("position", mpImplData->maKitTwipsPos.toString()); // twips |
| } |
| else |
| { |
| SetKitNotifier(pParent->GetKitNotifier()); |
| if (dynamic_cast<HelpTextWindow*>(this)) |
| aItems.emplace_back("type", "tooltip"); |
| else |
| aItems.emplace_back("type", "child"); |
| |
| aItems.emplace_back("parentId", OString::number(pParent->GetKitWindowId())); |
| if (mbInPopupMode) |
| aItems.emplace_back("position", mpImplData->maPos.toString()); // pixels |
| else // mpImplData->maPos is not set |
| aItems.emplace_back("position", GetPosPixel().toString()); |
| |
| } |
| aItems.emplace_back("size", GetSizePixel().toString()); |
| GetKitNotifier()->notifyWindow(GetKitWindowId(), u"created"_ustr, aItems); |
| } |
| else if (!IsVisible() && nType == StateChangedType::Visible) |
| { |
| if (const vcl::ICOKitNotifier* pNotifier = GetKitNotifier()) |
| { |
| pNotifier->notifyWindow(GetKitWindowId(), u"close"_ustr); |
| ReleaseKitNotifier(); |
| } |
| } |
| } |
| |
| if ( nType == StateChangedType::ControlBackground ) |
| { |
| ImplInitSettings(); |
| Invalidate(); |
| } |
| } |
| |
| void FloatingWindow::DataChanged( const DataChangedEvent& rDCEvt ) |
| { |
| SystemWindow::DataChanged( rDCEvt ); |
| |
| if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && |
| (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) |
| { |
| ImplInitSettings(); |
| Invalidate(); |
| } |
| } |
| |
| void FloatingWindow::ImplCallPopupModeEnd() |
| { |
| // PopupMode is finished |
| mbInPopupMode = false; |
| |
| // call Handler asynchronously. |
| if ( mpImplData && !mnPostId ) |
| mnPostId = Application::PostUserEvent(LINK(this, FloatingWindow, ImplEndPopupModeHdl)); |
| } |
| |
| void FloatingWindow::PopupModeEnd() |
| { |
| maPopupModeEndHdl.Call( this ); |
| } |
| |
| void FloatingWindow::SetTitleType( FloatWinTitleType nTitle ) |
| { |
| if ( (mnTitle == nTitle) || !mpWindowImpl->mpBorderWindow ) |
| return; |
| |
| mnTitle = nTitle; |
| Size aOutSize = GetOutputSizePixel(); |
| BorderWindowTitleType nTitleStyle; |
| if ( nTitle == FloatWinTitleType::Normal ) |
| nTitleStyle = BorderWindowTitleType::Small; |
| else if ( nTitle == FloatWinTitleType::TearOff ) |
| nTitleStyle = BorderWindowTitleType::Tearoff; |
| else if ( nTitle == FloatWinTitleType::Popup ) |
| nTitleStyle = BorderWindowTitleType::Popup; |
| else // nTitle == FloatWinTitleType::NONE |
| nTitleStyle = BorderWindowTitleType::NONE; |
| static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetTitleType( nTitleStyle, aOutSize ); |
| static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder ); |
| } |
| |
| void FloatingWindow::StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nFlags ) |
| { |
| // remove title |
| mnOldTitle = mnTitle; |
| if ( ( mpWindowImpl->mnStyle & WB_POPUP ) && !GetText().isEmpty() ) |
| SetTitleType( FloatWinTitleType::Popup ); |
| else if ( nFlags & FloatWinPopupFlags::AllowTearOff ) |
| SetTitleType( FloatWinTitleType::TearOff ); |
| else |
| SetTitleType( FloatWinTitleType::NONE ); |
| |
| // avoid close on focus change for decorated floating windows only |
| if( mpWindowImpl->mbFrame && (GetStyle() & WB_MOVEABLE) ) |
| nFlags |= FloatWinPopupFlags::NoAppFocusClose; |
| |
| // compute window position according to flags and arrangement |
| sal_uInt16 nArrangeIndex; |
| DoInitialLayout(); |
| mpImplData->maPos = ImplCalcPos(this, rRect, nFlags, nArrangeIndex, &mpImplData->maKitTwipsPos); |
| SetPosPixel( mpImplData->maPos ); |
| ImplGetFrame()->PositionByToolkit(rRect, nFlags); |
| |
| // set data and display window |
| // convert maFloatRect to absolute device coordinates |
| // so they can be compared across different frames |
| // !!! rRect is expected to be in screen coordinates of the parent frame window !!! |
| maFloatRect = FloatingWindow::ImplConvertToAbsPos(GetParent(), rRect); |
| |
| maFloatRect.AdjustLeft( -2 ); |
| maFloatRect.AdjustTop( -2 ); |
| maFloatRect.AdjustRight(2 ); |
| maFloatRect.AdjustBottom(2 ); |
| mnPopupModeFlags = nFlags; |
| mbInPopupMode = true; |
| mbPopupMode = true; |
| mbPopupModeCanceled = false; |
| mbPopupModeTearOff = false; |
| mbMouseDown = false; |
| |
| // add FloatingWindow to list of windows that are in popup mode |
| ImplSVData* pSVData = ImplGetSVData(); |
| mpNextFloat = pSVData->mpWinData->mpFirstFloat; |
| pSVData->mpWinData->mpFirstFloat = this; |
| bool bGrabFocus(nFlags & FloatWinPopupFlags::GrabFocus); |
| if (bGrabFocus) |
| { |
| // force key input even without focus (useful for menus) |
| mbGrabFocus = true; |
| mxPrevFocusWin = Window::SaveFocus(); |
| mpWindowImpl->mpFrameData->mbHasFocus = true; |
| } |
| Show( true, ShowFlags::NoActivate ); |
| if (bGrabFocus) |
| GrabFocus(); |
| } |
| |
| void FloatingWindow::StartPopupMode( ToolBox* pBox, FloatWinPopupFlags nFlags ) |
| { |
| mpImplData->mpBox = pBox; |
| |
| // get selected button |
| ToolBoxItemId nItemId = pBox->GetDownItemId(); |
| |
| if ( nItemId ) |
| pBox->ImplFloatControl( true, this ); |
| |
| // retrieve some data from the ToolBox |
| tools::Rectangle aRect = nItemId ? pBox->GetItemRect( nItemId ) : pBox->GetOverflowRect(); |
| |
| // convert to parent's screen coordinates |
| mpImplData->maPos = GetParent()->OutputToScreenPixel( GetParent()->AbsoluteScreenToOutputPixel( pBox->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) ) ); |
| aRect.SetPos( mpImplData->maPos ); |
| |
| nFlags |= |
| FloatWinPopupFlags::AllMouseButtonClose | |
| FloatWinPopupFlags::NoMouseUpClose; |
| |
| // set Flags for positioning |
| if ( !(nFlags & (FloatWinPopupFlags::Down | FloatWinPopupFlags::Up | |
| FloatWinPopupFlags::Left | FloatWinPopupFlags::Right)) ) |
| { |
| if ( pBox->IsHorizontal() ) |
| nFlags |= FloatWinPopupFlags::Down; |
| else |
| nFlags |= FloatWinPopupFlags::Right; |
| } |
| |
| // start FloatingMode |
| StartPopupMode( aRect, nFlags ); |
| } |
| |
| void FloatingWindow::ImplEndPopupMode( FloatWinPopupEndFlags nFlags, const VclPtr<vcl::Window>& xFocusId ) |
| { |
| if ( !mbInPopupMode ) |
| return; |
| |
| ImplSVData* pSVData = ImplGetSVData(); |
| |
| mbInCleanUp = true; // prevent killing this window due to focus change while working with it |
| |
| if (!(nFlags & FloatWinPopupEndFlags::NoCloseChildren)) |
| { |
| // stop the PopupMode also for all PopupMode windows created after us |
| std::vector<VclPtr<FloatingWindow>> aCancelFloats; |
| // stop the PopupMode also for all following PopupMode windows |
| for (auto pFloat = pSVData->mpWinData->mpFirstFloat; |
| pFloat != nullptr && pFloat != this; |
| pFloat = pFloat->mpNextFloat) |
| aCancelFloats.push_back(pFloat); |
| for (auto & it : aCancelFloats) |
| it->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::NoCloseChildren); |
| } |
| |
| // delete window from the list |
| pSVData->mpWinData->mpFirstFloat = mpNextFloat; |
| mpNextFloat = nullptr; |
| |
| FloatWinPopupFlags nPopupModeFlags = mnPopupModeFlags; |
| mbPopupModeTearOff = nFlags & FloatWinPopupEndFlags::TearOff && |
| nPopupModeFlags & FloatWinPopupFlags::AllowTearOff; |
| |
| // hide window again if it was not deleted |
| if (!mbPopupModeTearOff) |
| Show( false, ShowFlags::NoFocusChange ); |
| |
| if (HasChildPathFocus() && xFocusId != nullptr) |
| { |
| // restore focus to previous focus window if we still have the focus |
| Window::EndSaveFocus(xFocusId); |
| } |
| else if ( pSVData->mpWinData->mpFocusWin && pSVData->mpWinData->mpFirstFloat && |
| ImplIsWindowOrChild( pSVData->mpWinData->mpFocusWin ) ) |
| { |
| // maybe pass focus on to a suitable FloatingWindow |
| pSVData->mpWinData->mpFirstFloat->GrabFocus(); |
| } |
| |
| mbPopupModeCanceled = bool(nFlags & FloatWinPopupEndFlags::Cancel); |
| |
| // redo title |
| SetTitleType( mnOldTitle ); |
| |
| // set ToolBox again to normal |
| if (mpImplData && mpImplData->mpBox) |
| { |
| mpImplData->mpBox->ImplFloatControl( false, this ); |
| // if the parent ToolBox is in popup mode, it should be closed too. |
| if ( GetDockingManager()->IsInPopupMode( mpImplData->mpBox ) ) |
| nFlags |= FloatWinPopupEndFlags::CloseAll; |
| |
| mpImplData->mpBox = nullptr; |
| } |
| |
| // call PopupModeEnd-Handler depending on parameter |
| if ( !(nFlags & FloatWinPopupEndFlags::DontCallHdl) ) |
| ImplCallPopupModeEnd(); |
| |
| // close all other windows depending on parameter |
| if ( nFlags & FloatWinPopupEndFlags::CloseAll ) |
| { |
| if ( !(nPopupModeFlags & FloatWinPopupFlags::NewLevel) ) |
| { |
| if (pSVData->mpWinData->mpFirstFloat) |
| { |
| FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); |
| pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); |
| } |
| } |
| } |
| |
| mbInCleanUp = false; |
| } |
| |
| void FloatingWindow::EndPopupMode( FloatWinPopupEndFlags nFlags ) |
| { |
| ImplEndPopupMode(nFlags, mxPrevFocusWin); |
| } |
| |
| void FloatingWindow::AddPopupModeWindow(vcl::Window* pWindow) |
| { |
| // !!! up-to-now only 1 window and not yet a list |
| mpFirstPopupModeWin = pWindow; |
| } |
| |
| bool SystemWindow::UpdatePositionData() |
| { |
| // tdf#164337 don't update position data when waiting for a system resize |
| // When entering and exiting LibreOffice's internal full screen mode, |
| // updating position data causes the "exit full screen" floating |
| // toolbar to migrate after cycle. |
| if (mpWindowImpl->mbWaitSystemResize) |
| return false; |
| |
| auto pWin = ImplGetParent(); |
| if (pWin) |
| { |
| // Simulate Move, so the relative position of the floating window will be recalculated |
| pWin->ImplCallMove(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |