diff options
Diffstat (limited to '')
-rw-r--r-- | framework/source/layoutmanager/helpers.cxx | 340 | ||||
-rw-r--r-- | framework/source/layoutmanager/helpers.hxx | 67 | ||||
-rw-r--r-- | framework/source/layoutmanager/layoutmanager.cxx | 3079 | ||||
-rw-r--r-- | framework/source/layoutmanager/toolbarlayoutmanager.cxx | 4116 | ||||
-rw-r--r-- | framework/source/layoutmanager/toolbarlayoutmanager.hxx | 283 | ||||
-rw-r--r-- | framework/source/layoutmanager/uielement.cxx | 99 |
6 files changed, 7984 insertions, 0 deletions
diff --git a/framework/source/layoutmanager/helpers.cxx b/framework/source/layoutmanager/helpers.cxx new file mode 100644 index 000000000..7508d58cb --- /dev/null +++ b/framework/source/layoutmanager/helpers.cxx @@ -0,0 +1,340 @@ +/* -*- 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 "helpers.hxx" + +#include <com/sun/star/ui/DockingArea.hpp> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/frame/DispatchHelper.hpp> +#include <com/sun/star/awt/XDockableWindow.hpp> +#include <com/sun/star/awt/XDockableWindowListener.hpp> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/ui/XUIElement.hpp> + +#include <comphelper/lok.hxx> +#include <comphelper/propertyvalue.hxx> +#include <unotools/mediadescriptor.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/string_view.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +using namespace com::sun::star; + +namespace framework +{ + +bool hasEmptySize( const css::awt::Size& rSize ) +{ + return ( rSize.Width == 0 ) && ( rSize.Height == 0 ); +} + +bool hasDefaultPosValue( const css::awt::Point& rPos ) +{ + return (( rPos.X == SAL_MAX_INT32 ) || ( rPos.Y == SAL_MAX_INT32 )); +} + +bool isDefaultPos( const css::awt::Point& rPos ) +{ + return (( rPos.X == SAL_MAX_INT32 ) && ( rPos.Y == SAL_MAX_INT32 )); +} + +bool isReverseOrderDockingArea( const sal_Int32 nDockArea ) +{ + ui::DockingArea eDockArea = static_cast< ui::DockingArea >( nDockArea ); + return (( eDockArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || + ( eDockArea == ui::DockingArea_DOCKINGAREA_RIGHT )); +} + +bool isToolboxHorizontalAligned( ToolBox const * pToolBox ) +{ + if ( pToolBox ) + return (( pToolBox->GetAlign() == WindowAlign::Top ) || ( pToolBox->GetAlign() == WindowAlign::Bottom )); + return false; +} + +bool isHorizontalDockingArea( const ui::DockingArea& nDockingArea ) +{ + return (( nDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) || + ( nDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM )); +} + +bool isHorizontalDockingArea( const sal_Int32 nDockArea ) +{ + return isHorizontalDockingArea(static_cast< ui::DockingArea >( nDockArea )); +} + +OUString retrieveToolbarNameFromHelpURL( vcl::Window* pWindow ) +{ + OUString aToolbarName; + + if ( pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBox = dynamic_cast<ToolBox *>( pWindow ); + if ( pToolBox ) + { + aToolbarName = OStringToOUString( pToolBox->GetHelpId(), RTL_TEXTENCODING_UTF8 ); + sal_Int32 i = aToolbarName.lastIndexOf( ':' ); + if ( !aToolbarName.isEmpty() && ( i > 0 ) && (( i + 1 ) < aToolbarName.getLength() )) + aToolbarName = aToolbarName.copy( i+1 ); // Remove ".HelpId:" protocol from toolbar name + else + aToolbarName.clear(); + } + } + return aToolbarName; +} + +ToolBox* getToolboxPtr( vcl::Window* pWindow ) +{ + ToolBox* pToolbox(nullptr); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + pToolbox = dynamic_cast<ToolBox*>( pWindow ); + return pToolbox; +} + +vcl::Window* getWindowFromXUIElement( const uno::Reference< ui::XUIElement >& xUIElement ) +{ + SolarMutexGuard aGuard; + uno::Reference< awt::XWindow > xWindow; + if ( xUIElement.is() ) + xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY ); + return VCLUnoHelper::GetWindow( xWindow ); +} + +SystemWindow* getTopSystemWindow( const uno::Reference< awt::XWindow >& xWindow ) +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + while ( pWindow && !pWindow->IsSystemWindow() ) + pWindow = pWindow->GetParent(); + + if ( pWindow ) + return static_cast<SystemWindow *>(pWindow.get()); + else + return nullptr; +} + +// ATTENTION! +// This value is directly copied from the sfx2 project. +// You have to change BOTH values, see sfx2/inc/sfx2/sfxsids.hrc (SID_DOCKWIN_START) +const sal_Int32 DOCKWIN_ID_BASE = 9800; + +bool lcl_checkUIElement(const uno::Reference< ui::XUIElement >& xUIElement, awt::Rectangle& _rPosSize, uno::Reference< awt::XWindow >& _xWindow) +{ + bool bRet = xUIElement.is(); + if ( bRet ) + { + SolarMutexGuard aGuard; + _xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY ); + _rPosSize = _xWindow->getPosSize(); + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( _xWindow ); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + { + ::Size aSize = static_cast<ToolBox*>(pWindow.get())->CalcWindowSizePixel( 1 ); + _rPosSize.Width = aSize.Width(); + _rPosSize.Height = aSize.Height(); + } + } // if ( xUIElement.is() ) + return bRet; +} + +uno::Reference< awt::XWindowPeer > createToolkitWindow( const uno::Reference< uno::XComponentContext >& rxContext, const uno::Reference< awt::XWindowPeer >& rParent, const char* pService ) +{ + uno::Reference< awt::XToolkit2 > xToolkit = awt::Toolkit::create( rxContext ); + + // describe window properties. + css::awt::WindowDescriptor aDescriptor; + aDescriptor.Type = awt::WindowClass_SIMPLE; + aDescriptor.WindowServiceName = OUString::createFromAscii( pService ); + aDescriptor.ParentIndex = -1; + aDescriptor.Parent = rParent; + aDescriptor.Bounds = awt::Rectangle(0,0,0,0); + aDescriptor.WindowAttributes = 0; + + // create an awt window + uno::Reference< awt::XWindowPeer > xPeer = xToolkit->createWindow( aDescriptor ); + + return xPeer; +} + +// convert alignment constant to vcl's WindowAlign type +WindowAlign ImplConvertAlignment( ui::DockingArea aAlignment ) +{ + if ( aAlignment == ui::DockingArea_DOCKINGAREA_LEFT ) + return WindowAlign::Left; + else if ( aAlignment == ui::DockingArea_DOCKINGAREA_RIGHT ) + return WindowAlign::Right; + else if ( aAlignment == ui::DockingArea_DOCKINGAREA_TOP ) + return WindowAlign::Top; + else + return WindowAlign::Bottom; +} + +std::u16string_view getElementTypeFromResourceURL( std::u16string_view aResourceURL ) +{ + if ( o3tl::starts_with(aResourceURL, UIRESOURCE_URL ) ) + { + sal_Int32 nIndex{ UIRESOURCE_URL.getLength() }; + return o3tl::getToken(aResourceURL, 1, '/', nIndex ); + } + + return std::u16string_view(); +} + +void parseResourceURL( std::u16string_view aResourceURL, OUString& aElementType, OUString& aElementName ) +{ + if ( o3tl::starts_with(aResourceURL, UIRESOURCE_URL) ) + { + sal_Int32 nIndex{ UIRESOURCE_URL.getLength() }; + aElementType = o3tl::getToken(aResourceURL, 1, '/', nIndex ); + aElementName = o3tl::getToken(aResourceURL, 0, '/', nIndex ); + } +} + +css::awt::Rectangle putRectangleValueToAWT( const ::tools::Rectangle& rRect ) +{ + css::awt::Rectangle aRect; + aRect.X = rRect.Left(); + aRect.Y = rRect.Top(); + aRect.Width = rRect.Right(); + aRect.Height = rRect.Bottom(); + + return aRect; +} + +::tools::Rectangle putAWTToRectangle( const css::awt::Rectangle& rRect ) +{ + ::tools::Rectangle aRect; + aRect.SetLeft( rRect.X ); + aRect.SetTop( rRect.Y ); + aRect.SetRight( rRect.Width ); + aRect.SetBottom( rRect.Height ); + + return aRect; +} + +bool equalRectangles( const css::awt::Rectangle& rRect1, + const css::awt::Rectangle& rRect2 ) +{ + return (( rRect1.X == rRect2.X ) && + ( rRect1.Y == rRect2.Y ) && + ( rRect1.Width == rRect2.Width ) && + ( rRect1.Height == rRect2.Height )); +} + +uno::Reference< frame::XModel > impl_getModelFromFrame( const uno::Reference< frame::XFrame >& rFrame ) +{ + // Query for the model to get check the context information + uno::Reference< frame::XModel > xModel; + if ( rFrame.is() ) + { + uno::Reference< frame::XController > xController = rFrame->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + } + + return xModel; +} + +bool implts_isPreviewModel( const uno::Reference< frame::XModel >& xModel ) +{ + // the cost in calc of calling getArgs for this property + // includes measuring the entire sheet - which is extremely slow. + if (comphelper::LibreOfficeKit::isActive()) + return false; + + if ( xModel.is() ) + { + utl::MediaDescriptor aDesc( xModel->getArgs() ); + return aDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false); + } + else + return false; +} + +bool implts_isFrameOrWindowTop( const uno::Reference< frame::XFrame >& xFrame ) +{ + if (xFrame->isTop()) + return true; + + uno::Reference< awt::XTopWindow > xWindowCheck(xFrame->getContainerWindow(), uno::UNO_QUERY); // don't use _THROW here ... it's a check only + if (xWindowCheck.is()) + { + // #i76867# top and system window is required. + SolarMutexGuard aGuard; + uno::Reference< awt::XWindow > xWindow( xWindowCheck, uno::UNO_QUERY ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + return pWindow && pWindow->IsSystemWindow(); + } + + return false; +} + +void impl_setDockingWindowVisibility( const css::uno::Reference< css::uno::XComponentContext>& rxContext, const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rDockingWindowName, bool bVisible ) +{ + sal_Int32 nID = o3tl::toInt32(rDockingWindowName); + sal_Int32 nIndex = nID - DOCKWIN_ID_BASE; + + css::uno::Reference< css::frame::XDispatchProvider > xProvider(rFrame, css::uno::UNO_QUERY); + if ( !(nIndex >= 0 && xProvider.is()) ) + return; + + OUString aDockWinArgName = "DockingWindow" + OUString::number( nIndex ); + + css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + aDockWinArgName, bVisible) }; + + css::uno::Reference< css::frame::XDispatchHelper > xDispatcher = css::frame::DispatchHelper::create( rxContext ); + + OUString aDockWinCommand = ".uno:" + aDockWinArgName; + xDispatcher->executeDispatch( + xProvider, + aDockWinCommand, + "_self", + 0, + aArgs); +} + +void impl_addWindowListeners( + const css::uno::Reference< css::uno::XInterface >& xThis, + const css::uno::Reference< css::ui::XUIElement >& xUIElement ) +{ + css::uno::Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XDockableWindow > xDockWindow( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + if ( !(xDockWindow.is() && xWindow.is()) ) + return; + + try + { + xDockWindow->addDockableWindowListener( + css::uno::Reference< css::awt::XDockableWindowListener >( + xThis, css::uno::UNO_QUERY )); + xWindow->addWindowListener( + css::uno::Reference< css::awt::XWindowListener >( + xThis, css::uno::UNO_QUERY )); + xDockWindow->enableDocking( true ); + } + catch ( const css::uno::Exception& ) + { + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/helpers.hxx b/framework/source/layoutmanager/helpers.hxx new file mode 100644 index 000000000..14f9642a3 --- /dev/null +++ b/framework/source/layoutmanager/helpers.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/ui/XUIElement.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/ui/DockingArea.hpp> +#include <com/sun/star/awt/Point.hpp> + +#include <vcl/window.hxx> +#include <vcl/toolbox.hxx> + +inline constexpr OUStringLiteral UIRESOURCE_URL = u"private:resource"; +inline constexpr OUStringLiteral UIRESOURCETYPE_TOOLBAR = u"toolbar"; +#define UIRESOURCETYPE_MENUBAR "menubar" + +namespace framework +{ + +bool hasEmptySize( const css::awt::Size& rSize ); +bool hasDefaultPosValue( const css::awt::Point& rPos ); +bool isDefaultPos( const css::awt::Point& rPos ); +bool isToolboxHorizontalAligned( ToolBox const * pToolBox ); +bool isReverseOrderDockingArea( const sal_Int32 nDockArea ); +bool isHorizontalDockingArea( const sal_Int32 nDockArea ); +bool isHorizontalDockingArea( const css::ui::DockingArea& nDockArea ); +OUString retrieveToolbarNameFromHelpURL( vcl::Window* pWindow ); +ToolBox* getToolboxPtr( vcl::Window* pWindow ); +vcl::Window* getWindowFromXUIElement( const css::uno::Reference< css::ui::XUIElement >& xUIElement ); +SystemWindow* getTopSystemWindow( const css::uno::Reference< css::awt::XWindow >& xWindow ); +bool equalRectangles( const css::awt::Rectangle& rRect1, const css::awt::Rectangle& rRect2 ); +bool lcl_checkUIElement(const css::uno::Reference< css::ui::XUIElement >& xUIElement,css::awt::Rectangle& _rPosSize, css::uno::Reference< css::awt::XWindow >& _xWindow); +css::uno::Reference< css::awt::XWindowPeer > createToolkitWindow( const css::uno::Reference< css::uno::XComponentContext >& rxContext, const css::uno::Reference< css::awt::XWindowPeer >& rParent, const char* pService ); +WindowAlign ImplConvertAlignment( css::ui::DockingArea aAlignment ); +std::u16string_view getElementTypeFromResourceURL( std::u16string_view aResourceURL ); +void parseResourceURL( std::u16string_view aResourceURL, OUString& aElementType, OUString& aElementName ); +::tools::Rectangle putAWTToRectangle( const css::awt::Rectangle& rRect ); +css::awt::Rectangle putRectangleValueToAWT( const ::tools::Rectangle& rRect ); +css::uno::Reference< css::frame::XModel > impl_getModelFromFrame( const css::uno::Reference< css::frame::XFrame >& rFrame ); +bool implts_isPreviewModel( const css::uno::Reference< css::frame::XModel >& xModel ); +bool implts_isFrameOrWindowTop( const css::uno::Reference< css::frame::XFrame >& xFrame ); +void impl_setDockingWindowVisibility( const css::uno::Reference< css::uno::XComponentContext>& rxContext, const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rDockingWindowName, bool bVisible ); +void impl_addWindowListeners( const css::uno::Reference< css::uno::XInterface >& xThis, const css::uno::Reference< css::ui::XUIElement >& xUIElement ); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/layoutmanager.cxx b/framework/source/layoutmanager/layoutmanager.cxx new file mode 100644 index 000000000..9220b805c --- /dev/null +++ b/framework/source/layoutmanager/layoutmanager.cxx @@ -0,0 +1,3079 @@ +/* -*- 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 <memory> +#include <config_feature_desktop.h> + +#include <properties.h> +#include <services/layoutmanager.hxx> +#include "helpers.hxx" + +#include <framework/sfxhelperfunctions.hxx> +#include <uielement/menubarwrapper.hxx> +#include <uielement/progressbarwrapper.hxx> +#include <uiconfiguration/globalsettings.hxx> +#include <uiconfiguration/windowstateproperties.hxx> +#include "toolbarlayoutmanager.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/FrameAction.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/theWindowStateConfiguration.hpp> +#include <com/sun/star/ui/theUIElementFactoryManager.hpp> +#include <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/LayoutManagerEvents.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/DispatchHelper.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <comphelper/lok.hxx> +#include <comphelper/propertyvalue.hxx> +#include <vcl/status.hxx> +#include <vcl/settings.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <comphelper/uno3.hxx> +#include <officecfg/Office/Compatibility.hxx> + +#include <rtl/ref.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <algorithm> + +// using namespace +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::frame; + +constexpr OUStringLiteral STATUS_BAR_ALIAS = u"private:resource/statusbar/statusbar"; + +namespace framework +{ + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( LayoutManager, LayoutManager_Base, LayoutManager_PBase ) +IMPLEMENT_FORWARD_XINTERFACE2( LayoutManager, LayoutManager_Base, LayoutManager_PBase ) + +LayoutManager::LayoutManager( const Reference< XComponentContext >& xContext ) : + ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >(m_aMutex) + , LayoutManager_PBase( *static_cast< ::cppu::OBroadcastHelper* >(this) ) + , m_xContext( xContext ) + , m_xURLTransformer( URLTransformer::create(xContext) ) + , m_nLockCount( 0 ) + , m_bInplaceMenuSet( false ) + , m_bMenuVisible( true ) + , m_bVisible( true ) + , m_bParentWindowVisible( false ) + , m_bMustDoLayout( true ) +#if HAVE_FEATURE_DESKTOP + , m_bAutomaticToolbars( true ) +#else + , m_bAutomaticToolbars( false ) +#endif + , m_bHideCurrentUI( false ) + , m_bGlobalSettings( false ) + , m_bPreserveContentSize( false ) + , m_bMenuBarCloseButton( false ) + , m_xModuleManager( ModuleManager::create( xContext )) + , m_xUIElementFactoryManager( ui::theUIElementFactoryManager::get(xContext) ) + , m_xPersistentWindowStateSupplier( ui::theWindowStateConfiguration::get( xContext ) ) + , m_aAsyncLayoutTimer( "framework::LayoutManager m_aAsyncLayoutTimer" ) + , m_aListenerContainer( m_aMutex ) +{ + // Initialize statusbar member + m_aStatusBarElement.m_aType = "statusbar"; + m_aStatusBarElement.m_aName = STATUS_BAR_ALIAS; + + if (!comphelper::LibreOfficeKit::isActive()) + { + m_xToolbarManager = new ToolbarLayoutManager( xContext, Reference<XUIElementFactory>(m_xUIElementFactoryManager, UNO_QUERY_THROW), this ); + } + + m_aAsyncLayoutTimer.SetPriority( TaskPriority::HIGH_IDLE ); + m_aAsyncLayoutTimer.SetTimeout( 50 ); + m_aAsyncLayoutTimer.SetInvokeHandler( LINK( this, LayoutManager, AsyncLayoutHdl ) ); + + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_AUTOMATICTOOLBARS, LAYOUTMANAGER_PROPHANDLE_AUTOMATICTOOLBARS, css::beans::PropertyAttribute::TRANSIENT, &m_bAutomaticToolbars, cppu::UnoType<decltype(m_bAutomaticToolbars)>::get() ); + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_HIDECURRENTUI, LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI, beans::PropertyAttribute::TRANSIENT, &m_bHideCurrentUI, cppu::UnoType<decltype(m_bHideCurrentUI)>::get() ); + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_LOCKCOUNT, LAYOUTMANAGER_PROPHANDLE_LOCKCOUNT, beans::PropertyAttribute::TRANSIENT | beans::PropertyAttribute::READONLY, &m_nLockCount, cppu::UnoType<decltype(m_nLockCount)>::get() ); + registerProperty( LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER, beans::PropertyAttribute::TRANSIENT, &m_bMenuBarCloseButton, cppu::UnoType<decltype(m_bMenuBarCloseButton)>::get() ); + registerPropertyNoMember( LAYOUTMANAGER_PROPNAME_ASCII_REFRESHVISIBILITY, LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY, beans::PropertyAttribute::TRANSIENT, cppu::UnoType<bool>::get(), css::uno::Any(false) ); + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_PRESERVE_CONTENT_SIZE, LAYOUTMANAGER_PROPHANDLE_PRESERVE_CONTENT_SIZE, beans::PropertyAttribute::TRANSIENT, &m_bPreserveContentSize, cppu::UnoType<decltype(m_bPreserveContentSize)>::get() ); +} + +LayoutManager::~LayoutManager() +{ + m_aAsyncLayoutTimer.Stop(); + setDockingAreaAcceptor(nullptr); + m_pGlobalSettings.reset(); +} + +void LayoutManager::implts_createMenuBar(const OUString& rMenuBarName) +{ + SolarMutexGuard aWriteLock; + + // Create a customized menu if compatibility mode is on + if (m_aModuleIdentifier == "com.sun.star.text.TextDocument" && officecfg::Office::Compatibility::View::MSCompatibleFormsMenu::get()) + { + implts_createMSCompatibleMenuBar(rMenuBarName); + } + + // Create the default menubar otherwise + if (m_bInplaceMenuSet || m_xMenuBar.is()) + return; + + m_xMenuBar = implts_createElement( rMenuBarName ); + if ( !m_xMenuBar.is() ) + return; + + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( !pSysWindow ) + return; + + Reference< awt::XMenuBar > xMenuBar; + + Reference< XPropertySet > xPropSet( m_xMenuBar, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("XMenuBar") >>= xMenuBar; + } + catch (const beans::UnknownPropertyException&) + { + } + catch (const lang::WrappedTargetException&) + { + } + } + + if ( !xMenuBar.is() ) + return; + + VCLXMenu* pAwtMenuBar = comphelper::getFromUnoTunnel<VCLXMenu>( xMenuBar ); + if ( pAwtMenuBar ) + { + MenuBar* pMenuBar = static_cast<MenuBar*>(pAwtMenuBar->GetMenu()); + if ( pMenuBar ) + { + pSysWindow->SetMenuBar(pMenuBar); + pMenuBar->SetDisplayable( m_bMenuVisible ); + implts_updateMenuBarClose(); + } + } +} + +// Internal helper function +void LayoutManager::impl_clearUpMenuBar() +{ + implts_lock(); + + // Clear up VCL menu bar to prepare shutdown + if ( m_xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pSetMenuBar = nullptr; + if ( m_xInplaceMenuBar.is() ) + pSetMenuBar = static_cast<MenuBar *>(m_xInplaceMenuBar->GetMenuBar()); + else + { + Reference< awt::XMenuBar > xMenuBar; + + Reference< XPropertySet > xPropSet( m_xMenuBar, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("XMenuBar") >>= xMenuBar; + } + catch (const beans::UnknownPropertyException&) + { + } + catch (const lang::WrappedTargetException&) + { + } + } + + VCLXMenu* pAwtMenuBar = comphelper::getFromUnoTunnel<VCLXMenu>( xMenuBar ); + if ( pAwtMenuBar ) + pSetMenuBar = static_cast<MenuBar*>(pAwtMenuBar->GetMenu()); + } + + MenuBar* pTopMenuBar = pSysWindow->GetMenuBar(); + if ( pSetMenuBar == pTopMenuBar ) + pSysWindow->SetMenuBar( nullptr ); + } + } + + // reset inplace menubar manager + VclPtr<Menu> pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); + m_bInplaceMenuSet = false; + + Reference< XComponent > xComp( m_xMenuBar, UNO_QUERY ); + if ( xComp.is() ) + xComp->dispose(); + m_xMenuBar.clear(); + implts_unlock(); +} + +void LayoutManager::implts_lock() +{ + SolarMutexGuard g; + ++m_nLockCount; +} + +bool LayoutManager::implts_unlock() +{ + SolarMutexGuard g; + m_nLockCount = std::max( m_nLockCount-1, static_cast<sal_Int32>(0) ); + return ( m_nLockCount == 0 ); +} + +void LayoutManager::implts_reset( bool bAttached ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame = m_xFrame; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< XUIConfiguration > xModuleCfgMgr( m_xModuleCfgMgr, UNO_QUERY ); + Reference< XUIConfiguration > xDocCfgMgr( m_xDocCfgMgr, UNO_QUERY ); + Reference< XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + Reference< XComponentContext > xContext( m_xContext ); + Reference< XNameAccess > xPersistentWindowStateSupplier( m_xPersistentWindowStateSupplier ); + rtl::Reference<ToolbarLayoutManager> xToolbarManager( m_xToolbarManager ); + OUString aModuleIdentifier( m_aModuleIdentifier ); + bool bAutomaticToolbars( m_bAutomaticToolbars ); + aReadLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + implts_lock(); + + Reference< XModel > xModel; + if ( xFrame.is() ) + { + if ( bAttached ) + { + OUString aOldModuleIdentifier( aModuleIdentifier ); + try + { + aModuleIdentifier = m_xModuleManager->identify( xFrame ); + } + catch( const Exception& ) {} + + if ( !aModuleIdentifier.isEmpty() && aOldModuleIdentifier != aModuleIdentifier ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier; + if ( xContext.is() ) + xModuleCfgSupplier = theModuleUIConfigurationManagerSupplier::get( xContext ); + + if ( xModuleCfgMgr.is() ) + { + try + { + // Remove listener to old module ui configuration manager + xModuleCfgMgr->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + try + { + // Add listener to new module ui configuration manager + xModuleCfgMgr.set( xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier ), UNO_QUERY ); + if ( xModuleCfgMgr.is() ) + xModuleCfgMgr->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + + try + { + // Retrieve persistent window state reference for our new module + if ( xPersistentWindowStateSupplier.is() ) + xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= xPersistentWindowState; + } + catch (const NoSuchElementException&) + { + } + catch (const WrappedTargetException&) + { + } + } + + xModel = impl_getModelFromFrame( xFrame ); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY ); + if ( xUIConfigurationManagerSupplier.is() ) + { + if ( xDocCfgMgr.is() ) + { + try + { + // Remove listener to old ui configuration manager + xDocCfgMgr->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + try + { + xDocCfgMgr.set( xUIConfigurationManagerSupplier->getUIConfigurationManager(), UNO_QUERY ); + if ( xDocCfgMgr.is() ) + xDocCfgMgr->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + } + } + else + { + // Remove configuration listeners before we can release our references + if ( xModuleCfgMgr.is() ) + { + try + { + xModuleCfgMgr->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + if ( xDocCfgMgr.is() ) + { + try + { + xDocCfgMgr->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + // Release references to our configuration managers as we currently don't have + // an attached module. + xModuleCfgMgr.clear(); + xDocCfgMgr.clear(); + xPersistentWindowState.clear(); + aModuleIdentifier.clear(); + } + + Reference< XUIConfigurationManager > xModCfgMgr( xModuleCfgMgr, UNO_QUERY ); + Reference< XUIConfigurationManager > xDokCfgMgr( xDocCfgMgr, UNO_QUERY ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + m_aDockingArea = awt::Rectangle(); + m_aModuleIdentifier = aModuleIdentifier; + m_xModuleCfgMgr = xModCfgMgr; + m_xDocCfgMgr = xDokCfgMgr; + m_xPersistentWindowState = xPersistentWindowState; + m_aStatusBarElement.m_bStateRead = false; // reset state to read data again! + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // reset/notify toolbar layout manager + if ( xToolbarManager.is() ) + { + if ( bAttached ) + { + xToolbarManager->attach( xFrame, xModCfgMgr, xDokCfgMgr, xPersistentWindowState ); + uno::Reference< awt::XWindowPeer > xParent( xContainerWindow, UNO_QUERY ); + xToolbarManager->setParentWindow( xParent ); + if ( bAutomaticToolbars ) + xToolbarManager->createStaticToolbars(); + } + else + { + xToolbarManager->reset(); + implts_destroyElements(); + } + } + } + + implts_unlock(); +} + +bool LayoutManager::implts_isEmbeddedLayoutManager() const +{ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame = m_xFrame; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + aReadLock.clear(); + + Reference< awt::XWindow > xFrameContainerWindow = xFrame->getContainerWindow(); + return xFrameContainerWindow != xContainerWindow; +} + +void LayoutManager::implts_destroyElements() +{ + SolarMutexResettableGuard aWriteLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aWriteLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->destroyToolbars(); + + implts_destroyStatusBar(); + + aWriteLock.reset(); + impl_clearUpMenuBar(); + aWriteLock.clear(); +} + +void LayoutManager::implts_toggleFloatingUIElementsVisibility( bool bActive ) +{ + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->setFloatingToolbarsVisibility( bActive ); +} + +uno::Reference< ui::XUIElement > LayoutManager::implts_findElement( std::u16string_view aName ) +{ + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + return m_xMenuBar; + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + return m_aStatusBarElement.m_xUIElement; + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + return m_aProgressBarElement.m_xUIElement; + + return uno::Reference< ui::XUIElement >(); +} + +bool LayoutManager::implts_readWindowStateData( const OUString& aName, UIElement& rElementData ) +{ + return readWindowStateData( aName, rElementData, m_xPersistentWindowState, + m_pGlobalSettings, m_bGlobalSettings, m_xContext ); +} + +bool LayoutManager::readWindowStateData( const OUString& aName, UIElement& rElementData, + const Reference< XNameAccess > &rPersistentWindowState, + std::unique_ptr<GlobalSettings> &rGlobalSettings, bool &bInGlobalSettings, + const Reference< XComponentContext > &rComponentContext ) +{ + if ( !rPersistentWindowState.is() ) + return false; + + bool bGetSettingsState( false ); + + SolarMutexClearableGuard aWriteLock; + bool bGlobalSettings( bInGlobalSettings ); + if ( rGlobalSettings == nullptr ) + { + rGlobalSettings.reset( new GlobalSettings( rComponentContext ) ); + bGetSettingsState = true; + } + GlobalSettings* pGlobalSettings = rGlobalSettings.get(); + aWriteLock.clear(); + + try + { + Sequence< PropertyValue > aWindowState; + if ( rPersistentWindowState->hasByName( aName ) && (rPersistentWindowState->getByName( aName ) >>= aWindowState) ) + { + bool bValue( false ); + for ( PropertyValue const & rProp : std::as_const(aWindowState) ) + { + if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKED ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bFloating = !bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_VISIBLE ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bVisible = bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKINGAREA ) + { + ui::DockingArea eDockingArea; + if ( rProp.Value >>= eDockingArea ) + rElementData.m_aDockedData.m_nDockedArea = eDockingArea; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKPOS ) + { + awt::Point aPoint; + if (rProp.Value >>= aPoint) + { + //tdf#90256 repair these broken Docking positions + if (aPoint.X < 0) + aPoint.X = SAL_MAX_INT32; + if (aPoint.Y < 0) + aPoint.Y = SAL_MAX_INT32; + rElementData.m_aDockedData.m_aPos = aPoint; + } + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_POS ) + { + awt::Point aPoint; + if ( rProp.Value >>= aPoint ) + rElementData.m_aFloatingData.m_aPos = aPoint; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_SIZE ) + { + awt::Size aSize; + if ( rProp.Value >>= aSize ) + rElementData.m_aFloatingData.m_aSize = aSize; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_UINAME ) + rProp.Value >>= rElementData.m_aUIName; + else if ( rProp.Name == WINDOWSTATE_PROPERTY_STYLE ) + { + sal_Int32 nStyle = 0; + if ( rProp.Value >>= nStyle ) + rElementData.m_nStyle = static_cast<ButtonType>( nStyle ); + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_LOCKED ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_aDockedData.m_bLocked = bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_CONTEXT ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bContextSensitive = bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_NOCLOSE ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bNoClose = bValue; + } + } + } + + // oversteer values with global settings + if (bGetSettingsState || bGlobalSettings) + { + if ( pGlobalSettings->HasToolbarStatesInfo()) + { + { + SolarMutexGuard aWriteLock2; + bInGlobalSettings = true; + } + + uno::Any aValue; + if ( pGlobalSettings->GetToolbarStateInfo( + GlobalSettings::STATEINFO_LOCKED, + aValue )) + aValue >>= rElementData.m_aDockedData.m_bLocked; + if ( pGlobalSettings->GetToolbarStateInfo( + GlobalSettings::STATEINFO_DOCKED, + aValue )) + { + bool bValue; + if ( aValue >>= bValue ) + rElementData.m_bFloating = !bValue; + } + } + } + + const bool bDockingSupportCrippled = !StyleSettings::GetDockingFloatsSupported(); + if (bDockingSupportCrippled) + rElementData.m_bFloating = false; + + return true; + } + catch (const NoSuchElementException&) + { + } + + return false; +} + +void LayoutManager::implts_writeWindowStateData( const OUString& aName, const UIElement& rElementData ) +{ + SolarMutexClearableGuard aWriteLock; + Reference< XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + + aWriteLock.clear(); + + bool bPersistent( false ); + Reference< XPropertySet > xPropSet( rElementData.m_xUIElement, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + // Check persistent flag of the user interface element + xPropSet->getPropertyValue("Persistent") >>= bPersistent; + } + catch (const beans::UnknownPropertyException&) + { + // Non-configurable elements should at least store their dimension/position + bPersistent = true; + } + catch (const lang::WrappedTargetException&) + { + } + } + + if ( !(bPersistent && xPersistentWindowState.is()) ) + return; + + try + { + Sequence< PropertyValue > aWindowState{ + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKED, !rElementData.m_bFloating), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_VISIBLE, rElementData.m_bVisible), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKINGAREA, + rElementData.m_aDockedData.m_nDockedArea), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKPOS, + rElementData.m_aDockedData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_POS, + rElementData.m_aFloatingData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_SIZE, + rElementData.m_aFloatingData.m_aSize), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED, + rElementData.m_aDockedData.m_bLocked) + }; + + if ( xPersistentWindowState->hasByName( aName )) + { + Reference< XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY ); + xReplace->replaceByName( aName, Any( aWindowState )); + } + else + { + Reference< XNameContainer > xInsert( xPersistentWindowState, uno::UNO_QUERY ); + xInsert->insertByName( aName, Any( aWindowState )); + } + } + catch (const Exception&) + { + } +} + +::Size LayoutManager::implts_getContainerWindowOutputSize() +{ + ::Size aContainerWinSize; + vcl::Window* pContainerWindow( nullptr ); + + // Retrieve output size from container Window + SolarMutexGuard aGuard; + pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + if ( pContainerWindow ) + aContainerWinSize = pContainerWindow->GetOutputSizePixel(); + + return aContainerWinSize; +} + +Reference< XUIElement > LayoutManager::implts_createElement( const OUString& aName ) +{ + Reference< ui::XUIElement > xUIElement; + + SolarMutexGuard g; + Sequence< PropertyValue > aPropSeq{ comphelper::makePropertyValue("Frame", m_xFrame), + comphelper::makePropertyValue("Persistent", true) }; + + try + { + xUIElement = m_xUIElementFactoryManager->createUIElement( aName, aPropSeq ); + } + catch (const NoSuchElementException&) + { + } + catch (const IllegalArgumentException&) + { + } + + return xUIElement; +} + +void LayoutManager::implts_setVisibleState( bool bShow ) +{ + { + SolarMutexGuard aWriteLock; + m_aStatusBarElement.m_bMasterHide = !bShow; + } + + implts_updateUIElementsVisibleState( bShow ); +} + +void LayoutManager::implts_updateUIElementsVisibleState( bool bSetVisible ) +{ + // notify listeners + uno::Any a; + if ( bSetVisible ) + implts_notifyListeners( frame::LayoutManagerEvents::VISIBLE, a ); + else + implts_notifyListeners( frame::LayoutManagerEvents::INVISIBLE, a ); + + SolarMutexResettableGuard aWriteLock; + Reference< XUIElement > xMenuBar = m_xMenuBar; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + rtl::Reference< MenuBarManager > xInplaceMenuBar( m_xInplaceMenuBar ); + aWriteLock.clear(); + + if (( xMenuBar.is() || xInplaceMenuBar.is() ) && xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + + MenuBar* pMenuBar( nullptr ); + if ( xInplaceMenuBar.is() ) + pMenuBar = static_cast<MenuBar *>(xInplaceMenuBar->GetMenuBar()); + else + { + MenuBarWrapper* pMenuBarWrapper = static_cast< MenuBarWrapper* >(xMenuBar.get()); + pMenuBar = static_cast<MenuBar *>(pMenuBarWrapper->GetMenuBarManager()->GetMenuBar()); + } + + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow ) + { + if ( bSetVisible ) + { + pSysWindow->SetMenuBar(pMenuBar); + } + else + pSysWindow->SetMenuBar( nullptr ); + } + } + + bool bMustDoLayout; + // Hide/show the statusbar according to bSetVisible + if ( bSetVisible ) + bMustDoLayout = !implts_showStatusBar(); + else + bMustDoLayout = !implts_hideStatusBar(); + + aWriteLock.reset(); + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aWriteLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setVisible( bSetVisible ); + bMustDoLayout = pToolbarManager->isLayoutDirty(); + } + + if ( bMustDoLayout ) + implts_doLayout_notify( false ); +} + +void LayoutManager::implts_setCurrentUIVisibility( bool bShow ) +{ + { + SolarMutexGuard aWriteLock; + if (!bShow && m_aStatusBarElement.m_bVisible && m_aStatusBarElement.m_xUIElement.is()) + m_aStatusBarElement.m_bMasterHide = true; + else if (bShow && m_aStatusBarElement.m_bVisible) + m_aStatusBarElement.m_bMasterHide = false; + } + + implts_updateUIElementsVisibleState( bShow ); +} + +void LayoutManager::implts_destroyStatusBar() +{ + Reference< XComponent > xCompStatusBar; + + SolarMutexClearableGuard aWriteLock; + m_aStatusBarElement.m_aName.clear(); + xCompStatusBar.set( m_aStatusBarElement.m_xUIElement, UNO_QUERY ); + m_aStatusBarElement.m_xUIElement.clear(); + aWriteLock.clear(); + + if ( xCompStatusBar.is() ) + xCompStatusBar->dispose(); + + implts_destroyProgressBar(); +} + +void LayoutManager::implts_createStatusBar( const OUString& aStatusBarName ) +{ + { + SolarMutexGuard aWriteLock; + if (!m_aStatusBarElement.m_xUIElement.is()) + { + implts_readStatusBarState(aStatusBarName); + m_aStatusBarElement.m_aName = aStatusBarName; + m_aStatusBarElement.m_xUIElement = implts_createElement(aStatusBarName); + } + } + + implts_createProgressBar(); +} + +void LayoutManager::implts_readStatusBarState( const OUString& rStatusBarName ) +{ + SolarMutexGuard g; + if ( !m_aStatusBarElement.m_bStateRead ) + { + // Read persistent data for status bar if not yet read! + if ( implts_readWindowStateData( rStatusBarName, m_aStatusBarElement )) + m_aStatusBarElement.m_bStateRead = true; + } +} + +void LayoutManager::implts_createProgressBar() +{ + Reference< XUIElement > xStatusBar; + Reference< XUIElement > xProgressBar; + Reference< XUIElement > xProgressBarBackup; + Reference< awt::XWindow > xContainerWindow; + + SolarMutexResettableGuard aWriteLock; + xStatusBar = m_aStatusBarElement.m_xUIElement; + xProgressBar = m_aProgressBarElement.m_xUIElement; + xProgressBarBackup = m_xProgressBarBackup; + m_xProgressBarBackup.clear(); + xContainerWindow = m_xContainerWindow; + aWriteLock.clear(); + + bool bRecycled = xProgressBarBackup.is(); + rtl::Reference<ProgressBarWrapper> pWrapper; + if ( bRecycled ) + pWrapper = static_cast<ProgressBarWrapper*>(xProgressBarBackup.get()); + else if ( xProgressBar.is() ) + pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get()); + else + pWrapper = new ProgressBarWrapper(); + + if ( xStatusBar.is() ) + { + Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY ); + pWrapper->setStatusBar( xWindow ); + } + else + { + Reference< awt::XWindow > xStatusBarWindow = pWrapper->getStatusBar(); + + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pStatusBarWnd = VCLUnoHelper::GetWindow( xStatusBarWindow ); + if ( !pStatusBarWnd ) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + if ( pWindow ) + { + VclPtrInstance<StatusBar> pStatusBar( pWindow, WinBits( WB_LEFT | WB_3DLOOK ) ); + Reference< awt::XWindow > xStatusBarWindow2( VCLUnoHelper::GetInterface( pStatusBar )); + pWrapper->setStatusBar( xStatusBarWindow2, true ); + } + } + } + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + aWriteLock.reset(); + m_aProgressBarElement.m_xUIElement = pWrapper; + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( bRecycled ) + implts_showProgressBar(); +} + +void LayoutManager::implts_backupProgressBarWrapper() +{ + SolarMutexGuard g; + + if (m_xProgressBarBackup.is()) + return; + + // safe a backup copy of the current progress! + // This copy will be used automatically inside createProgressBar() which is called + // implicitly from implts_doLayout() .-) + m_xProgressBarBackup = m_aProgressBarElement.m_xUIElement; + + // remove the relation between this old progress bar and our old status bar. + // Otherwise we work on disposed items ... + // The internal used ProgressBarWrapper can handle a NULL reference. + if ( m_xProgressBarBackup.is() ) + { + ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(m_xProgressBarBackup.get()); + if ( pWrapper ) + pWrapper->setStatusBar( Reference< awt::XWindow >() ); + } + + // prevent us from dispose() the m_aProgressBarElement.m_xUIElement inside implts_reset() + m_aProgressBarElement.m_xUIElement.clear(); +} + +void LayoutManager::implts_destroyProgressBar() +{ + // don't remove the progressbar in general + // We must reuse it if a new status bar is created later. + // Of course there exists one backup only. + // And further this backup will be released inside our dtor. + implts_backupProgressBarWrapper(); +} + +void LayoutManager::implts_setStatusBarPosSize( const ::Point& rPos, const ::Size& rSize ) +{ + Reference< XUIElement > xStatusBar; + Reference< XUIElement > xProgressBar; + Reference< awt::XWindow > xContainerWindow; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + xStatusBar = m_aStatusBarElement.m_xUIElement; + xProgressBar = m_aProgressBarElement.m_xUIElement; + xContainerWindow = m_xContainerWindow; + + Reference< awt::XWindow > xWindow; + if ( xStatusBar.is() ) + xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY ); + else if ( xProgressBar.is() ) + { + ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + } + aReadLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( !xWindow.is() ) + return; + + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pParentWindow && ( pWindow && pWindow->GetType() == WindowType::STATUSBAR )) + { + vcl::Window* pOldParentWindow = pWindow->GetParent(); + if ( pParentWindow != pOldParentWindow ) + pWindow->SetParent( pParentWindow ); + static_cast<StatusBar *>(pWindow.get())->SetPosSizePixel( rPos, rSize ); + } +} + +bool LayoutManager::implts_showProgressBar() +{ + Reference< XUIElement > xStatusBar; + Reference< XUIElement > xProgressBar; + Reference< awt::XWindow > xWindow; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexGuard aWriteLock; + xStatusBar = m_aStatusBarElement.m_xUIElement; + xProgressBar = m_aProgressBarElement.m_xUIElement; + bool bVisible( m_bVisible ); + + m_aProgressBarElement.m_bVisible = true; + if ( bVisible ) + { + if ( xStatusBar.is() && !m_aStatusBarElement.m_bMasterHide ) + { + xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY ); + } + else if ( xProgressBar.is() ) + { + ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + } + } + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + if ( !pWindow->IsVisible() ) + { + implts_setOffset( pWindow->GetSizePixel().Height() ); + pWindow->Show(); + implts_doLayout_notify( false ); + } + return true; + } + + return false; +} + +bool LayoutManager::implts_hideProgressBar() +{ + Reference< XUIElement > xProgressBar; + Reference< awt::XWindow > xWindow; + bool bHideStatusBar( false ); + + SolarMutexGuard g; + xProgressBar = m_aProgressBarElement.m_xUIElement; + + bool bInternalStatusBar( false ); + if ( xProgressBar.is() ) + { + Reference< awt::XWindow > xStatusBar; + ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + Reference< ui::XUIElement > xStatusBarElement = m_aStatusBarElement.m_xUIElement; + if ( xStatusBarElement.is() ) + xStatusBar.set( xStatusBarElement->getRealInterface(), UNO_QUERY ); + bInternalStatusBar = xStatusBar != xWindow; + } + m_aProgressBarElement.m_bVisible = false; + implts_readStatusBarState( STATUS_BAR_ALIAS ); + bHideStatusBar = !m_aStatusBarElement.m_bVisible; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() && ( bHideStatusBar || bInternalStatusBar )) + { + implts_setOffset( 0 ); + pWindow->Hide(); + implts_doLayout_notify( false ); + return true; + } + + return false; +} + +bool LayoutManager::implts_showStatusBar( bool bStoreState ) +{ + SolarMutexClearableGuard aWriteLock; + Reference< ui::XUIElement > xStatusBar = m_aStatusBarElement.m_xUIElement; + if ( bStoreState ) + m_aStatusBarElement.m_bVisible = true; + aWriteLock.clear(); + + if ( xStatusBar.is() ) + { + Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY ); + + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && !pWindow->IsVisible() ) + { + implts_setOffset( pWindow->GetSizePixel().Height() ); + pWindow->Show(); + implts_doLayout_notify( false ); + return true; + } + } + + return false; +} + +bool LayoutManager::implts_hideStatusBar( bool bStoreState ) +{ + SolarMutexClearableGuard aWriteLock; + Reference< ui::XUIElement > xStatusBar = m_aStatusBarElement.m_xUIElement; + if ( bStoreState ) + m_aStatusBarElement.m_bVisible = false; + aWriteLock.clear(); + + if ( xStatusBar.is() ) + { + Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY ); + + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() ) + { + implts_setOffset( 0 ); + pWindow->Hide(); + implts_doLayout_notify( false ); + return true; + } + } + + return false; +} + +void LayoutManager::implts_setOffset( const sal_Int32 nBottomOffset ) +{ + if ( m_xToolbarManager.is() ) + m_xToolbarManager->setDockingAreaOffsets({ 0, 0, 0, nBottomOffset }); +} + +void LayoutManager::implts_setInplaceMenuBar( const Reference< XIndexAccess >& xMergedMenuBar ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + + if ( m_bInplaceMenuSet ) + return; + + SolarMutexGuard aGuard; + + // Reset old inplace menubar! + VclPtr<Menu> pOldMenuBar; + if (m_xInplaceMenuBar.is()) + { + pOldMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pOldMenuBar.disposeAndClear(); + + m_bInplaceMenuSet = false; + + if ( m_xFrame.is() && m_xContainerWindow.is() ) + { + Reference< XDispatchProvider > xDispatchProvider; + + VclPtr<MenuBar> pMenuBar = VclPtr<MenuBar>::Create(); + m_xInplaceMenuBar = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, xDispatchProvider, OUString(), pMenuBar, true ); + m_xInplaceMenuBar->SetItemContainer( xMergedMenuBar ); + + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + pSysWindow->SetMenuBar(pMenuBar); + + m_bInplaceMenuSet = true; + } + + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + implts_updateMenuBarClose(); +} + +void LayoutManager::implts_resetInplaceMenuBar() +{ + SolarMutexGuard g; + m_bInplaceMenuSet = false; + + if ( m_xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + MenuBarWrapper* pMenuBarWrapper = static_cast< MenuBarWrapper* >(m_xMenuBar.get()); + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + if ( pMenuBarWrapper ) + pSysWindow->SetMenuBar(static_cast<MenuBar *>(pMenuBarWrapper->GetMenuBarManager()->GetMenuBar())); + else + pSysWindow->SetMenuBar(nullptr); + } + } + + // Remove inplace menu bar + VclPtr<Menu> pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); +} + +void SAL_CALL LayoutManager::attachFrame( const Reference< XFrame >& xFrame ) +{ + SolarMutexGuard g; + m_xFrame = xFrame; +} + +void SAL_CALL LayoutManager::reset() +{ + implts_reset( true ); +} + +// XMenuBarMergingAcceptor + +sal_Bool SAL_CALL LayoutManager::setMergedMenuBar( + const Reference< XIndexAccess >& xMergedMenuBar ) +{ + implts_setInplaceMenuBar( xMergedMenuBar ); + + uno::Any a; + implts_notifyListeners( frame::LayoutManagerEvents::MERGEDMENUBAR, a ); + return true; +} + +void SAL_CALL LayoutManager::removeMergedMenuBar() +{ + implts_resetInplaceMenuBar(); +} + +awt::Rectangle SAL_CALL LayoutManager::getCurrentDockingArea() +{ + SolarMutexGuard g; + return m_aDockingArea; +} + +Reference< XDockingAreaAcceptor > SAL_CALL LayoutManager::getDockingAreaAcceptor() +{ + SolarMutexGuard g; + return m_xDockingAreaAcceptor; +} + +void SAL_CALL LayoutManager::setDockingAreaAcceptor( const Reference< ui::XDockingAreaAcceptor >& xDockingAreaAcceptor ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + + if (( m_xDockingAreaAcceptor == xDockingAreaAcceptor ) || !m_xFrame.is() ) + return; + + // IMPORTANT: Be sure to stop layout timer if don't have a docking area acceptor! + if ( !xDockingAreaAcceptor.is() ) + m_aAsyncLayoutTimer.Stop(); + + bool bAutomaticToolbars( m_bAutomaticToolbars ); + + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + + if ( !xDockingAreaAcceptor.is() ) + m_aAsyncLayoutTimer.Stop(); + + // Remove listener from old docking area acceptor + if ( m_xDockingAreaAcceptor.is() ) + { + Reference< awt::XWindow > xWindow( m_xDockingAreaAcceptor->getContainerWindow() ); + if ( xWindow.is() && ( m_xFrame->getContainerWindow() != m_xContainerWindow || !xDockingAreaAcceptor.is() ) ) + xWindow->removeWindowListener( Reference< awt::XWindowListener >(this) ); + + m_aDockingArea = awt::Rectangle(); + if ( pToolbarManager ) + pToolbarManager->resetDockingArea(); + + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pContainerWindow ) + pContainerWindow->RemoveChildEventListener( LINK( this, LayoutManager, WindowEventListener ) ); + } + + m_xDockingAreaAcceptor = xDockingAreaAcceptor; + if ( m_xDockingAreaAcceptor.is() ) + { + m_aDockingArea = awt::Rectangle(); + m_xContainerWindow = m_xDockingAreaAcceptor->getContainerWindow(); + m_xContainerTopWindow.set( m_xContainerWindow, UNO_QUERY ); + m_xContainerWindow->addWindowListener( Reference< awt::XWindowListener >(this) ); + + // we always must keep a connection to the window of our frame for resize events + if ( m_xContainerWindow != m_xFrame->getContainerWindow() ) + m_xFrame->getContainerWindow()->addWindowListener( Reference< awt::XWindowListener >(this) ); + + // #i37884# set initial visibility state - in the plugin case the container window is already shown + // and we get no notification anymore + { + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + if( pContainerWindow ) + m_bParentWindowVisible = pContainerWindow->IsVisible(); + } + } + + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( xDockingAreaAcceptor.is() ) + { + SolarMutexGuard aGuard; + + // Add layout manager as listener to get notifications about toolbar button activities + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + if ( pContainerWindow ) + pContainerWindow->AddChildEventListener( LINK( this, LayoutManager, WindowEventListener ) ); + + // We have now a new container window, reparent all child windows! + implts_reparentChildWindows(); + } + else + implts_destroyElements(); // remove all elements + + if ( pToolbarManager && xDockingAreaAcceptor.is() ) + { + if ( bAutomaticToolbars ) + { + lock(); + pToolbarManager->createStaticToolbars(); + unlock(); + } + implts_doLayout( true, false ); + } +} + +void LayoutManager::implts_reparentChildWindows() +{ + SolarMutexResettableGuard aWriteLock; + UIElement aStatusBarElement = m_aStatusBarElement; + uno::Reference< awt::XWindow > xContainerWindow = m_xContainerWindow; + aWriteLock.clear(); + + uno::Reference< awt::XWindow > xStatusBarWindow; + if ( aStatusBarElement.m_xUIElement.is() ) + { + try + { + xStatusBarWindow.set( aStatusBarElement.m_xUIElement->getRealInterface(), UNO_QUERY ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + + if ( xStatusBarWindow.is() ) + { + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xStatusBarWindow ); + if ( pWindow && pContainerWindow ) + pWindow->SetParent( pContainerWindow ); + } + + implts_resetMenuBar(); + + aWriteLock.reset(); + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + if ( pToolbarManager ) + pToolbarManager->setParentWindow( uno::Reference< awt::XWindowPeer >( xContainerWindow, uno::UNO_QUERY )); + aWriteLock.clear(); +} + +uno::Reference< ui::XUIElement > LayoutManager::implts_createDockingWindow( const OUString& aElementName ) +{ + Reference< XUIElement > xUIElement = implts_createElement( aElementName ); + return xUIElement; +} + +IMPL_LINK( LayoutManager, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + vcl::Window* pWindow = rEvent.GetWindow(); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->childWindowEvent( &rEvent ); + } +} + +void SAL_CALL LayoutManager::createElement( const OUString& aName ) +{ + SAL_INFO( "fwk", "LayoutManager::createElement " << aName ); + + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame = m_xFrame; + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + + bool bMustBeLayouted( false ); + bool bNotify( false ); + + bool bPreviewFrame; + if (m_xToolbarManager.is()) + // Assumes that we created the ToolbarLayoutManager with our frame, if + // not then we're somewhat fouled up ... + bPreviewFrame = m_xToolbarManager->isPreviewFrame(); + else + { + Reference< XModel > xModel( impl_getModelFromFrame( xFrame ) ); + bPreviewFrame = implts_isPreviewModel( xModel ); + } + + if ( m_xContainerWindow.is() && !bPreviewFrame ) // no UI elements on preview frames + { + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ) && m_xToolbarManager.is() ) + { + bNotify = m_xToolbarManager->createToolbar( aName ); + bMustBeLayouted = m_xToolbarManager->isLayoutDirty(); + } + else if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") && + implts_isFrameOrWindowTop(xFrame) ) + { + implts_createMenuBar( aName ); + if (m_bMenuVisible) + bNotify = true; + + aWriteLock.clear(); + } + else if ( aElementType.equalsIgnoreAsciiCase("statusbar") && + ( implts_isFrameOrWindowTop(xFrame) || implts_isEmbeddedLayoutManager() )) + { + implts_createStatusBar( aName ); + bNotify = true; + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") && + implts_isFrameOrWindowTop(xFrame) ) + { + implts_createProgressBar(); + bNotify = true; + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + // Add layout manager as listener for docking and other window events + uno::Reference< uno::XInterface > xThis( static_cast< OWeakObject* >(this), uno::UNO_QUERY ); + uno::Reference< ui::XUIElement > xUIElement( implts_createDockingWindow( aName )); + + if ( xUIElement.is() ) + { + impl_addWindowListeners( xThis, xUIElement ); + } + + // The docking window is created by a factory method located in the sfx2 library. +// CreateDockingWindow( xFrame, aElementName ); + } + } + + if ( bMustBeLayouted ) + implts_doLayout_notify( true ); + + if ( bNotify ) + { + // UI element is invisible - provide information to listeners + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( aName ) ); + } +} + +void SAL_CALL LayoutManager::destroyElement( const OUString& aName ) +{ + SAL_INFO( "fwk", "LayoutManager::destroyElement " << aName ); + + bool bMustBeLayouted(false); + bool bNotify(false); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexClearableGuard aWriteLock; + + OUString aElementType; + OUString aElementName; + + parseResourceURL(aName, aElementType, aElementName); + + if (aElementType.equalsIgnoreAsciiCase("menubar") + && aElementName.equalsIgnoreAsciiCase("menubar")) + { + if (!m_bInplaceMenuSet) + { + impl_clearUpMenuBar(); + m_xMenuBar.clear(); + bNotify = true; + } + } + else if ((aElementType.equalsIgnoreAsciiCase("statusbar") + && aElementName.equalsIgnoreAsciiCase("statusbar")) + || (m_aStatusBarElement.m_aName == aName)) + { + aWriteLock.clear(); + implts_destroyStatusBar(); + bMustBeLayouted = true; + bNotify = true; + } + else if (aElementType.equalsIgnoreAsciiCase("progressbar") + && aElementName.equalsIgnoreAsciiCase("progressbar")) + { + aWriteLock.clear(); + implts_createProgressBar(); + bMustBeLayouted = true; + bNotify = true; + } + else if (aElementType.equalsIgnoreAsciiCase(UIRESOURCETYPE_TOOLBAR) + && m_xToolbarManager.is()) + { + aWriteLock.clear(); + bNotify = m_xToolbarManager->destroyToolbar(aName); + bMustBeLayouted = m_xToolbarManager->isLayoutDirty(); + } + else if (aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + uno::Reference<frame::XFrame> xFrame(m_xFrame); + uno::Reference<XComponentContext> xContext(m_xContext); + aWriteLock.clear(); + + impl_setDockingWindowVisibility(xContext, xFrame, aElementName, false); + bMustBeLayouted = false; + bNotify = false; + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( bMustBeLayouted ) + doLayout(); + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_INVISIBLE, uno::Any( aName ) ); +} + +sal_Bool SAL_CALL LayoutManager::requestElement( const OUString& rResourceURL ) +{ + bool bResult( false ); + bool bNotify( false ); + OUString aElementType; + OUString aElementName; + + parseResourceURL( rResourceURL, aElementType, aElementName ); + + SolarMutexClearableGuard aWriteLock; + + OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk", "LayoutManager::requestElement " << aResName ); + + if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == rResourceURL )) + { + implts_readStatusBarState( rResourceURL ); + if ( m_aStatusBarElement.m_bVisible && !m_aStatusBarElement.m_bMasterHide ) + { + aWriteLock.clear(); + createElement( rResourceURL ); + + // There are some situation where we are not able to create an element. + // Therefore we have to check the reference before further action. + // See #i70019# + uno::Reference< ui::XUIElement > xUIElement( m_aStatusBarElement.m_xUIElement ); + if ( xUIElement.is() ) + { + // we need VCL here to pass special flags to Show() + SolarMutexGuard aGuard; + Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + bResult = true; + bNotify = true; + } + } + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + aWriteLock.clear(); + implts_showProgressBar(); + bResult = true; + bNotify = true; + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ) && m_bVisible ) + { + bool bComponentAttached( !m_aModuleIdentifier.isEmpty() ); + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aWriteLock.clear(); + + if ( pToolbarManager && bComponentAttached ) + { + bNotify = pToolbarManager->requestToolbar( rResourceURL ); + } + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + aWriteLock.clear(); + + CreateDockingWindow( xFrame, aElementName ); + } + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( rResourceURL ) ); + + return bResult; +} + +Reference< XUIElement > SAL_CALL LayoutManager::getElement( const OUString& aName ) +{ + Reference< XUIElement > xUIElement = implts_findElement( aName ); + if ( !xUIElement.is() ) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + xUIElement = pToolbarManager->getToolbar( aName ); + } + + return xUIElement; +} + +Sequence< Reference< ui::XUIElement > > SAL_CALL LayoutManager::getElements() +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< ui::XUIElement > xMenuBar( m_xMenuBar ); + uno::Reference< ui::XUIElement > xStatusBar( m_aStatusBarElement.m_xUIElement ); + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + Sequence< Reference< ui::XUIElement > > aSeq; + if ( pToolbarManager ) + aSeq = pToolbarManager->getToolbars(); + + sal_Int32 nSize = aSeq.getLength(); + sal_Int32 nMenuBarIndex(-1); + sal_Int32 nStatusBarIndex(-1); + if ( xMenuBar.is() ) + { + nMenuBarIndex = nSize; + ++nSize; + } + if ( xStatusBar.is() ) + { + nStatusBarIndex = nSize; + ++nSize; + } + + aSeq.realloc(nSize); + auto pSeq = aSeq.getArray(); + if ( nMenuBarIndex >= 0 ) + pSeq[nMenuBarIndex] = xMenuBar; + if ( nStatusBarIndex >= 0 ) + pSeq[nStatusBarIndex] = xStatusBar; + + return aSeq; +} + +sal_Bool SAL_CALL LayoutManager::showElement( const OUString& aName ) +{ + bool bResult( false ); + bool bNotify( false ); + bool bMustLayout( false ); + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + + OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk", "LayoutManager::showElement " << aResName ); + + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + { + SolarMutexGuard aWriteLock; + m_bMenuVisible = true; + } + + bResult = implts_resetMenuBar(); + bNotify = bResult; + } + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + { + SolarMutexClearableGuard aWriteLock; + if ( m_aStatusBarElement.m_xUIElement.is() && !m_aStatusBarElement.m_bMasterHide && + implts_showStatusBar( true )) + { + aWriteLock.clear(); + + implts_writeWindowStateData( STATUS_BAR_ALIAS, m_aStatusBarElement ); + bMustLayout = true; + bResult = true; + bNotify = true; + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + bNotify = bResult = implts_showProgressBar(); + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bNotify = pToolbarManager->showToolbar( aName ); + bMustLayout = pToolbarManager->isLayoutDirty(); + } + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + SolarMutexClearableGuard aReadGuard; + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + uno::Reference< XComponentContext > xContext( m_xContext ); + aReadGuard.clear(); + + impl_setDockingWindowVisibility( xContext, xFrame, aElementName, true ); + } + + if ( bMustLayout ) + doLayout(); + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( aName ) ); + + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::hideElement( const OUString& aName ) +{ + bool bNotify( false ); + bool bMustLayout( false ); + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk", "LayoutManager::hideElement " << aResName ); + + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + SolarMutexGuard g; + + if ( m_xContainerWindow.is() ) + { + m_bMenuVisible = false; + + SolarMutexGuard aGuard; + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pMenuBar = pSysWindow->GetMenuBar(); + if ( pMenuBar ) + { + pMenuBar->SetDisplayable( false ); + bNotify = true; + } + } + } + } + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + { + SolarMutexGuard g; + if ( m_aStatusBarElement.m_xUIElement.is() && !m_aStatusBarElement.m_bMasterHide && + implts_hideStatusBar( true )) + { + implts_writeWindowStateData( STATUS_BAR_ALIAS, m_aStatusBarElement ); + bMustLayout = true; + bNotify = true; + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + bNotify = implts_hideProgressBar(); + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bNotify = pToolbarManager->hideToolbar( aName ); + bMustLayout = pToolbarManager->isLayoutDirty(); + } + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + SolarMutexClearableGuard aReadGuard; + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + uno::Reference< XComponentContext > xContext( m_xContext ); + aReadGuard.clear(); + + impl_setDockingWindowVisibility( xContext, xFrame, aElementName, false ); + } + + if ( bMustLayout ) + doLayout(); + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_INVISIBLE, uno::Any( aName ) ); + + return false; +} + +sal_Bool SAL_CALL LayoutManager::dockWindow( const OUString& aName, DockingArea DockingArea, const awt::Point& Pos ) +{ + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->dockToolbar( aName, DockingArea, Pos ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return false; +} + +sal_Bool SAL_CALL LayoutManager::dockAllWindows( ::sal_Int16 /*nElementType*/ ) +{ + SolarMutexClearableGuard aReadLock; + bool bResult( false ); + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->dockAllToolbars(); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::floatWindow( const OUString& aName ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->floatToolbar( aName ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::lockWindow( const OUString& aName ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->lockToolbar( aName ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::unlockWindow( const OUString& aName ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->unlockToolbar( aName ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + +void SAL_CALL LayoutManager::setElementSize( const OUString& aName, const awt::Size& aSize ) +{ + if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + return; + + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setToolbarSize( aName, aSize ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } +} + +void SAL_CALL LayoutManager::setElementPos( const OUString& aName, const awt::Point& aPos ) +{ + if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + return; + + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setToolbarPos( aName, aPos ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } +} + +void SAL_CALL LayoutManager::setElementPosSize( const OUString& aName, const awt::Point& aPos, const awt::Size& aSize ) +{ + if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + return; + + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setToolbarPosSize( aName, aPos, aSize ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } +} + +sal_Bool SAL_CALL LayoutManager::isElementVisible( const OUString& aName ) +{ + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + SolarMutexResettableGuard aReadLock; + if ( m_xContainerWindow.is() ) + { + aReadLock.clear(); + + SolarMutexGuard aGuard; + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pMenuBar = pSysWindow->GetMenuBar(); + if ( pMenuBar && pMenuBar->IsDisplayable() ) + return true; + } + else + { + aReadLock.reset(); + return m_bMenuVisible; + } + } + } + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + { + if ( m_aStatusBarElement.m_xUIElement.is() ) + { + Reference< awt::XWindow > xWindow( m_aStatusBarElement.m_xUIElement->getRealInterface(), UNO_QUERY ); + if ( xWindow.is() ) + { + SolarMutexGuard g; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() ) + return true; + else + return false; + } + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + if ( m_aProgressBarElement.m_xUIElement.is() ) + return m_aProgressBarElement.m_bVisible; + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarVisible( aName ); + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + SolarMutexClearableGuard aReadGuard; + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + aReadGuard.clear(); + + return IsDockingWindowVisible( xFrame, aElementName ); + } + + return false; +} + +sal_Bool SAL_CALL LayoutManager::isElementFloating( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarFloating( aName ); + } + + return false; +} + +sal_Bool SAL_CALL LayoutManager::isElementDocked( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarDocked( aName ); + } + + return false; +} + +sal_Bool SAL_CALL LayoutManager::isElementLocked( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarLocked( aName ); + } + + return false; +} + +awt::Size SAL_CALL LayoutManager::getElementSize( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->getToolbarSize( aName ); + } + + return awt::Size(); +} + +awt::Point SAL_CALL LayoutManager::getElementPos( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->getToolbarPos( aName ); + } + + return awt::Point(); +} + +void SAL_CALL LayoutManager::lock() +{ + implts_lock(); + + SolarMutexClearableGuard aReadLock; + sal_Int32 nLockCount( m_nLockCount ); + aReadLock.clear(); + + SAL_INFO( "fwk", "LayoutManager::lock " << reinterpret_cast<sal_Int64>(this) << " - " << nLockCount ); + + Any a( nLockCount ); + implts_notifyListeners( frame::LayoutManagerEvents::LOCK, a ); +} + +void SAL_CALL LayoutManager::unlock() +{ + bool bDoLayout( implts_unlock() ); + + SolarMutexClearableGuard aReadLock; + sal_Int32 nLockCount( m_nLockCount ); + aReadLock.clear(); + + SAL_INFO( "fwk", "LayoutManager::unlock " << reinterpret_cast<sal_Int64>(this) << " - " << nLockCount); + + // conform to documentation: unlock with lock count == 0 means force a layout + + { + SolarMutexGuard aWriteLock; + if (bDoLayout) + m_aAsyncLayoutTimer.Stop(); + } + + Any a( nLockCount ); + implts_notifyListeners( frame::LayoutManagerEvents::UNLOCK, a ); + + if ( bDoLayout ) + implts_doLayout_notify( true ); +} + +void SAL_CALL LayoutManager::doLayout() +{ + implts_doLayout_notify( true ); +} + +// ILayoutNotifications + +void LayoutManager::requestLayout() +{ + doLayout(); +} + +void LayoutManager::implts_doLayout_notify( bool bOuterResize ) +{ + bool bLayouted = implts_doLayout( false, bOuterResize ); + if ( bLayouted ) + implts_notifyListeners( frame::LayoutManagerEvents::LAYOUT, Any() ); +} + +bool LayoutManager::implts_doLayout( bool bForceRequestBorderSpace, bool bOuterResize ) +{ + SAL_INFO( "fwk", "LayoutManager::implts_doLayout" ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + + if ( !m_xFrame.is() || !m_bParentWindowVisible ) + return false; + + bool bPreserveContentSize( m_bPreserveContentSize ); + bool bMustDoLayout( m_bMustDoLayout ); + bool bNoLock = ( m_nLockCount == 0 ); + awt::Rectangle aCurrBorderSpace( m_aDockingArea ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< awt::XTopWindow2 > xContainerTopWindow( m_xContainerTopWindow ); + Reference< awt::XWindow > xComponentWindow; + try { + xComponentWindow = m_xFrame->getComponentWindow(); + } catch (css::lang::DisposedException &) { + // There can be a race between one thread calling Frame::dispose + // (framework/source/services/frame.cxx) -> Frame::disableLayoutManager + // -> LayoutManager::attachFrame(null) setting m_xFrame to null, and + // the main thread firing the timer-triggered + // LayoutManager::AsyncLayoutHdl -> LayoutManager::implts_doLayout and + // calling into the in-dispose m_xFrame here, so silently ignore a + // DisposedException here: + return false; + } + Reference< XDockingAreaAcceptor > xDockingAreaAcceptor( m_xDockingAreaAcceptor ); + aReadLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + bool bLayouted( false ); + + if ( bNoLock && xDockingAreaAcceptor.is() && xContainerWindow.is() && xComponentWindow.is() ) + { + bLayouted = true; + + awt::Rectangle aDockSpace( implts_calcDockingAreaSizes() ); + awt::Rectangle aBorderSpace( aDockSpace ); + bool bGotRequestedBorderSpace( true ); + + // We have to add the height of a possible status bar + aBorderSpace.Height += implts_getStatusBarSize().Height(); + + if ( !equalRectangles( aBorderSpace, aCurrBorderSpace ) || bForceRequestBorderSpace || bMustDoLayout ) + { + // we always resize the content window (instead of the complete container window) if we're not set up + // to (attempt to) preserve the content window's size + if ( bOuterResize && !bPreserveContentSize ) + bOuterResize = false; + + // maximized windows can resized their content window only, not their container window + if ( bOuterResize && xContainerTopWindow.is() && xContainerTopWindow->getIsMaximized() ) + bOuterResize = false; + + // if the component window does not have a size (yet), then we can't use it to calc the container + // window size + awt::Rectangle aComponentRect = xComponentWindow->getPosSize(); + if ( bOuterResize && ( aComponentRect.Width == 0 ) && ( aComponentRect.Height == 0 ) ) + bOuterResize = false; + + bGotRequestedBorderSpace = false; + if ( bOuterResize ) + { + Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY ); + awt::DeviceInfo aContainerInfo = xDevice->getInfo(); + + awt::Size aRequestedSize( aComponentRect.Width + aContainerInfo.LeftInset + aContainerInfo.RightInset + aBorderSpace.X + aBorderSpace.Width, + aComponentRect.Height + aContainerInfo.TopInset + aContainerInfo.BottomInset + aBorderSpace.Y + aBorderSpace.Height ); + awt::Point aComponentPos( aBorderSpace.X, aBorderSpace.Y ); + + bGotRequestedBorderSpace = implts_resizeContainerWindow( aRequestedSize, aComponentPos ); + } + + // if we did not do a container window resize, or it failed, then use the DockingAcceptor as usual + if ( !bGotRequestedBorderSpace ) + bGotRequestedBorderSpace = xDockingAreaAcceptor->requestDockingAreaSpace( aBorderSpace ); + + if ( bGotRequestedBorderSpace ) + { + SolarMutexGuard aWriteGuard; + m_aDockingArea = aBorderSpace; + m_bMustDoLayout = false; + } + } + + if ( bGotRequestedBorderSpace ) + { + ::Size aContainerSize; + ::Size aStatusBarSize; + + // Interim solution to let the layout method within the + // toolbar layout manager. + implts_setOffset( implts_getStatusBarSize().Height() ); + if ( m_xToolbarManager.is() ) + m_xToolbarManager->setDockingArea( aDockSpace ); + + // Subtract status bar size from our container output size. Docking area windows + // don't contain the status bar! + aStatusBarSize = implts_getStatusBarSize(); + aContainerSize = implts_getContainerWindowOutputSize(); + aContainerSize.AdjustHeight( -(aStatusBarSize.Height()) ); + + if ( m_xToolbarManager.is() ) + m_xToolbarManager->doLayout(aContainerSize); + + // Position the status bar + if ( aStatusBarSize.Height() > 0 ) + { + implts_setStatusBarPosSize( ::Point( 0, std::max(( aContainerSize.Height() ), tools::Long( 0 ))), + ::Size( aContainerSize.Width(),aStatusBarSize.Height() )); + } + + xDockingAreaAcceptor->setDockingAreaSpace( aBorderSpace ); + } + } + + return bLayouted; +} + +bool LayoutManager::implts_resizeContainerWindow( const awt::Size& rContainerSize, + const awt::Point& rComponentPos ) +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow = m_xContainerWindow; + Reference< awt::XTopWindow2 > xContainerTopWindow = m_xContainerTopWindow; + Reference< awt::XWindow > xComponentWindow = m_xFrame->getComponentWindow(); + aReadLock.clear(); + + // calculate the maximum size we have for the container window + sal_Int32 nDisplay = xContainerTopWindow->getDisplay(); + tools::Rectangle aWorkArea = Application::GetScreenPosSizePixel( nDisplay ); + + if (!aWorkArea.IsEmpty()) + { + if (( rContainerSize.Width > aWorkArea.GetWidth() ) || ( rContainerSize.Height > aWorkArea.GetHeight() )) + return false; + // Strictly, this is not correct. If we have a multi-screen display (css.awt.DisplayAccess.MultiDisplay == true), + // the "effective work area" would be much larger than the work area of a single display, since we could in theory + // position the container window across multiple screens. + // However, this should suffice as a heuristics here ... (nobody really wants to check whether the different screens are + // stacked horizontally or vertically, whether their work areas can really be combined, or are separated by non-work-areas, + // and the like ... right?) + } + + // resize our container window + xContainerWindow->setPosSize( 0, 0, rContainerSize.Width, rContainerSize.Height, awt::PosSize::SIZE ); + // position the component window + xComponentWindow->setPosSize( rComponentPos.X, rComponentPos.Y, 0, 0, awt::PosSize::POS ); + return true; +} + +void SAL_CALL LayoutManager::setVisible( sal_Bool bVisible ) +{ + SolarMutexClearableGuard aWriteLock; + bool bWasVisible( m_bVisible ); + m_bVisible = bVisible; + aWriteLock.clear(); + + if ( bWasVisible != bool(bVisible) ) + implts_setVisibleState( bVisible ); +} + +sal_Bool SAL_CALL LayoutManager::isVisible() +{ + SolarMutexGuard g; + return m_bVisible; +} + +::Size LayoutManager::implts_getStatusBarSize() +{ + SolarMutexClearableGuard aReadLock; + bool bStatusBarVisible( isElementVisible( STATUS_BAR_ALIAS )); + bool bProgressBarVisible( isElementVisible( "private:resource/progressbar/progressbar" )); + bool bVisible( m_bVisible ); + Reference< XUIElement > xStatusBar( m_aStatusBarElement.m_xUIElement ); + Reference< XUIElement > xProgressBar( m_aProgressBarElement.m_xUIElement ); + + Reference< awt::XWindow > xWindow; + if ( bStatusBarVisible && bVisible && xStatusBar.is() ) + xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY ); + else if ( xProgressBar.is() && !xStatusBar.is() && bProgressBarVisible ) + { + ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + } + aReadLock.clear(); + + if ( xWindow.is() ) + { + awt::Rectangle aPosSize = xWindow->getPosSize(); + return ::Size( aPosSize.Width, aPosSize.Height ); + } + else + return ::Size(); +} + +awt::Rectangle LayoutManager::implts_calcDockingAreaSizes() +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< XDockingAreaAcceptor > xDockingAreaAcceptor( m_xDockingAreaAcceptor ); + aReadLock.clear(); + + awt::Rectangle aBorderSpace; + if ( m_xToolbarManager.is() && xDockingAreaAcceptor.is() && xContainerWindow.is() ) + aBorderSpace = m_xToolbarManager->getDockingArea(); + + return aBorderSpace; +} + +void LayoutManager::implts_setDockingAreaWindowSizes() +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + aReadLock.clear(); + + uno::Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY ); + // Convert relative size to output size. + awt::Rectangle aRectangle = xContainerWindow->getPosSize(); + awt::DeviceInfo aInfo = xDevice->getInfo(); + awt::Size aContainerClientSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset, + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + ::Size aStatusBarSize = implts_getStatusBarSize(); + + // Position the status bar + if ( aStatusBarSize.Height() > 0 ) + { + implts_setStatusBarPosSize( ::Point( 0, std::max(( aContainerClientSize.Height - aStatusBarSize.Height() ), tools::Long( 0 ))), + ::Size( aContainerClientSize.Width, aStatusBarSize.Height() )); + } +} + +void LayoutManager::implts_updateMenuBarClose() +{ + SolarMutexClearableGuard aWriteLock; + bool bShowCloseButton( m_bMenuBarCloseButton ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + aWriteLock.clear(); + + if ( !xContainerWindow.is() ) + return; + + SolarMutexGuard aGuard; + + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pMenuBar = pSysWindow->GetMenuBar(); + if ( pMenuBar ) + { + // TODO remove link on sal_False ?! + pMenuBar->ShowCloseButton(bShowCloseButton); + pMenuBar->SetCloseButtonClickHdl(LINK(this, LayoutManager, MenuBarClose)); + } + } +} + +bool LayoutManager::implts_resetMenuBar() +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexGuard aWriteLock; + bool bMenuVisible( m_bMenuVisible ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + + MenuBar* pSetMenuBar = nullptr; + if ( m_xInplaceMenuBar.is() ) + pSetMenuBar = static_cast<MenuBar *>(m_xInplaceMenuBar->GetMenuBar()); + else + { + MenuBarWrapper* pMenuBarWrapper = static_cast< MenuBarWrapper* >( m_xMenuBar.get() ); + if ( pMenuBarWrapper ) + pSetMenuBar = static_cast<MenuBar*>(pMenuBarWrapper->GetMenuBarManager()->GetMenuBar()); + } + + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow && bMenuVisible && pSetMenuBar ) + { + pSysWindow->SetMenuBar(pSetMenuBar); + pSetMenuBar->SetDisplayable( true ); + return true; + } + + return false; +} + +void LayoutManager::implts_createMSCompatibleMenuBar( const OUString& aName ) +{ + SolarMutexGuard aWriteLock; + + // Find Form menu in the original menubar + m_xMenuBar = implts_createElement( aName ); + uno::Reference< XUIElementSettings > xMenuBarSettings(m_xMenuBar, UNO_QUERY); + uno::Reference< container::XIndexReplace > xMenuIndex(xMenuBarSettings->getSettings(true), UNO_QUERY); + + sal_Int32 nFormsMenu = -1; + for (sal_Int32 nIndex = 0; nIndex < xMenuIndex->getCount(); ++nIndex) + { + uno::Sequence< beans::PropertyValue > aProps; + xMenuIndex->getByIndex( nIndex ) >>= aProps; + OUString aCommand; + for ( beans::PropertyValue const & rProp : std::as_const(aProps) ) + { + if (rProp.Name == "CommandURL") + { + rProp.Value >>= aCommand; + break; + } + } + + if (aCommand == ".uno:FormatFormMenu") + nFormsMenu = nIndex; + } + assert(nFormsMenu != -1); + + // Create the MS compatible Form menu + css::uno::Reference< css::ui::XUIElement > xFormsMenu = implts_createElement( "private:resource/menubar/mscompatibleformsmenu" ); + if(!xFormsMenu.is()) + return; + + // Merge the MS compatible Form menu into the menubar + uno::Reference< XUIElementSettings > xFormsMenuSettings(xFormsMenu, UNO_QUERY); + uno::Reference< container::XIndexAccess > xFormsMenuIndex(xFormsMenuSettings->getSettings(true)); + + assert(xFormsMenuIndex->getCount() >= 1); + uno::Sequence< beans::PropertyValue > aNewFormsMenu; + xFormsMenuIndex->getByIndex( 0 ) >>= aNewFormsMenu; + xMenuIndex->replaceByIndex(nFormsMenu, uno::Any(aNewFormsMenu)); + + setMergedMenuBar( xMenuIndex ); + + // Clear up the temporal forms menubar + Reference< XComponent > xFormsMenuComp( xFormsMenu, UNO_QUERY ); + if ( xFormsMenuComp.is() ) + xFormsMenuComp->dispose(); + xFormsMenu.clear(); +} + +IMPL_LINK_NOARG(LayoutManager, MenuBarClose, void*, void) +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< frame::XDispatchProvider > xProvider(m_xFrame, uno::UNO_QUERY); + uno::Reference< XComponentContext > xContext( m_xContext ); + aReadLock.clear(); + + if ( !xProvider.is()) + return; + + uno::Reference< frame::XDispatchHelper > xDispatcher = frame::DispatchHelper::create( xContext ); + + xDispatcher->executeDispatch( + xProvider, + ".uno:CloseWin", + "_self", + 0, + uno::Sequence< beans::PropertyValue >()); +} + +// XLayoutManagerEventBroadcaster + +void SAL_CALL LayoutManager::addLayoutManagerEventListener( const uno::Reference< frame::XLayoutManagerListener >& xListener ) +{ + m_aListenerContainer.addInterface( cppu::UnoType<frame::XLayoutManagerListener>::get(), xListener ); +} + +void SAL_CALL LayoutManager::removeLayoutManagerEventListener( const uno::Reference< frame::XLayoutManagerListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType<frame::XLayoutManagerListener>::get(), xListener ); +} + +void LayoutManager::implts_notifyListeners(short nEvent, const uno::Any& rInfoParam) +{ + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<frame::XLayoutManagerListener>::get()); + if (pContainer==nullptr) + return; + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject*>(this) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<frame::XLayoutManagerListener*>(pIterator.next())->layoutEvent(aSource, nEvent, rInfoParam); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } +} + +// XWindowListener + +void SAL_CALL LayoutManager::windowResized( const awt::WindowEvent& aEvent ) +{ + SolarMutexGuard g; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + + Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY ); + if ( xIfac == aEvent.Source && m_bVisible ) + { + // We have to call our resize handler at least once synchronously, as some + // application modules need this. So we have to check if this is the first + // call after the async layout time expired. + m_bMustDoLayout = true; + if ( !m_aAsyncLayoutTimer.IsActive() ) + { + m_aAsyncLayoutTimer.Invoke(); + if ( m_nLockCount == 0 ) + m_aAsyncLayoutTimer.Start(); + } + } + else if ( m_xFrame.is() && aEvent.Source == m_xFrame->getContainerWindow() ) + { + // the container window of my DockingAreaAcceptor is not the same as of my frame + // I still have to resize my frames' window as nobody else will do it + Reference< awt::XWindow > xComponentWindow( m_xFrame->getComponentWindow() ); + if( xComponentWindow.is() ) + { + uno::Reference< awt::XDevice > xDevice( m_xFrame->getContainerWindow(), uno::UNO_QUERY ); + + // Convert relative size to output size. + awt::Rectangle aRectangle = m_xFrame->getContainerWindow()->getPosSize(); + awt::DeviceInfo aInfo = xDevice->getInfo(); + awt::Size aSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + + // Resize our component window. + xComponentWindow->setPosSize( 0, 0, aSize.Width, aSize.Height, awt::PosSize::POSSIZE ); + } + } +} + +void SAL_CALL LayoutManager::windowMoved( const awt::WindowEvent& ) +{ +} + +void SAL_CALL LayoutManager::windowShown( const lang::EventObject& aEvent ) +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + bool bParentWindowVisible( m_bParentWindowVisible ); + aReadLock.clear(); + + Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY ); + if ( xIfac == aEvent.Source ) + { + SolarMutexClearableGuard aWriteLock; + m_bParentWindowVisible = true; + bool bSetVisible = ( m_bParentWindowVisible != bParentWindowVisible ); + aWriteLock.clear(); + + if ( bSetVisible ) + implts_updateUIElementsVisibleState( true ); + } +} + +void SAL_CALL LayoutManager::windowHidden( const lang::EventObject& aEvent ) +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + bool bParentWindowVisible( m_bParentWindowVisible ); + aReadLock.clear(); + + Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY ); + if ( xIfac == aEvent.Source ) + { + SolarMutexClearableGuard aWriteLock; + m_bParentWindowVisible = false; + bool bSetInvisible = ( m_bParentWindowVisible != bParentWindowVisible ); + aWriteLock.clear(); + + if ( bSetInvisible ) + implts_updateUIElementsVisibleState( false ); + } +} + +IMPL_LINK_NOARG(LayoutManager, AsyncLayoutHdl, Timer *, void) +{ + { + SolarMutexGuard aReadLock; + + if (!m_xContainerWindow.is()) + return; + } + + implts_setDockingAreaWindowSizes(); + implts_doLayout( true, false ); +} + +// XFrameActionListener + +void SAL_CALL LayoutManager::frameAction( const FrameActionEvent& aEvent ) +{ + if (( aEvent.Action == FrameAction_COMPONENT_ATTACHED ) || ( aEvent.Action == FrameAction_COMPONENT_REATTACHED )) + { + SAL_INFO( "fwk", "LayoutManager::frameAction (COMPONENT_ATTACHED|REATTACHED)" ); + + { + SolarMutexGuard aWriteLock; + m_bMustDoLayout = true; + } + + implts_reset( true ); + implts_doLayout( true, false ); + implts_doLayout( true, true ); + } + else if (( aEvent.Action == FrameAction_FRAME_UI_ACTIVATED ) || ( aEvent.Action == FrameAction_FRAME_UI_DEACTIVATING )) + { + SAL_INFO( "fwk", "LayoutManager::frameAction (FRAME_UI_ACTIVATED|DEACTIVATING)" ); + + implts_toggleFloatingUIElementsVisibility( aEvent.Action == FrameAction_FRAME_UI_ACTIVATED ); + } + else if ( aEvent.Action == FrameAction_COMPONENT_DETACHING ) + { + SAL_INFO( "fwk", "LayoutManager::frameAction (COMPONENT_DETACHING)" ); + + implts_reset( false ); + } +} + +void SAL_CALL LayoutManager::disposing( const lang::EventObject& rEvent ) +{ + bool bDisposeAndClear( false ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aWriteLock; + + if (rEvent.Source == Reference<XInterface>(m_xFrame, UNO_QUERY)) + { + // Our frame gets disposed, release all our references that depends on a working frame reference. + + setDockingAreaAcceptor(Reference<ui::XDockingAreaAcceptor>()); + + // destroy all elements, it's possible that detaching is NOT called! + implts_destroyElements(); + impl_clearUpMenuBar(); + m_xMenuBar.clear(); + VclPtr<Menu> pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); + m_xContainerWindow.clear(); + m_xContainerTopWindow.clear(); + + // forward disposing call to toolbar manager + if (m_xToolbarManager.is()) + m_xToolbarManager->disposing(rEvent); + + if (m_xModuleCfgMgr.is()) + { + try + { + Reference<XUIConfiguration> xModuleCfgMgr(m_xModuleCfgMgr, UNO_QUERY); + xModuleCfgMgr->removeConfigurationListener(Reference<XUIConfigurationListener>(this)); + } + catch (const Exception&) + { + } + } + + if (m_xDocCfgMgr.is()) + { + try + { + Reference<XUIConfiguration> xDocCfgMgr(m_xDocCfgMgr, UNO_QUERY); + xDocCfgMgr->removeConfigurationListener(Reference<XUIConfigurationListener>(this)); + } + catch (const Exception&) + { + } + } + + m_xDocCfgMgr.clear(); + m_xModuleCfgMgr.clear(); + m_xFrame.clear(); + m_pGlobalSettings.reset(); + + bDisposeAndClear = true; + } + else if (rEvent.Source == Reference<XInterface>(m_xContainerWindow, UNO_QUERY)) + { + // Our container window gets disposed. Remove all user interface elements. + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + if (pToolbarManager) + { + uno::Reference<awt::XWindowPeer> aEmptyWindowPeer; + pToolbarManager->setParentWindow(aEmptyWindowPeer); + } + impl_clearUpMenuBar(); + m_xMenuBar.clear(); + VclPtr<Menu> pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); + m_xContainerWindow.clear(); + m_xContainerTopWindow.clear(); + } + else if (rEvent.Source == Reference<XInterface>(m_xDocCfgMgr, UNO_QUERY)) + m_xDocCfgMgr.clear(); + else if (rEvent.Source == Reference<XInterface>(m_xModuleCfgMgr, UNO_QUERY)) + m_xModuleCfgMgr.clear(); + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // Send disposing to our listener when we have lost our frame. + if ( bDisposeAndClear ) + { + // Send message to all listener and forget her references. + uno::Reference< frame::XLayoutManager > xThis(this); + lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + } +} + +void SAL_CALL LayoutManager::elementInserted( const ui::ConfigurationEvent& Event ) +{ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame( m_xFrame ); + rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager ); + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + OUString aElementType; + OUString aElementName; + bool bRefreshLayout(false); + + parseResourceURL( Event.ResourceURL, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + if ( xToolbarManager.is() ) + { + xToolbarManager->elementInserted( Event ); + bRefreshLayout = xToolbarManager->isLayoutDirty(); + } + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_MENUBAR )) + { + Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL ); + Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY ); + if ( xElementSettings.is() ) + { + uno::Reference< XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + if ( Event.Source == uno::Reference< uno::XInterface >( m_xDocCfgMgr, uno::UNO_QUERY )) + xPropSet->setPropertyValue( "ConfigurationSource", Any( m_xDocCfgMgr )); + } + xElementSettings->updateSettings(); + } + } + + if ( bRefreshLayout ) + doLayout(); +} + +void SAL_CALL LayoutManager::elementRemoved( const ui::ConfigurationEvent& Event ) +{ + SolarMutexClearableGuard aReadLock; + Reference< frame::XFrame > xFrame( m_xFrame ); + rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< ui::XUIElement > xMenuBar( m_xMenuBar ); + Reference< ui::XUIConfigurationManager > xModuleCfgMgr( m_xModuleCfgMgr ); + Reference< ui::XUIConfigurationManager > xDocCfgMgr( m_xDocCfgMgr ); + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + OUString aElementType; + OUString aElementName; + bool bRefreshLayout(false); + + parseResourceURL( Event.ResourceURL, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + if ( xToolbarManager.is() ) + { + xToolbarManager->elementRemoved( Event ); + bRefreshLayout = xToolbarManager->isLayoutDirty(); + } + } + else + { + Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL ); + Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY ); + if ( xElementSettings.is() ) + { + bool bNoSettings( false ); + OUString aConfigSourcePropName( "ConfigurationSource" ); + Reference< XInterface > xElementCfgMgr; + Reference< XPropertySet > xPropSet( xElementSettings, UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( aConfigSourcePropName ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => check further + if ( Event.Source == xElementCfgMgr ) + { + // Same UI configuration manager where our element has its settings + if ( Event.Source == Reference< XInterface >( xDocCfgMgr, UNO_QUERY )) + { + // document settings removed + if ( xModuleCfgMgr->hasSettings( Event.ResourceURL )) + { + xPropSet->setPropertyValue( aConfigSourcePropName, Any( m_xModuleCfgMgr )); + xElementSettings->updateSettings(); + return; + } + } + + bNoSettings = true; + } + + // No settings anymore, element must be destroyed + if ( xContainerWindow.is() && bNoSettings ) + { + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow && !m_bInplaceMenuSet ) + pSysWindow->SetMenuBar( nullptr ); + + Reference< XComponent > xComp( xMenuBar, UNO_QUERY ); + if ( xComp.is() ) + xComp->dispose(); + + SolarMutexGuard g; + m_xMenuBar.clear(); + } + } + } + } + + if ( bRefreshLayout ) + doLayout(); +} + +void SAL_CALL LayoutManager::elementReplaced( const ui::ConfigurationEvent& Event ) +{ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame( m_xFrame ); + rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager ); + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + OUString aElementType; + OUString aElementName; + bool bRefreshLayout(false); + + parseResourceURL( Event.ResourceURL, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + if ( xToolbarManager.is() ) + { + xToolbarManager->elementReplaced( Event ); + bRefreshLayout = xToolbarManager->isLayoutDirty(); + } + } + else + { + Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL ); + Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY ); + if ( xElementSettings.is() ) + { + Reference< XInterface > xElementCfgMgr; + Reference< XPropertySet > xPropSet( xElementSettings, UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( "ConfigurationSource" ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => update settings + if ( Event.Source == xElementCfgMgr ) + xElementSettings->updateSettings(); + } + } + + if ( bRefreshLayout ) + doLayout(); +} + +void SAL_CALL LayoutManager::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, + const uno::Any& aValue ) +{ + if ( nHandle != LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY ) + LayoutManager_PBase::setFastPropertyValue_NoBroadcast( nHandle, aValue ); + + switch( nHandle ) + { + case LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER: + implts_updateMenuBarClose(); + break; + + case LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY: + { + bool bValue(false); + if (( aValue >>= bValue ) && bValue ) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + bool bAutomaticToolbars( m_bAutomaticToolbars ); + aReadLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->refreshToolbarsVisibility( bAutomaticToolbars ); + } + break; + } + + case LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI: + implts_setCurrentUIVisibility( !m_bHideCurrentUI ); + break; + default: break; + } +} + +namespace detail +{ + class InfoHelperBuilder + { + private: + std::unique_ptr<::cppu::OPropertyArrayHelper> m_pInfoHelper; + public: + explicit InfoHelperBuilder(const LayoutManager &rManager) + { + uno::Sequence< beans::Property > aProperties; + rManager.describeProperties(aProperties); + m_pInfoHelper.reset( new ::cppu::OPropertyArrayHelper(aProperties, true) ); + } + InfoHelperBuilder(const InfoHelperBuilder&) = delete; + InfoHelperBuilder& operator=(const InfoHelperBuilder&) = delete; + + ::cppu::OPropertyArrayHelper& getHelper() { return *m_pInfoHelper; } + }; +} + +::cppu::IPropertyArrayHelper& SAL_CALL LayoutManager::getInfoHelper() +{ + static detail::InfoHelperBuilder INFO(*this); + return INFO.getHelper(); +} + +uno::Reference< beans::XPropertySetInfo > SAL_CALL LayoutManager::getPropertySetInfo() +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_LayoutManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new framework::LayoutManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.cxx b/framework/source/layoutmanager/toolbarlayoutmanager.cxx new file mode 100644 index 000000000..34b2b0899 --- /dev/null +++ b/framework/source/layoutmanager/toolbarlayoutmanager.cxx @@ -0,0 +1,4116 @@ +/* -*- 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 "toolbarlayoutmanager.hxx" +#include <uiconfiguration/windowstateproperties.hxx> +#include <uielement/addonstoolbarwrapper.hxx> +#include "helpers.hxx" +#include <services/layoutmanager.hxx> +#include <strings.hrc> +#include <classes/fwkresid.hxx> + +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/ui/XUIElementSettings.hpp> +#include <com/sun/star/ui/XUIFunctionListener.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <o3tl/string_view.hxx> +#include <unotools/cmdoptions.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> +#include <utility> +#include <vcl/i18nhelp.hxx> +#include <vcl/dockingarea.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <tools/gen.hxx> + + +using namespace ::com::sun::star; + +namespace framework +{ + +ToolbarLayoutManager::ToolbarLayoutManager( + uno::Reference< uno::XComponentContext > xContext, + uno::Reference< ui::XUIElementFactory > xUIElementFactory, + LayoutManager* pParentLayouter ): + m_xContext(std::move( xContext)), + m_xUIElementFactoryManager(std::move( xUIElementFactory )), + m_pParentLayouter( pParentLayouter ), + m_aDockingArea(0, 0, 0, 0), + m_aDockingAreaOffsets(0, 0, 0, 0), + m_eDockOperation( DOCKOP_ON_COLROW ), + m_ePreviewDetection( PREVIEWFRAME_UNKNOWN ), + m_bComponentAttached( false ), + m_bLayoutDirty( false ), + m_bGlobalSettings( false ), + m_bDockingInProgress( false ), + m_bLayoutInProgress( false ), + m_bToolbarCreation( false ) +{ +} + +ToolbarLayoutManager::~ToolbarLayoutManager() +{ + m_pGlobalSettings.reset(); + m_pAddonOptions.reset(); +} + +// XInterface + +void SAL_CALL ToolbarLayoutManager::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ToolbarLayoutManager::release() noexcept +{ + OWeakObject::release(); +} + +uno::Any SAL_CALL ToolbarLayoutManager::queryInterface( const uno::Type & rType ) +{ + uno::Any a = ::cppu::queryInterface( rType, + static_cast< awt::XDockableWindowListener* >(this), + static_cast< ui::XUIConfigurationListener* >(this), + static_cast< awt::XWindowListener* >(this)); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ToolbarLayoutManager::disposing( const lang::EventObject& aEvent ) +{ + if ( aEvent.Source == m_xFrame ) + { + // Reset all internal references + reset(); + implts_destroyDockingAreaWindows(); + } +} + +awt::Rectangle ToolbarLayoutManager::getDockingArea() +{ + SolarMutexResettableGuard aWriteLock; + tools::Rectangle aNewDockingArea( m_aDockingArea ); + aWriteLock.clear(); + + if ( isLayoutDirty() ) + aNewDockingArea = implts_calcDockingArea(); + + aWriteLock.reset(); + m_aDockingArea = aNewDockingArea; + aWriteLock.clear(); + + return putRectangleValueToAWT(aNewDockingArea); +} + +void ToolbarLayoutManager::setDockingArea( const awt::Rectangle& rDockingArea ) +{ + SolarMutexGuard g; + m_aDockingArea = putAWTToRectangle( rDockingArea ); + m_bLayoutDirty = true; +} + +void ToolbarLayoutManager::implts_setDockingAreaWindowSizes( const awt::Rectangle& rBorderSpace ) +{ + SolarMutexClearableGuard aReadLock; + tools::Rectangle aDockOffsets = m_aDockingAreaOffsets; + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + uno::Reference< awt::XWindow > xTopDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xBottomDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + uno::Reference< awt::XWindow > xLeftDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + aReadLock.clear(); + + uno::Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY ); + + // Convert relative size to output size. + awt::Rectangle aRectangle = xContainerWindow->getPosSize(); + awt::DeviceInfo aInfo = xDevice->getInfo(); + awt::Size aContainerClientSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + tools::Long aStatusBarHeight = aDockOffsets.GetHeight(); + + sal_Int32 nLeftRightDockingAreaHeight( aContainerClientSize.Height ); + if ( rBorderSpace.Y >= 0 ) + { + // Top docking area window + xTopDockAreaWindow->setPosSize( 0, 0, aContainerClientSize.Width, rBorderSpace.Y, awt::PosSize::POSSIZE ); + xTopDockAreaWindow->setVisible( true ); + nLeftRightDockingAreaHeight -= rBorderSpace.Y; + } + + if ( rBorderSpace.Height >= 0 ) + { + // Bottom docking area window + sal_Int32 nBottomPos = std::max( sal_Int32( aContainerClientSize.Height - rBorderSpace.Height - aStatusBarHeight + 1 ), sal_Int32( 0 )); + sal_Int32 nHeight = ( nBottomPos == 0 ) ? 0 : rBorderSpace.Height; + + xBottomDockAreaWindow->setPosSize( 0, nBottomPos, aContainerClientSize.Width, nHeight, awt::PosSize::POSSIZE ); + xBottomDockAreaWindow->setVisible( true ); + nLeftRightDockingAreaHeight -= nHeight - 1; + } + + nLeftRightDockingAreaHeight -= aStatusBarHeight; + if ( rBorderSpace.X >= 0 || nLeftRightDockingAreaHeight > 0 ) + { + // Left docking area window + // We also have to change our right docking area window if the top or bottom area has changed. They have a higher priority! + sal_Int32 nHeight = std::max<sal_Int32>( 0, nLeftRightDockingAreaHeight ); + + xLeftDockAreaWindow->setPosSize( 0, rBorderSpace.Y, rBorderSpace.X, nHeight, awt::PosSize::POSSIZE ); + xLeftDockAreaWindow->setVisible( true ); + } + if ( rBorderSpace.Width >= 0 || nLeftRightDockingAreaHeight > 0 ) + { + // Right docking area window + // We also have to change our right docking area window if the top or bottom area has changed. They have a higher priority! + sal_Int32 nLeftPos = std::max<sal_Int32>( 0, aContainerClientSize.Width - rBorderSpace.Width ); + sal_Int32 nHeight = std::max<sal_Int32>( 0, nLeftRightDockingAreaHeight ); + sal_Int32 nWidth = ( nLeftPos == 0 ) ? 0 : rBorderSpace.Width; + + xRightDockAreaWindow->setPosSize( nLeftPos, rBorderSpace.Y, nWidth, nHeight, awt::PosSize::POSSIZE ); + xRightDockAreaWindow->setVisible( true ); + } +} + + +void ToolbarLayoutManager::doLayout(const ::Size& aContainerSize) +{ + SolarMutexResettableGuard aWriteLock; + bool bLayoutInProgress( m_bLayoutInProgress ); + m_bLayoutInProgress = true; + awt::Rectangle aDockingArea = putRectangleValueToAWT( m_aDockingArea ); + aWriteLock.clear(); + + if ( bLayoutInProgress ) + return; + + // Retrieve row/column dependent data from all docked user-interface elements + for ( sal_Int32 i = 0; i < DOCKINGAREAS_COUNT; i++ ) + { + bool bReverse( isReverseOrderDockingArea( i )); + std::vector< SingleRowColumnWindowData > aRowColumnsWindowData; + + implts_getDockingAreaElementInfos( static_cast<ui::DockingArea>(i), aRowColumnsWindowData ); + + sal_Int32 nOffset( 0 ); + const sal_uInt32 nCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 j = 0; j < nCount; ++j ) + { + sal_uInt32 nIndex = bReverse ? nCount-j-1 : j; + implts_calcWindowPosSizeOnSingleRowColumn( i, nOffset, aRowColumnsWindowData[nIndex], aContainerSize ); + nOffset += aRowColumnsWindowData[j].nStaticSize; + } + } + + implts_setDockingAreaWindowSizes( aDockingArea ); + + aWriteLock.reset(); + m_bLayoutDirty = false; + m_bLayoutInProgress = false; + aWriteLock.clear(); +} + +bool ToolbarLayoutManager::implts_isParentWindowVisible() const +{ + SolarMutexGuard g; + bool bVisible( false ); + if ( m_xContainerWindow.is() ) + bVisible = m_xContainerWindow->isVisible(); + + return bVisible; +} + +tools::Rectangle ToolbarLayoutManager::implts_calcDockingArea() +{ + SolarMutexClearableGuard aReadLock; + UIElementVector aWindowVector( m_aUIElements ); + aReadLock.clear(); + + tools::Rectangle aBorderSpace; + sal_Int32 nCurrRowColumn( 0 ); + sal_Int32 nCurrPos( 0 ); + ui::DockingArea nCurrDockingArea( ui::DockingArea_DOCKINGAREA_TOP ); + std::vector< sal_Int32 > aRowColumnSizes[DOCKINGAREAS_COUNT]; + + // initialize rectangle with zero values! + aBorderSpace.setWidth(0); + aBorderSpace.setHeight(0); + + aRowColumnSizes[static_cast<int>(nCurrDockingArea)].clear(); + aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 ); + + for (auto const& window : aWindowVector) + { + if ( window.m_xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow( window.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xWindow.is() && xDockWindow.is() ) + { + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && !xDockWindow->isFloating() && window.m_bVisible && !window.m_bMasterHide ) + { + awt::Rectangle aPosSize = xWindow->getPosSize(); + if ( window.m_aDockedData.m_nDockedArea != nCurrDockingArea ) + { + nCurrDockingArea = window.m_aDockedData.m_nDockedArea; + nCurrRowColumn = 0; + nCurrPos = 0; + aRowColumnSizes[static_cast<int>(nCurrDockingArea)].clear(); + aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 ); + } + + if ( window.m_aDockedData.m_nDockedArea == nCurrDockingArea ) + { + if ( isHorizontalDockingArea( window.m_aDockedData.m_nDockedArea )) + { + if ( window.m_aDockedData.m_aPos.Y > nCurrPos ) + { + ++nCurrRowColumn; + nCurrPos = window.m_aDockedData.m_aPos.Y; + aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 ); + } + + if ( aPosSize.Height > aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] ) + aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] = aPosSize.Height; + } + else + { + if ( window.m_aDockedData.m_aPos.X > nCurrPos ) + { + ++nCurrRowColumn; + nCurrPos = window.m_aDockedData.m_aPos.X; + aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 ); + } + + if ( aPosSize.Width > aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] ) + aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] = aPosSize.Width; + } + } + } + } + } + } + + // Sum up max heights from every row/column + if ( !aWindowVector.empty() ) + { + for ( sal_Int32 i = 0; i <= sal_Int32(ui::DockingArea_DOCKINGAREA_RIGHT); i++ ) + { + sal_Int32 nSize( 0 ); + const sal_uInt32 nCount = aRowColumnSizes[i].size(); + for ( sal_uInt32 j = 0; j < nCount; j++ ) + nSize += aRowColumnSizes[i][j]; + + if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_TOP) ) + aBorderSpace.SetTop( nSize ); + else if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_BOTTOM) ) + aBorderSpace.SetBottom( nSize ); + else if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_LEFT) ) + aBorderSpace.SetLeft( nSize ); + else + aBorderSpace.SetRight( nSize ); + } + } + + return aBorderSpace; +} + +void ToolbarLayoutManager::reset() +{ + { + SolarMutexGuard aWriteLock; + m_xModuleCfgMgr.clear(); + m_xDocCfgMgr.clear(); + m_ePreviewDetection = PREVIEWFRAME_UNKNOWN; + m_bComponentAttached = false; + } + + destroyToolbars(); + resetDockingArea(); +} + +void ToolbarLayoutManager::attach( + const uno::Reference< frame::XFrame >& xFrame, + const uno::Reference< ui::XUIConfigurationManager >& xModuleCfgMgr, + const uno::Reference< ui::XUIConfigurationManager >& xDocCfgMgr, + const uno::Reference< container::XNameAccess >& xPersistentWindowState ) +{ + // reset toolbar manager if we lose our current frame + if ( m_xFrame.is() && m_xFrame != xFrame ) + reset(); + + SolarMutexGuard g; + m_xFrame = xFrame; + m_xModuleCfgMgr = xModuleCfgMgr; + m_xDocCfgMgr = xDocCfgMgr; + m_xPersistentWindowState = xPersistentWindowState; + m_bComponentAttached = true; +} + +bool ToolbarLayoutManager::isPreviewFrame() +{ + SolarMutexGuard g; + if (m_ePreviewDetection == PREVIEWFRAME_UNKNOWN) + { + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + + uno::Reference< frame::XModel > xModel( impl_getModelFromFrame( xFrame )); + + m_ePreviewDetection = (implts_isPreviewModel( xModel ) ? PREVIEWFRAME_YES : PREVIEWFRAME_NO); + } + return m_ePreviewDetection == PREVIEWFRAME_YES; +} + +void ToolbarLayoutManager::createStaticToolbars() +{ + resetDockingArea(); + implts_createCustomToolBars(); + implts_createAddonsToolBars(); + implts_createNonContextSensitiveToolBars(); + implts_sortUIElements(); +} + +bool ToolbarLayoutManager::requestToolbar( const OUString& rResourceURL ) +{ + if (isPreviewFrame()) + return false; // no toolbars for preview frame! + + bool bNotify( false ); + bool bMustCallCreate( false ); + uno::Reference< ui::XUIElement > xUIElement; + + UIElement aRequestedToolbar = impl_findToolbar( rResourceURL ); + if ( aRequestedToolbar.m_aName != rResourceURL ) + { + bMustCallCreate = true; + aRequestedToolbar.m_aName = rResourceURL; + aRequestedToolbar.m_aType = UIRESOURCETYPE_TOOLBAR; + aRequestedToolbar.m_xUIElement = xUIElement; + implts_readWindowStateData( rResourceURL, aRequestedToolbar ); + } + + xUIElement = aRequestedToolbar.m_xUIElement; + if ( !xUIElement.is() ) + bMustCallCreate = true; + + bool bCreateOrShowToolbar( aRequestedToolbar.m_bVisible && !aRequestedToolbar.m_bMasterHide ); + + if ( bCreateOrShowToolbar ) + bNotify = bMustCallCreate ? createToolbar( rResourceURL ) : showToolbar( rResourceURL ); + + return bNotify; +} + +bool ToolbarLayoutManager::createToolbar( const OUString& rResourceURL ) +{ + bool bNotify( false ); + + uno::Reference<frame::XFrame> xFrame; + uno::Reference<awt::XWindow2> xContainerWindow; + { + SolarMutexGuard aReadLock; + xFrame.set(m_xFrame); + xContainerWindow.set(m_xContainerWindow); + } + + if ( !xFrame.is() || !xContainerWindow.is() ) + return false; + + UIElement aToolbarElement = implts_findToolbar( rResourceURL ); + if ( !aToolbarElement.m_xUIElement.is() ) + { + uno::Reference< ui::XUIElement > xUIElement; + + uno::Sequence< beans::PropertyValue > aPropSeq{ + comphelper::makePropertyValue("Frame", xFrame), + comphelper::makePropertyValue("Persistent", true) + }; + uno::Reference<ui::XUIElementFactory> xUIElementFactory; + { + SolarMutexGuard aReadLock; + xUIElementFactory.set(m_xUIElementFactoryManager); + } + + implts_setToolbarCreation(); + try + { + if ( xUIElementFactory.is() ) + xUIElement = xUIElementFactory->createUIElement( rResourceURL, aPropSeq ); + } + catch (const container::NoSuchElementException&) + { + } + catch (const lang::IllegalArgumentException&) + { + } + implts_setToolbarCreation( false ); + + if ( xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xDockWindow.is() && xWindow.is() ) + { + try + { + xDockWindow->addDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) ); + xWindow->addWindowListener( uno::Reference< awt::XWindowListener >(this) ); + xDockWindow->enableDocking( true ); + } + catch (const uno::Exception&) + { + } + } + + bool bVisible = false; + bool bFloating = false; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexClearableGuard aWriteLock; + + UIElement& rElement = impl_findToolbar(rResourceURL); + if (rElement.m_xUIElement.is()) + { + // somebody else must have created it while we released + // the SolarMutex - just dispose our new instance and + // do nothing. (We have to dispose either the new or the + // existing m_xUIElement.) + aWriteLock.clear(); + uno::Reference<lang::XComponent> const xC(xUIElement, uno::UNO_QUERY); + xC->dispose(); + return false; + } + if (!rElement.m_aName.isEmpty()) + { + // Reuse a local entry so we are able to use the latest + // UI changes for this document. + implts_setElementData(rElement, xDockWindow); + rElement.m_xUIElement = xUIElement; + bVisible = rElement.m_bVisible; + bFloating = rElement.m_bFloating; + } + else + { + // Create new UI element and try to read its state data + UIElement aNewToolbar(rResourceURL, UIRESOURCETYPE_TOOLBAR, xUIElement); + implts_readWindowStateData(rResourceURL, aNewToolbar); + implts_setElementData(aNewToolbar, xDockWindow); + implts_insertToolbar(aNewToolbar); + bVisible = aNewToolbar.m_bVisible; + bFloating = rElement.m_bFloating; + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // set toolbar menu style according to customize command state + SvtCommandOptions aCmdOptions; + + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolbar = static_cast<ToolBox *>(pWindow.get()); + ToolBoxMenuType nMenuType = pToolbar->GetMenuType(); + if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, "ConfigureDialog" )) + pToolbar->SetMenuType( nMenuType & ~ToolBoxMenuType::Customize ); + else + pToolbar->SetMenuType( nMenuType | ToolBoxMenuType::Customize ); + } + bNotify = true; + + implts_sortUIElements(); + + if ( bVisible && !bFloating ) + implts_setLayoutDirty(); + } + } + + return bNotify; +} + +bool ToolbarLayoutManager::destroyToolbar( std::u16string_view rResourceURL ) +{ + uno::Reference< lang::XComponent > xComponent; + + bool bNotify( false ); + bool bMustBeSorted( false ); + bool bMustLayouted( false ); + bool bMustBeDestroyed( !o3tl::starts_with(rResourceURL, u"private:resource/toolbar/addon_") ); + + { + SolarMutexGuard aWriteLock; + for (auto & elem : m_aUIElements) + { + if (elem.m_aName == rResourceURL) + { + xComponent.set(elem.m_xUIElement, uno::UNO_QUERY); + if (bMustBeDestroyed) + elem.m_xUIElement.clear(); + else + elem.m_bVisible = false; + break; + } + } + } + + uno::Reference< ui::XUIElement > xUIElement( xComponent, uno::UNO_QUERY ); + if ( xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + + if ( bMustBeDestroyed ) + { + try + { + if ( xWindow.is() ) + xWindow->removeWindowListener( uno::Reference< awt::XWindowListener >(this) ); + } + catch (const uno::Exception&) + { + } + + try + { + if ( xDockWindow.is() ) + xDockWindow->removeDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) ); + } + catch (const uno::Exception&) + { + } + } + else + { + if ( xWindow.is() ) + xWindow->setVisible( false ); + bNotify = true; + } + + if ( !xDockWindow->isFloating() ) + bMustLayouted = true; + bMustBeSorted = true; + } + + if ( bMustBeDestroyed ) + { + if ( xComponent.is() ) + xComponent->dispose(); + bNotify = true; + } + + if ( bMustLayouted ) + implts_setLayoutDirty(); + + if ( bMustBeSorted ) + implts_sortUIElements(); + + return bNotify; +} + +void ToolbarLayoutManager::destroyToolbars() +{ + UIElementVector aUIElementVector; + implts_getUIElementVectorCopy( aUIElementVector ); + + { + SolarMutexGuard aWriteLock; + m_aUIElements.clear(); + m_bLayoutDirty = true; + } + + for (auto const& elem : aUIElementVector) + { + uno::Reference< lang::XComponent > xComponent( elem.m_xUIElement, uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } +} + +bool ToolbarLayoutManager::showToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + SolarMutexGuard aGuard; + vcl::Window* pWindow = getWindowFromXUIElement( aUIElement.m_xUIElement ); + + // Addons appear to need to be populated at start, but we don't + // want to populate them with (scaled) images until later. + AddonsToolBarWrapper *pAddOns; + pAddOns = dynamic_cast<AddonsToolBarWrapper *>( aUIElement.m_xUIElement.get()); + if (pAddOns) + pAddOns->populateImages(); + + if ( pWindow ) + { + if ( !aUIElement.m_bFloating ) + implts_setLayoutDirty(); + else + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + + aUIElement.m_bVisible = true; + implts_writeWindowStateData( aUIElement ); + implts_setToolbar( aUIElement ); + implts_sortUIElements(); + return true; + } + + return false; +} + +bool ToolbarLayoutManager::hideToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + SolarMutexGuard aGuard; + vcl::Window* pWindow = getWindowFromXUIElement( aUIElement.m_xUIElement ); + if ( pWindow ) + { + pWindow->Show( false ); + if ( !aUIElement.m_bFloating ) + implts_setLayoutDirty(); + + aUIElement.m_bVisible = false; + implts_writeWindowStateData( aUIElement ); + implts_setToolbar( aUIElement ); + return true; + } + + return false; +} + +void ToolbarLayoutManager::refreshToolbarsVisibility( bool bAutomaticToolbars ) +{ + UIElementVector aUIElementVector; + + if ( !bAutomaticToolbars ) + return; + + implts_getUIElementVectorCopy( aUIElementVector ); + + UIElement aUIElement; + SolarMutexGuard aGuard; + for (auto const& elem : aUIElementVector) + { + if ( implts_readWindowStateData( elem.m_aName, aUIElement ) && + ( elem.m_bVisible != aUIElement.m_bVisible ) && !elem.m_bMasterHide ) + { + SolarMutexGuard g; + UIElement& rUIElement = impl_findToolbar( elem.m_aName ); + if ( rUIElement.m_aName == elem.m_aName ) + { + rUIElement.m_bVisible = aUIElement.m_bVisible; + implts_setLayoutDirty(); + } + } + } +} + +void ToolbarLayoutManager::setFloatingToolbarsVisibility( bool bVisible ) +{ + UIElementVector aUIElementVector; + implts_getUIElementVectorCopy( aUIElementVector ); + + SolarMutexGuard aGuard; + for (auto const& elem : aUIElementVector) + { + vcl::Window* pWindow = getWindowFromXUIElement( elem.m_xUIElement ); + if ( pWindow && elem.m_bFloating ) + { + if ( bVisible ) + { + if ( elem.m_bVisible && !elem.m_bMasterHide ) + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + else + pWindow->Show( false ); + } + } +} + +void ToolbarLayoutManager::setVisible( bool bVisible ) +{ + UIElementVector aUIElementVector; + implts_getUIElementVectorCopy( aUIElementVector ); + + SolarMutexGuard aGuard; + for (auto & elem : aUIElementVector) + { + if (!elem.m_bFloating) + { + elem.m_bMasterHide = !bVisible; + implts_setToolbar(elem); + implts_setLayoutDirty(); + } + + vcl::Window* pWindow = getWindowFromXUIElement( elem.m_xUIElement ); + if ( pWindow ) + { + bool bSetVisible( elem.m_bVisible && bVisible ); + if ( !bSetVisible ) + pWindow->Hide(); + else + { + if ( elem.m_bFloating ) + pWindow->Show(true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + } + } + + if ( !bVisible ) + resetDockingArea(); +} + +bool ToolbarLayoutManager::dockToolbar( std::u16string_view rResourceURL, ui::DockingArea eDockingArea, const awt::Point& aPos ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XWindow > xWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xDockWindow.is() ) + { + if ( eDockingArea != ui::DockingArea_DOCKINGAREA_DEFAULT ) + aUIElement.m_aDockedData.m_nDockedArea = eDockingArea; + + if ( !isDefaultPos( aPos )) + aUIElement.m_aDockedData.m_aPos = aPos; + + if ( !xDockWindow->isFloating() ) + { + vcl::Window* pWindow( nullptr ); + ToolBox* pToolBox( nullptr ); + + { + SolarMutexGuard aGuard; + pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + pToolBox = static_cast<ToolBox *>(pWindow); + + // We have to set the alignment of the toolbox. It's possible that the toolbox is moved from a + // horizontal to a vertical docking area! + pToolBox->SetAlign( ImplConvertAlignment( aUIElement.m_aDockedData.m_nDockedArea )); + } + } + + if ( hasDefaultPosValue( aUIElement.m_aDockedData.m_aPos )) + { + // Docking on its default position without a preset position - + // we have to find a good place for it. + ::Size aSize; + + SolarMutexGuard aGuard; + { + if (pToolBox) + aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( aUIElement.m_aDockedData.m_nDockedArea ) ); + else if (pWindow) + aSize = pWindow->GetSizePixel(); + } + + ::Point aPixelPos; + awt::Point aDockPos; + implts_findNextDockingPos(aUIElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos ); + aUIElement.m_aDockedData.m_aPos = aDockPos; + } + } + + implts_setToolbar( aUIElement ); + + if ( xDockWindow->isFloating() ) + { + // ATTENTION: This will call toggleFloatingMode() via notifications which + // sets the floating member of the UIElement correctly! + xDockWindow->setFloatingMode( false ); + } + else + { + implts_writeWindowStateData( aUIElement ); + implts_sortUIElements(); + + if ( aUIElement.m_bVisible ) + implts_setLayoutDirty(); + } + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::dockAllToolbars() +{ + std::vector< OUString > aToolBarNameVector; + + { + SolarMutexGuard aReadLock; + for (auto const& elem : m_aUIElements) + { + if (elem.m_aType == "toolbar" && elem.m_xUIElement.is() && elem.m_bFloating + && elem.m_bVisible) + aToolBarNameVector.push_back(elem.m_aName); + } + } + + bool bResult(true); + const sal_uInt32 nCount = aToolBarNameVector.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + awt::Point aPoint; + aPoint.X = aPoint.Y = SAL_MAX_INT32; + bResult &= dockToolbar( aToolBarNameVector[i], ui::DockingArea_DOCKINGAREA_DEFAULT, aPoint ); + } + + return bResult; +} + +void ToolbarLayoutManager::childWindowEvent( VclSimpleEvent const * pEvent ) +{ + // To enable toolbar controllers to change their image when a sub-toolbar function + // is activated, we need this mechanism. We have NO connection between these toolbars + // anymore! + auto pWindowEvent = dynamic_cast< const VclWindowEvent* >(pEvent); + if (!pWindowEvent) + return; + + if ( pEvent->GetId() == VclEventId::ToolboxSelect ) + { + OUString aToolbarName; + OUString aCommand; + ToolBox* pToolBox = getToolboxPtr( pWindowEvent->GetWindow() ); + + if ( pToolBox ) + { + aToolbarName = retrieveToolbarNameFromHelpURL( pToolBox ); + ToolBoxItemId nId = pToolBox->GetCurItemId(); + if ( nId > ToolBoxItemId(0) ) + aCommand = pToolBox->GetItemCommand( nId ); + } + + if ( !aToolbarName.isEmpty() && !aCommand.isEmpty() ) + { + SolarMutexClearableGuard aReadLock; + ::std::vector< uno::Reference< ui::XUIFunctionListener > > aListenerArray; + + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + uno::Reference< ui::XUIFunctionListener > xListener( elem.m_xUIElement, uno::UNO_QUERY ); + if ( xListener.is() ) + aListenerArray.push_back( xListener ); + } + } + aReadLock.clear(); + + const sal_uInt32 nCount = aListenerArray.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + try + { + aListenerArray[i]->functionExecute( aToolbarName, aCommand ); + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + } + } + } + } + else if ( pEvent->GetId() == VclEventId::ToolboxFormatChanged ) + { + if ( !implts_isToolbarCreationActive() ) + { + ToolBox* pToolBox = getToolboxPtr( pWindowEvent->GetWindow() ); + if ( pToolBox ) + { + OUString aToolbarName = retrieveToolbarNameFromHelpURL( pToolBox ); + if ( !aToolbarName.isEmpty() ) + { + OUString aToolbarUrl = "private:resource/toolbar/" + aToolbarName; + + UIElement aToolbar = implts_findToolbar( aToolbarUrl ); + if ( aToolbar.m_xUIElement.is() && !aToolbar.m_bFloating ) + { + implts_setLayoutDirty(); + m_pParentLayouter->requestLayout(); + } + } + } + } + } +} + +void ToolbarLayoutManager::resetDockingArea() +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + aReadLock.clear(); + + if ( xTopDockingWindow.is() ) + xTopDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); + if ( xLeftDockingWindow.is() ) + xLeftDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); + if ( xRightDockingWindow.is() ) + xRightDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); + if ( xBottomDockingWindow.is() ) + xBottomDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); +} + +void ToolbarLayoutManager::setParentWindow( + const uno::Reference< awt::XWindowPeer >& xParentWindow ) +{ + static const char DOCKINGAREASTRING[] = "dockingarea"; + + uno::Reference< awt::XWindow > xTopDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xLeftDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xRightDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xBottomDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + + { + SolarMutexGuard aWriteLock; + m_xContainerWindow.set(xParentWindow, uno::UNO_QUERY); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] = xTopDockWindow; + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] = xLeftDockWindow; + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] = xRightDockWindow; + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] = xBottomDockWindow; + } + + if ( xParentWindow.is() ) + { + SolarMutexGuard aGuard; + VclPtr< ::DockingAreaWindow > pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xTopDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Top ); + pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xBottomDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Bottom ); + pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xLeftDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Left ); + pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xRightDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Right ); + implts_reparentToolbars(); + } + else + { + destroyToolbars(); + resetDockingArea(); + } +} + +void ToolbarLayoutManager::setDockingAreaOffsets( const ::tools::Rectangle& rOffsets ) +{ + SolarMutexGuard g; + m_aDockingAreaOffsets = rOffsets; + m_bLayoutDirty = true; +} + +OUString ToolbarLayoutManager::implts_generateGenericAddonToolbarTitle( sal_Int32 nNumber ) const +{ + OUString aAddonGenericTitle(FwkResId(STR_TOOLBAR_TITLE_ADDON)); + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + + OUString aNumStr = rI18nHelper.GetNum( nNumber, 0, false, false ); + aAddonGenericTitle = aAddonGenericTitle.replaceFirst( "%num%", aNumStr ); + + return aAddonGenericTitle; +} + +void ToolbarLayoutManager::implts_createAddonsToolBars() +{ + SolarMutexClearableGuard aWriteLock; + if ( !m_pAddonOptions ) + m_pAddonOptions.reset( new AddonsOptions ); + + uno::Reference< ui::XUIElementFactory > xUIElementFactory( m_xUIElementFactoryManager ); + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + aWriteLock.clear(); + + if (isPreviewFrame()) + return; // no addon toolbars for preview frame! + + uno::Sequence< uno::Sequence< beans::PropertyValue > > aAddonToolBarData; + uno::Reference< ui::XUIElement > xUIElement; + + sal_uInt32 nCount = m_pAddonOptions->GetAddonsToolBarCount(); + + uno::Sequence< beans::PropertyValue > aPropSeq( 2 ); + auto pPropSeq = aPropSeq.getArray(); + pPropSeq[0].Name = "Frame"; + pPropSeq[0].Value <<= xFrame; + pPropSeq[1].Name = "ConfigurationData"; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + OUString aAddonToolBarName( "private:resource/toolbar/addon_" + + m_pAddonOptions->GetAddonsToolbarResourceName(i) ); + aAddonToolBarData = m_pAddonOptions->GetAddonsToolBarPart( i ); + pPropSeq[1].Value <<= aAddonToolBarData; + + UIElement aElement = implts_findToolbar( aAddonToolBarName ); + + // #i79828 + // It's now possible that we are called more than once. Be sure to not create + // add-on toolbars more than once! + if ( aElement.m_xUIElement.is() ) + continue; + + try + { + xUIElement = xUIElementFactory->createUIElement( aAddonToolBarName, aPropSeq ); + if ( xUIElement.is() ) + { + uno::Reference< awt::XDockableWindow > xDockWindow( xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() ) + { + try + { + xDockWindow->addDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) ); + xDockWindow->enableDocking( true ); + uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY ); + if ( xWindow.is() ) + xWindow->addWindowListener( uno::Reference< awt::XWindowListener >(this) ); + } + catch (const uno::Exception&) + { + } + } + + OUString aGenericAddonTitle = implts_generateGenericAddonToolbarTitle( i+1 ); + + if ( !aElement.m_aName.isEmpty() ) + { + // Reuse a local entry so we are able to use the latest + // UI changes for this document. + implts_setElementData( aElement, xDockWindow ); + aElement.m_xUIElement = xUIElement; + if ( aElement.m_aUIName.isEmpty() ) + { + aElement.m_aUIName = aGenericAddonTitle; + implts_writeWindowStateData( aElement ); + } + } + else + { + // Create new UI element and try to read its state data + UIElement aNewToolbar( aAddonToolBarName, "toolbar", xUIElement ); + aNewToolbar.m_bFloating = true; + implts_readWindowStateData( aAddonToolBarName, aNewToolbar ); + implts_setElementData( aNewToolbar, xDockWindow ); + if ( aNewToolbar.m_aUIName.isEmpty() ) + { + aNewToolbar.m_aUIName = aGenericAddonTitle; + implts_writeWindowStateData( aNewToolbar ); + } + implts_insertToolbar( aNewToolbar ); + } + + uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY ); + if ( xWindow.is() ) + { + // Set generic title for add-on toolbar + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow->GetText().isEmpty() ) + pWindow->SetText( aGenericAddonTitle ); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolbar = static_cast<ToolBox *>(pWindow.get()); + pToolbar->SetMenuType(); + } + } + } + } + catch (const container::NoSuchElementException&) + { + } + catch (const lang::IllegalArgumentException&) + { + } + } +} + +void ToolbarLayoutManager::implts_createCustomToolBars() +{ + SolarMutexClearableGuard aReadLock; + if ( !m_bComponentAttached ) + return; + + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + uno::Reference< ui::XUIConfigurationManager > xModuleCfgMgr = m_xModuleCfgMgr; + uno::Reference< ui::XUIConfigurationManager > xDocCfgMgr = m_xDocCfgMgr; + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + if (isPreviewFrame()) + return; // no custom toolbars for preview frame! + + uno::Sequence< uno::Sequence< beans::PropertyValue > > aTbxSeq; + if ( xDocCfgMgr.is() ) + { + aTbxSeq = xDocCfgMgr->getUIElementsInfo( ui::UIElementType::TOOLBAR ); + implts_createCustomToolBars( aTbxSeq ); // first create all document based toolbars + } + if ( xModuleCfgMgr.is() ) + { + aTbxSeq = xModuleCfgMgr->getUIElementsInfo( ui::UIElementType::TOOLBAR ); + implts_createCustomToolBars( aTbxSeq ); // second create module based toolbars + } +} + +void ToolbarLayoutManager::implts_createNonContextSensitiveToolBars() +{ + SolarMutexClearableGuard aReadLock; + + if ( !m_xPersistentWindowState.is() || !m_xFrame.is() || !m_bComponentAttached ) + return; + + uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + aReadLock.clear(); + + if (isPreviewFrame()) + return; + + std::vector< OUString > aMakeVisibleToolbars; + + try + { + const uno::Sequence< OUString > aToolbarNames = xPersistentWindowState->getElementNames(); + + if ( aToolbarNames.hasElements() ) + { + OUString aElementType; + OUString aElementName; + + aMakeVisibleToolbars.reserve(aToolbarNames.getLength()); + + SolarMutexGuard g; + + for ( OUString const & aName : aToolbarNames ) + { + parseResourceURL( aName, aElementType, aElementName ); + + // Check that we only create: + // - Toolbars (the statusbar is also member of the persistent window state) + // - Not custom toolbars, there are created with their own method (implts_createCustomToolbars) + if ( aElementType.equalsIgnoreAsciiCase("toolbar") && + aElementName.indexOf( "custom_" ) == -1 ) + { + UIElement aNewToolbar = implts_findToolbar( aName ); + bool bFound = ( aNewToolbar.m_aName == aName ); + if ( !bFound ) + implts_readWindowStateData( aName, aNewToolbar ); + + if ( aNewToolbar.m_bVisible && !aNewToolbar.m_bContextSensitive ) + { + if ( !bFound ) + implts_insertToolbar( aNewToolbar ); + aMakeVisibleToolbars.push_back( aName ); + } + } + } + } + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + } + + for (auto const& rURL : aMakeVisibleToolbars) + { + requestToolbar(rURL); + } +} + +void ToolbarLayoutManager::implts_createCustomToolBars( const uno::Sequence< uno::Sequence< beans::PropertyValue > >& aTbxSeqSeq ) +{ + for ( const uno::Sequence< beans::PropertyValue >& rTbxSeq : aTbxSeqSeq ) + { + OUString aTbxResName; + OUString aTbxTitle; + for ( const beans::PropertyValue& rProp : rTbxSeq ) + { + if ( rProp.Name == "ResourceURL" ) + rProp.Value >>= aTbxResName; + else if ( rProp.Name == "UIName" ) + rProp.Value >>= aTbxTitle; + } + + // Only create custom toolbars. Their name have to start with "custom_"! + if ( !aTbxResName.isEmpty() && ( aTbxResName.indexOf( "custom_" ) != -1 ) ) + implts_createCustomToolBar( aTbxResName, aTbxTitle ); + } +} + +void ToolbarLayoutManager::implts_createCustomToolBar( const OUString& aTbxResName, const OUString& aTitle ) +{ + if ( aTbxResName.isEmpty() ) + return; + + if ( !createToolbar( aTbxResName ) ) + SAL_WARN("fwk.uielement", "ToolbarLayoutManager cannot create custom toolbar"); + + uno::Reference< ui::XUIElement > xUIElement = getToolbar( aTbxResName ); + + if ( !aTitle.isEmpty() && xUIElement.is() ) + { + SolarMutexGuard aGuard; + + vcl::Window* pWindow = getWindowFromXUIElement( xUIElement ); + if ( pWindow ) + pWindow->SetText( aTitle ); + } +} + +void ToolbarLayoutManager::implts_reparentToolbars() +{ + SolarMutexClearableGuard aWriteLock; + UIElementVector aUIElementVector = m_aUIElements; + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + VclPtr<vcl::Window> pTopDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + VclPtr<vcl::Window> pBottomDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + VclPtr<vcl::Window> pLeftDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + VclPtr<vcl::Window> pRightDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + aWriteLock.clear(); + + SolarMutexGuard aGuard; + if ( !pContainerWindow ) + return; + + for (auto const& elem : aUIElementVector) + { + uno::Reference< ui::XUIElement > xUIElement( elem.m_xUIElement ); + if ( xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow; + try + { + // We have to retrieve the window reference with try/catch as it is + // possible that all elements have been disposed! + xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY ); + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + } + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + // Reparent our child windows according to their current state. + if ( elem.m_bFloating ) + pWindow->SetParent( pContainerWindow ); + else + { + if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) + pWindow->SetParent( pTopDockWindow ); + else if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + pWindow->SetParent( pBottomDockWindow ); + else if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ) + pWindow->SetParent( pLeftDockWindow ); + else + pWindow->SetParent( pRightDockWindow ); + } + } + } + } +} + +void ToolbarLayoutManager::implts_setToolbarCreation( bool bStart ) +{ + SolarMutexGuard g; + m_bToolbarCreation = bStart; +} + +bool ToolbarLayoutManager::implts_isToolbarCreationActive() +{ + SolarMutexGuard g; + return m_bToolbarCreation; +} + +void ToolbarLayoutManager::implts_setElementData( UIElement& rElement, const uno::Reference< awt::XDockableWindow >& rDockWindow ) +{ + SolarMutexClearableGuard aReadLock; + bool bShowElement( rElement.m_bVisible && !rElement.m_bMasterHide && implts_isParentWindowVisible() ); + aReadLock.clear(); + + uno::Reference< awt::XWindow2 > xWindow( rDockWindow, uno::UNO_QUERY ); + + vcl::Window* pWindow( nullptr ); + ToolBox* pToolBox( nullptr ); + + if ( !(rDockWindow.is() && xWindow.is()) ) + return; + + { + SolarMutexGuard aGuard; + pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + OUString aText = pWindow->GetText(); + if ( aText.isEmpty() ) + pWindow->SetText( rElement.m_aUIName ); + if ( rElement.m_bNoClose ) + pWindow->SetStyle( pWindow->GetStyle() & ~WB_CLOSEABLE ); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast<ToolBox *>(pWindow); + } + if ( pToolBox ) + { + pToolBox->SetButtonType( rElement.m_nStyle ); + if ( rElement.m_bNoClose ) + pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() & ~WB_CLOSEABLE ); + } + } + + if ( rElement.m_bFloating ) + { + if ( pWindow ) + { + SolarMutexGuard aGuard; + OUString aText = pWindow->GetText(); + if ( aText.isEmpty() ) + pWindow->SetText( rElement.m_aUIName ); + } + + awt::Point aPos(rElement.m_aFloatingData.m_aPos); + bool bWriteData( false ); + bool bUndefPos = hasDefaultPosValue( rElement.m_aFloatingData.m_aPos ); + bool bSetSize = ( rElement.m_aFloatingData.m_aSize.Width != 0 && + rElement.m_aFloatingData.m_aSize.Height != 0 ); + rDockWindow->setFloatingMode( true ); + if ( bUndefPos ) + { + aPos = implts_findNextCascadeFloatingPos(); + rElement.m_aFloatingData.m_aPos = aPos; // set new cascaded position + bWriteData = true; + } + + if( bSetSize ) + xWindow->setOutputSize(rElement.m_aFloatingData.m_aSize); + else + { + if( pToolBox ) + { + // set an optimal initial floating size + SolarMutexGuard aGuard; + ::Size aSize( pToolBox->CalcFloatingWindowSizePixel() ); + pToolBox->SetOutputSizePixel( aSize ); + } + } + + // #i60882# IMPORTANT: Set position after size as it is + // possible that we position some part of the toolbar + // outside of the desktop. A default constructed toolbar + // always has one line. Now VCL automatically + // position the toolbar back into the desktop. Therefore + // we resize the toolbar with the new (wrong) position. + // To fix this problem we have to set the size BEFORE the + // position. + xWindow->setPosSize( aPos.X, aPos.Y, 0, 0, awt::PosSize::POS ); + + if ( bWriteData ) + implts_writeWindowStateData( rElement ); + if ( bShowElement && pWindow ) + { + SolarMutexGuard aGuard; + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + } + else + { + bool bSetSize( false ); + ::Point aPixelPos; + ::Size aSize; + + if ( pToolBox ) + { + SolarMutexGuard aGuard; + pToolBox->SetAlign( ImplConvertAlignment(rElement.m_aDockedData.m_nDockedArea ) ); + pToolBox->SetLineCount( 1 ); + rDockWindow->setFloatingMode( false ); + if ( rElement.m_aDockedData.m_bLocked ) + rDockWindow->lock(); + aSize = pToolBox->CalcWindowSizePixel(); + bSetSize = true; + + if ( isDefaultPos( rElement.m_aDockedData.m_aPos )) + { + awt::Point aDockPos; + implts_findNextDockingPos( rElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos ); + rElement.m_aDockedData.m_aPos = aDockPos; + } + } + + xWindow->setPosSize( aPixelPos.X(), aPixelPos.Y(), 0, 0, awt::PosSize::POS ); + if( bSetSize ) + xWindow->setOutputSize( AWTSize( aSize) ); + + if ( pWindow ) + { + SolarMutexGuard aGuard; + if ( !bShowElement ) + pWindow->Hide(); + } + } +} + +void ToolbarLayoutManager::implts_destroyDockingAreaWindows() +{ + SolarMutexClearableGuard aWriteLock; + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)].clear(); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)].clear(); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)].clear(); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)].clear(); + aWriteLock.clear(); + + // destroy windows + xTopDockingWindow->dispose(); + xLeftDockingWindow->dispose(); + xRightDockingWindow->dispose(); + xBottomDockingWindow->dispose(); +} + +// persistence methods + +bool ToolbarLayoutManager::implts_readWindowStateData( const OUString& aName, UIElement& rElementData ) +{ + return LayoutManager::readWindowStateData( aName, rElementData, m_xPersistentWindowState, + m_pGlobalSettings, m_bGlobalSettings, m_xContext ); +} + +void ToolbarLayoutManager::implts_writeWindowStateData( const UIElement& rElementData ) +{ + SolarMutexClearableGuard aWriteLock; + uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + aWriteLock.clear(); + + bool bPersistent( false ); + uno::Reference< beans::XPropertySet > xPropSet( rElementData.m_xUIElement, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + // Check persistent flag of the user interface element + xPropSet->getPropertyValue("Persistent") >>= bPersistent; + } + catch (const beans::UnknownPropertyException&) + { + bPersistent = true; // Non-configurable elements should at least store their dimension/position + } + catch (const lang::WrappedTargetException&) + { + } + } + + if ( !(bPersistent && xPersistentWindowState.is()) ) + return; + + try + { + uno::Sequence<beans::PropertyValue> aWindowState{ + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKED, !rElementData.m_bFloating), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_VISIBLE, rElementData.m_bVisible), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKINGAREA, + rElementData.m_aDockedData.m_nDockedArea), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKPOS, + rElementData.m_aDockedData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_POS, + rElementData.m_aFloatingData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_SIZE, + rElementData.m_aFloatingData.m_aSize), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED, + rElementData.m_aDockedData.m_bLocked), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_STYLE, + static_cast<sal_uInt16>(rElementData.m_nStyle)) + }; + + OUString aName = rElementData.m_aName; + if ( xPersistentWindowState->hasByName( aName )) + { + uno::Reference< container::XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY ); + xReplace->replaceByName( aName, uno::Any( aWindowState )); + } + else + { + uno::Reference< container::XNameContainer > xInsert( xPersistentWindowState, uno::UNO_QUERY ); + xInsert->insertByName( aName, uno::Any( aWindowState )); + } + } + catch (const uno::Exception&) + { + } +} + +/****************************************************************************** + LOOKUP PART FOR TOOLBARS +******************************************************************************/ + +UIElement& ToolbarLayoutManager::impl_findToolbar( std::u16string_view aName ) +{ + static UIElement aEmptyElement; + + SolarMutexGuard g; + for (auto & elem : m_aUIElements) + { + if ( elem.m_aName == aName ) + return elem; + } + + return aEmptyElement; +} + +UIElement ToolbarLayoutManager::implts_findToolbar( std::u16string_view aName ) +{ + SolarMutexGuard g; + return impl_findToolbar( aName ); +} + +UIElement ToolbarLayoutManager::implts_findToolbar( const uno::Reference< uno::XInterface >& xToolbar ) +{ + UIElement aToolbar; + + SolarMutexGuard g; + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + uno::Reference< uno::XInterface > xIfac( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xIfac == xToolbar ) + { + aToolbar = elem; + break; + } + } + } + + return aToolbar; +} + +uno::Reference< awt::XWindow > ToolbarLayoutManager::implts_getXWindow( std::u16string_view aName ) +{ + uno::Reference< awt::XWindow > xWindow; + + SolarMutexGuard g; + for (auto const& elem : m_aUIElements) + { + if ( elem.m_aName == aName && elem.m_xUIElement.is() ) + { + xWindow.set( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + break; + } + } + + return xWindow; +} + +vcl::Window* ToolbarLayoutManager::implts_getWindow( std::u16string_view aName ) +{ + uno::Reference< awt::XWindow > xWindow = implts_getXWindow( aName ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + + return pWindow; +} + +bool ToolbarLayoutManager::implts_insertToolbar( const UIElement& rUIElement ) +{ + UIElement aTempData; + bool bFound( false ); + bool bResult( false ); + + aTempData = implts_findToolbar( rUIElement.m_aName ); + if ( aTempData.m_aName == rUIElement.m_aName ) + bFound = true; + + if ( !bFound ) + { + SolarMutexGuard g; + m_aUIElements.push_back( rUIElement ); + bResult = true; + } + + return bResult; +} + +void ToolbarLayoutManager::implts_setToolbar( const UIElement& rUIElement ) +{ + SolarMutexGuard g; + UIElement& rData = impl_findToolbar( rUIElement.m_aName ); + if ( rData.m_aName == rUIElement.m_aName ) + rData = rUIElement; + else + m_aUIElements.push_back( rUIElement ); +} + +/****************************************************************************** + LAYOUT CODE PART FOR TOOLBARS +******************************************************************************/ + +awt::Point ToolbarLayoutManager::implts_findNextCascadeFloatingPos() +{ + const sal_Int32 nHotZoneX = 50; + const sal_Int32 nHotZoneY = 50; + const sal_Int32 nCascadeIndentX = 15; + const sal_Int32 nCascadeIndentY = 15; + + SolarMutexClearableGuard aReadLock; + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + aReadLock.clear(); + + awt::Point aStartPos( nCascadeIndentX, nCascadeIndentY ); + awt::Point aCurrPos( aStartPos ); + + if ( xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + if ( pContainerWindow ) + aStartPos = AWTPoint(pContainerWindow->OutputToScreenPixel(VCLPoint(aStartPos))); + } + + // Determine size of top and left docking area + awt::Rectangle aTopRect( xTopDockingWindow->getPosSize() ); + awt::Rectangle aLeftRect( xLeftDockingWindow->getPosSize() ); + + aStartPos.X += aLeftRect.Width + nCascadeIndentX; + aStartPos.Y += aTopRect.Height + nCascadeIndentY; + aCurrPos = aStartPos; + + // Try to find a cascaded position for the new floating window + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + uno::Reference< awt::XDockableWindow > xDockWindow( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY ); + if ( xDockWindow.is() && xDockWindow->isFloating() ) + { + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() ) + { + awt::Rectangle aFloatRect = xWindow->getPosSize(); + if ((( aFloatRect.X - nHotZoneX ) <= aCurrPos.X ) && + ( aFloatRect.X >= aCurrPos.X ) && + (( aFloatRect.Y - nHotZoneY ) <= aCurrPos.Y ) && + ( aFloatRect.Y >= aCurrPos.Y )) + { + aCurrPos.X = aFloatRect.X + nCascadeIndentX; + aCurrPos.Y = aFloatRect.Y + nCascadeIndentY; + } + } + } + } + } + + return aCurrPos; +} + +void ToolbarLayoutManager::implts_sortUIElements() +{ + SolarMutexGuard g; + + std::stable_sort( m_aUIElements.begin(), m_aUIElements.end()); // first created element should first + + // We have to reset our temporary flags. + for (auto & elem : m_aUIElements) + elem.m_bUserActive = false; +} + +void ToolbarLayoutManager::implts_getUIElementVectorCopy( UIElementVector& rCopy ) +{ + SolarMutexGuard g; + rCopy = m_aUIElements; +} + +::Size ToolbarLayoutManager::implts_getTopBottomDockingAreaSizes() +{ + ::Size aSize; + uno::Reference< awt::XWindow > xTopDockingAreaWindow; + uno::Reference< awt::XWindow > xBottomDockingAreaWindow; + + { + SolarMutexGuard aReadLock; + xTopDockingAreaWindow = m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)]; + xBottomDockingAreaWindow = m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)]; + } + + if ( xTopDockingAreaWindow.is() ) + aSize.setWidth( xTopDockingAreaWindow->getPosSize().Height ); + if ( xBottomDockingAreaWindow.is() ) + aSize.setHeight( xBottomDockingAreaWindow->getPosSize().Height ); + + return aSize; +} + +void ToolbarLayoutManager::implts_getDockingAreaElementInfos( ui::DockingArea eDockingArea, std::vector< SingleRowColumnWindowData >& rRowColumnsWindowData ) +{ + std::vector< UIElement > aWindowVector; + + if (( eDockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + + uno::Reference< awt::XWindow > xDockAreaWindow; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aReadLock; + aWindowVector.reserve(m_aUIElements.size()); + xDockAreaWindow = m_xDockAreaWindows[static_cast<int>(eDockingArea)]; + for (auto const& elem : m_aUIElements) + { + if (elem.m_aDockedData.m_nDockedArea == eDockingArea && elem.m_bVisible) + { + uno::Reference<ui::XUIElement> xUIElement(elem.m_xUIElement); + if (xUIElement.is()) + { + uno::Reference<awt::XWindow> xWindow(xUIElement->getRealInterface(), + uno::UNO_QUERY); + uno::Reference<awt::XDockableWindow> xDockWindow(xWindow, uno::UNO_QUERY); + if (xDockWindow.is()) + { + if (!elem.m_bFloating) + { + // docked windows + aWindowVector.push_back(elem); + } + else + { + // floating windows + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow); + DockingManager* pDockMgr = vcl::Window::GetDockingManager(); + if (pDockMgr != nullptr) + { + SystemWindow* pFloatingWindow = pDockMgr->GetFloatingWindow(pWindow); + if (pFloatingWindow) + { + // update the position data of the floating window + if (pFloatingWindow->UpdatePositionData()) + { + awt::Rectangle aTmpRect = xWindow->getPosSize(); + UIElement uiElem = elem; + uiElem.m_aFloatingData.m_aPos + = awt::Point(aTmpRect.X, aTmpRect.Y); + implts_setToolbar(uiElem); + implts_writeWindowStateData(uiElem); + } + } + } + } + } + } + } + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + rRowColumnsWindowData.clear(); + + // Collect data from windows that are on the same row/column + sal_Int32 j; + sal_Int32 nIndex( 0 ); + sal_Int32 nLastPos( 0 ); + sal_Int32 nCurrPos( -1 ); + sal_Int32 nLastRowColPixelPos( 0 ); + awt::Rectangle aDockAreaRect; + + if ( xDockAreaWindow.is() ) + aDockAreaRect = xDockAreaWindow->getPosSize(); + + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + nLastRowColPixelPos = 0; + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + nLastRowColPixelPos = aDockAreaRect.Height; + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + nLastRowColPixelPos = 0; + else + nLastRowColPixelPos = aDockAreaRect.Width; + + const sal_uInt32 nCount = aWindowVector.size(); + for ( j = 0; j < sal_Int32( nCount); j++ ) + { + const UIElement& rElement = aWindowVector[j]; + uno::Reference< awt::XWindow > xWindow; + uno::Reference< ui::XUIElement > xUIElement( rElement.m_xUIElement ); + awt::Rectangle aPosSize; + + if ( !lcl_checkUIElement(xUIElement,aPosSize,xWindow) ) + continue; + if ( isHorizontalDockingArea( eDockingArea )) + { + if ( nCurrPos == -1 ) + { + nCurrPos = rElement.m_aDockedData.m_aPos.Y; + nLastPos = 0; + + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + sal_Int32 nSpace( 0 ); + if ( rElement.m_aDockedData.m_aPos.Y != nCurrPos ) + { + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + nLastRowColPixelPos += rRowColumnsWindowData[nIndex].nStaticSize; + else + nLastRowColPixelPos -= rRowColumnsWindowData[nIndex].nStaticSize; + ++nIndex; + nLastPos = 0; + nCurrPos = rElement.m_aDockedData.m_aPos.Y; + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + // Calc space before an element and store it + nSpace = ( rElement.m_aDockedData.m_aPos.X - nLastPos ); + if ( rElement.m_aDockedData.m_aPos.X >= nLastPos ) + { + rRowColumnsWindowData[nIndex].nSpace += nSpace; + nLastPos = rElement.m_aDockedData.m_aPos.X + aPosSize.Width; + } + else + { + nSpace = 0; + nLastPos += aPosSize.Width; + } + rRowColumnsWindowData[nIndex].aRowColumnSpace.push_back( nSpace ); + + rRowColumnsWindowData[nIndex].aRowColumnWindows.push_back( xWindow ); + rRowColumnsWindowData[nIndex].aUIElementNames.push_back( rElement.m_aName ); + rRowColumnsWindowData[nIndex].aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, + rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, + aPosSize.Height ); + if ( rRowColumnsWindowData[nIndex].nStaticSize < aPosSize.Height ) + rRowColumnsWindowData[nIndex].nStaticSize = aPosSize.Height; + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( 0, nLastRowColPixelPos, + aDockAreaRect.Width, aPosSize.Height ); + else + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( 0, ( nLastRowColPixelPos - aPosSize.Height ), + aDockAreaRect.Width, aPosSize.Height ); + rRowColumnsWindowData[nIndex].nVarSize += aPosSize.Width + nSpace; + } + else + { + if ( nCurrPos == -1 ) + { + nCurrPos = rElement.m_aDockedData.m_aPos.X; + nLastPos = 0; + + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + sal_Int32 nSpace( 0 ); + if ( rElement.m_aDockedData.m_aPos.X != nCurrPos ) + { + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + nLastRowColPixelPos += rRowColumnsWindowData[nIndex].nStaticSize; + else + nLastRowColPixelPos -= rRowColumnsWindowData[nIndex].nStaticSize; + ++nIndex; + nLastPos = 0; + nCurrPos = rElement.m_aDockedData.m_aPos.X; + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + // Calc space before an element and store it + nSpace = ( rElement.m_aDockedData.m_aPos.Y - nLastPos ); + if ( rElement.m_aDockedData.m_aPos.Y > nLastPos ) + { + rRowColumnsWindowData[nIndex].nSpace += nSpace; + nLastPos = rElement.m_aDockedData.m_aPos.Y + aPosSize.Height; + } + else + { + nSpace = 0; + nLastPos += aPosSize.Height; + } + rRowColumnsWindowData[nIndex].aRowColumnSpace.push_back( nSpace ); + + rRowColumnsWindowData[nIndex].aRowColumnWindows.push_back( xWindow ); + rRowColumnsWindowData[nIndex].aUIElementNames.push_back( rElement.m_aName ); + rRowColumnsWindowData[nIndex].aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, + rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, + aPosSize.Height ); + if ( rRowColumnsWindowData[nIndex].nStaticSize < aPosSize.Width ) + rRowColumnsWindowData[nIndex].nStaticSize = aPosSize.Width; + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( nLastRowColPixelPos, 0, + aPosSize.Width, aDockAreaRect.Height ); + else + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( ( nLastRowColPixelPos - aPosSize.Width ), 0, + aPosSize.Width, aDockAreaRect.Height ); + rRowColumnsWindowData[nIndex].nVarSize += aPosSize.Height + nSpace; + } + } +} + +void ToolbarLayoutManager::implts_getDockingAreaElementInfoOnSingleRowCol( ui::DockingArea eDockingArea, sal_Int32 nRowCol, SingleRowColumnWindowData& rRowColumnWindowData ) +{ + std::vector< UIElement > aWindowVector; + + if (( eDockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + + bool bHorzDockArea = isHorizontalDockingArea( eDockingArea ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aReadLock; + for (auto const& elem : m_aUIElements) + { + if (elem.m_aDockedData.m_nDockedArea == eDockingArea) + { + bool bSameRowCol = bHorzDockArea ? (elem.m_aDockedData.m_aPos.Y == nRowCol) + : (elem.m_aDockedData.m_aPos.X == nRowCol); + uno::Reference<ui::XUIElement> xUIElement(elem.m_xUIElement); + + if (bSameRowCol && xUIElement.is()) + { + uno::Reference<awt::XWindow> xWindow(xUIElement->getRealInterface(), + uno::UNO_QUERY); + if (xWindow.is()) + { + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow); + uno::Reference<awt::XDockableWindow> xDockWindow(xWindow, uno::UNO_QUERY); + if (pWindow && elem.m_bVisible && xDockWindow.is() && !elem.m_bFloating) + aWindowVector.push_back(elem); // docked windows + } + } + } + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // Initialize structure + rRowColumnWindowData.aUIElementNames.clear(); + rRowColumnWindowData.aRowColumnWindows.clear(); + rRowColumnWindowData.aRowColumnWindowSizes.clear(); + rRowColumnWindowData.aRowColumnSpace.clear(); + rRowColumnWindowData.nVarSize = 0; + rRowColumnWindowData.nStaticSize = 0; + rRowColumnWindowData.nSpace = 0; + rRowColumnWindowData.nRowColumn = nRowCol; + + // Collect data from windows that are on the same row/column + sal_Int32 j; + sal_Int32 nLastPos( 0 ); + + const sal_uInt32 nCount = aWindowVector.size(); + for ( j = 0; j < sal_Int32( nCount); j++ ) + { + const UIElement& rElement = aWindowVector[j]; + uno::Reference< awt::XWindow > xWindow; + uno::Reference< ui::XUIElement > xUIElement( rElement.m_xUIElement ); + awt::Rectangle aPosSize; + if ( !lcl_checkUIElement(xUIElement,aPosSize,xWindow) ) + continue; + + sal_Int32 nSpace; + if ( isHorizontalDockingArea( eDockingArea )) + { + nSpace = ( rElement.m_aDockedData.m_aPos.X - nLastPos ); + + // Calc space before an element and store it + if ( rElement.m_aDockedData.m_aPos.X > nLastPos ) + rRowColumnWindowData.nSpace += nSpace; + else + nSpace = 0; + + nLastPos = rElement.m_aDockedData.m_aPos.X + aPosSize.Width; + + rRowColumnWindowData.aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, aPosSize.Height ); + if ( rRowColumnWindowData.nStaticSize < aPosSize.Height ) + rRowColumnWindowData.nStaticSize = aPosSize.Height; + rRowColumnWindowData.nVarSize += aPosSize.Width; + } + else + { + // Calc space before an element and store it + nSpace = ( rElement.m_aDockedData.m_aPos.Y - nLastPos ); + if ( rElement.m_aDockedData.m_aPos.Y > nLastPos ) + rRowColumnWindowData.nSpace += nSpace; + else + nSpace = 0; + + nLastPos = rElement.m_aDockedData.m_aPos.Y + aPosSize.Height; + + rRowColumnWindowData.aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, aPosSize.Height ); + if ( rRowColumnWindowData.nStaticSize < aPosSize.Width ) + rRowColumnWindowData.nStaticSize = aPosSize.Width; + rRowColumnWindowData.nVarSize += aPosSize.Height; + } + + rRowColumnWindowData.aUIElementNames.push_back( rElement.m_aName ); + rRowColumnWindowData.aRowColumnWindows.push_back( xWindow ); + rRowColumnWindowData.aRowColumnSpace.push_back( nSpace ); + rRowColumnWindowData.nVarSize += nSpace; + } +} + +::tools::Rectangle ToolbarLayoutManager::implts_getWindowRectFromRowColumn( + ui::DockingArea DockingArea, + const SingleRowColumnWindowData& rRowColumnWindowData, + const ::Point& rMousePos, + std::u16string_view rExcludeElementName ) +{ + ::tools::Rectangle aWinRect; + + if (( DockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + DockingArea = ui::DockingArea_DOCKINGAREA_TOP; + + if ( rRowColumnWindowData.aRowColumnWindows.empty() ) + return aWinRect; + else + { + SolarMutexClearableGuard aReadLock; + VclPtr<vcl::Window> pContainerWindow( VCLUnoHelper::GetWindow( m_xContainerWindow )); + VclPtr<vcl::Window> pDockingAreaWindow( VCLUnoHelper::GetWindow( m_xDockAreaWindows[static_cast<int>(DockingArea)] )); + aReadLock.clear(); + + // Calc correct position of the column/row rectangle to be able to compare it with mouse pos/tracking rect + SolarMutexGuard aGuard; + + // Retrieve output size from container Window + if ( pDockingAreaWindow && pContainerWindow ) + { + const sal_uInt32 nCount = rRowColumnWindowData.aRowColumnWindows.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + awt::Rectangle aWindowRect = rRowColumnWindowData.aRowColumnWindows[i]->getPosSize(); + ::tools::Rectangle aRect( aWindowRect.X, aWindowRect.Y, aWindowRect.X+aWindowRect.Width, aWindowRect.Y+aWindowRect.Height ); + aRect.SetPos( pContainerWindow->ScreenToOutputPixel( pDockingAreaWindow->OutputToScreenPixel( aRect.TopLeft() ))); + if ( aRect.Contains( rMousePos )) + { + // Check if we have found the excluded element. If yes, we have to provide an empty rectangle. + // We prevent that a toolbar cannot be moved when the mouse pointer is inside its own rectangle! + if ( rExcludeElementName != rRowColumnWindowData.aUIElementNames[i] ) + return aRect; + else + break; + } + } + } + } + + return aWinRect; +} + +::tools::Rectangle ToolbarLayoutManager::implts_determineFrontDockingRect( + ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + const ::tools::Rectangle& rDockedElementRect, + std::u16string_view rMovedElementName, + const ::tools::Rectangle& rMovedElementRect ) +{ + SingleRowColumnWindowData aRowColumnWindowData; + + bool bHorzDockArea( isHorizontalDockingArea( eDockingArea )); + implts_getDockingAreaElementInfoOnSingleRowCol( eDockingArea, nRowCol, aRowColumnWindowData ); + if ( aRowColumnWindowData.aRowColumnWindows.empty() ) + return rMovedElementRect; + else + { + sal_Int32 nSpace( 0 ); + ::tools::Rectangle aFrontDockingRect( rMovedElementRect ); + const sal_uInt32 nCount = aRowColumnWindowData.aRowColumnWindows.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + if ( bHorzDockArea ) + { + if ( aRowColumnWindowData.aRowColumnWindowSizes[i].X >= rDockedElementRect.Left() ) + { + nSpace += aRowColumnWindowData.aRowColumnSpace[i]; + break; + } + else if ( aRowColumnWindowData.aUIElementNames[i] == rMovedElementName ) + nSpace += aRowColumnWindowData.aRowColumnWindowSizes[i].Width + + aRowColumnWindowData.aRowColumnSpace[i]; + else + nSpace = 0; + } + else + { + if ( aRowColumnWindowData.aRowColumnWindowSizes[i].Y >= rDockedElementRect.Top() ) + { + nSpace += aRowColumnWindowData.aRowColumnSpace[i]; + break; + } + else if ( aRowColumnWindowData.aUIElementNames[i] == rMovedElementName ) + nSpace += aRowColumnWindowData.aRowColumnWindowSizes[i].Height + + aRowColumnWindowData.aRowColumnSpace[i]; + else + nSpace = 0; + } + } + + if ( nSpace > 0 ) + { + sal_Int32 nMove = std::min( nSpace, static_cast<sal_Int32>(aFrontDockingRect.getWidth()) ); + if ( bHorzDockArea ) + aFrontDockingRect.Move( -nMove, 0 ); + else + aFrontDockingRect.Move( 0, -nMove ); + } + + return aFrontDockingRect; + } +} + +void ToolbarLayoutManager::implts_findNextDockingPos( ui::DockingArea DockingArea, const ::Size& aUIElementSize, awt::Point& rVirtualPos, ::Point& rPixelPos ) +{ + SolarMutexClearableGuard aReadLock; + if (( DockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + DockingArea = ui::DockingArea_DOCKINGAREA_TOP; + uno::Reference< awt::XWindow > xDockingWindow( m_xDockAreaWindows[static_cast<int>(DockingArea)] ); + ::Size aDockingWinSize; + + // Retrieve output size from container Window + vcl::Window* pDockingWindow = VCLUnoHelper::GetWindow( xDockingWindow ); + if ( pDockingWindow ) + aDockingWinSize = pDockingWindow->GetOutputSizePixel(); + aReadLock.clear(); + + sal_Int32 nFreeRowColPixelPos( 0 ); + sal_Int32 nMaxSpace( 0 ); + sal_Int32 nNeededSpace( 0 ); + sal_Int32 nTopDockingAreaSize( 0 ); + + if ( isHorizontalDockingArea( DockingArea )) + { + nMaxSpace = aDockingWinSize.Width(); + nNeededSpace = aUIElementSize.Width(); + } + else + { + nMaxSpace = aDockingWinSize.Height(); + nNeededSpace = aUIElementSize.Height(); + nTopDockingAreaSize = implts_getTopBottomDockingAreaSizes().Width(); + } + + std::vector< SingleRowColumnWindowData > aRowColumnsWindowData; + + implts_getDockingAreaElementInfos( DockingArea, aRowColumnsWindowData ); + sal_Int32 nPixelPos( 0 ); + const sal_uInt32 nCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + SingleRowColumnWindowData& rRowColumnWindowData = aRowColumnsWindowData[i]; + + if (( DockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || + ( DockingArea == ui::DockingArea_DOCKINGAREA_RIGHT )) + nPixelPos += rRowColumnWindowData.nStaticSize; + + if ((( nMaxSpace - rRowColumnWindowData.nVarSize ) >= nNeededSpace ) || + ( rRowColumnWindowData.nSpace >= nNeededSpace )) + { + // Check current row where we can find the needed space + sal_Int32 nCurrPos( 0 ); + const sal_uInt32 nWindowSizesCount = rRowColumnWindowData.aRowColumnWindowSizes.size(); + for ( sal_uInt32 j = 0; j < nWindowSizesCount; j++ ) + { + awt::Rectangle rRect = rRowColumnWindowData.aRowColumnWindowSizes[j]; + sal_Int32& rSpace = rRowColumnWindowData.aRowColumnSpace[j]; + if ( isHorizontalDockingArea( DockingArea )) + { + if ( rSpace >= nNeededSpace ) + { + rVirtualPos = awt::Point( nCurrPos, rRowColumnWindowData.nRowColumn ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rPixelPos = ::Point( nCurrPos, nPixelPos ); + else + rPixelPos = ::Point( nCurrPos, aDockingWinSize.Height() - nPixelPos ); + return; + } + nCurrPos = rRect.X + rRect.Width; + } + else + { + if ( rSpace >= nNeededSpace ) + { + rVirtualPos = awt::Point( rRowColumnWindowData.nRowColumn, nCurrPos ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rPixelPos = ::Point( nPixelPos, nTopDockingAreaSize + nCurrPos ); + else + rPixelPos = ::Point( aDockingWinSize.Width() - nPixelPos , nTopDockingAreaSize + nCurrPos ); + return; + } + nCurrPos = rRect.Y + rRect.Height; + } + } + + if (( nCurrPos + nNeededSpace ) <= nMaxSpace ) + { + if ( isHorizontalDockingArea( DockingArea )) + { + rVirtualPos = awt::Point( nCurrPos, rRowColumnWindowData.nRowColumn ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rPixelPos = ::Point( nCurrPos, nPixelPos ); + else + rPixelPos = ::Point( nCurrPos, aDockingWinSize.Height() - nPixelPos ); + return; + } + else + { + rVirtualPos = awt::Point( rRowColumnWindowData.nRowColumn, nCurrPos ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rPixelPos = ::Point( nPixelPos, nTopDockingAreaSize + nCurrPos ); + else + rPixelPos = ::Point( aDockingWinSize.Width() - nPixelPos , nTopDockingAreaSize + nCurrPos ); + return; + } + } + } + + if (( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT )) + nPixelPos += rRowColumnWindowData.nStaticSize; + } + + sal_Int32 nNextFreeRowCol( 0 ); + sal_Int32 nRowColumnsCount = aRowColumnsWindowData.size(); + if ( nRowColumnsCount > 0 ) + nNextFreeRowCol = aRowColumnsWindowData[nRowColumnsCount-1].nRowColumn+1; + else + nNextFreeRowCol = 0; + + if ( nNextFreeRowCol == 0 ) + { + if ( DockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + nFreeRowColPixelPos = aDockingWinSize.Height() - aUIElementSize.Height(); + else if ( DockingArea == ui::DockingArea_DOCKINGAREA_RIGHT ) + nFreeRowColPixelPos = aDockingWinSize.Width() - aUIElementSize.Width(); + } + + if ( isHorizontalDockingArea( DockingArea )) + { + rVirtualPos = awt::Point( 0, nNextFreeRowCol ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rPixelPos = ::Point( 0, nFreeRowColPixelPos ); + else + rPixelPos = ::Point( 0, aDockingWinSize.Height() - nFreeRowColPixelPos ); + } + else + { + rVirtualPos = awt::Point( nNextFreeRowCol, 0 ); + rPixelPos = ::Point( aDockingWinSize.Width() - nFreeRowColPixelPos, 0 ); + } +} + +void ToolbarLayoutManager::implts_calcWindowPosSizeOnSingleRowColumn( + sal_Int32 nDockingArea, + sal_Int32 nOffset, + SingleRowColumnWindowData& rRowColumnWindowData, + const ::Size& rContainerSize ) +{ + sal_Int32 nDiff(0); + sal_Int32 nRCSpace( rRowColumnWindowData.nSpace ); + sal_Int32 nContainerClientSize(0); + + if ( rRowColumnWindowData.aRowColumnWindows.empty() ) + return; + + if ( isHorizontalDockingArea( nDockingArea )) + { + nContainerClientSize = rContainerSize.Width(); + nDiff = nContainerClientSize - rRowColumnWindowData.nVarSize; + } + else + { + sal_Int32 nTopDockingAreaSize = implts_getTopBottomDockingAreaSizes().Width(); + sal_Int32 nBottomDockingAreaSize = implts_getTopBottomDockingAreaSizes().Height(); + nContainerClientSize = ( rContainerSize.Height() - nTopDockingAreaSize - nBottomDockingAreaSize ); + nDiff = nContainerClientSize - rRowColumnWindowData.nVarSize; + } + + const sal_uInt32 nCount = rRowColumnWindowData.aRowColumnWindowSizes.size(); + if (( nDiff < 0 ) && ( nRCSpace > 0 )) + { + // First we try to reduce the size of blank space before/behind docked windows + sal_Int32 i = nCount - 1; + while ( i >= 0 ) + { + sal_Int32 nSpace = rRowColumnWindowData.aRowColumnSpace[i]; + if ( nSpace >= -nDiff ) + { + if ( isHorizontalDockingArea( nDockingArea )) + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].X += nDiff; + } + else + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].Y += nDiff; + } + nDiff = 0; + + break; + } + else if ( nSpace > 0 ) + { + if ( isHorizontalDockingArea( nDockingArea )) + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].X -= nSpace; + } + else + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].Y -= nSpace; + } + nDiff += nSpace; + } + --i; + } + } + + // Check if we have to reduce further + if ( nDiff < 0 ) + { + // Now we have to reduce the size of certain docked windows + sal_Int32 i = sal_Int32( nCount - 1 ); + while ( i >= 0 ) + { + awt::Rectangle& rWinRect = rRowColumnWindowData.aRowColumnWindowSizes[i]; + ::Size aMinSize; + + SolarMutexGuard aGuard; + { + uno::Reference< awt::XWindow > xWindow = rRowColumnWindowData.aRowColumnWindows[i]; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + aMinSize = static_cast<ToolBox *>(pWindow.get())->CalcMinimumWindowSizePixel(); + } + + if ( !aMinSize.IsEmpty() ) + { + if ( isHorizontalDockingArea( nDockingArea )) + { + sal_Int32 nMaxReducation = rWinRect.Width - aMinSize.Width(); + if ( nMaxReducation >= -nDiff ) + { + rWinRect.Width = rWinRect.Width + nDiff; + nDiff = 0; + } + else + { + rWinRect.Width = aMinSize.Width(); + nDiff += nMaxReducation; + } + + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].X += nDiff; + } + else + { + sal_Int32 nMaxReducation = rWinRect.Height - aMinSize.Height(); + if ( nMaxReducation >= -nDiff ) + { + rWinRect.Height = rWinRect.Height + nDiff; + nDiff = 0; + } + else + { + rWinRect.Height = aMinSize.Height(); + nDiff += nMaxReducation; + } + + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].Y += nDiff; + } + } + + if ( nDiff >= 0 ) + break; + + --i; + } + } + + SolarMutexClearableGuard aReadLock; + VclPtr<vcl::Window> pDockAreaWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[nDockingArea] ); + aReadLock.clear(); + + sal_Int32 nCurrPos( 0 ); + + SolarMutexGuard aGuard; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + uno::Reference< awt::XWindow > xWindow = rRowColumnWindowData.aRowColumnWindows[i]; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + vcl::Window* pOldParentWindow = pWindow->GetParent(); + + if ( pDockAreaWindow != pOldParentWindow ) + pWindow->SetParent( pDockAreaWindow ); + + awt::Rectangle aWinRect = rRowColumnWindowData.aRowColumnWindowSizes[i]; + if ( isHorizontalDockingArea( nDockingArea )) + { + if ( aWinRect.X < nCurrPos ) + aWinRect.X = nCurrPos; + pWindow->SetPosSizePixel( ::Point( aWinRect.X, nOffset ), ::Size( aWinRect.Width, rRowColumnWindowData.nStaticSize )); + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + nCurrPos += ( aWinRect.X - nCurrPos ) + aWinRect.Width; + } + else + { + if ( aWinRect.Y < nCurrPos ) + aWinRect.Y = nCurrPos; + pWindow->SetPosSizePixel( ::Point( nOffset, aWinRect.Y ), ::Size( rRowColumnWindowData.nStaticSize, aWinRect.Height )); + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + nCurrPos += ( aWinRect.Y - nCurrPos ) + aWinRect.Height; + } + } +} + +void ToolbarLayoutManager::implts_setLayoutDirty() +{ + SolarMutexGuard g; + m_bLayoutDirty = true; +} + +void ToolbarLayoutManager::implts_setLayoutInProgress( bool bInProgress ) +{ + SolarMutexGuard g; + m_bLayoutInProgress = bInProgress; +} + +::tools::Rectangle ToolbarLayoutManager::implts_calcHotZoneRect( const ::tools::Rectangle& rRect, sal_Int32 nHotZoneOffset ) +{ + ::tools::Rectangle aRect( rRect ); + + aRect.AdjustLeft( -nHotZoneOffset ); + aRect.AdjustTop( -nHotZoneOffset ); + aRect.AdjustRight(nHotZoneOffset ); + aRect.AdjustBottom(nHotZoneOffset ); + + return aRect; +} + +void ToolbarLayoutManager::implts_calcDockingPosSize( + UIElement& rUIElement, + DockingOperation& rDockingOperation, + ::tools::Rectangle& rTrackingRect, + const Point& rMousePos ) +{ + SolarMutexResettableGuard aReadLock; + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + ::Size aContainerWinSize; + vcl::Window* pContainerWindow( nullptr ); + ::tools::Rectangle aDockingAreaOffsets( m_aDockingAreaOffsets ); + aReadLock.clear(); + + if ( !rUIElement.m_xUIElement.is() ) + { + rTrackingRect = ::tools::Rectangle(); + return; + } + + { + // Retrieve output size from container Window + SolarMutexGuard aGuard; + pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + aContainerWinSize = pContainerWindow->GetOutputSizePixel(); + } + + vcl::Window* pDockingAreaWindow( nullptr ); + uno::Reference< awt::XWindow > xWindow( rUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xDockingAreaWindow; + ::tools::Rectangle aTrackingRect( rTrackingRect ); + ui::DockingArea eDockedArea( rUIElement.m_aDockedData.m_nDockedArea ); + sal_Int32 nTopDockingAreaSize( implts_getTopBottomDockingAreaSizes().Width() ); + sal_Int32 nBottomDockingAreaSize( implts_getTopBottomDockingAreaSizes().Height() ); + bool bHorizontalDockArea(( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) || + ( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM )); + sal_Int32 nMaxLeftRightDockAreaSize = aContainerWinSize.Height() - + nTopDockingAreaSize - + nBottomDockingAreaSize - + aDockingAreaOffsets.Top() - + aDockingAreaOffsets.Bottom(); + ::tools::Rectangle aDockingAreaRect; + + aReadLock.reset(); + xDockingAreaWindow = m_xDockAreaWindows[static_cast<int>(eDockedArea)]; + aReadLock.clear(); + + { + SolarMutexGuard aGuard; + pDockingAreaWindow = VCLUnoHelper::GetWindow( xDockingAreaWindow ); + VclPtr<vcl::Window> pDockWindow = VCLUnoHelper::GetWindow( xWindow ); + ToolBox* pToolBox( nullptr ); + if ( pDockWindow && pDockWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast<ToolBox *>(pDockWindow.get()); + + aDockingAreaRect = ::tools::Rectangle( pDockingAreaWindow->GetPosPixel(), pDockingAreaWindow->GetSizePixel() ); + if ( pToolBox ) + { + // docked toolbars always have one line + ::Size aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( eDockedArea ) ); + aTrackingRect.SetSize( ::Size( aSize.Width(), aSize.Height() )); + } + } + + // default docking operation, dock on the given row/column + bool bOpOutsideOfDockingArea( !aDockingAreaRect.Contains( rMousePos )); + + std::vector< SingleRowColumnWindowData > aRowColumnsWindowData; + + rDockingOperation = DOCKOP_ON_COLROW; + implts_getDockingAreaElementInfos( eDockedArea, aRowColumnsWindowData ); + + // determine current first row/column and last row/column + sal_Int32 nMaxRowCol( -1 ); + sal_Int32 nMinRowCol( SAL_MAX_INT32 ); + const sal_uInt32 nCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + if ( aRowColumnsWindowData[i].nRowColumn > nMaxRowCol ) + nMaxRowCol = aRowColumnsWindowData[i].nRowColumn; + if ( aRowColumnsWindowData[i].nRowColumn < nMinRowCol ) + nMinRowCol = aRowColumnsWindowData[i].nRowColumn; + } + + if ( !bOpOutsideOfDockingArea ) + { + // docking inside our docking area + sal_Int32 nIndex( -1 ); + sal_Int32 nRowCol( -1 ); + ::tools::Rectangle aWindowRect; + ::tools::Rectangle aRowColumnRect; + + const sal_uInt32 nWindowDataCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 i = 0; i < nWindowDataCount; i++ ) + { + ::tools::Rectangle aRect( aRowColumnsWindowData[i].aRowColumnRect.X, + aRowColumnsWindowData[i].aRowColumnRect.Y, + aRowColumnsWindowData[i].aRowColumnRect.X + aRowColumnsWindowData[i].aRowColumnRect.Width, + aRowColumnsWindowData[i].aRowColumnRect.Y + aRowColumnsWindowData[i].aRowColumnRect.Height ); + + { + // Calc correct position of the column/row rectangle to be able to compare it with mouse pos/tracking rect + SolarMutexGuard aGuard; + aRect.SetPos( pContainerWindow->ScreenToOutputPixel( pDockingAreaWindow->OutputToScreenPixel( aRect.TopLeft() ))); + } + + bool bIsInsideRowCol( aRect.Contains( rMousePos ) ); + if ( bIsInsideRowCol ) + { + nIndex = i; + nRowCol = aRowColumnsWindowData[i].nRowColumn; + rDockingOperation = implts_determineDockingOperation( eDockedArea, aRect, rMousePos ); + aWindowRect = implts_getWindowRectFromRowColumn( eDockedArea, aRowColumnsWindowData[i], rMousePos, rUIElement.m_aName ); + aRowColumnRect = aRect; + break; + } + } + + OSL_ENSURE( ( nIndex >= 0 ) && ( nRowCol >= 0 ), "Impossible case - no row/column found but mouse pointer is inside our docking area" ); + if (( nIndex >= 0 ) && ( nRowCol >= 0 )) + { + if ( rDockingOperation == DOCKOP_ON_COLROW ) + { + if ( !aWindowRect.IsEmpty()) + { + // Tracking rect is on a row/column and mouse is over a docked toolbar. + // Determine if the tracking rect must be located before/after the docked toolbar. + + ::tools::Rectangle aUIElementRect( aWindowRect ); + sal_Int32 nMiddle( bHorizontalDockArea ? ( aWindowRect.Left() + aWindowRect.getWidth() / 2 ) : + ( aWindowRect.Top() + aWindowRect.getHeight() / 2 )); + bool bInsertBefore( bHorizontalDockArea ? ( rMousePos.X() < nMiddle ) : ( rMousePos.Y() < nMiddle )); + if ( bInsertBefore ) + { + if ( bHorizontalDockArea ) + { + sal_Int32 nSize = std::clamp( sal_Int32(aContainerWinSize.Width() - aWindowRect.Left()), + sal_Int32(0), sal_Int32(aTrackingRect.getWidth()) ); + if ( nSize == 0 ) + nSize = aWindowRect.getWidth(); + + aUIElementRect.SetSize( ::Size( nSize, aWindowRect.getHeight() )); + aWindowRect = implts_determineFrontDockingRect( eDockedArea, nRowCol, aWindowRect,rUIElement.m_aName, aUIElementRect ); + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.X = aWindowRect.Left(); + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + } + else + { + sal_Int32 nSize = std::clamp( sal_Int32(nTopDockingAreaSize + nMaxLeftRightDockAreaSize - aWindowRect.Top()), + sal_Int32(0), sal_Int32(aTrackingRect.getHeight()) ); + if ( nSize == 0 ) + nSize = aWindowRect.getHeight(); + + aUIElementRect.SetSize( ::Size( aWindowRect.getWidth(), nSize )); + aWindowRect = implts_determineFrontDockingRect( eDockedArea, nRowCol, aWindowRect, rUIElement.m_aName, aUIElementRect ); + + // Set virtual position + sal_Int32 nPosY = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( aWindowRect.TopLeft() )).Y(); + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + rUIElement.m_aDockedData.m_aPos.Y = nPosY; + } + + rTrackingRect = aWindowRect; + return; + } + else + { + if ( bHorizontalDockArea ) + { + sal_Int32 nSize = ::std::clamp( sal_Int32(aContainerWinSize.Width() - aWindowRect.Right()), + sal_Int32(0), sal_Int32(aTrackingRect.getWidth()) ); + if ( nSize == 0 ) + { + aUIElementRect.SetPos( ::Point( aContainerWinSize.Width() - aTrackingRect.getWidth(), aWindowRect.Top() )); + aUIElementRect.SetSize( ::Size( aTrackingRect.getWidth(), aWindowRect.getHeight() )); + rUIElement.m_aDockedData.m_aPos.X = aUIElementRect.Left(); + + } + else + { + aUIElementRect.SetPos( ::Point( aWindowRect.Right(), aWindowRect.Top() )); + aUIElementRect.SetSize( ::Size( nSize, aWindowRect.getHeight() )); + rUIElement.m_aDockedData.m_aPos.X = aWindowRect.Right(); + } + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + } + else + { + sal_Int32 nSize = std::clamp( sal_Int32(nTopDockingAreaSize + nMaxLeftRightDockAreaSize - aWindowRect.Bottom()), + sal_Int32(0), sal_Int32(aTrackingRect.getHeight()) ); + aUIElementRect.SetPos( ::Point( aWindowRect.Left(), aWindowRect.Bottom() )); + aUIElementRect.SetSize( ::Size( aWindowRect.getWidth(), nSize )); + + // Set virtual position + sal_Int32 nPosY( 0 ); + { + SolarMutexGuard aGuard; + nPosY = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( aWindowRect.BottomRight() )).Y(); + } + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + rUIElement.m_aDockedData.m_aPos.Y = nPosY; + } + + rTrackingRect = aUIElementRect; + return; + } + } + else + { + implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect ); + rTrackingRect = implts_calcTrackingAndElementRect( + eDockedArea, nRowCol, rUIElement, + aTrackingRect, aRowColumnRect, aContainerWinSize ); + return; + } + } + else + { + if ((( nRowCol == nMinRowCol ) && ( rDockingOperation == DOCKOP_BEFORE_COLROW )) || + (( nRowCol == nMaxRowCol ) && ( rDockingOperation == DOCKOP_AFTER_COLROW ))) + bOpOutsideOfDockingArea = true; + else + { + // handle docking before/after a row + implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect ); + rTrackingRect = implts_calcTrackingAndElementRect( + eDockedArea, nRowCol, rUIElement, + aTrackingRect, aRowColumnRect, aContainerWinSize ); + + sal_Int32 nOffsetX( 0 ); + sal_Int32 nOffsetY( 0 ); + if ( bHorizontalDockArea ) + nOffsetY = sal_Int32( floor( aRowColumnRect.getHeight() / 2.0 + 0.5 )); + else + nOffsetX = sal_Int32( floor( aRowColumnRect.getWidth() / 2.0 + 0.5 )); + + if ( rDockingOperation == DOCKOP_BEFORE_COLROW ) + { + if (( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT )) + { + // Docking before/after means move track rectangle half column/row. + // As left and top are ordered 0...n instead of right and bottom + // which uses n...0, we have to use negative values for top/left. + nOffsetX *= -1; + nOffsetY *= -1; + } + } + else + { + if (( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || ( eDockedArea == ui::DockingArea_DOCKINGAREA_RIGHT )) + { + // Docking before/after means move track rectangle half column/row. + // As left and top are ordered 0...n instead of right and bottom + // which uses n...0, we have to use negative values for top/left. + nOffsetX *= -1; + nOffsetY *= -1; + } + nRowCol++; + } + + if ( bHorizontalDockArea ) + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + else + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + + rTrackingRect.Move( nOffsetX, nOffsetY ); + rTrackingRect.SetSize( aTrackingRect.GetSize() ); + } + } + } + } + + // Docking outside of our docking window area => + // Users want to dock before/after first/last docked element or to an empty docking area + if ( !bOpOutsideOfDockingArea ) + return; + + // set correct size for docking + implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect ); + rTrackingRect = aTrackingRect; + + if ( bHorizontalDockArea ) + { + sal_Int32 nPosX( std::max( sal_Int32( rTrackingRect.Left()), sal_Int32( 0 ))); + if (( nPosX + rTrackingRect.getWidth()) > aContainerWinSize.Width() ) + nPosX = std::min( nPosX, + std::max( sal_Int32( aContainerWinSize.Width() - rTrackingRect.getWidth() ), + sal_Int32( 0 ))); + + sal_Int32 nSize = std::min( aContainerWinSize.Width(), rTrackingRect.getWidth() ); + sal_Int32 nDockHeight = std::max( static_cast<sal_Int32>(aDockingAreaRect.getHeight()), sal_Int32( 0 )); + if ( nDockHeight == 0 ) + { + sal_Int32 nPosY( std::max( aDockingAreaRect.Top(), aDockingAreaRect.Bottom() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + nPosY -= rTrackingRect.getHeight(); + rTrackingRect.SetPos( Point( nPosX, nPosY )); + rUIElement.m_aDockedData.m_aPos.Y = 0; + } + else if ( rMousePos.Y() < ( aDockingAreaRect.Top() + ( nDockHeight / 2 ))) + { + rTrackingRect.SetPos( Point( nPosX, aDockingAreaRect.Top() - rTrackingRect.getHeight() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) + rUIElement.m_aDockedData.m_aPos.Y = 0; + else + rUIElement.m_aDockedData.m_aPos.Y = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + rDockingOperation = DOCKOP_BEFORE_COLROW; + } + else + { + rTrackingRect.SetPos( Point( nPosX, aDockingAreaRect.Bottom() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) + rUIElement.m_aDockedData.m_aPos.Y = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + else + rUIElement.m_aDockedData.m_aPos.Y = 0; + rDockingOperation = DOCKOP_AFTER_COLROW; + } + rTrackingRect.setWidth( nSize ); + + { + SolarMutexGuard aGuard; + nPosX = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( rTrackingRect.TopLeft() )).X(); + } + rUIElement.m_aDockedData.m_aPos.X = nPosX; + } + else + { + sal_Int32 nMaxDockingAreaHeight = std::max<sal_Int32>( 0, nMaxLeftRightDockAreaSize ); + sal_Int32 nPosY( std::max<sal_Int32>( aTrackingRect.Top(), nTopDockingAreaSize )); + if (( nPosY + aTrackingRect.getHeight()) > ( nTopDockingAreaSize + nMaxDockingAreaHeight )) + nPosY = std::min( nPosY, + std::max<sal_Int32>( nTopDockingAreaSize + ( nMaxDockingAreaHeight - aTrackingRect.getHeight() ), + nTopDockingAreaSize )); + + sal_Int32 nSize = std::min( nMaxDockingAreaHeight, static_cast<sal_Int32>(aTrackingRect.getHeight()) ); + sal_Int32 nDockWidth = std::max( static_cast<sal_Int32>(aDockingAreaRect.getWidth()), sal_Int32( 0 )); + if ( nDockWidth == 0 ) + { + sal_Int32 nPosX( std::max( aDockingAreaRect.Left(), aDockingAreaRect.Right() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_RIGHT ) + nPosX -= rTrackingRect.getWidth(); + rTrackingRect.SetPos( Point( nPosX, nPosY )); + rUIElement.m_aDockedData.m_aPos.X = 0; + } + else if ( rMousePos.X() < ( aDockingAreaRect.Left() + ( nDockWidth / 2 ))) + { + rTrackingRect.SetPos( Point( aDockingAreaRect.Left() - rTrackingRect.getWidth(), nPosY )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rUIElement.m_aDockedData.m_aPos.X = 0; + else + rUIElement.m_aDockedData.m_aPos.X = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + rDockingOperation = DOCKOP_BEFORE_COLROW; + } + else + { + rTrackingRect.SetPos( Point( aDockingAreaRect.Right(), nPosY )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rUIElement.m_aDockedData.m_aPos.X = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + else + rUIElement.m_aDockedData.m_aPos.X = 0; + rDockingOperation = DOCKOP_AFTER_COLROW; + } + rTrackingRect.setHeight( nSize ); + + { + SolarMutexGuard aGuard; + nPosY = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( rTrackingRect.TopLeft() )).Y(); + } + rUIElement.m_aDockedData.m_aPos.Y = nPosY; + } +} + +framework::ToolbarLayoutManager::DockingOperation ToolbarLayoutManager::implts_determineDockingOperation( + ui::DockingArea DockingArea, + const ::tools::Rectangle& rRowColRect, + const Point& rMousePos ) +{ + constexpr sal_Int32 nHorzVerticalRegionSize = 6; + constexpr sal_Int32 nHorzVerticalMoveRegion = 4; + + if ( rRowColRect.Contains( rMousePos )) + { + if ( isHorizontalDockingArea( DockingArea )) + { + sal_Int32 nRegion = rRowColRect.getHeight() / nHorzVerticalRegionSize; + sal_Int32 nPosY = rRowColRect.Top() + nRegion; + + if ( rMousePos.Y() < nPosY ) + return ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ? DOCKOP_BEFORE_COLROW : DOCKOP_AFTER_COLROW; + else if ( rMousePos.Y() < ( nPosY + nRegion*nHorzVerticalMoveRegion )) + return DOCKOP_ON_COLROW; + else + return ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ? DOCKOP_AFTER_COLROW : DOCKOP_BEFORE_COLROW; + } + else + { + sal_Int32 nRegion = rRowColRect.getWidth() / nHorzVerticalRegionSize; + sal_Int32 nPosX = rRowColRect.Left() + nRegion; + + if ( rMousePos.X() < nPosX ) + return ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) ? DOCKOP_BEFORE_COLROW : DOCKOP_AFTER_COLROW; + else if ( rMousePos.X() < ( nPosX + nRegion*nHorzVerticalMoveRegion )) + return DOCKOP_ON_COLROW; + else + return ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) ? DOCKOP_AFTER_COLROW : DOCKOP_BEFORE_COLROW; + } + } + else + return DOCKOP_ON_COLROW; +} + +::tools::Rectangle ToolbarLayoutManager::implts_calcTrackingAndElementRect( + ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + UIElement& rUIElement, + const ::tools::Rectangle& rTrackingRect, + const ::tools::Rectangle& rRowColumnRect, + const ::Size& rContainerWinSize ) +{ + SolarMutexResettableGuard aReadGuard; + ::tools::Rectangle aDockingAreaOffsets( m_aDockingAreaOffsets ); + aReadGuard.clear(); + + bool bHorizontalDockArea( isHorizontalDockingArea( eDockingArea )); + + sal_Int32 nTopDockingAreaSize( implts_getTopBottomDockingAreaSizes().Width() ); + sal_Int32 nBottomDockingAreaSize( implts_getTopBottomDockingAreaSizes().Height() ); + + sal_Int32 nMaxLeftRightDockAreaSize = rContainerWinSize.Height() - + nTopDockingAreaSize - + nBottomDockingAreaSize - + aDockingAreaOffsets.Top() - + aDockingAreaOffsets.Bottom(); + + ::tools::Rectangle aTrackingRect( rTrackingRect ); + if ( bHorizontalDockArea ) + { + sal_Int32 nPosX( std::max( sal_Int32( rTrackingRect.Left()), sal_Int32( 0 ))); + if (( nPosX + rTrackingRect.getWidth()) > rContainerWinSize.Width() ) + nPosX = std::min( nPosX, + std::max( sal_Int32( rContainerWinSize.Width() - rTrackingRect.getWidth() ), + sal_Int32( 0 ))); + + sal_Int32 nSize = std::min( rContainerWinSize.Width(), rTrackingRect.getWidth() ); + + aTrackingRect.SetPos( ::Point( nPosX, rRowColumnRect.Top() )); + aTrackingRect.setWidth( nSize ); + aTrackingRect.setHeight( rRowColumnRect.getHeight() ); + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.X = nPosX; + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + } + else + { + sal_Int32 nMaxDockingAreaHeight = std::max<sal_Int32>( 0, nMaxLeftRightDockAreaSize ); + + sal_Int32 nPosY( std::max<sal_Int32>( aTrackingRect.Top(), nTopDockingAreaSize )); + if (( nPosY + aTrackingRect.getHeight()) > ( nTopDockingAreaSize + nMaxDockingAreaHeight )) + nPosY = std::min( nPosY, + std::max<sal_Int32>( nTopDockingAreaSize + ( nMaxDockingAreaHeight - aTrackingRect.getHeight() ), + nTopDockingAreaSize )); + + sal_Int32 nSize = std::min( nMaxDockingAreaHeight, static_cast<sal_Int32>(aTrackingRect.getHeight()) ); + + aTrackingRect.SetPos( ::Point( rRowColumnRect.Left(), nPosY )); + aTrackingRect.setWidth( rRowColumnRect.getWidth() ); + aTrackingRect.setHeight( nSize ); + + aReadGuard.reset(); + uno::Reference< awt::XWindow > xDockingAreaWindow( m_xDockAreaWindows[static_cast<int>(eDockingArea)] ); + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + aReadGuard.clear(); + + sal_Int32 nDockPosY( 0 ); + { + SolarMutexGuard aGuard; + vcl::Window* pDockingAreaWindow = VCLUnoHelper::GetWindow( xDockingAreaWindow ); + VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + nDockPosY = pDockingAreaWindow->ScreenToOutputPixel( pContainerWindow->OutputToScreenPixel( ::Point( 0, nPosY ))).Y(); + } + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + rUIElement.m_aDockedData.m_aPos.Y = nDockPosY; + } + + return aTrackingRect; +} + +void ToolbarLayoutManager::implts_setTrackingRect( ui::DockingArea eDockingArea, const ::Point& rMousePos, ::tools::Rectangle& rTrackingRect ) +{ + ::Point aPoint( rTrackingRect.TopLeft()); + if ( isHorizontalDockingArea( eDockingArea )) + aPoint.setX( rMousePos.X() ); + else + aPoint.setY( rMousePos.Y() ); + rTrackingRect.SetPos( aPoint ); +} + +void ToolbarLayoutManager::implts_renumberRowColumnData( + ui::DockingArea eDockingArea, + const UIElement& rUIElement ) +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + aReadLock.clear(); + + bool bHorzDockingArea( isHorizontalDockingArea( eDockingArea )); + sal_Int32 nRowCol( bHorzDockingArea ? rUIElement.m_aDockedData.m_aPos.Y : rUIElement.m_aDockedData.m_aPos.X ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aWriteLock; + for (auto& elem : m_aUIElements) + { + if ((elem.m_aDockedData.m_nDockedArea == eDockingArea) + && (elem.m_aName != rUIElement.m_aName)) + { + // Don't change toolbars without a valid docking position! + if (isDefaultPos(elem.m_aDockedData.m_aPos)) + continue; + + sal_Int32 nWindowRowCol + = bHorzDockingArea ? elem.m_aDockedData.m_aPos.Y : elem.m_aDockedData.m_aPos.X; + if (nWindowRowCol >= nRowCol) + { + if (bHorzDockingArea) + elem.m_aDockedData.m_aPos.Y += 1; + else + elem.m_aDockedData.m_aPos.X += 1; + } + } + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // We have to change the persistent window state part + if ( !xPersistentWindowState.is() ) + return; + + try + { + const uno::Sequence< OUString > aWindowElements = xPersistentWindowState->getElementNames(); + for ( OUString const & rWindowElementName : aWindowElements ) + { + if ( rUIElement.m_aName != rWindowElementName ) + { + try + { + uno::Sequence< beans::PropertyValue > aPropValueSeq; + awt::Point aDockedPos; + ui::DockingArea nDockedArea( ui::DockingArea_DOCKINGAREA_DEFAULT ); + + xPersistentWindowState->getByName( rWindowElementName ) >>= aPropValueSeq; + for ( beans::PropertyValue const & rProp : std::as_const(aPropValueSeq) ) + { + if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKINGAREA ) + rProp.Value >>= nDockedArea; + else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKPOS ) + rProp.Value >>= aDockedPos; + } + + // Don't change toolbars without a valid docking position! + if ( isDefaultPos( aDockedPos )) + continue; + + sal_Int32 nWindowRowCol = bHorzDockingArea ? aDockedPos.Y : aDockedPos.X; + if (( nDockedArea == eDockingArea ) && ( nWindowRowCol >= nRowCol )) + { + if ( bHorzDockingArea ) + aDockedPos.Y += 1; + else + aDockedPos.X += 1; + + uno::Reference< container::XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY ); + xReplace->replaceByName( rWindowElementName, css::uno::Any( aPropValueSeq )); + } + } + catch (const uno::Exception&) + { + } + } + } + } + catch (const uno::Exception&) + { + } +} + +// XWindowListener + +void SAL_CALL ToolbarLayoutManager::windowResized( const awt::WindowEvent& aEvent ) +{ + SolarMutexClearableGuard aWriteLock; + bool bLocked( m_bDockingInProgress ); + bool bLayoutInProgress( m_bLayoutInProgress ); + aWriteLock.clear(); + + // Do not do anything if we are in the middle of a docking process. This would interfere all other + // operations. We will store the new position and size in the docking handlers. + // Do not do anything if we are in the middle of our layouting process. We will adapt the position + // and size of the user interface elements. + if ( bLocked || bLayoutInProgress ) + return; + + bool bNotify( false ); + uno::Reference< awt::XWindow > xWindow( aEvent.Source, uno::UNO_QUERY ); + + UIElement aUIElement = implts_findToolbar( aEvent.Source ); + if ( aUIElement.m_xUIElement.is() ) + { + if ( aUIElement.m_bFloating ) + { + uno::Reference< awt::XWindow2 > xWindow2( xWindow, uno::UNO_QUERY ); + + if( xWindow2.is() ) + { + awt::Rectangle aPos = xWindow2->getPosSize(); + awt::Size aSize = xWindow2->getOutputSize(); // always use output size for consistency + bool bVisible = xWindow2->isVisible(); + + // update element data + aUIElement.m_aFloatingData.m_aPos = awt::Point(aPos.X, aPos.Y); + aUIElement.m_aFloatingData.m_aSize = aSize; + aUIElement.m_bVisible = bVisible; + } + + implts_writeWindowStateData( aUIElement ); + } + else + { + implts_setLayoutDirty(); + bNotify = true; + } + } + + if ( bNotify ) + m_pParentLayouter->requestLayout(); +} + +void SAL_CALL ToolbarLayoutManager::windowMoved( const awt::WindowEvent& /*aEvent*/ ) +{ +} + +void SAL_CALL ToolbarLayoutManager::windowShown( const lang::EventObject& /*aEvent*/ ) +{ +} + +void SAL_CALL ToolbarLayoutManager::windowHidden( const lang::EventObject& /*aEvent*/ ) +{ +} + +// XDockableWindowListener + +void SAL_CALL ToolbarLayoutManager::startDocking( const awt::DockingEvent& e ) +{ + bool bWinFound( false ); + + SolarMutexClearableGuard aReadGuard; + uno::Reference< awt::XWindow2 > xWindow( e.Source, uno::UNO_QUERY ); + aReadGuard.clear(); + + UIElement aUIElement = implts_findToolbar( e.Source ); + + if ( aUIElement.m_xUIElement.is() && xWindow.is() ) + { + bWinFound = true; + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xDockWindow->isFloating() ) + { + awt::Rectangle aPos = xWindow->getPosSize(); + awt::Size aSize = xWindow->getOutputSize(); + + aUIElement.m_aFloatingData.m_aPos = awt::Point(aPos.X, aPos.Y); + aUIElement.m_aFloatingData.m_aSize = aSize; + + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBox = static_cast<ToolBox *>(pWindow.get()); + aUIElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines(); + aUIElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + } + } + } + + SolarMutexGuard g; + m_bDockingInProgress = bWinFound; + m_aDockUIElement = aUIElement; + m_aDockUIElement.m_bUserActive = true; +} + +awt::DockingData SAL_CALL ToolbarLayoutManager::docking( const awt::DockingEvent& e ) +{ + constexpr sal_Int32 MAGNETIC_DISTANCE_UNDOCK = 25; + constexpr sal_Int32 MAGNETIC_DISTANCE_DOCK = 20; + + SolarMutexClearableGuard aReadLock; + awt::DockingData aDockingData; + uno::Reference< awt::XDockableWindow > xDockWindow( e.Source, uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xWindow( e.Source, uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + UIElement aUIDockingElement( m_aDockUIElement ); + + bool bDockingInProgress( m_bDockingInProgress ); + aReadLock.clear(); + + if ( bDockingInProgress ) + aDockingData.TrackingRectangle = e.TrackingRectangle; + + if ( bDockingInProgress && xDockWindow.is() && xWindow.is() ) + { + try + { + SolarMutexGuard aGuard; + + DockingOperation eDockingOperation( DOCKOP_ON_COLROW ); + ui::DockingArea eDockingArea( ui::DockingArea(-1) ); // none + sal_Int32 nMagneticZone( aUIDockingElement.m_bFloating ? MAGNETIC_DISTANCE_DOCK : MAGNETIC_DISTANCE_UNDOCK ); + ::tools::Rectangle aTrackingRect( e.TrackingRectangle.X, e.TrackingRectangle.Y, + ( e.TrackingRectangle.X + e.TrackingRectangle.Width ), + ( e.TrackingRectangle.Y + e.TrackingRectangle.Height )); + + awt::Rectangle aTmpRect = xTopDockingWindow->getPosSize(); + ::tools::Rectangle aTopDockRect( aTmpRect.X, aTmpRect.Y, aTmpRect.Width, aTmpRect.Height ); + ::tools::Rectangle aHotZoneTopDockRect( implts_calcHotZoneRect( aTopDockRect, nMagneticZone )); + + aTmpRect = xBottomDockingWindow->getPosSize(); + ::tools::Rectangle aBottomDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width), ( aTmpRect.Y + aTmpRect.Height )); + ::tools::Rectangle aHotZoneBottomDockRect( implts_calcHotZoneRect( aBottomDockRect, nMagneticZone )); + + aTmpRect = xLeftDockingWindow->getPosSize(); + ::tools::Rectangle aLeftDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width ), ( aTmpRect.Y + aTmpRect.Height )); + ::tools::Rectangle aHotZoneLeftDockRect( implts_calcHotZoneRect( aLeftDockRect, nMagneticZone )); + + aTmpRect = xRightDockingWindow->getPosSize(); + ::tools::Rectangle aRightDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width ), ( aTmpRect.Y + aTmpRect.Height )); + ::tools::Rectangle aHotZoneRightDockRect( implts_calcHotZoneRect( aRightDockRect, nMagneticZone )); + + VclPtr<vcl::Window> pContainerWindow( VCLUnoHelper::GetWindow( xContainerWindow ) ); + ::Point aMousePos( pContainerWindow->ScreenToOutputPixel( ::Point( e.MousePos.X, e.MousePos.Y ))); + + if ( aHotZoneTopDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + else if ( aHotZoneBottomDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_BOTTOM; + else if ( aHotZoneLeftDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_LEFT; + else if ( aHotZoneRightDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_RIGHT; + + // Higher priority for movements inside the real docking area + if ( aTopDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + else if ( aBottomDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_BOTTOM; + else if ( aLeftDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_LEFT; + else if ( aRightDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_RIGHT; + + // Determine if we have a toolbar and set alignment according to the docking area! + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + ToolBox* pToolBox = nullptr; + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast<ToolBox *>(pWindow.get()); + + if ( eDockingArea != ui::DockingArea(-1) ) + { + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_TOP; + aUIDockingElement.m_bFloating = false; + } + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_BOTTOM; + aUIDockingElement.m_bFloating = false; + } + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_LEFT; + aUIDockingElement.m_bFloating = false; + } + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_RIGHT ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_RIGHT; + aUIDockingElement.m_bFloating = false; + } + + ::Point aOutputPos = pContainerWindow->ScreenToOutputPixel( aTrackingRect.TopLeft() ); + aTrackingRect.SetPos( aOutputPos ); + + ::tools::Rectangle aNewDockingRect( aTrackingRect ); + + implts_calcDockingPosSize( aUIDockingElement, eDockingOperation, aNewDockingRect, aMousePos ); + + ::Point aScreenPos = pContainerWindow->OutputToScreenPixel( aNewDockingRect.TopLeft() ); + aDockingData.TrackingRectangle = awt::Rectangle( aScreenPos.X(), aScreenPos.Y(), + aNewDockingRect.getWidth(), aNewDockingRect.getHeight() ); + } + else if (pToolBox) + { + bool bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + awt::Size aFloatSize = aUIDockingElement.m_aFloatingData.m_aSize; + if ( aFloatSize.Width > 0 && aFloatSize.Height > 0 ) + { + aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pContainerWindow->ScreenToOutputPixel(VCLPoint(e.MousePos))); + aDockingData.TrackingRectangle.Height = aFloatSize.Height; + aDockingData.TrackingRectangle.Width = aFloatSize.Width; + } + else + { + aFloatSize = AWTSize(pToolBox->CalcWindowSizePixel()); + if ( !bIsHorizontal ) + { + // Floating toolbars are always horizontal aligned! We have to swap + // width/height if we have a vertical aligned toolbar. + sal_Int32 nTemp = aFloatSize.Height; + aFloatSize.Height = aFloatSize.Width; + aFloatSize.Width = nTemp; + } + + aDockingData.TrackingRectangle.Height = aFloatSize.Height; + aDockingData.TrackingRectangle.Width = aFloatSize.Width; + + // For the first time we don't have any data about the floating size of a toolbar. + // We calculate it and store it for later use. + aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pContainerWindow->ScreenToOutputPixel(VCLPoint(e.MousePos))); + aUIDockingElement.m_aFloatingData.m_aSize = aFloatSize; + aUIDockingElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines(); + aUIDockingElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + } + aDockingData.TrackingRectangle.X = e.MousePos.X; + aDockingData.TrackingRectangle.Y = e.MousePos.Y; + } + + aDockingData.bFloating = ( eDockingArea == ui::DockingArea(-1) ); + + // Write current data to the member docking progress data + SolarMutexGuard g; + m_aDockUIElement.m_bFloating = aDockingData.bFloating; + if ( !aDockingData.bFloating ) + { + m_aDockUIElement.m_aDockedData = aUIDockingElement.m_aDockedData; + + m_eDockOperation = eDockingOperation; + } + else + m_aDockUIElement.m_aFloatingData = aUIDockingElement.m_aFloatingData; + } + catch (const uno::Exception&) + { + } + } + + return aDockingData; +} + +void SAL_CALL ToolbarLayoutManager::endDocking( const awt::EndDockingEvent& e ) +{ + if (e.bCancelled) + return; + + bool bDockingInProgress( false ); + bool bStartDockFloated( false ); + bool bFloating( false ); + UIElement aUIDockingElement; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + bDockingInProgress = m_bDockingInProgress; + aUIDockingElement = m_aDockUIElement; + bFloating = aUIDockingElement.m_bFloating; + + UIElement& rUIElement = impl_findToolbar( aUIDockingElement.m_aName ); + if ( rUIElement.m_aName == aUIDockingElement.m_aName ) + { + if ( aUIDockingElement.m_bFloating ) + { + // Write last position into position data + uno::Reference< awt::XWindow > xWindow( aUIDockingElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + rUIElement.m_aFloatingData = aUIDockingElement.m_aFloatingData; + awt::Rectangle aTmpRect = xWindow->getPosSize(); + rUIElement.m_aFloatingData.m_aPos = awt::Point(aTmpRect.X, aTmpRect.Y); + // make changes also for our local data as we use it to make data persistent + aUIDockingElement.m_aFloatingData = rUIElement.m_aFloatingData; + } + else + { + rUIElement.m_aDockedData = aUIDockingElement.m_aDockedData; + rUIElement.m_aFloatingData.m_aSize = aUIDockingElement.m_aFloatingData.m_aSize; + + if ( m_eDockOperation != DOCKOP_ON_COLROW ) + { + // we have to renumber our row/column data to insert a new row/column + implts_renumberRowColumnData(aUIDockingElement.m_aDockedData.m_nDockedArea, aUIDockingElement ); + } + } + + bStartDockFloated = rUIElement.m_bFloating; + rUIElement.m_bFloating = m_aDockUIElement.m_bFloating; + rUIElement.m_bUserActive = true; + } + + // reset member for next docking operation + m_aDockUIElement.m_xUIElement.clear(); + m_eDockOperation = DOCKOP_ON_COLROW; + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + implts_writeWindowStateData( aUIDockingElement ); + + if ( bDockingInProgress ) + { + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( uno::Reference< awt::XWindow >( e.Source, uno::UNO_QUERY )); + ToolBox* pToolBox = nullptr; + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast<ToolBox *>(pWindow.get()); + + if ( pToolBox ) + { + if( e.bFloating ) + { + if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal ) + pToolBox->SetAlign(); + else + pToolBox->SetAlign( WindowAlign::Left ); + } + else + { + ::Size aSize; + + pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) ); + + // Docked toolbars have always one line + aSize = pToolBox->CalcWindowSizePixel( 1 ); + + // Lock layouting updates as our listener would be called due to SetSizePixel + pToolBox->SetOutputSizePixel( aSize ); + } + } + } + + implts_sortUIElements(); + + aWriteLock.reset(); + m_bDockingInProgress = false; + m_bLayoutDirty = !bStartDockFloated || !bFloating; + bool bNotify = m_bLayoutDirty; + aWriteLock.clear(); + + if ( bNotify ) + m_pParentLayouter->requestLayout(); +} + +sal_Bool SAL_CALL ToolbarLayoutManager::prepareToggleFloatingMode( const lang::EventObject& e ) +{ + SolarMutexClearableGuard aReadLock; + bool bDockingInProgress = m_bDockingInProgress; + aReadLock.clear(); + + UIElement aUIDockingElement = implts_findToolbar( e.Source ); + bool bWinFound( !aUIDockingElement.m_aName.isEmpty() ); + uno::Reference< awt::XWindow > xWindow( e.Source, uno::UNO_QUERY ); + + if ( !bWinFound || !xWindow.is() ) + return true; + + if ( bDockingInProgress ) + return true; + + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( !xDockWindow->isFloating() ) + return true; + + { + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBox = static_cast< ToolBox *>( pWindow.get() ); + aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pToolBox->GetPosPixel()); + aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pToolBox->GetOutputSizePixel()); + aUIDockingElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines(); + aUIDockingElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + } + } + + UIElement aUIElement = implts_findToolbar( aUIDockingElement.m_aName ); + if ( aUIElement.m_aName == aUIDockingElement.m_aName ) + implts_setToolbar( aUIDockingElement ); + + return true; +} + +void SAL_CALL ToolbarLayoutManager::toggleFloatingMode( const lang::EventObject& e ) +{ + UIElement aUIDockingElement; + + SolarMutexResettableGuard aReadLock; + bool bDockingInProgress( m_bDockingInProgress ); + if ( bDockingInProgress ) + aUIDockingElement = m_aDockUIElement; + aReadLock.clear(); + + vcl::Window* pWindow( nullptr ); + ToolBox* pToolBox( nullptr ); + uno::Reference< awt::XWindow2 > xWindow; + + { + SolarMutexGuard aGuard; + xWindow.set( e.Source, uno::UNO_QUERY ); + pWindow = VCLUnoHelper::GetWindow( xWindow ); + + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast<ToolBox *>(pWindow); + } + + if ( !bDockingInProgress ) + { + aUIDockingElement = implts_findToolbar( e.Source ); + bool bWinFound = !aUIDockingElement.m_aName.isEmpty(); + + if ( bWinFound && xWindow.is() ) + { + aUIDockingElement.m_bFloating = !aUIDockingElement.m_bFloating; + aUIDockingElement.m_bUserActive = true; + + implts_setLayoutInProgress(); + if ( aUIDockingElement.m_bFloating ) + { + SolarMutexGuard aGuard; + if ( pToolBox ) + { + pToolBox->SetLineCount( aUIDockingElement.m_aFloatingData.m_nLines ); + if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal ) + pToolBox->SetAlign(); + else + pToolBox->SetAlign( WindowAlign::Left ); + } + + bool bUndefPos = hasDefaultPosValue( aUIDockingElement.m_aFloatingData.m_aPos ); + bool bSetSize = !hasEmptySize( aUIDockingElement.m_aFloatingData.m_aSize ); + + if ( bUndefPos ) + aUIDockingElement.m_aFloatingData.m_aPos = implts_findNextCascadeFloatingPos(); + + if ( !bSetSize ) + { + if ( pToolBox ) + aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pToolBox->CalcFloatingWindowSizePixel()); + else if ( pWindow ) + aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pWindow->GetOutputSizePixel()); + } + + xWindow->setPosSize( aUIDockingElement.m_aFloatingData.m_aPos.X, + aUIDockingElement.m_aFloatingData.m_aPos.Y, + 0, 0, awt::PosSize::POS ); + xWindow->setOutputSize(aUIDockingElement.m_aFloatingData.m_aSize); + } + else + { + if ( isDefaultPos( aUIDockingElement.m_aDockedData.m_aPos )) + { + // Docking on its default position without a preset position - + // we have to find a good place for it. + ::Point aPixelPos; + awt::Point aDockPos; + ::Size aSize; + + { + SolarMutexGuard aGuard; + if ( pToolBox ) + aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea ) ); + else if ( pWindow ) + aSize = pWindow->GetSizePixel(); + } + + implts_findNextDockingPos(aUIDockingElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos ); + aUIDockingElement.m_aDockedData.m_aPos = aDockPos; + } + + SolarMutexGuard aGuard; + if ( pToolBox ) + { + pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) ); + ::Size aSize = pToolBox->CalcWindowSizePixel( 1 ); + awt::Rectangle aRect = xWindow->getPosSize(); + xWindow->setPosSize( aRect.X, aRect.Y, 0, 0, awt::PosSize::POS ); + xWindow->setOutputSize( AWTSize( aSize ) ); + } + } + + implts_setLayoutInProgress( false ); + implts_setToolbar( aUIDockingElement ); + implts_writeWindowStateData( aUIDockingElement ); + implts_sortUIElements(); + implts_setLayoutDirty(); + + aReadLock.reset(); + LayoutManager* pParentLayouter( m_pParentLayouter ); + aReadLock.clear(); + + if ( pParentLayouter ) + pParentLayouter->requestLayout(); + } + } + else + { + SolarMutexGuard aGuard; + if ( pToolBox ) + { + if ( aUIDockingElement.m_bFloating ) + { + if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal ) + pToolBox->SetAlign(); + else + pToolBox->SetAlign( WindowAlign::Left ); + } + else + pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) ); + } + } +} + +void SAL_CALL ToolbarLayoutManager::closed( const lang::EventObject& e ) +{ + OUString aName; + UIElement aUIElement; + + { + SolarMutexGuard aWriteLock; + for (auto& elem : m_aUIElements) + { + uno::Reference<ui::XUIElement> xUIElement(elem.m_xUIElement); + if (xUIElement.is()) + { + uno::Reference<uno::XInterface> xIfac(xUIElement->getRealInterface(), + uno::UNO_QUERY); + if (xIfac == e.Source) + { + aName = elem.m_aName; + + // user closes a toolbar => + // context sensitive toolbar: only destroy toolbar and store state. + // non context sensitive toolbar: make it invisible, store state and destroy it. + if (!elem.m_bContextSensitive) + elem.m_bVisible = false; + + aUIElement = elem; + break; + } + } + } + } + + // destroy element + if ( aName.isEmpty() ) + return; + + implts_writeWindowStateData( aUIElement ); + destroyToolbar( aName ); + + SolarMutexClearableGuard aReadLock; + bool bLayoutDirty = m_bLayoutDirty; + LayoutManager* pParentLayouter( m_pParentLayouter ); + aReadLock.clear(); + + if ( bLayoutDirty && pParentLayouter ) + pParentLayouter->requestLayout(); +} + +void SAL_CALL ToolbarLayoutManager::endPopupMode( const awt::EndPopupModeEvent& /*e*/ ) +{ +} + +// XUIConfigurationListener + +void SAL_CALL ToolbarLayoutManager::elementInserted( const ui::ConfigurationEvent& rEvent ) +{ + UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL ); + + uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY ); + if ( xElementSettings.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + if ( rEvent.Source == uno::Reference< uno::XInterface >( m_xDocCfgMgr, uno::UNO_QUERY )) + xPropSet->setPropertyValue( "ConfigurationSource", css::uno::Any( m_xDocCfgMgr )); + } + xElementSettings->updateSettings(); + } + else + { + OUString aElementType; + OUString aElementName; + parseResourceURL( rEvent.ResourceURL, aElementType, aElementName ); + if ( aElementName.indexOf( "custom_" ) != -1 ) + { + // custom toolbar must be directly created, shown and layouted! + createToolbar( rEvent.ResourceURL ); + uno::Reference< ui::XUIElement > xUIElement = getToolbar( rEvent.ResourceURL ); + if ( xUIElement.is() ) + { + OUString aUIName; + uno::Reference< ui::XUIConfigurationManager > xCfgMgr; + uno::Reference< beans::XPropertySet > xPropSet; + + try + { + xCfgMgr.set( rEvent.Source, uno::UNO_QUERY ); + xPropSet.set( xCfgMgr->getSettings( rEvent.ResourceURL, false ), uno::UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue("UIName") >>= aUIName; + } + catch (const container::NoSuchElementException&) + { + } + catch (const beans::UnknownPropertyException&) + { + } + catch (const lang::WrappedTargetException&) + { + } + + { + SolarMutexGuard aGuard; + vcl::Window* pWindow = getWindowFromXUIElement( xUIElement ); + if ( pWindow ) + pWindow->SetText( aUIName ); + } + + showToolbar( rEvent.ResourceURL ); + } + } + } +} + +void SAL_CALL ToolbarLayoutManager::elementRemoved( const ui::ConfigurationEvent& rEvent ) +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< awt::XWindow > xContainerWindow = m_xContainerWindow; + uno::Reference< ui::XUIConfigurationManager > xModuleCfgMgr( m_xModuleCfgMgr ); + uno::Reference< ui::XUIConfigurationManager > xDocCfgMgr( m_xDocCfgMgr ); + aReadLock.clear(); + + UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL ); + uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY ); + if ( !xElementSettings.is() ) + return; + + bool bNoSettings( false ); + OUString aConfigSourcePropName( "ConfigurationSource" ); + uno::Reference< uno::XInterface > xElementCfgMgr; + uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( aConfigSourcePropName ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => check further + if ( rEvent.Source == xElementCfgMgr ) + { + // Same UI configuration manager where our element has its settings + if ( rEvent.Source == uno::Reference< uno::XInterface >( xDocCfgMgr, uno::UNO_QUERY )) + { + // document settings removed + if ( xModuleCfgMgr->hasSettings( rEvent.ResourceURL )) + { + xPropSet->setPropertyValue( aConfigSourcePropName, css::uno::Any( xModuleCfgMgr )); + xElementSettings->updateSettings(); + return; + } + } + + bNoSettings = true; + } + + // No settings anymore, element must be destroyed + if ( xContainerWindow.is() && bNoSettings ) + destroyToolbar( rEvent.ResourceURL ); +} + +void SAL_CALL ToolbarLayoutManager::elementReplaced( const ui::ConfigurationEvent& rEvent ) +{ + UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL ); + + uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY ); + if ( !xElementSettings.is() ) + return; + + uno::Reference< uno::XInterface > xElementCfgMgr; + uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( "ConfigurationSource" ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => update settings + if ( rEvent.Source != xElementCfgMgr ) + return; + + xElementSettings->updateSettings(); + + SolarMutexClearableGuard aWriteLock; + bool bNotify = !aUIElement.m_bFloating; + m_bLayoutDirty = bNotify; + LayoutManager* pParentLayouter( m_pParentLayouter ); + aWriteLock.clear(); + + if ( bNotify && pParentLayouter ) + pParentLayouter->requestLayout(); +} + +uno::Reference< ui::XUIElement > ToolbarLayoutManager::getToolbar( std::u16string_view aName ) +{ + return implts_findToolbar( aName ).m_xUIElement; +} + +uno::Sequence< uno::Reference< ui::XUIElement > > ToolbarLayoutManager::getToolbars() +{ + uno::Sequence< uno::Reference< ui::XUIElement > > aSeq; + + SolarMutexGuard g; + if ( !m_aUIElements.empty() ) + { + sal_uInt32 nCount(0); + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + ++nCount; + aSeq.realloc( nCount ); + aSeq.getArray()[nCount-1] = elem.m_xUIElement; + } + } + } + + return aSeq; +} + +bool ToolbarLayoutManager::floatToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() && !xDockWindow->isFloating() ) + { + aUIElement.m_bFloating = true; + implts_writeWindowStateData( aUIElement ); + xDockWindow->setFloatingMode( true ); + + implts_setLayoutDirty(); + implts_setToolbar( aUIElement ); + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::lockToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() && !xDockWindow->isFloating() && !xDockWindow->isLocked() ) + { + aUIElement.m_aDockedData.m_bLocked = true; + implts_writeWindowStateData( aUIElement ); + xDockWindow->lock(); + + implts_setLayoutDirty(); + implts_setToolbar( aUIElement ); + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::unlockToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() && !xDockWindow->isFloating() && xDockWindow->isLocked() ) + { + aUIElement.m_aDockedData.m_bLocked = false; + implts_writeWindowStateData( aUIElement ); + xDockWindow->unlock(); + + implts_setLayoutDirty(); + implts_setToolbar( aUIElement ); + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::isToolbarVisible( std::u16string_view rResourceURL ) +{ + uno::Reference< awt::XWindow2 > xWindow2( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + return ( xWindow2.is() && xWindow2->isVisible() ); +} + +bool ToolbarLayoutManager::isToolbarFloating( std::u16string_view rResourceURL ) +{ + uno::Reference< awt::XDockableWindow > xDockWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + return ( xDockWindow.is() && xDockWindow->isFloating() ); +} + +bool ToolbarLayoutManager::isToolbarDocked( std::u16string_view rResourceURL ) +{ + return !isToolbarFloating( rResourceURL ); +} + +bool ToolbarLayoutManager::isToolbarLocked( std::u16string_view rResourceURL ) +{ + uno::Reference< awt::XDockableWindow > xDockWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + return ( xDockWindow.is() && xDockWindow->isLocked() ); +} + +awt::Size ToolbarLayoutManager::getToolbarSize( std::u16string_view rResourceURL ) +{ + vcl::Window* pWindow = implts_getWindow( rResourceURL ); + + SolarMutexGuard aGuard; + if ( pWindow ) + { + ::Size aSize = pWindow->GetSizePixel(); + awt::Size aWinSize; + aWinSize.Width = aSize.Width(); + aWinSize.Height = aSize.Height(); + return aWinSize; + } + + return awt::Size(); +} + +awt::Point ToolbarLayoutManager::getToolbarPos( std::u16string_view rResourceURL ) +{ + awt::Point aPos; + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + uno::Reference< awt::XWindow > xWindow( implts_getXWindow( rResourceURL )); + if ( xWindow.is() ) + { + if ( aUIElement.m_bFloating ) + { + awt::Rectangle aRect = xWindow->getPosSize(); + aPos.X = aRect.X; + aPos.Y = aRect.Y; + } + else + aPos = aUIElement.m_aDockedData.m_aPos; + } + + return aPos; +} + +void ToolbarLayoutManager::setToolbarSize( std::u16string_view rResourceURL, const awt::Size& aSize ) +{ + uno::Reference< awt::XWindow2 > xWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + if ( xWindow.is() && xDockWindow.is() && xDockWindow->isFloating() ) + { + xWindow->setOutputSize( aSize ); + aUIElement.m_aFloatingData.m_aSize = aSize; + implts_setToolbar( aUIElement ); + implts_writeWindowStateData( aUIElement ); + implts_sortUIElements(); + } +} + +void ToolbarLayoutManager::setToolbarPos( std::u16string_view rResourceURL, const awt::Point& aPos ) +{ + uno::Reference< awt::XWindow > xWindow( implts_getXWindow( rResourceURL )); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + if ( xWindow.is() && xDockWindow.is() && xDockWindow->isFloating() ) + { + xWindow->setPosSize( aPos.X, aPos.Y, 0, 0, awt::PosSize::POS ); + aUIElement.m_aFloatingData.m_aPos = aPos; + implts_setToolbar( aUIElement ); + implts_writeWindowStateData( aUIElement ); + implts_sortUIElements(); + } +} + +void ToolbarLayoutManager::setToolbarPosSize( std::u16string_view rResourceURL, const awt::Point& aPos, const awt::Size& aSize ) +{ + setToolbarPos( rResourceURL, aPos ); + setToolbarSize( rResourceURL, aSize ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.hxx b/framework/source/layoutmanager/toolbarlayoutmanager.hxx new file mode 100644 index 000000000..3e7b631c6 --- /dev/null +++ b/framework/source/layoutmanager/toolbarlayoutmanager.hxx @@ -0,0 +1,283 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> +#include <vector> + +#include <uiconfiguration/globalsettings.hxx> +#include <framework/addonsoptions.hxx> +#include <uielement/uielement.hxx> +#include <services/layoutmanager.hxx> + +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/ui/XUIElementFactory.hpp> +#include <com/sun/star/ui/DockingArea.hpp> +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/awt/XDockableWindow.hpp> +#include <com/sun/star/awt/XDockableWindowListener.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace framework +{ + +class ToolbarLayoutManager : public ::cppu::WeakImplHelper< css::awt::XDockableWindowListener, + css::ui::XUIConfigurationListener, + css::awt::XWindowListener > +{ + public: + enum { DOCKINGAREAS_COUNT = 4 }; + + enum PreviewFrameDetection + { + PREVIEWFRAME_UNKNOWN, + PREVIEWFRAME_NO, + PREVIEWFRAME_YES + }; + + ToolbarLayoutManager( css::uno::Reference< css::uno::XComponentContext > xContext, + css::uno::Reference< css::ui::XUIElementFactory > xUIElementFactory, + LayoutManager* pParentLayouter ); + virtual ~ToolbarLayoutManager() override; + + void reset(); + void attach( const css::uno::Reference< css::frame::XFrame >& xFrame, + const css::uno::Reference< css::ui::XUIConfigurationManager >& xModuleCfgMgr, + const css::uno::Reference< css::ui::XUIConfigurationManager >& xDocCfgMgr, + const css::uno::Reference< css::container::XNameAccess >& xPersistentWindowState ); + + void setParentWindow( const css::uno::Reference< css::awt::XWindowPeer >& xParentWindow ); + void setDockingAreaOffsets(const ::tools::Rectangle& rOffsets); + + void resetDockingArea(); + + css::awt::Rectangle getDockingArea(); + void setDockingArea( const css::awt::Rectangle& rDockingArea ); + + bool isPreviewFrame(); + + // layouting + bool isLayoutDirty() const { return m_bLayoutDirty;} + void doLayout(const ::Size& aContainerSize); + + // creation/destruction + void createStaticToolbars(); + void destroyToolbars(); + + bool requestToolbar( const OUString& rResourceURL ); + bool createToolbar( const OUString& rResourceURL ); + bool destroyToolbar( std::u16string_view rResourceURL ); + + // visibility + bool showToolbar( std::u16string_view rResourceURL ); + bool hideToolbar( std::u16string_view rResourceURL ); + + void refreshToolbarsVisibility( bool bAutomaticToolbars ); + void setFloatingToolbarsVisibility( bool bVisible ); + void setVisible(bool bVisible); + + // docking and further functions + bool dockToolbar( std::u16string_view rResourceURL, css::ui::DockingArea eDockingArea, const css::awt::Point& aPos ); + bool dockAllToolbars(); + bool floatToolbar( std::u16string_view rResourceURL ); + bool lockToolbar( std::u16string_view rResourceURL ); + bool unlockToolbar( std::u16string_view rResourceURL ); + void setToolbarPos( std::u16string_view rResourceURL, const css::awt::Point& aPos ); + void setToolbarSize( std::u16string_view rResourceURL, const css::awt::Size& aSize ); + void setToolbarPosSize( std::u16string_view rResourceURL, const css::awt::Point& aPos, const css::awt::Size& aSize ); + bool isToolbarVisible( std::u16string_view rResourceURL ); + bool isToolbarFloating( std::u16string_view rResourceURL ); + bool isToolbarDocked( std::u16string_view rResourceURL ); + bool isToolbarLocked( std::u16string_view rResourceURL ); + css::awt::Point getToolbarPos( std::u16string_view rResourceURL ); + css::awt::Size getToolbarSize( std::u16string_view rResourceURL ); + css::uno::Reference< css::ui::XUIElement > getToolbar( std::u16string_view aName ); + css::uno::Sequence< css::uno::Reference< css::ui::XUIElement > > getToolbars(); + + // child window notifications + void childWindowEvent( VclSimpleEvent const * pEvent ); + + // XInterface + + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + // XWindowListener + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& aEvent ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& aEvent ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& aEvent ) override; + + // XDockableWindowListener + virtual void SAL_CALL startDocking( const css::awt::DockingEvent& e ) override; + virtual css::awt::DockingData SAL_CALL docking( const css::awt::DockingEvent& e ) override; + virtual void SAL_CALL endDocking( const css::awt::EndDockingEvent& e ) override; + virtual sal_Bool SAL_CALL prepareToggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL toggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL closed( const css::lang::EventObject& e ) override; + virtual void SAL_CALL endPopupMode( const css::awt::EndPopupModeEvent& e ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + private: + enum DockingOperation + { + DOCKOP_BEFORE_COLROW, + DOCKOP_ON_COLROW, + DOCKOP_AFTER_COLROW + }; + + typedef std::vector< UIElement > UIElementVector; + struct SingleRowColumnWindowData + { + SingleRowColumnWindowData() + : nVarSize(0) + , nStaticSize(0) + , nSpace(0) + , nRowColumn(0) + {} + + std::vector< OUString > aUIElementNames; + std::vector< css::uno::Reference< css::awt::XWindow > > aRowColumnWindows; + std::vector< css::awt::Rectangle > aRowColumnWindowSizes; + std::vector< sal_Int32 > aRowColumnSpace; + css::awt::Rectangle aRowColumnRect; + sal_Int32 nVarSize; + sal_Int32 nStaticSize; + sal_Int32 nSpace; + sal_Int32 nRowColumn; + }; + + // internal helper methods + + bool implts_isParentWindowVisible() const; + ::tools::Rectangle implts_calcDockingArea(); + void implts_sortUIElements(); + void implts_reparentToolbars(); + OUString implts_generateGenericAddonToolbarTitle( sal_Int32 nNumber ) const; + void implts_setElementData( UIElement& rUIElement, const css::uno::Reference< css::awt::XDockableWindow >& rDockWindow ); + void implts_destroyDockingAreaWindows(); + + // layout methods + + void implts_setDockingAreaWindowSizes( const css::awt::Rectangle& rBorderSpace ); + css::awt::Point implts_findNextCascadeFloatingPos(); + void implts_renumberRowColumnData( css::ui::DockingArea eDockingArea, const UIElement& rUIElement ); + void implts_calcWindowPosSizeOnSingleRowColumn( sal_Int32 nDockingArea, + sal_Int32 nOffset, + SingleRowColumnWindowData& rRowColumnWindowData, + const ::Size& rContainerSize ); + void implts_setLayoutDirty(); + void implts_setLayoutInProgress( bool bInProgress = true ); + + // lookup/container methods + + UIElement implts_findToolbar( std::u16string_view aName ); + UIElement implts_findToolbar( const css::uno::Reference< css::uno::XInterface >& xToolbar ); + UIElement& impl_findToolbar( std::u16string_view aName ); + css::uno::Reference< css::awt::XWindow > implts_getXWindow( std::u16string_view aName ); + vcl::Window* implts_getWindow( std::u16string_view aName ); + bool implts_insertToolbar( const UIElement& rUIElement ); + void implts_setToolbar( const UIElement& rUIElement ); + ::Size implts_getTopBottomDockingAreaSizes(); + void implts_getUIElementVectorCopy( UIElementVector& rCopy ); + + // internal docking methods + + ::tools::Rectangle implts_calcHotZoneRect( const ::tools::Rectangle& rRect, sal_Int32 nHotZoneOffset ); + void implts_calcDockingPosSize( UIElement& aUIElement, DockingOperation& eDockOperation, ::tools::Rectangle& rTrackingRect, const Point& rMousePos ); + DockingOperation implts_determineDockingOperation( css::ui::DockingArea DockingArea, const ::tools::Rectangle& rRowColRect, const Point& rMousePos ); + ::tools::Rectangle implts_getWindowRectFromRowColumn( css::ui::DockingArea DockingArea, const SingleRowColumnWindowData& rRowColumnWindowData, const ::Point& rMousePos, std::u16string_view rExcludeElementName ); + ::tools::Rectangle implts_determineFrontDockingRect( css::ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + const ::tools::Rectangle& rDockedElementRect, + std::u16string_view rMovedElementName, + const ::tools::Rectangle& rMovedElementRect ); + ::tools::Rectangle implts_calcTrackingAndElementRect( css::ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + UIElement& rUIElement, + const ::tools::Rectangle& rTrackingRect, + const ::tools::Rectangle& rRowColumnRect, + const ::Size& rContainerWinSize ); + + void implts_getDockingAreaElementInfos( css::ui::DockingArea DockingArea, std::vector< SingleRowColumnWindowData >& rRowColumnsWindowData ); + void implts_getDockingAreaElementInfoOnSingleRowCol( css::ui::DockingArea, sal_Int32 nRowCol, SingleRowColumnWindowData& rRowColumnWindowData ); + void implts_findNextDockingPos( css::ui::DockingArea DockingArea, const ::Size& aUIElementSize, css::awt::Point& rVirtualPos, ::Point& rPixelPos ); + void implts_setTrackingRect( css::ui::DockingArea eDockingArea, const ::Point& rMousePos, ::tools::Rectangle& rTrackingRect ); + + // creation methods + + void implts_createAddonsToolBars(); + void implts_createCustomToolBars(); + void implts_createNonContextSensitiveToolBars(); + void implts_createCustomToolBars( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& aCustomTbxSeq ); + void implts_createCustomToolBar( const OUString& aTbxResName, const OUString& aTitle ); + void implts_setToolbarCreation( bool bStart = true ); + bool implts_isToolbarCreationActive(); + + // persistence methods + + bool implts_readWindowStateData( const OUString& aName, UIElement& rElementData ); + void implts_writeWindowStateData( const UIElement& rElementData ); + + // members + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::awt::XWindow2 > m_xContainerWindow; + css::uno::Reference< css::awt::XWindow > m_xDockAreaWindows[DOCKINGAREAS_COUNT]; + css::uno::Reference< css::ui::XUIElementFactory > m_xUIElementFactoryManager; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xModuleCfgMgr; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xDocCfgMgr; + css::uno::Reference< css::container::XNameAccess > m_xPersistentWindowState; + LayoutManager* m_pParentLayouter; + + UIElementVector m_aUIElements; + UIElement m_aDockUIElement; + tools::Rectangle m_aDockingArea; + tools::Rectangle m_aDockingAreaOffsets; + DockingOperation m_eDockOperation; + PreviewFrameDetection m_ePreviewDetection; + + std::unique_ptr<AddonsOptions> m_pAddonOptions; + std::unique_ptr<GlobalSettings> m_pGlobalSettings; + + bool m_bComponentAttached; + bool m_bLayoutDirty; + bool m_bGlobalSettings; + bool m_bDockingInProgress; + bool m_bLayoutInProgress; + bool m_bToolbarCreation; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/uielement.cxx b/framework/source/layoutmanager/uielement.cxx new file mode 100644 index 000000000..582187515 --- /dev/null +++ b/framework/source/layoutmanager/uielement.cxx @@ -0,0 +1,99 @@ +/* -*- 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 <uielement/uielement.hxx> + +#include <com/sun/star/ui/DockingArea.hpp> + +using namespace ::com::sun::star; + +namespace framework +{ + + bool UIElement::operator< ( const ::framework::UIElement& aUIElement ) const +{ + if ( !m_xUIElement.is() && aUIElement.m_xUIElement.is() ) + return false; + else if ( m_xUIElement.is() && !aUIElement.m_xUIElement.is() ) + return true; + else if ( !m_bVisible && aUIElement.m_bVisible ) + return false; + else if ( m_bVisible && !aUIElement.m_bVisible ) + return true; + else if ( !m_bFloating && aUIElement.m_bFloating ) + return true; + else if ( m_bFloating && !aUIElement.m_bFloating ) + return false; + else + { + if ( m_bFloating ) + { + bool bEqual = ( m_aFloatingData.m_aPos.Y == aUIElement.m_aFloatingData.m_aPos.Y ); + if ( bEqual ) + return ( m_aFloatingData.m_aPos.X < aUIElement.m_aFloatingData.m_aPos.X ); + else + return ( m_aFloatingData.m_aPos.Y < aUIElement.m_aFloatingData.m_aPos.Y ); + } + else + { + if ( m_aDockedData.m_nDockedArea < aUIElement.m_aDockedData.m_nDockedArea ) + return true; + else if ( m_aDockedData.m_nDockedArea > aUIElement.m_aDockedData.m_nDockedArea ) + return false; + else + { + if ( m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_TOP || + m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + { + if ( m_aDockedData.m_aPos.Y != aUIElement.m_aDockedData.m_aPos.Y ) + return ( m_aDockedData.m_aPos.Y < aUIElement.m_aDockedData.m_aPos.Y ); + else + { + bool bEqual = ( m_aDockedData.m_aPos.X == aUIElement.m_aDockedData.m_aPos.X ); + if ( bEqual ) + { + return m_bUserActive && !aUIElement.m_bUserActive; + } + else + return ( m_aDockedData.m_aPos.X <= aUIElement.m_aDockedData.m_aPos.X ); + } + } + else + { + if ( m_aDockedData.m_aPos.X != aUIElement.m_aDockedData.m_aPos.X ) + return ( m_aDockedData.m_aPos.X < aUIElement.m_aDockedData.m_aPos.X ); + else + { + bool bEqual = ( m_aDockedData.m_aPos.Y == aUIElement.m_aDockedData.m_aPos.Y ); + if ( bEqual ) + { + return m_bUserActive && !aUIElement.m_bUserActive; + } + else + return ( m_aDockedData.m_aPos.Y <= aUIElement.m_aDockedData.m_aPos.Y ); + } + } + } + } + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |