Files
loongoffice/vcl/source/window/stacking.cxx
Luboš Luňák b22d478531 clean up ambiguous confusing rectangle APIs like IsInside()
Reading 'rectA.IsInside( rectB )' kind of suggests that the code
checks whether 'rectA is inside rectB', but it's actually the other
way around. Rename IsInside() -> Contains(), IsOver() -> Overlaps(),
which should make it clear which way the logic goes.

Change-Id: I9347450fe7dc34c96df6d636a4e3e660de1801ac
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122271
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
Tested-by: Jenkins
2021-09-20 12:10:44 +02:00

1152 lines
38 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice 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 <vcl/syswin.hxx>
#include <vcl/window.hxx>
#include <vcl/taskpanelist.hxx>
#include <sal/log.hxx>
#include <salframe.hxx>
#include <salobj.hxx>
#include <svdata.hxx>
#include <window.h>
#include <brdwin.hxx>
#include <com/sun/star/awt/XTopWindow.hpp>
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::datatransfer::clipboard;
using namespace ::com::sun::star::datatransfer::dnd;
using namespace ::com::sun::star;
using ::com::sun::star::awt::XTopWindow;
struct ImplCalcToTopData
{
std::unique_ptr<ImplCalcToTopData> mpNext;
VclPtr<vcl::Window> mpWindow;
std::unique_ptr<vcl::Region> mpInvalidateRegion;
};
namespace vcl {
vcl::Window* Window::ImplGetTopmostFrameWindow()
{
vcl::Window *pTopmostParent = this;
while( pTopmostParent->ImplGetParent() )
pTopmostParent = pTopmostParent->ImplGetParent();
return pTopmostParent->mpWindowImpl->mpFrameWindow;
}
void Window::ImplInsertWindow( vcl::Window* pParent )
{
mpWindowImpl->mpParent = pParent;
mpWindowImpl->mpRealParent = pParent;
if ( !pParent || mpWindowImpl->mbFrame )
return;
// search frame window and set window frame data
vcl::Window* pFrameParent = pParent->mpWindowImpl->mpFrameWindow;
mpWindowImpl->mpFrameData = pFrameParent->mpWindowImpl->mpFrameData;
if (mpWindowImpl->mpFrame != pFrameParent->mpWindowImpl->mpFrame)
{
mpWindowImpl->mpFrame = pFrameParent->mpWindowImpl->mpFrame;
if (mpWindowImpl->mpSysObj)
mpWindowImpl->mpSysObj->Reparent(mpWindowImpl->mpFrame);
}
mpWindowImpl->mpFrameWindow = pFrameParent;
mpWindowImpl->mbFrame = false;
// search overlap window and insert window in list
if ( ImplIsOverlapWindow() )
{
vcl::Window* pFirstOverlapParent = pParent;
while ( !pFirstOverlapParent->ImplIsOverlapWindow() )
pFirstOverlapParent = pFirstOverlapParent->ImplGetParent();
mpWindowImpl->mpOverlapWindow = pFirstOverlapParent;
mpWindowImpl->mpNextOverlap = mpWindowImpl->mpFrameData->mpFirstOverlap;
mpWindowImpl->mpFrameData->mpFirstOverlap = this;
// Overlap-Windows are by default the uppermost
mpWindowImpl->mpNext = pFirstOverlapParent->mpWindowImpl->mpFirstOverlap;
pFirstOverlapParent->mpWindowImpl->mpFirstOverlap = this;
if ( !pFirstOverlapParent->mpWindowImpl->mpLastOverlap )
pFirstOverlapParent->mpWindowImpl->mpLastOverlap = this;
else
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = this;
}
else
{
if ( pParent->ImplIsOverlapWindow() )
mpWindowImpl->mpOverlapWindow = pParent;
else
mpWindowImpl->mpOverlapWindow = pParent->mpWindowImpl->mpOverlapWindow;
mpWindowImpl->mpPrev = pParent->mpWindowImpl->mpLastChild;
pParent->mpWindowImpl->mpLastChild = this;
if ( !pParent->mpWindowImpl->mpFirstChild )
pParent->mpWindowImpl->mpFirstChild = this;
else
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
}
}
void Window::ImplRemoveWindow( bool bRemoveFrameData )
{
// remove window from the lists
if ( !mpWindowImpl->mbFrame )
{
if ( ImplIsOverlapWindow() )
{
if ( mpWindowImpl->mpFrameData->mpFirstOverlap.get() == this )
mpWindowImpl->mpFrameData->mpFirstOverlap = mpWindowImpl->mpNextOverlap;
else
{
vcl::Window* pTempWin = mpWindowImpl->mpFrameData->mpFirstOverlap;
while ( pTempWin->mpWindowImpl->mpNextOverlap.get() != this )
pTempWin = pTempWin->mpWindowImpl->mpNextOverlap;
pTempWin->mpWindowImpl->mpNextOverlap = mpWindowImpl->mpNextOverlap;
}
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
}
else
{
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else if ( mpWindowImpl->mpParent )
mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else if ( mpWindowImpl->mpParent )
mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = mpWindowImpl->mpPrev;
}
mpWindowImpl->mpPrev = nullptr;
mpWindowImpl->mpNext = nullptr;
}
if ( bRemoveFrameData )
{
// release the graphic
OutputDevice *pOutDev = GetOutDev();
pOutDev->ReleaseGraphics();
}
}
void Window::reorderWithinParent(sal_uInt16 nNewPosition)
{
sal_uInt16 nChildCount = 0;
vcl::Window *pSource = mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild;
while (pSource)
{
if (nChildCount == nNewPosition)
break;
pSource = pSource->mpWindowImpl->mpNext;
nChildCount++;
}
if (pSource == this) //already at the right place
return;
ImplRemoveWindow(false);
if (pSource)
{
mpWindowImpl->mpNext = pSource;
mpWindowImpl->mpPrev = pSource->mpWindowImpl->mpPrev;
pSource->mpWindowImpl->mpPrev = this;
}
else
mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = this;
if (mpWindowImpl->mpPrev)
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
else
mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = this;
}
void Window::ImplToBottomChild()
{
if ( ImplIsOverlapWindow() || mpWindowImpl->mbReallyVisible || (mpWindowImpl->mpParent->mpWindowImpl->mpLastChild.get() == this) )
return;
// put the window to the end of the list
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else
mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
mpWindowImpl->mpPrev = mpWindowImpl->mpParent->mpWindowImpl->mpLastChild;
mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = this;
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
mpWindowImpl->mpNext = nullptr;
}
void Window::ImplCalcToTop( ImplCalcToTopData* pPrevData )
{
SAL_WARN_IF( !ImplIsOverlapWindow(), "vcl", "Window::ImplCalcToTop(): Is not an OverlapWindow" );
if ( mpWindowImpl->mbFrame )
return;
if ( !IsReallyVisible() )
return;
// calculate region, where the window overlaps with other windows
vcl::Region aRegion( GetOutputRectPixel() );
vcl::Region aInvalidateRegion;
ImplCalcOverlapRegionOverlaps( aRegion, aInvalidateRegion );
if ( !aInvalidateRegion.IsEmpty() )
{
ImplCalcToTopData* pData = new ImplCalcToTopData;
pPrevData->mpNext.reset(pData);
pData->mpWindow = this;
pData->mpInvalidateRegion.reset(new vcl::Region( aInvalidateRegion ));
}
}
void Window::ImplToTop( ToTopFlags nFlags )
{
SAL_WARN_IF( !ImplIsOverlapWindow(), "vcl", "Window::ImplToTop(): Is not an OverlapWindow" );
if ( mpWindowImpl->mbFrame )
{
// on a mouse click in the external window, it is the latter's
// responsibility to assure our frame is put in front
if ( !mpWindowImpl->mpFrameData->mbHasFocus &&
!mpWindowImpl->mpFrameData->mbSysObjFocus &&
!mpWindowImpl->mpFrameData->mbInSysObjFocusHdl &&
!mpWindowImpl->mpFrameData->mbInSysObjToTopHdl )
{
// do not bring floating windows on the client to top
if( !ImplGetClientWindow() || !(ImplGetClientWindow()->GetStyle() & WB_SYSTEMFLOATWIN) )
{
SalFrameToTop nSysFlags = SalFrameToTop::NONE;
if ( nFlags & ToTopFlags::RestoreWhenMin )
nSysFlags |= SalFrameToTop::RestoreWhenMin;
if ( nFlags & ToTopFlags::ForegroundTask )
nSysFlags |= SalFrameToTop::ForegroundTask;
if ( nFlags & ToTopFlags::GrabFocusOnly )
nSysFlags |= SalFrameToTop::GrabFocusOnly;
mpWindowImpl->mpFrame->ToTop( nSysFlags );
}
}
}
else
{
if ( mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap.get() != this )
{
// remove window from the list
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
// take AlwaysOnTop into account
bool bOnTop = IsAlwaysOnTopEnabled();
vcl::Window* pNextWin = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
if ( !bOnTop )
{
while ( pNextWin )
{
if ( !pNextWin->IsAlwaysOnTopEnabled() )
break;
pNextWin = pNextWin->mpWindowImpl->mpNext;
}
}
// add the window to the list again
mpWindowImpl->mpNext = pNextWin;
if ( pNextWin )
{
mpWindowImpl->mpPrev = pNextWin->mpWindowImpl->mpPrev;
pNextWin->mpWindowImpl->mpPrev = this;
}
else
{
mpWindowImpl->mpPrev = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap;
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = this;
}
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = this;
// recalculate ClipRegion of this and all overlapping windows
if ( IsReallyVisible() )
{
mpWindowImpl->mpOverlapWindow->ImplSetClipFlagOverlapWindows();
}
}
}
}
void Window::ImplStartToTop( ToTopFlags nFlags )
{
ImplCalcToTopData aStartData;
ImplCalcToTopData* pCurData;
vcl::Window* pOverlapWindow;
if ( ImplIsOverlapWindow() )
pOverlapWindow = this;
else
pOverlapWindow = mpWindowImpl->mpOverlapWindow;
// first calculate paint areas
vcl::Window* pTempOverlapWindow = pOverlapWindow;
aStartData.mpNext = nullptr;
pCurData = &aStartData;
do
{
pTempOverlapWindow->ImplCalcToTop( pCurData );
if ( pCurData->mpNext )
pCurData = pCurData->mpNext.get();
pTempOverlapWindow = pTempOverlapWindow->mpWindowImpl->mpOverlapWindow;
}
while ( !pTempOverlapWindow->mpWindowImpl->mbFrame );
// next calculate the paint areas of the ChildOverlap windows
pTempOverlapWindow = mpWindowImpl->mpFirstOverlap;
while ( pTempOverlapWindow )
{
pTempOverlapWindow->ImplCalcToTop( pCurData );
if ( pCurData->mpNext )
pCurData = pCurData->mpNext.get();
pTempOverlapWindow = pTempOverlapWindow->mpWindowImpl->mpNext;
}
// and next change the windows list
pTempOverlapWindow = pOverlapWindow;
do
{
pTempOverlapWindow->ImplToTop( nFlags );
pTempOverlapWindow = pTempOverlapWindow->mpWindowImpl->mpOverlapWindow;
}
while ( !pTempOverlapWindow->mpWindowImpl->mbFrame );
// as last step invalidate the invalid areas
pCurData = aStartData.mpNext.get();
while ( pCurData )
{
pCurData->mpWindow->ImplInvalidateFrameRegion( pCurData->mpInvalidateRegion.get(), InvalidateFlags::Children );
pCurData = pCurData->mpNext.get();
}
}
void Window::ImplFocusToTop( ToTopFlags nFlags, bool bReallyVisible )
{
// do we need to fetch the focus?
if ( !(nFlags & ToTopFlags::NoGrabFocus) )
{
// first window with GrabFocus-Activate gets the focus
vcl::Window* pFocusWindow = this;
while ( !pFocusWindow->ImplIsOverlapWindow() )
{
// if the window has no BorderWindow, we
// should always find the belonging BorderWindow
if ( !pFocusWindow->mpWindowImpl->mpBorderWindow )
{
if ( pFocusWindow->mpWindowImpl->mnActivateMode & ActivateModeFlags::GrabFocus )
break;
}
pFocusWindow = pFocusWindow->ImplGetParent();
}
if ( (pFocusWindow->mpWindowImpl->mnActivateMode & ActivateModeFlags::GrabFocus) &&
!pFocusWindow->HasChildPathFocus( true ) )
pFocusWindow->GrabFocus();
}
if ( bReallyVisible )
ImplGenerateMouseMove();
}
void Window::ImplShowAllOverlaps()
{
vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
while ( pOverlapWindow )
{
if ( pOverlapWindow->mpWindowImpl->mbOverlapVisible )
{
pOverlapWindow->Show( true, ShowFlags::NoActivate );
pOverlapWindow->mpWindowImpl->mbOverlapVisible = false;
}
pOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
}
}
void Window::ImplHideAllOverlaps()
{
vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
while ( pOverlapWindow )
{
if ( pOverlapWindow->IsVisible() )
{
pOverlapWindow->mpWindowImpl->mbOverlapVisible = true;
pOverlapWindow->Show( false );
}
pOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
}
}
void Window::ToTop( ToTopFlags nFlags )
{
if (!mpWindowImpl)
return;
ImplStartToTop( nFlags );
ImplFocusToTop( nFlags, IsReallyVisible() );
}
void Window::SetZOrder( vcl::Window* pRefWindow, ZOrderFlags nFlags )
{
if ( mpWindowImpl->mpBorderWindow )
{
mpWindowImpl->mpBorderWindow->SetZOrder( pRefWindow, nFlags );
return;
}
if ( nFlags & ZOrderFlags::First )
{
if ( ImplIsOverlapWindow() )
pRefWindow = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
else
pRefWindow = mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild;
nFlags |= ZOrderFlags::Before;
}
else if ( nFlags & ZOrderFlags::Last )
{
if ( ImplIsOverlapWindow() )
pRefWindow = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap;
else
pRefWindow = mpWindowImpl->mpParent->mpWindowImpl->mpLastChild;
nFlags |= ZOrderFlags::Behind;
}
while ( pRefWindow && pRefWindow->mpWindowImpl->mpBorderWindow )
pRefWindow = pRefWindow->mpWindowImpl->mpBorderWindow;
if (!pRefWindow || pRefWindow == this || mpWindowImpl->mbFrame)
return;
SAL_WARN_IF( pRefWindow->mpWindowImpl->mpParent != mpWindowImpl->mpParent, "vcl", "Window::SetZOrder() - pRefWindow has other parent" );
if ( nFlags & ZOrderFlags::Before )
{
if ( pRefWindow->mpWindowImpl->mpPrev.get() == this )
return;
if ( ImplIsOverlapWindow() )
{
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
if ( !pRefWindow->mpWindowImpl->mpPrev )
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = this;
}
else
{
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else
mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else
mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = mpWindowImpl->mpPrev;
if ( !pRefWindow->mpWindowImpl->mpPrev )
mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = this;
}
mpWindowImpl->mpPrev = pRefWindow->mpWindowImpl->mpPrev;
mpWindowImpl->mpNext = pRefWindow;
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = this;
}
else if ( nFlags & ZOrderFlags::Behind )
{
if ( pRefWindow->mpWindowImpl->mpNext.get() == this )
return;
if ( ImplIsOverlapWindow() )
{
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
if ( !pRefWindow->mpWindowImpl->mpNext )
mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = this;
}
else
{
if ( mpWindowImpl->mpPrev )
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
else
mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
else
mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = mpWindowImpl->mpPrev;
if ( !pRefWindow->mpWindowImpl->mpNext )
mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = this;
}
mpWindowImpl->mpPrev = pRefWindow;
mpWindowImpl->mpNext = pRefWindow->mpWindowImpl->mpNext;
if ( mpWindowImpl->mpNext )
mpWindowImpl->mpNext->mpWindowImpl->mpPrev = this;
mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
}
if ( !IsReallyVisible() )
return;
if ( !mpWindowImpl->mbInitWinClipRegion && mpWindowImpl->maWinClipRegion.IsEmpty() )
return;
bool bInitWinClipRegion = mpWindowImpl->mbInitWinClipRegion;
ImplSetClipFlag();
// When ClipRegion was not initialised, assume
// the window has not been sent, therefore do not
// trigger any Invalidates. This is an optimization
// for HTML documents with many controls. If this
// check gives problems, a flag should be introduced
// which tracks whether the window has already been
// emitted after Show
if ( bInitWinClipRegion )
return;
// Invalidate all windows which are next to each other
// Is INCOMPLETE !!!
tools::Rectangle aWinRect = GetOutputRectPixel();
vcl::Window* pWindow = nullptr;
if ( ImplIsOverlapWindow() )
{
if ( mpWindowImpl->mpOverlapWindow )
pWindow = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
}
else
pWindow = ImplGetParent()->mpWindowImpl->mpFirstChild;
// Invalidate all windows in front of us and which are covered by us
while ( pWindow )
{
if ( pWindow == this )
break;
tools::Rectangle aCompRect = pWindow->GetOutputRectPixel();
if ( aWinRect.Overlaps( aCompRect ) )
pWindow->Invalidate( InvalidateFlags::Children | InvalidateFlags::NoTransparent );
pWindow = pWindow->mpWindowImpl->mpNext;
}
// If we are covered by a window in the background
// we should redraw it
while ( pWindow )
{
if ( pWindow != this )
{
tools::Rectangle aCompRect = pWindow->GetOutputRectPixel();
if ( aWinRect.Overlaps( aCompRect ) )
{
Invalidate( InvalidateFlags::Children | InvalidateFlags::NoTransparent );
break;
}
}
pWindow = pWindow->mpWindowImpl->mpNext;
}
}
void Window::EnableAlwaysOnTop( bool bEnable )
{
mpWindowImpl->mbAlwaysOnTop = bEnable;
if ( mpWindowImpl->mpBorderWindow )
mpWindowImpl->mpBorderWindow->EnableAlwaysOnTop( bEnable );
else if ( bEnable && IsReallyVisible() )
ToTop();
if ( mpWindowImpl->mbFrame )
mpWindowImpl->mpFrame->SetAlwaysOnTop( bEnable );
}
bool Window::IsTopWindow() const
{
if ( !mpWindowImpl || mpWindowImpl->mbInDispose )
return false;
// topwindows must be frames or they must have a borderwindow which is a frame
if( !mpWindowImpl->mbFrame && (!mpWindowImpl->mpBorderWindow || !mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame ) )
return false;
ImplGetWinData();
if( mpWindowImpl->mpWinData->mnIsTopWindow == sal_uInt16(~0)) // still uninitialized
{
// #113722#, cache result of expensive queryInterface call
vcl::Window *pThisWin = const_cast<vcl::Window*>(this);
uno::Reference< XTopWindow > xTopWindow( pThisWin->GetComponentInterface(), UNO_QUERY );
pThisWin->mpWindowImpl->mpWinData->mnIsTopWindow = xTopWindow.is() ? 1 : 0;
}
return mpWindowImpl->mpWinData->mnIsTopWindow == 1;
}
vcl::Window* Window::ImplFindWindow( const Point& rFramePos )
{
vcl::Window* pTempWindow;
vcl::Window* pFindWindow;
// first check all overlapping windows
pTempWindow = mpWindowImpl->mpFirstOverlap;
while ( pTempWindow )
{
pFindWindow = pTempWindow->ImplFindWindow( rFramePos );
if ( pFindWindow )
return pFindWindow;
pTempWindow = pTempWindow->mpWindowImpl->mpNext;
}
// then we check our window
if ( !mpWindowImpl->mbVisible )
return nullptr;
WindowHitTest nHitTest = ImplHitTest( rFramePos );
if ( nHitTest & WindowHitTest::Inside )
{
// and then we check all child windows
pTempWindow = mpWindowImpl->mpFirstChild;
while ( pTempWindow )
{
pFindWindow = pTempWindow->ImplFindWindow( rFramePos );
if ( pFindWindow )
return pFindWindow;
pTempWindow = pTempWindow->mpWindowImpl->mpNext;
}
if ( nHitTest & WindowHitTest::Transparent )
return nullptr;
else
return this;
}
return nullptr;
}
bool Window::ImplIsRealParentPath( const vcl::Window* pWindow ) const
{
pWindow = pWindow->GetParent();
while ( pWindow )
{
if ( pWindow == this )
return true;
pWindow = pWindow->GetParent();
}
return false;
}
bool Window::ImplIsChild( const vcl::Window* pWindow, bool bSystemWindow ) const
{
do
{
if ( !bSystemWindow && pWindow->ImplIsOverlapWindow() )
break;
pWindow = pWindow->ImplGetParent();
if ( pWindow == this )
return true;
}
while ( pWindow );
return false;
}
bool Window::ImplIsWindowOrChild( const vcl::Window* pWindow, bool bSystemWindow ) const
{
if ( this == pWindow )
return true;
return ImplIsChild( pWindow, bSystemWindow );
}
void Window::ImplResetReallyVisible()
{
bool bBecameReallyInvisible = mpWindowImpl->mbReallyVisible;
GetOutDev()->mbDevOutput = false;
mpWindowImpl->mbReallyVisible = false;
mpWindowImpl->mbReallyShown = false;
// the SHOW/HIDE events serve as indicators to send child creation/destroy events to the access bridge.
// For this, the data member of the event must not be NULL.
// Previously, we did this in Window::Show, but there some events got lost in certain situations.
if( bBecameReallyInvisible && ImplIsAccessibleCandidate() )
CallEventListeners( VclEventId::WindowHide, this );
// TODO. It's kind of a hack that we're re-using the VclEventId::WindowHide. Normally, we should
// introduce another event which explicitly triggers the Accessibility implementations.
vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
while ( pWindow )
{
if ( pWindow->mpWindowImpl->mbReallyVisible )
pWindow->ImplResetReallyVisible();
pWindow = pWindow->mpWindowImpl->mpNext;
}
pWindow = mpWindowImpl->mpFirstChild;
while ( pWindow )
{
if ( pWindow->mpWindowImpl->mbReallyVisible )
pWindow->ImplResetReallyVisible();
pWindow = pWindow->mpWindowImpl->mpNext;
}
}
void Window::ImplUpdateWindowPtr( vcl::Window* pWindow )
{
if ( mpWindowImpl->mpFrameWindow != pWindow->mpWindowImpl->mpFrameWindow )
{
// release graphic
OutputDevice *pOutDev = GetOutDev();
pOutDev->ReleaseGraphics();
}
mpWindowImpl->mpFrameData = pWindow->mpWindowImpl->mpFrameData;
if (mpWindowImpl->mpFrame != pWindow->mpWindowImpl->mpFrame)
{
mpWindowImpl->mpFrame = pWindow->mpWindowImpl->mpFrame;
if (mpWindowImpl->mpSysObj)
mpWindowImpl->mpSysObj->Reparent(mpWindowImpl->mpFrame);
}
mpWindowImpl->mpFrameWindow = pWindow->mpWindowImpl->mpFrameWindow;
if ( pWindow->ImplIsOverlapWindow() )
mpWindowImpl->mpOverlapWindow = pWindow;
else
mpWindowImpl->mpOverlapWindow = pWindow->mpWindowImpl->mpOverlapWindow;
vcl::Window* pChild = mpWindowImpl->mpFirstChild;
while ( pChild )
{
pChild->ImplUpdateWindowPtr( pWindow );
pChild = pChild->mpWindowImpl->mpNext;
}
}
void Window::ImplUpdateWindowPtr()
{
vcl::Window* pChild = mpWindowImpl->mpFirstChild;
while ( pChild )
{
pChild->ImplUpdateWindowPtr( this );
pChild = pChild->mpWindowImpl->mpNext;
}
}
void Window::ImplUpdateOverlapWindowPtr( bool bNewFrame )
{
bool bVisible = IsVisible();
Show( false );
ImplRemoveWindow( bNewFrame );
vcl::Window* pRealParent = mpWindowImpl->mpRealParent;
ImplInsertWindow( ImplGetParent() );
mpWindowImpl->mpRealParent = pRealParent;
ImplUpdateWindowPtr();
if ( ImplUpdatePos() )
ImplUpdateSysObjPos();
if ( bNewFrame )
{
vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
while ( pOverlapWindow )
{
vcl::Window* pNextOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
pOverlapWindow->ImplUpdateOverlapWindowPtr( bNewFrame );
pOverlapWindow = pNextOverlapWindow;
}
}
if ( bVisible )
Show();
}
SystemWindow* Window::GetSystemWindow() const
{
const vcl::Window* pWin = this;
while ( pWin && !pWin->IsSystemWindow() )
pWin = pWin->GetParent();
return static_cast<SystemWindow*>(const_cast<Window*>(pWin));
}
static SystemWindow *ImplGetLastSystemWindow( vcl::Window *pWin )
{
// get the most top-level system window, the one that contains the taskpanelist
SystemWindow *pSysWin = nullptr;
if( !pWin )
return pSysWin;
vcl::Window *pMyParent = pWin;
while ( pMyParent )
{
if ( pMyParent->IsSystemWindow() )
pSysWin = static_cast<SystemWindow*>(pMyParent);
pMyParent = pMyParent->GetParent();
}
return pSysWin;
}
void Window::SetParent( vcl::Window* pNewParent )
{
SAL_WARN_IF( !pNewParent, "vcl", "Window::SetParent(): pParent == NULL" );
SAL_WARN_IF( pNewParent == this, "vcl", "someone tried to reparent a window to itself" );
if( !pNewParent || pNewParent == this )
return;
// check if the taskpanelist would change and move the window pointer accordingly
SystemWindow *pSysWin = ImplGetLastSystemWindow(this);
SystemWindow *pNewSysWin = nullptr;
bool bChangeTaskPaneList = false;
if( pSysWin && pSysWin->ImplIsInTaskPaneList( this ) )
{
pNewSysWin = ImplGetLastSystemWindow( pNewParent );
if( pNewSysWin && pNewSysWin != pSysWin )
{
bChangeTaskPaneList = true;
pSysWin->GetTaskPaneList()->RemoveWindow( this );
}
}
// remove ownerdraw decorated windows from list in the top-most frame window
if( (GetStyle() & WB_OWNERDRAWDECORATION) && mpWindowImpl->mbFrame )
{
::std::vector< VclPtr<vcl::Window> >& rList = ImplGetOwnerDrawList();
auto p = ::std::find( rList.begin(), rList.end(), VclPtr<vcl::Window>(this) );
if( p != rList.end() )
rList.erase( p );
}
ImplSetFrameParent( pNewParent );
if ( mpWindowImpl->mpBorderWindow )
{
mpWindowImpl->mpRealParent = pNewParent;
mpWindowImpl->mpBorderWindow->SetParent( pNewParent );
return;
}
if ( mpWindowImpl->mpParent.get() == pNewParent )
return;
if ( mpWindowImpl->mbFrame )
mpWindowImpl->mpFrame->SetParent( pNewParent->mpWindowImpl->mpFrame );
bool bVisible = IsVisible();
Show( false, ShowFlags::NoFocusChange );
// check if the overlap window changes
vcl::Window* pOldOverlapWindow;
vcl::Window* pNewOverlapWindow = nullptr;
if ( ImplIsOverlapWindow() )
pOldOverlapWindow = nullptr;
else
{
pNewOverlapWindow = pNewParent->ImplGetFirstOverlapWindow();
if ( mpWindowImpl->mpOverlapWindow.get() != pNewOverlapWindow )
pOldOverlapWindow = mpWindowImpl->mpOverlapWindow;
else
pOldOverlapWindow = nullptr;
}
// convert windows in the hierarchy
bool bFocusOverlapWin = HasChildPathFocus( true );
bool bFocusWin = HasChildPathFocus();
bool bNewFrame = pNewParent->mpWindowImpl->mpFrameWindow != mpWindowImpl->mpFrameWindow;
if ( bNewFrame )
{
if ( mpWindowImpl->mpFrameData->mpFocusWin )
{
if ( IsWindowOrChild( mpWindowImpl->mpFrameData->mpFocusWin ) )
mpWindowImpl->mpFrameData->mpFocusWin = nullptr;
}
if ( mpWindowImpl->mpFrameData->mpMouseMoveWin )
{
if ( IsWindowOrChild( mpWindowImpl->mpFrameData->mpMouseMoveWin ) )
mpWindowImpl->mpFrameData->mpMouseMoveWin = nullptr;
}
if ( mpWindowImpl->mpFrameData->mpMouseDownWin )
{
if ( IsWindowOrChild( mpWindowImpl->mpFrameData->mpMouseDownWin ) )
mpWindowImpl->mpFrameData->mpMouseDownWin = nullptr;
}
}
ImplRemoveWindow( bNewFrame );
ImplInsertWindow( pNewParent );
if ( mpWindowImpl->mnParentClipMode & ParentClipMode::Clip )
pNewParent->mpWindowImpl->mbClipChildren = true;
ImplUpdateWindowPtr();
if ( ImplUpdatePos() )
ImplUpdateSysObjPos();
// If the Overlap-Window has changed, we need to test whether
// OverlapWindows that had the Child window as their parent
// need to be put into the window hierarchy.
if ( ImplIsOverlapWindow() )
{
if ( bNewFrame )
{
vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
while ( pOverlapWindow )
{
vcl::Window* pNextOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
pOverlapWindow->ImplUpdateOverlapWindowPtr( bNewFrame );
pOverlapWindow = pNextOverlapWindow;
}
}
}
else if ( pOldOverlapWindow )
{
// reset Focus-Save
if ( bFocusWin ||
(pOldOverlapWindow->mpWindowImpl->mpLastFocusWindow &&
IsWindowOrChild( pOldOverlapWindow->mpWindowImpl->mpLastFocusWindow )) )
pOldOverlapWindow->mpWindowImpl->mpLastFocusWindow = nullptr;
vcl::Window* pOverlapWindow = pOldOverlapWindow->mpWindowImpl->mpFirstOverlap;
while ( pOverlapWindow )
{
vcl::Window* pNextOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
if ( ImplIsRealParentPath( pOverlapWindow->ImplGetWindow() ) )
pOverlapWindow->ImplUpdateOverlapWindowPtr( bNewFrame );
pOverlapWindow = pNextOverlapWindow;
}
// update activate-status at next overlap window
if ( HasChildPathFocus( true ) )
ImplCallFocusChangeActivate( pNewOverlapWindow, pOldOverlapWindow );
}
// also convert Activate-Status
if ( bNewFrame )
{
if ( (GetType() == WindowType::BORDERWINDOW) &&
(ImplGetWindow()->GetType() == WindowType::FLOATINGWINDOW) )
static_cast<ImplBorderWindow*>(this)->SetDisplayActive( mpWindowImpl->mpFrameData->mbHasFocus );
}
// when required give focus to new frame if
// FocusWindow is changed with SetParent()
if ( bFocusOverlapWin )
{
mpWindowImpl->mpFrameData->mpFocusWin = Application::GetFocusWindow();
if ( !mpWindowImpl->mpFrameData->mbHasFocus )
{
mpWindowImpl->mpFrame->ToTop( SalFrameToTop::NONE );
}
}
// Assure DragSource and DropTarget members are created
if ( bNewFrame )
{
GetDropTarget();
}
if( bChangeTaskPaneList )
pNewSysWin->GetTaskPaneList()->AddWindow( this );
if( (GetStyle() & WB_OWNERDRAWDECORATION) && mpWindowImpl->mbFrame )
ImplGetOwnerDrawList().emplace_back(this );
if ( bVisible )
Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
}
bool Window::IsAncestorOf( const vcl::Window& rWindow ) const
{
return ImplIsRealParentPath(&rWindow);
}
sal_uInt16 Window::GetChildCount() const
{
if (!mpWindowImpl)
return 0;
sal_uInt16 nChildCount = 0;
vcl::Window* pChild = mpWindowImpl->mpFirstChild;
while ( pChild )
{
nChildCount++;
pChild = pChild->mpWindowImpl->mpNext;
}
return nChildCount;
}
vcl::Window* Window::GetChild( sal_uInt16 nChild ) const
{
if (!mpWindowImpl)
return nullptr;
sal_uInt16 nChildCount = 0;
vcl::Window* pChild = mpWindowImpl->mpFirstChild;
while ( pChild )
{
if ( nChild == nChildCount )
return pChild;
pChild = pChild->mpWindowImpl->mpNext;
nChildCount++;
}
return nullptr;
}
vcl::Window* Window::GetWindow( GetWindowType nType ) const
{
if (!mpWindowImpl)
return nullptr;
switch ( nType )
{
case GetWindowType::Parent:
return mpWindowImpl->mpRealParent;
case GetWindowType::FirstChild:
return mpWindowImpl->mpFirstChild;
case GetWindowType::LastChild:
return mpWindowImpl->mpLastChild;
case GetWindowType::Prev:
return mpWindowImpl->mpPrev;
case GetWindowType::Next:
return mpWindowImpl->mpNext;
case GetWindowType::FirstOverlap:
return mpWindowImpl->mpFirstOverlap;
case GetWindowType::Overlap:
if ( ImplIsOverlapWindow() )
return const_cast<vcl::Window*>(this);
else
return mpWindowImpl->mpOverlapWindow;
case GetWindowType::ParentOverlap:
if ( ImplIsOverlapWindow() )
return mpWindowImpl->mpOverlapWindow;
else
return mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpOverlapWindow;
case GetWindowType::Client:
return this->ImplGetWindow();
case GetWindowType::RealParent:
return ImplGetParent();
case GetWindowType::Frame:
return mpWindowImpl->mpFrameWindow;
case GetWindowType::Border:
if ( mpWindowImpl->mpBorderWindow )
return mpWindowImpl->mpBorderWindow->GetWindow( GetWindowType::Border );
return const_cast<vcl::Window*>(this);
case GetWindowType::FirstTopWindowChild:
return ImplGetWinData()->maTopWindowChildren.empty() ? nullptr : (*ImplGetWinData()->maTopWindowChildren.begin()).get();
case GetWindowType::NextTopWindowSibling:
{
if ( !mpWindowImpl->mpRealParent )
return nullptr;
const ::std::list< VclPtr<vcl::Window> >& rTopWindows( mpWindowImpl->mpRealParent->ImplGetWinData()->maTopWindowChildren );
::std::list< VclPtr<vcl::Window> >::const_iterator myPos =
::std::find( rTopWindows.begin(), rTopWindows.end(), this );
if ( ( myPos == rTopWindows.end() ) || ( ++myPos == rTopWindows.end() ) )
return nullptr;
return *myPos;
}
}
return nullptr;
}
bool Window::IsChild( const vcl::Window* pWindow ) const
{
do
{
if ( pWindow->ImplIsOverlapWindow() )
break;
pWindow = pWindow->ImplGetParent();
if ( pWindow == this )
return true;
}
while ( pWindow );
return false;
}
bool Window::IsWindowOrChild( const vcl::Window* pWindow, bool bSystemWindow ) const
{
if ( this == pWindow )
return true;
return ImplIsChild( pWindow, bSystemWindow );
}
void Window::ImplSetFrameParent( const vcl::Window* pParent )
{
vcl::Window* pFrameWindow = ImplGetSVData()->maFrameData.mpFirstFrame;
while( pFrameWindow )
{
// search all frames that are children of this window
// and reparent them
if( ImplIsRealParentPath( pFrameWindow ) )
{
SAL_WARN_IF( mpWindowImpl->mpFrame == pFrameWindow->mpWindowImpl->mpFrame, "vcl", "SetFrameParent to own" );
SAL_WARN_IF( !mpWindowImpl->mpFrame, "vcl", "no frame" );
SalFrame* pParentFrame = pParent ? pParent->mpWindowImpl->mpFrame : nullptr;
pFrameWindow->mpWindowImpl->mpFrame->SetParent( pParentFrame );
}
pFrameWindow = pFrameWindow->mpWindowImpl->mpFrameData->mpNextFrame;
}
}
} /* namespace vcl */
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */