office-gobmx/vcl/source/window/dockmgr.cxx
Michael Weghorn 0008ff3597 tdf#141101 a11y: Ensure focused event after showing popup content
If the `FloatWinPopupFlags::GrabFocus` flag is set,
don't only pass it to `FloatingWindow::StartPopupMode`,
but explicitly grab focus again after showing the
docking window (client window of the floating window).

At least proper handling for accessible focus/focused
events depends on the window being visible at the point
it receives focus.

For winaccessibility, the a11y event listener only
gets registered once the window gets shown
(`VCLXAccessibleComponent::ProcessWindowChildEvent`
forwards a `VclEventId::WindowShow` as
`accessibility::AccessibleEventId::CHILD` event, which
winaccessibility then processes and registers an
event listener for the new child).

This makes NVDA on Windows announce focus for the
"Automatic" button in the color popup in Writer's
character style dialog also when the popup shows
for the first time.

Change-Id: I2e4028eb4ec68a8b586cbd1a0dc5351d1453937e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155843
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
2023-08-18 20:13:55 +02:00

1083 lines
37 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 <tools/time.hxx>
#include <sal/log.hxx>
#include <o3tl/deleter.hxx>
#include <brdwin.hxx>
#include <svdata.hxx>
#include <window.h>
#include <vcl/event.hxx>
#include <vcl/toolkit/floatwin.hxx>
#include <vcl/dockwin.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/svapp.hxx>
#include <vcl/timer.hxx>
#include <vcl/settings.hxx>
#include "impldockingwrapper.hxx"
#define DOCKWIN_FLOATSTYLES (WB_SIZEABLE | WB_MOVEABLE | WB_CLOSEABLE | WB_STANDALONE)
namespace {
class ImplDockFloatWin2 : public FloatingWindow
{
private:
ImplDockingWindowWrapper* mpDockWin;
sal_uInt64 mnLastTicks;
Timer m_aDockTimer;
Timer m_aEndDockTimer;
Point maDockPos;
tools::Rectangle maDockRect;
bool mbInMove;
ImplSVEvent * mnLastUserEvent;
DECL_LINK(DockingHdl, void *, void);
DECL_LINK(DockTimerHdl, Timer *, void);
DECL_LINK(EndDockTimerHdl, Timer *, void);
public:
ImplDockFloatWin2( vcl::Window* pParent, WinBits nWinBits,
ImplDockingWindowWrapper* pDockingWin );
virtual ~ImplDockFloatWin2() override;
virtual void dispose() override;
virtual void Move() override;
virtual void Resize() override;
virtual void TitleButtonClick( TitleButton nButton ) override;
virtual void Resizing( Size& rSize ) override;
virtual bool Close() override;
};
}
ImplDockFloatWin2::ImplDockFloatWin2( vcl::Window* pParent, WinBits nWinBits,
ImplDockingWindowWrapper* pDockingWin ) :
FloatingWindow( pParent, nWinBits ),
mpDockWin( pDockingWin ),
mnLastTicks( tools::Time::GetSystemTicks() ),
m_aDockTimer("vcl::ImplDockFloatWin2 m_aDockTimer"),
m_aEndDockTimer( "vcl::ImplDockFloatWin2 m_aEndDockTimer" ),
mbInMove( false ),
mnLastUserEvent( nullptr )
{
// copy state of DockingWindow
if ( pDockingWin )
{
GetOutDev()->SetSettings( pDockingWin->GetWindow()->GetSettings() );
Enable( pDockingWin->GetWindow()->IsEnabled(), false );
EnableInput( pDockingWin->GetWindow()->IsInputEnabled(), false );
AlwaysEnableInput( pDockingWin->GetWindow()->IsAlwaysEnableInput(), false );
EnableAlwaysOnTop( pDockingWin->GetWindow()->IsAlwaysOnTopEnabled() );
SetActivateMode( pDockingWin->GetWindow()->GetActivateMode() );
}
SetBackground( GetSettings().GetStyleSettings().GetFaceColor() );
m_aDockTimer.SetInvokeHandler( LINK( this, ImplDockFloatWin2, DockTimerHdl ) );
m_aDockTimer.SetPriority( TaskPriority::HIGH_IDLE );
m_aDockTimer.SetTimeout( 50 );
m_aEndDockTimer.SetInvokeHandler( LINK( this, ImplDockFloatWin2, EndDockTimerHdl ) );
m_aEndDockTimer.SetPriority( TaskPriority::HIGH_IDLE );
m_aEndDockTimer.SetTimeout( 50 );
}
ImplDockFloatWin2::~ImplDockFloatWin2()
{
disposeOnce();
}
void ImplDockFloatWin2::dispose()
{
if( mnLastUserEvent )
Application::RemoveUserEvent( mnLastUserEvent );
FloatingWindow::dispose();
}
IMPL_LINK_NOARG(ImplDockFloatWin2, DockTimerHdl, Timer *, void)
{
SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "docktimer called but not floating" );
PointerState aState = GetPointerState();
if( aState.mnState & KEY_MOD1 )
{
// i43499 CTRL disables docking now
mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
if( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) )
m_aDockTimer.Start();
}
else if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
{
mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
mpDockWin->EndDocking( maDockRect, false );
}
else
{
mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
m_aDockTimer.Start();
}
}
IMPL_LINK_NOARG(ImplDockFloatWin2, EndDockTimerHdl, Timer *, void)
{
SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "enddocktimer called but not floating" );
PointerState aState = GetPointerState();
if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
{
mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
mpDockWin->EndDocking( maDockRect, true );
}
else
m_aEndDockTimer.Start();
}
IMPL_LINK_NOARG(ImplDockFloatWin2, DockingHdl, void*, void)
{
// called during move of a floating window
mnLastUserEvent = nullptr;
vcl::Window *pDockingArea = mpDockWin->GetWindow()->GetParent();
PointerState aState = pDockingArea->GetPointerState();
bool bRealMove = true;
if( GetStyle() & WB_OWNERDRAWDECORATION )
{
// for windows with ownerdraw decoration
// we allow docking only when the window was moved
// by dragging its caption
// and ignore move request due to resizing
vcl::Window *pBorder = GetWindow( GetWindowType::Border );
if( pBorder != this )
{
tools::Rectangle aBorderRect( Point(), pBorder->GetSizePixel() );
sal_Int32 nLeft, nTop, nRight, nBottom;
GetBorder( nLeft, nTop, nRight, nBottom );
// limit borderrect to the caption part only and without the resizing borders
aBorderRect.SetBottom( aBorderRect.Top() + nTop );
aBorderRect.AdjustLeft(nLeft );
aBorderRect.AdjustRight( -nRight );
PointerState aBorderState = pBorder->GetPointerState();
bRealMove = aBorderRect.Contains( aBorderState.maPos );
}
}
if( mpDockWin->GetWindow()->IsVisible() &&
(tools::Time::GetSystemTicks() - mnLastTicks > 500) &&
( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) &&
!(aState.mnState & KEY_MOD1) && // i43499 CTRL disables docking now
bRealMove )
{
maDockPos = pDockingArea->OutputToScreenPixel( pDockingArea->AbsoluteScreenToOutputPixel( OutputToAbsoluteScreenPixel( Point() ) ) );
maDockRect = tools::Rectangle( maDockPos, mpDockWin->GetSizePixel() );
// mouse pos in screen pixels
Point aMousePos = pDockingArea->OutputToScreenPixel( aState.maPos );
if( ! mpDockWin->IsDocking() )
mpDockWin->StartDocking( aMousePos, maDockRect );
bool bFloatMode = mpDockWin->Docking( aMousePos, maDockRect );
if( ! bFloatMode )
{
// indicates that the window could be docked at maDockRect
maDockRect.SetPos( mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ScreenToOutputPixel(
maDockRect.TopLeft() ) );
mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
m_aEndDockTimer.Stop();
m_aDockTimer.Invoke();
}
else
{
mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
m_aDockTimer.Stop();
m_aEndDockTimer.Invoke();
}
}
mbInMove = false;
}
void ImplDockFloatWin2::Move()
{
if( mbInMove )
return;
mbInMove = true;
FloatingWindow::Move();
mpDockWin->GetWindow()->Move();
/*
* note: the window should only dock if KEY_MOD1 is pressed
* and the user releases all mouse buttons. The real problem here
* is that we don't get mouse events (at least not on X)
* if the mouse is on the decoration. So we have to start an
* awkward timer based process that polls the modifier/buttons
* to see whether they are in the right condition shortly after the
* last Move message.
*/
if( ! mnLastUserEvent )
mnLastUserEvent = Application::PostUserEvent( LINK( this, ImplDockFloatWin2, DockingHdl ), nullptr, true );
}
void ImplDockFloatWin2::Resize()
{
// forwarding of resize only required if we have no borderwindow ( GetWindow() then returns 'this' )
if( GetWindow( GetWindowType::Border ) == this )
{
FloatingWindow::Resize();
Size aSize( GetSizePixel() );
mpDockWin->GetWindow()->ImplPosSizeWindow( 0, 0, aSize.Width(), aSize.Height(), PosSizeFlags::PosSize ); // TODO: is this needed ???
}
}
void ImplDockFloatWin2::TitleButtonClick( TitleButton nButton )
{
FloatingWindow::TitleButtonClick( nButton );
mpDockWin->TitleButtonClick( nButton );
}
void ImplDockFloatWin2::Resizing( Size& rSize )
{
FloatingWindow::Resizing( rSize );
mpDockWin->Resizing( rSize );
}
bool ImplDockFloatWin2::Close()
{
return true;
}
DockingManager::DockingManager()
{
}
DockingManager::~DockingManager()
{
}
ImplDockingWindowWrapper* DockingManager::GetDockingWindowWrapper( const vcl::Window *pWindow )
{
for( const auto& xWrapper : mvDockingWindows )
{
if (xWrapper && xWrapper->mpDockingWindow == pWindow)
return xWrapper.get();
}
return nullptr;
}
bool DockingManager::IsDockable( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
/*
if( pWindow->HasDockingHandler() )
return true;
*/
return (pWrapper != nullptr);
}
bool DockingManager::IsFloating( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
return pWrapper->IsFloatingMode();
else
return false;
}
bool DockingManager::IsLocked( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
return pWrapper && pWrapper->IsLocked();
}
void DockingManager::Lock( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->Lock();
}
void DockingManager::Unlock( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->Unlock();
}
void DockingManager::SetFloatingMode( const vcl::Window *pWindow, bool bFloating )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->SetFloatingMode( bFloating );
}
void DockingManager::StartPopupMode( const vcl::Window *pWindow, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->StartPopupMode( rRect, nFlags );
}
void DockingManager::StartPopupMode( ToolBox *pParentToolBox, const vcl::Window *pWindow, FloatWinPopupFlags nFlags )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->StartPopupMode( pParentToolBox, nFlags );
}
void DockingManager::StartPopupMode( ToolBox *pParentToolBox, const vcl::Window *pWindow )
{
StartPopupMode( pParentToolBox, pWindow, FloatWinPopupFlags::AllowTearOff |
FloatWinPopupFlags::AllMouseButtonClose |
FloatWinPopupFlags::NoMouseUpClose );
}
bool DockingManager::IsInPopupMode( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
return pWrapper && pWrapper->IsInPopupMode();
}
void DockingManager::EndPopupMode( const vcl::Window *pWin )
{
ImplDockingWindowWrapper *pWrapper = GetDockingWindowWrapper( pWin );
if( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() )
static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->EndPopupMode();
}
SystemWindow* DockingManager::GetFloatingWindow(const vcl::Window *pWin)
{
ImplDockingWindowWrapper *pWrapper = GetDockingWindowWrapper( pWin );
if (pWrapper)
return pWrapper->GetFloatingWindow();
return nullptr;
}
void DockingManager::SetPopupModeEndHdl( const vcl::Window *pWindow, const Link<FloatingWindow*,void>& rLink )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->SetPopupModeEndHdl(rLink);
}
void DockingManager::AddWindow( const vcl::Window *pWindow )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
return;
mvDockingWindows.emplace_back( new ImplDockingWindowWrapper( pWindow ) );
}
void DockingManager::RemoveWindow( const vcl::Window *pWindow )
{
for( auto it = mvDockingWindows.begin(); it != mvDockingWindows.end(); ++it )
{
const auto& xWrapper = *it;
if (xWrapper && xWrapper->mpDockingWindow == pWindow)
{
// deleting wrappers calls set of actions which may want to use
// wrapper we want to delete - avoid crash using temporary owner
// while erasing
auto pTemporaryOwner = std::move(*it);
mvDockingWindows.erase( it );
break;
}
}
}
void DockingManager::SetPosSizePixel( vcl::Window const *pWindow, tools::Long nX, tools::Long nY,
tools::Long nWidth, tools::Long nHeight,
PosSizeFlags nFlags )
{
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
pWrapper->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
}
tools::Rectangle DockingManager::GetPosSizePixel( const vcl::Window *pWindow )
{
tools::Rectangle aRect;
ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
if( pWrapper )
aRect = tools::Rectangle( pWrapper->GetPosPixel(), pWrapper->GetSizePixel() );
return aRect;
}
class ImplPopupFloatWin : public FloatingWindow
{
private:
bool mbToolBox;
public:
ImplPopupFloatWin( vcl::Window* pParent, bool bToolBox );
virtual ~ImplPopupFloatWin() override;
virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
};
ImplPopupFloatWin::ImplPopupFloatWin( vcl::Window* pParent, bool bToolBox ) :
FloatingWindow( pParent, bToolBox ? WB_BORDER | WB_POPUP | WB_SYSTEMWINDOW | WB_NOSHADOW : WB_STDPOPUP ),
mbToolBox( bToolBox )
{
if ( bToolBox )
{
// indicate window type, required for accessibility
// which should not see this window as a toplevel window
mpWindowImpl->mbToolbarFloatingWindow = true;
}
}
ImplPopupFloatWin::~ImplPopupFloatWin()
{
disposeOnce();
}
css::uno::Reference< css::accessibility::XAccessible > ImplPopupFloatWin::CreateAccessible()
{
if ( !mbToolBox )
return FloatingWindow::CreateAccessible();
// switch off direct accessibility support for this window
// this is to avoid appearance of this window as standalone window in the accessibility hierarchy
// as this window is only used as a helper for subtoolbars that are not teared-off, the parent toolbar
// has to provide accessibility support (as implemented in the toolkit)
// so the contained toolbar should appear as child of the corresponding toolbar item of the parent toolbar
return css::uno::Reference< css::accessibility::XAccessible >();
}
ImplDockingWindowWrapper::ImplDockingWindowWrapper( const vcl::Window *pWindow )
: mpDockingWindow(const_cast<vcl::Window*>(pWindow))
, mpFloatWin(nullptr)
, mpOldBorderWin(nullptr)
, mpParent(pWindow->GetParent())
, maMaxOutSize( SHRT_MAX, SHRT_MAX )
, mnTrackX(0)
, mnTrackY(0)
, mnTrackWidth(0)
, mnTrackHeight(0)
, mnDockLeft(0)
, mnDockTop(0)
, mnDockRight(0)
, mnDockBottom(0)
, mnFloatBits(WB_BORDER | WB_CLOSEABLE | WB_SIZEABLE | (pWindow->GetStyle() & DOCKWIN_FLOATSTYLES))
, mbDockCanceled(false)
, mbDocking(false)
, mbLastFloatMode(false)
, mbDockBtn(false)
, mbHideBtn(false)
// must be enabled in Window::Notify to prevent permanent docking during mouse move
, mbStartDockingEnabled(false)
, mbLocked(false)
{
assert(mpDockingWindow);
DockingWindow *pDockWin = dynamic_cast< DockingWindow* > ( mpDockingWindow.get() );
if( pDockWin )
mnFloatBits = pDockWin->GetFloatStyle();
}
ImplDockingWindowWrapper::~ImplDockingWindowWrapper()
{
if ( IsFloatingMode() )
{
GetWindow()->Show( false, ShowFlags::NoFocusChange );
SetFloatingMode(false);
}
}
void ImplDockingWindowWrapper::ImplStartDocking( const Point& rPos )
{
if( !mbStartDockingEnabled )
return;
maMouseOff = rPos;
mbDocking = true;
mbLastFloatMode = IsFloatingMode();
// calculate FloatingBorder
VclPtr<FloatingWindow> pWin;
if ( mpFloatWin )
pWin = mpFloatWin;
else
pWin = VclPtr<ImplDockFloatWin2>::Create( mpParent, mnFloatBits, nullptr );
pWin->GetBorder( mnDockLeft, mnDockTop, mnDockRight, mnDockBottom );
if ( !mpFloatWin )
pWin.disposeAndClear();
Point aPos = GetWindow()->OutputToScreenPixel( Point() );
Size aSize = GetWindow()->GetOutputSizePixel();
mnTrackX = aPos.X();
mnTrackY = aPos.Y();
mnTrackWidth = aSize.Width();
mnTrackHeight = aSize.Height();
if ( mbLastFloatMode )
{
maMouseOff.AdjustX(mnDockLeft );
maMouseOff.AdjustY(mnDockTop );
mnTrackX -= mnDockLeft;
mnTrackY -= mnDockTop;
mnTrackWidth += mnDockLeft+mnDockRight;
mnTrackHeight += mnDockTop+mnDockBottom;
}
vcl::Window *pDockingArea = GetWindow()->GetParent();
vcl::Window::PointerState aState = pDockingArea->GetPointerState();
// mouse pos in screen pixels
Point aMousePos = pDockingArea->OutputToScreenPixel( aState.maPos );
Point aDockPos = pDockingArea->AbsoluteScreenToOutputPixel( GetWindow()->OutputToAbsoluteScreenPixel( GetWindow()->GetPosPixel() ) );
tools::Rectangle aDockRect( aDockPos, GetWindow()->GetSizePixel() );
StartDocking( aMousePos, aDockRect );
GetWindow()->ImplUpdateAll();
GetWindow()->ImplGetFrameWindow()->ImplUpdateAll();
GetWindow()->StartTracking( StartTrackingFlags::KeyMod );
}
void ImplDockingWindowWrapper::Tracking( const TrackingEvent& rTEvt )
{
// used during docking of a currently docked window
if ( !mbDocking )
return;
if ( rTEvt.IsTrackingEnded() )
{
mbDocking = false;
GetWindow()->HideTracking();
if ( rTEvt.IsTrackingCanceled() )
{
mbDockCanceled = true;
EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
mbDockCanceled = false;
}
else
EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
}
// Docking only upon non-synthetic MouseEvents
else if ( !rTEvt.GetMouseEvent().IsSynthetic() || rTEvt.GetMouseEvent().IsModifierChanged() )
{
Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
Point aFrameMousePos = GetWindow()->OutputToScreenPixel( aMousePos );
Size aFrameSize = GetWindow()->ImplGetFrameWindow()->GetOutputSizePixel();
if ( aFrameMousePos.X() < 0 )
aFrameMousePos.setX( 0 );
if ( aFrameMousePos.Y() < 0 )
aFrameMousePos.setY( 0 );
if ( aFrameMousePos.X() > aFrameSize.Width()-1 )
aFrameMousePos.setX( aFrameSize.Width()-1 );
if ( aFrameMousePos.Y() > aFrameSize.Height()-1 )
aFrameMousePos.setY( aFrameSize.Height()-1 );
aMousePos = GetWindow()->ScreenToOutputPixel( aFrameMousePos );
aMousePos.AdjustX( -(maMouseOff.X()) );
aMousePos.AdjustY( -(maMouseOff.Y()) );
Point aPos = GetWindow()->OutputToScreenPixel( aMousePos );
tools::Rectangle aTrackRect( aPos, Size( mnTrackWidth, mnTrackHeight ) );
tools::Rectangle aCompRect = aTrackRect;
aPos.AdjustX(maMouseOff.X() );
aPos.AdjustY(maMouseOff.Y() );
bool bFloatMode = Docking( aPos, aTrackRect );
if ( mbLastFloatMode != bFloatMode )
{
if ( bFloatMode )
{
aTrackRect.AdjustLeft( -mnDockLeft );
aTrackRect.AdjustTop( -mnDockTop );
aTrackRect.AdjustRight(mnDockRight );
aTrackRect.AdjustBottom(mnDockBottom );
}
else
{
if ( aCompRect == aTrackRect )
{
aTrackRect.AdjustLeft(mnDockLeft );
aTrackRect.AdjustTop(mnDockTop );
aTrackRect.AdjustRight( -mnDockRight );
aTrackRect.AdjustBottom( -mnDockBottom );
}
}
mbLastFloatMode = bFloatMode;
}
ShowTrackFlags nTrackStyle;
if ( bFloatMode )
nTrackStyle = ShowTrackFlags::Object;
else
nTrackStyle = ShowTrackFlags::Big;
tools::Rectangle aShowTrackRect = aTrackRect;
aShowTrackRect.SetPos( GetWindow()->ScreenToOutputPixel( aShowTrackRect.TopLeft() ) );
GetWindow()->ShowTracking( aShowTrackRect, nTrackStyle );
// calculate mouse offset again, as the rectangle was changed
maMouseOff.setX( aPos.X() - aTrackRect.Left() );
maMouseOff.setY( aPos.Y() - aTrackRect.Top() );
mnTrackX = aTrackRect.Left();
mnTrackY = aTrackRect.Top();
mnTrackWidth = aTrackRect.GetWidth();
mnTrackHeight = aTrackRect.GetHeight();
}
}
void ImplDockingWindowWrapper::StartDocking( const Point& rPoint, tools::Rectangle const & rRect )
{
DockingData data( rPoint, rRect, IsFloatingMode() );
GetWindow()->CallEventListeners( VclEventId::WindowStartDocking, &data );
mbDocking = true;
}
bool ImplDockingWindowWrapper::Docking( const Point& rPoint, tools::Rectangle& rRect )
{
DockingData data( rPoint, rRect, IsFloatingMode() );
GetWindow()->CallEventListeners( VclEventId::WindowDocking, &data );
rRect = data.maTrackRect;
return data.mbFloating;
}
void ImplDockingWindowWrapper::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
{
tools::Rectangle aRect( rRect );
bool bOrigDockCanceled = mbDockCanceled;
if (bFloatMode && !StyleSettings::GetDockingFloatsSupported())
mbDockCanceled = true;
if ( !IsDockingCanceled() )
{
bool bShow = false;
if ( bFloatMode != IsFloatingMode() )
{
GetWindow()->Show( false, ShowFlags::NoFocusChange );
SetFloatingMode( bFloatMode );
bShow = true;
if ( bFloatMode )
{
// #i44800# always use outputsize - as in all other places
mpFloatWin->SetOutputSizePixel( aRect.GetSize() );
mpFloatWin->SetPosPixel( aRect.TopLeft() );
}
}
if ( !bFloatMode )
{
Point aPos = aRect.TopLeft();
aPos = GetWindow()->GetParent()->ScreenToOutputPixel( aPos );
GetWindow()->SetPosSizePixel( aPos, aRect.GetSize() );
}
if ( bShow )
GetWindow()->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
}
EndDockingData data( aRect, IsFloatingMode(), IsDockingCanceled() );
GetWindow()->CallEventListeners( VclEventId::WindowEndDocking, &data );
mbDocking = false;
// must be enabled in Window::Notify to prevent permanent docking during mouse move
mbStartDockingEnabled = false;
mbDockCanceled = bOrigDockCanceled;
}
bool ImplDockingWindowWrapper::PrepareToggleFloatingMode()
{
bool bFloating = true;
GetWindow()->CallEventListeners( VclEventId::WindowPrepareToggleFloating, &bFloating );
return bFloating;
}
void ImplDockingWindowWrapper::ToggleFloatingMode()
{
// notify dockingwindow/toolbox
// note: this must be done *before* notifying the
// listeners to have the toolbox in the proper state
if( GetWindow()->IsDockingWindow() )
static_cast<DockingWindow*>(GetWindow())->ToggleFloatingMode();
// now notify listeners
GetWindow()->CallEventListeners( VclEventId::WindowToggleFloating );
// must be enabled in Window::Notify to prevent permanent docking during mouse move
mbStartDockingEnabled = false;
}
void ImplDockingWindowWrapper::TitleButtonClick( TitleButton nType )
{
if( nType == TitleButton::Menu )
{
ToolBox *pToolBox = dynamic_cast< ToolBox* >( GetWindow() );
if( pToolBox )
{
pToolBox->ExecuteCustomMenu();
}
}
if( nType == TitleButton::Docking )
{
SetFloatingMode( !IsFloatingMode() );
}
}
void ImplDockingWindowWrapper::Resizing( Size& rSize )
{
// TODO: add virtual Resizing() to class Window, so we can get rid of class DockingWindow
DockingWindow *pDockingWindow = dynamic_cast< DockingWindow* >( GetWindow() );
if( pDockingWindow )
pDockingWindow->Resizing( rSize );
}
void ImplDockingWindowWrapper::ShowMenuTitleButton( bool bVisible )
{
if ( mpFloatWin )
mpFloatWin->ShowTitleButton( TitleButton::Menu, bVisible );
}
void ImplDockingWindowWrapper::ImplPreparePopupMode()
{
VclPtr<vcl::Window> xWindow = GetWindow();
xWindow->Show( false, ShowFlags::NoFocusChange );
// prepare reparenting
vcl::Window* pRealParent = xWindow->GetWindow( GetWindowType::Parent );
mpOldBorderWin = xWindow->GetWindow( GetWindowType::Border );
if( mpOldBorderWin.get() == xWindow )
mpOldBorderWin = nullptr; // no border window found
// the new parent for popup mode
VclPtrInstance<ImplPopupFloatWin> pWin( mpParent, xWindow->GetType() == WindowType::TOOLBOX );
pWin->SetPopupModeEndHdl( LINK( this, ImplDockingWindowWrapper, PopupModeEnd ) );
// At least for DockingWindow, GetText() has a side effect of setting deferred
// properties. This must be done before setting the border window (see below),
// so that the border width will end up in mpWindowImpl->mnBorderWidth, not in
// the border window (See DockingWindow::setPosSizeOnContainee() and
// DockingWindow::GetOptimalSize()).
pWin->SetText( xWindow->GetText() );
pWin->SetOutputSizePixel( xWindow->GetSizePixel() );
xWindow->mpWindowImpl->mpBorderWindow = nullptr;
xWindow->mpWindowImpl->mnLeftBorder = 0;
xWindow->mpWindowImpl->mnTopBorder = 0;
xWindow->mpWindowImpl->mnRightBorder = 0;
xWindow->mpWindowImpl->mnBottomBorder = 0;
// reparent borderwindow and window
if ( mpOldBorderWin )
mpOldBorderWin->SetParent( pWin );
xWindow->SetParent( pWin );
// correct border window pointers
xWindow->mpWindowImpl->mpBorderWindow = pWin;
pWin->mpWindowImpl->mpClientWindow = xWindow;
xWindow->mpWindowImpl->mpRealParent = pRealParent;
// set mpFloatWin not until all window positioning is done !!!
// (SetPosPixel etc. check for valid mpFloatWin pointer)
mpFloatWin = pWin;
}
void ImplDockingWindowWrapper::StartPopupMode( ToolBox *pParentToolBox, FloatWinPopupFlags nFlags )
{
// do nothing if window is floating
if( IsFloatingMode() )
return;
ImplPreparePopupMode();
// don't allow tearoff, if globally disabled
if( !StyleSettings::GetDockingFloatsSupported() )
nFlags &= ~FloatWinPopupFlags::AllowTearOff;
// if the subtoolbar was opened via keyboard make sure that key events
// will go into subtoolbar
if( pParentToolBox->IsKeyEvent() )
nFlags |= FloatWinPopupFlags::GrabFocus;
mpFloatWin->StartPopupMode( pParentToolBox, nFlags );
GetWindow()->Show();
// grab focus (again) after showing docking window, as e.g. a11y focus
// events require window to be visible first
if (nFlags & FloatWinPopupFlags::GrabFocus)
mpFloatWin->GrabFocus();
if( pParentToolBox->IsKeyEvent() )
{
// send HOME key to subtoolbar in order to select first item
KeyEvent aEvent( 0, vcl::KeyCode( KEY_HOME ) );
GetWindow()->KeyInput(aEvent);
}
}
void ImplDockingWindowWrapper::StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
{
// do nothing if window is floating
if( IsFloatingMode() )
return;
ImplPreparePopupMode();
mpFloatWin->StartPopupMode( rRect, nFlags );
GetWindow()->Show();
// grab focus (again) after showing docking window, as e.g. a11y focus
// events require window to be visible first
if (nFlags & FloatWinPopupFlags::GrabFocus)
mpFloatWin->GrabFocus();
}
IMPL_LINK_NOARG(ImplDockingWindowWrapper, PopupModeEnd, FloatingWindow*, void)
{
VclPtr<vcl::Window> xWindow = GetWindow();
xWindow->Show( false, ShowFlags::NoFocusChange );
// set parameter for handler before destroying floating window
EndPopupModeData aData( mpFloatWin->GetWindow( GetWindowType::Border )->GetPosPixel(), mpFloatWin->IsPopupModeTearOff() );
// before deleting change parent back, so we can delete the floating window alone
vcl::Window* pRealParent = xWindow->GetWindow( GetWindowType::Parent );
xWindow->mpWindowImpl->mpBorderWindow = nullptr;
if ( mpOldBorderWin )
{
xWindow->SetParent( mpOldBorderWin );
static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder(
xWindow->mpWindowImpl->mnLeftBorder, xWindow->mpWindowImpl->mnTopBorder,
xWindow->mpWindowImpl->mnRightBorder, xWindow->mpWindowImpl->mnBottomBorder );
mpOldBorderWin->Resize();
}
xWindow->mpWindowImpl->mpBorderWindow = mpOldBorderWin;
xWindow->SetParent( pRealParent );
xWindow->mpWindowImpl->mpRealParent = pRealParent;
// take ownership to local variable to protect against maPopupModeEndHdl destroying this object
auto xFloatWin = std::move(mpFloatWin);
maPopupModeEndHdl.Call(xFloatWin);
xFloatWin.disposeAndClear();
// call handler - which will destroy the window and thus the wrapper as well !
xWindow->CallEventListeners( VclEventId::WindowEndPopupMode, &aData );
}
bool ImplDockingWindowWrapper::IsInPopupMode() const
{
if( GetFloatingWindow() )
return static_cast<FloatingWindow*>(GetFloatingWindow())->IsInPopupMode();
else
return false;
}
void ImplDockingWindowWrapper::SetFloatingMode( bool bFloatMode )
{
// do nothing if window is docked and locked
if( !IsFloatingMode() && IsLocked() )
return;
if ( IsFloatingMode() == bFloatMode )
return;
if ( !PrepareToggleFloatingMode() )
return;
bool bVisible = GetWindow()->IsVisible();
if ( bFloatMode )
{
GetWindow()->Show( false, ShowFlags::NoFocusChange );
maDockPos = GetWindow()->GetPosPixel();
vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent );
mpOldBorderWin = GetWindow()->GetWindow( GetWindowType::Border );
if( mpOldBorderWin == mpDockingWindow )
mpOldBorderWin = nullptr; // no border window found
VclPtrInstance<ImplDockFloatWin2> pWin(
mpParent,
mnFloatBits & ( WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE ) ?
mnFloatBits | WB_SYSTEMWINDOW
| WB_OWNERDRAWDECORATION
: mnFloatBits,
this );
// At least for DockingWindow, GetText() has a side effect of setting deferred
// properties. This must be done before setting the border window (see below),
// so that the border width will end up in mpWindowImpl->mnBorderWidth, not in
// the border window (See DockingWindow::setPosSizeOnContainee() and
// DockingWindow::GetOptimalSize()).
pWin->SetText( GetWindow()->GetText() );
GetWindow()->mpWindowImpl->mpBorderWindow = nullptr;
GetWindow()->mpWindowImpl->mnLeftBorder = 0;
GetWindow()->mpWindowImpl->mnTopBorder = 0;
GetWindow()->mpWindowImpl->mnRightBorder = 0;
GetWindow()->mpWindowImpl->mnBottomBorder = 0;
// if the parent gets destroyed, we also have to reset the parent of the BorderWindow
if ( mpOldBorderWin )
mpOldBorderWin->SetParent( pWin );
GetWindow()->SetParent( pWin );
pWin->SetPosPixel( Point() );
GetWindow()->mpWindowImpl->mpBorderWindow = pWin;
pWin->mpWindowImpl->mpClientWindow = mpDockingWindow;
GetWindow()->mpWindowImpl->mpRealParent = pRealParent;
pWin->SetOutputSizePixel( GetWindow()->GetSizePixel() );
pWin->SetPosPixel( maFloatPos );
// pass on DockingData to FloatingWindow
pWin->ShowTitleButton( TitleButton::Docking, mbDockBtn );
pWin->ShowTitleButton( TitleButton::Hide, mbHideBtn );
pWin->SetMinOutputSizePixel( maMinOutSize );
pWin->SetMaxOutputSizePixel( maMaxOutSize );
mpFloatWin = pWin;
if ( bVisible )
GetWindow()->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
ToggleFloatingMode();
}
else
{
GetWindow()->Show( false, ShowFlags::NoFocusChange );
// store FloatingData in FloatingWindow
maFloatPos = mpFloatWin->GetPosPixel();
mbDockBtn = mpFloatWin->IsTitleButtonVisible( TitleButton::Docking );
mbHideBtn = mpFloatWin->IsTitleButtonVisible( TitleButton::Hide );
maMinOutSize = mpFloatWin->GetMinOutputSizePixel();
maMaxOutSize = mpFloatWin->GetMaxOutputSizePixel();
vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent ); //mpWindowImpl->mpRealParent;
GetWindow()->mpWindowImpl->mpBorderWindow = nullptr;
if ( mpOldBorderWin )
{
GetWindow()->SetParent( mpOldBorderWin );
static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder(
GetWindow()->mpWindowImpl->mnLeftBorder, GetWindow()->mpWindowImpl->mnTopBorder,
GetWindow()->mpWindowImpl->mnRightBorder, GetWindow()->mpWindowImpl->mnBottomBorder );
mpOldBorderWin->Resize();
}
GetWindow()->mpWindowImpl->mpBorderWindow = mpOldBorderWin;
GetWindow()->SetParent( pRealParent );
GetWindow()->mpWindowImpl->mpRealParent = pRealParent;
mpFloatWin.disposeAndClear();
GetWindow()->SetPosPixel( maDockPos );
if ( bVisible )
GetWindow()->Show();
ToggleFloatingMode();
}
}
void ImplDockingWindowWrapper::SetFloatStyle( WinBits nStyle )
{
mnFloatBits = nStyle;
}
void ImplDockingWindowWrapper::setPosSizePixel( tools::Long nX, tools::Long nY,
tools::Long nWidth, tools::Long nHeight,
PosSizeFlags nFlags )
{
if ( mpFloatWin )
mpFloatWin->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
else
GetWindow()->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
}
Point ImplDockingWindowWrapper::GetPosPixel() const
{
if ( mpFloatWin )
return mpFloatWin->GetPosPixel();
else
return mpDockingWindow->GetPosPixel();
}
Size ImplDockingWindowWrapper::GetSizePixel() const
{
if ( mpFloatWin )
return mpFloatWin->GetSizePixel();
else
return mpDockingWindow->GetSizePixel();
}
// old inlines from DockingWindow
void ImplDockingWindowWrapper::SetMinOutputSizePixel( const Size& rSize )
{
if ( mpFloatWin )
mpFloatWin->SetMinOutputSizePixel( rSize );
maMinOutSize = rSize;
}
void ImplDockingWindowWrapper::SetMaxOutputSizePixel( const Size& rSize )
{
if ( mpFloatWin )
mpFloatWin->SetMaxOutputSizePixel( rSize );
maMaxOutSize = rSize;
}
bool ImplDockingWindowWrapper::IsFloatingMode() const
{
return (mpFloatWin != nullptr);
}
void ImplDockingWindowWrapper::SetDragArea( const tools::Rectangle& rRect )
{
maDragArea = rRect;
}
void ImplDockingWindowWrapper::Lock()
{
mbLocked = true;
// only toolbars support locking
ToolBox *pToolBox = dynamic_cast< ToolBox * >( GetWindow() );
if( pToolBox )
pToolBox->Lock( mbLocked );
}
void ImplDockingWindowWrapper::Unlock()
{
mbLocked = false;
// only toolbars support locking
ToolBox *pToolBox = dynamic_cast< ToolBox * >( GetWindow() );
if( pToolBox )
pToolBox->Lock( mbLocked );
}
SystemWindow* ImplDockingWindowWrapper::GetFloatingWindow() const
{
return mpFloatWin;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */