diff options
Diffstat (limited to 'framework/source/services/frame.cxx')
-rw-r--r-- | framework/source/services/frame.cxx | 3340 |
1 files changed, 3340 insertions, 0 deletions
diff --git a/framework/source/services/frame.cxx b/framework/source/services/frame.cxx new file mode 100644 index 000000000..5fc6309aa --- /dev/null +++ b/framework/source/services/frame.cxx @@ -0,0 +1,3340 @@ +/* -*- 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 <sal/config.h> + +#include <utility> + +#include <dispatch/dispatchprovider.hxx> +#include <dispatch/interceptionhelper.hxx> +#include <dispatch/windowcommanddispatch.hxx> +#include <loadenv/loadenv.hxx> +#include <helper/oframes.hxx> +#include <framework/framecontainer.hxx> +#include <framework/titlehelper.hxx> +#include <svtools/openfiledroptargetlistener.hxx> +#include <classes/taskcreator.hxx> +#include <loadenv/targethelper.hxx> +#include <framework/framelistanalyzer.hxx> +#include <helper/dockingareadefaultacceptor.hxx> +#include <dispatch/dispatchinformationprovider.hxx> + +#include <pattern/window.hxx> +#include <properties.h> +#include <targets.h> + +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyExistException.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> +#include <com/sun/star/frame/XFrame2.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XTitleChangeBroadcaster.hpp> +#include <com/sun/star/frame/LayoutManager.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/task/StatusIndicatorFactory.hpp> +#include <com/sun/star/task/theJobExecutor.hpp> +#include <com/sun/star/task/XJobExecutor.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <comphelper/multiinterfacecontainer3.hxx> +#include <comphelper/multicontainer2.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <sal/log.hxx> +#include <vcl/window.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/svapp.hxx> + +#include <toolkit/helper/vclunohelper.hxx> +#include <unotools/moduleoptions.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/cmdoptions.hxx> +#include <vcl/threadex.hxx> +#include <mutex> + +using namespace framework; + +namespace { + +// This enum can be used to set different active states of frames +enum EActiveState +{ + E_INACTIVE, // I am not a member of active path in tree and i don't have the focus. + E_ACTIVE, // I am in the middle of an active path in tree and i don't have the focus. + E_FOCUS // I have the focus now. I must a member of an active path! +}; + +/*-************************************************************************************************************ + @short implements a normal frame of hierarchy + @descr An instance of these class can be a normal node in frame tree. A frame support influencing of his + subtree, find of subframes, activate- and deactivate-mechanism as well as + set/get of a frame window, component or controller. +*//*-*************************************************************************************************************/ +class XFrameImpl: + private cppu::BaseMutex, + public cppu::PartialWeakComponentImplHelper< + css::lang::XServiceInfo, css::frame::XFrame2, css::awt::XWindowListener, + css::awt::XTopWindowListener, css::awt::XFocusListener, + css::document::XActionLockable, css::util::XCloseable, + css::frame::XComponentLoader, css::frame::XTitle, + css::frame::XTitleChangeBroadcaster, css::beans::XPropertySet, + css::beans::XPropertySetInfo> +{ +public: + + explicit XFrameImpl(css::uno::Reference< css::uno::XComponentContext > xContext); + + /// Initialization function after having acquire()'d. + void initListeners(); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.Frame"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.Frame"}; + } + + // XComponentLoader + + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponentFromURL( + const OUString& sURL, + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + + // XFramesSupplier + + virtual css::uno::Reference < css::frame::XFrames > SAL_CALL getFrames() override; + virtual css::uno::Reference < css::frame::XFrame > SAL_CALL getActiveFrame() override; + virtual void SAL_CALL setActiveFrame(const css::uno::Reference < css::frame::XFrame > & xFrame) override; + + // XFrame + + virtual void SAL_CALL initialize(const css::uno::Reference < css::awt::XWindow > & xWindow) override; + virtual css::uno::Reference < css::awt::XWindow > SAL_CALL getContainerWindow() override; + virtual void SAL_CALL setCreator(const css::uno::Reference < css::frame::XFramesSupplier > & xCreator) override; + virtual css::uno::Reference < css::frame::XFramesSupplier > SAL_CALL getCreator() override; + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName(const OUString & sName) override; + virtual css::uno::Reference < css::frame::XFrame > SAL_CALL findFrame( + const OUString & sTargetFrameName, + sal_Int32 nSearchFlags) override; + virtual sal_Bool SAL_CALL isTop() override; + virtual void SAL_CALL activate() override; + virtual void SAL_CALL deactivate() override; + virtual sal_Bool SAL_CALL isActive() override; + virtual void SAL_CALL contextChanged() override; + virtual sal_Bool SAL_CALL setComponent( + const css::uno::Reference < css::awt::XWindow > & xComponentWindow, + const css::uno::Reference < css::frame::XController > & xController) override; + virtual css::uno::Reference < css::awt::XWindow > SAL_CALL getComponentWindow() override; + virtual css::uno::Reference < css::frame::XController > SAL_CALL getController() override; + virtual void SAL_CALL addFrameActionListener(const css::uno::Reference < css::frame::XFrameActionListener > & xListener) override; + virtual void SAL_CALL removeFrameActionListener(const css::uno::Reference < css::frame::XFrameActionListener > & xListener) override; + + // XComponent + + virtual void SAL_CALL disposing() override; + virtual void SAL_CALL addEventListener(const css::uno::Reference < css::lang::XEventListener > & xListener) override; + virtual void SAL_CALL removeEventListener(const css::uno::Reference < css::lang::XEventListener > & xListener) override; + + // XStatusIndicatorFactory + + virtual css::uno::Reference < css::task::XStatusIndicator > SAL_CALL createStatusIndicator() override; + + // XDispatchProvider + + virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL queryDispatch(const css::util::URL & aURL, + const OUString & sTargetFrameName, + sal_Int32 nSearchFlags) override; + virtual css::uno::Sequence < css::uno::Reference < css::frame::XDispatch > > SAL_CALL queryDispatches( + const css::uno::Sequence < css::frame::DispatchDescriptor > & lDescriptor) override; + + // XDispatchProviderInterception + + virtual void SAL_CALL registerDispatchProviderInterceptor( + const css::uno::Reference < css::frame::XDispatchProviderInterceptor > & xInterceptor) override; + virtual void SAL_CALL releaseDispatchProviderInterceptor( + const css::uno::Reference < css::frame::XDispatchProviderInterceptor > & xInterceptor) override; + + // XDispatchInformationProvider + + virtual css::uno::Sequence < sal_Int16 > SAL_CALL getSupportedCommandGroups() override; + virtual css::uno::Sequence < css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation(sal_Int16 nCommandGroup) override; + + // XWindowListener + // Attention: windowResized() and windowShown() are implement only! All other are empty! + + 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; + + // XFocusListener + // Attention: focusLost() not implemented yet! + + virtual void SAL_CALL focusGained(const css::awt::FocusEvent & aEvent) override; + virtual void SAL_CALL focusLost(const css::awt::FocusEvent & /*aEvent*/ ) override {}; + + // XTopWindowListener + // Attention: windowActivated(), windowDeactivated() and windowClosing() are implement only! All other are empty! + + virtual void SAL_CALL windowActivated(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowDeactivated(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowOpened(const css::lang::EventObject & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowClosing(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowClosed(const css::lang::EventObject & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowMinimized(const css::lang::EventObject & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowNormalized(const css::lang::EventObject & /*aEvent*/ ) override {}; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject & aEvent) override; + + // XActionLockable + + virtual sal_Bool SAL_CALL isActionLocked() override; + virtual void SAL_CALL addActionLock() override; + virtual void SAL_CALL removeActionLock() override; + virtual void SAL_CALL setActionLocks(sal_Int16 nLock) override; + virtual sal_Int16 SAL_CALL resetActionLocks() override; + + // XCloseable + + virtual void SAL_CALL close(sal_Bool bDeliverOwnership) override; + + // XCloseBroadcaster + + virtual void SAL_CALL addCloseListener(const css::uno::Reference < css::util::XCloseListener > & xListener) override; + virtual void SAL_CALL removeCloseListener(const css::uno::Reference < css::util::XCloseListener > & xListener) override; + + // XTitle + + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL setTitle(const OUString & sTitle) override; + + // XTitleChangeBroadcaster + + virtual void SAL_CALL addTitleChangeListener(const css::uno::Reference < css::frame::XTitleChangeListener > & xListener) override; + virtual void SAL_CALL removeTitleChangeListener(const css::uno::Reference < css::frame::XTitleChangeListener > & xListenr) override; + + // XFrame2 attributes + + virtual css::uno::Reference < css::container::XNameContainer > SAL_CALL getUserDefinedAttributes() override; + + virtual css::uno::Reference < css::frame::XDispatchRecorderSupplier > SAL_CALL getDispatchRecorderSupplier() override; + virtual void SAL_CALL setDispatchRecorderSupplier(const css::uno::Reference < css::frame::XDispatchRecorderSupplier > & ) override; + + virtual css::uno::Reference < css::uno::XInterface > SAL_CALL getLayoutManager() override; + virtual void SAL_CALL setLayoutManager(const css::uno::Reference < css::uno::XInterface > & ) override; + + // XPropertySet + virtual css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + virtual void SAL_CALL setPropertyValue(const OUString & sProperty, const css::uno::Any & aValue) override; + + virtual css::uno::Any SAL_CALL getPropertyValue(const OUString & sProperty) override; + + virtual void SAL_CALL addPropertyChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XPropertyChangeListener > & xListener) override; + + virtual void SAL_CALL removePropertyChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XPropertyChangeListener > & xListener) override; + + virtual void SAL_CALL addVetoableChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XVetoableChangeListener > & xListener) override; + + virtual void SAL_CALL removeVetoableChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XVetoableChangeListener > & xListener) override; + + // XPropertySetInfo + virtual css::uno::Sequence < css::beans::Property > SAL_CALL getProperties() override; + + virtual css::beans::Property SAL_CALL getPropertyByName(const OUString & sName) override; + + virtual sal_Bool SAL_CALL hasPropertyByName(const OUString & sName) override; + + +private: + + void impl_setPropertyValue(sal_Int32 nHandle, + const css::uno::Any& aValue); + + css::uno::Any impl_getPropertyValue(sal_Int32 nHandle); + + /** set a new owner for this helper. + * + * This owner is used as source for all broadcasted events. + * Further we hold it weak, because we don't wish to be disposed() .-) + */ + void impl_setPropertyChangeBroadcaster(const css::uno::Reference< css::uno::XInterface >& xBroadcaster); + + /** add a new property info to the set of supported ones. + * + * @param aProperty + * describes the new property. + * + * @throw [css::beans::PropertyExistException] + * if a property with the same name already exists. + * + * Note: The consistence of the whole set of properties is not checked here. + * Means e.g. ... a handle which exists more than once is not detected. + * The owner of this class has to be sure, that every new property does + * not clash with any existing one. + */ + void impl_addPropertyInfo(const css::beans::Property& aProperty); + + /** mark the object as "dead". + */ + void impl_disablePropertySet(); + + bool impl_existsVeto(const css::beans::PropertyChangeEvent& aEvent); + + void impl_notifyChangeListener(const css::beans::PropertyChangeEvent& aEvent); + + /*-**************************************************************************************************** + @short helper methods + @descr Follow methods are needed at different points of our code (more than ones!). + + @attention Threadsafe methods are signed by "implts_..."! + *//*-*****************************************************************************************************/ + + // threadsafe + void implts_sendFrameActionEvent ( const css::frame::FrameAction& aAction ); + void implts_resizeComponentWindow ( ); + void implts_setIconOnWindow ( ); + void implts_startWindowListening ( ); + void implts_stopWindowListening ( ); + void implts_checkSuicide ( ); + void implts_forgetSubFrames ( ); + + // non threadsafe + void impl_checkMenuCloser ( ); + void impl_setCloser ( const css::uno::Reference< css::frame::XFrame2 >& xFrame , bool bState ); + + void disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager); + + void checkDisposed() { + osl::MutexGuard g(rBHelper.rMutex); + if (rBHelper.bInDispose || rBHelper.bDisposed) { + throw css::lang::DisposedException("Frame disposed"); + } + } + +// variables +// -threadsafe by SolarMutex + + /// reference to factory, which has create this instance + css::uno::Reference< css::uno::XComponentContext > m_xContext; + /// reference to factory helper to create status indicator objects + css::uno::Reference< css::task::XStatusIndicatorFactory > m_xIndicatorFactoryHelper; + /// points to an external set progress, which should be used instead of the internal one. + css::uno::WeakReference< css::task::XStatusIndicator > m_xIndicatorInterception; + /// helper for XDispatch/Provider and interception interfaces + css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchHelper; + /// helper for XFrames, XIndexAccess and XElementAccess interfaces + css::uno::Reference< css::frame::XFrames > m_xFramesHelper; + /// container for ALL Listener + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer; + /// parent of this frame + css::uno::Reference< css::frame::XFramesSupplier > m_xParent; + /// containerwindow of this frame for embedded components + css::uno::Reference< css::awt::XWindow > m_xContainerWindow; + /// window of the actual component + css::uno::Reference< css::awt::XWindow > m_xComponentWindow; + /// controller of the actual frame + css::uno::Reference< css::frame::XController > m_xController; + /// listen to drag & drop + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > m_xDropTargetListener; + /// state, if I am a member of active path in tree or I have the focus or... + EActiveState m_eActiveState; + /// name of this frame + OUString m_sName; + /// frame has no parent or the parent is a task or the desktop + bool m_bIsFrameTop; + /// due to FrameActionEvent + bool m_bConnected; + sal_Int16 m_nExternalLockCount; + /// is used for dispatch recording and will be set/get from outside. Frame provide it only! + css::uno::Reference< css::frame::XDispatchRecorderSupplier > m_xDispatchRecorderSupplier; + /// ref counted class to support disabling commands defined by configuration file + SvtCommandOptions m_aCommandOptions; + /// in case of CloseVetoException on method close() was thrown by ourself - we must close ourself later if no internal processes are running + bool m_bSelfClose; + /// indicates, if this frame is used in hidden mode or not + bool m_bIsHidden; + /// The container window has WindowExtendedStyle::DocHidden set. + bool m_bDocHidden = false; + /// is used to layout the child windows of the frame. + css::uno::Reference< css::frame::XLayoutManager2 > m_xLayoutManager; + css::uno::Reference< css::frame::XDispatchInformationProvider > m_xDispatchInfoHelper; + css::uno::Reference< css::frame::XTitle > m_xTitleHelper; + + std::unique_ptr<WindowCommandDispatch> m_pWindowCommandDispatch; + + typedef std::unordered_map<OUString, css::beans::Property> TPropInfoHash; + TPropInfoHash m_lProps; + + comphelper::OMultiTypeInterfaceContainerHelperVar3<css::beans::XPropertyChangeListener, OUString> m_lSimpleChangeListener; + comphelper::OMultiTypeInterfaceContainerHelperVar3<css::beans::XVetoableChangeListener, OUString> m_lVetoChangeListener; + + // hold it weak ... otherwise this helper has to be "killed" explicitly .-) + css::uno::WeakReference< css::uno::XInterface > m_xBroadcaster; + + FrameContainer m_aChildFrameContainer; /// array of child frames + /** + * URL of the file that is being loaded, when the load already started by we have no controller + * yet. + */ + OUString m_aURL; +}; + + +/*-**************************************************************************************************** + @short standard constructor to create instance by factory + @descr This constructor initialize a new instance of this class by valid factory, + and will be set valid values on his member and baseclasses. + + @attention a) Don't use your own reference during a UNO-Service-ctor! There is no guarantee, that you + will get over this. (e.g. using of your reference as parameter to initialize some member) + Do such things in DEFINE_INIT_SERVICE() method, which is called automatically after your ctor!!! + b) Baseclass OBroadcastHelper is a typedef in namespace cppu! + The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper. + If we write it without a namespace or expand the typedef to OBroadcastHelperVar<...> -> it will be OK!? + I don't know why! (other compiler not tested .. but it works!) + + @seealso method DEFINE_INIT_SERVICE() + + @param xContext is the multi service manager, which creates this instance. + The value must be different from NULL! + @onerror ASSERT in debug version or nothing in release version. +*//*-*****************************************************************************************************/ +XFrameImpl::XFrameImpl( css::uno::Reference< css::uno::XComponentContext > xContext ) + : PartialWeakComponentImplHelper(m_aMutex) + // init member + , m_xContext (std::move( xContext )) + , m_aListenerContainer ( m_aMutex ) + , m_eActiveState ( E_INACTIVE ) + , m_bIsFrameTop ( true ) // I think we are top without a parent ... and there is no parent yet! + , m_bConnected ( false ) // There exist no component inside of use => sal_False, we are not connected! + , m_nExternalLockCount ( 0 ) + , m_bSelfClose ( false ) // Important! + , m_bIsHidden ( true ) + , m_lSimpleChangeListener ( m_aMutex ) + , m_lVetoChangeListener ( m_aMutex ) +{ +} + +void XFrameImpl::initListeners() +{ + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW); + + // Initialize a new dispatchhelper-object to handle dispatches. + // We use these helper as slave for our interceptor helper ... not directly! + // But he is event listener on THIS instance! + rtl::Reference<DispatchProvider> xDispatchProvider = new DispatchProvider( m_xContext, this ); + + m_xDispatchInfoHelper = new DispatchInformationProvider(m_xContext, this); + + // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism. + // Set created dispatch provider as slowest slave of it. + // Hold interception helper by reference only - not by pointer! + // So it's easier to destroy it. + m_xDispatchHelper = new InterceptionHelper( this, xDispatchProvider ); + + // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess. + // We hold member as reference ... not as pointer too! + // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that. + // But look on dispose() for right order of deinitialization. + m_xFramesHelper = new OFrames( this, &m_aChildFrameContainer ); + + // Initialize the drop target listener. + // We hold member as reference ... not as pointer too! + m_xDropTargetListener = new OpenFileDropTargetListener( m_xContext, this ); + + // Safe impossible cases + // We can't work without these helpers! + SAL_WARN_IF( !xDispatchProvider.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Slowest slave for dispatch- and interception helper " + "is not valid. XDispatchProvider, XDispatch, XDispatchProviderInterception are not full supported!" ); + SAL_WARN_IF( !m_xDispatchHelper.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Interception helper is not valid. XDispatchProvider, " + "XDispatch, XDispatchProviderInterception are not full supported!" ); + SAL_WARN_IF( !m_xFramesHelper.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Frames helper is not valid. XFrames, " + "XIndexAccess and XElementAccess are not supported!" ); + SAL_WARN_IF( !m_xDropTargetListener.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): DropTarget helper is not valid. " + "Drag and drop without functionality!" ); + + // establish notifies for changing of "disabled commands" configuration during runtime + m_aCommandOptions.EstablishFrameCallback(this); + + // Create an initial layout manager + // Create layout manager and connect it to the newly created frame + m_xLayoutManager = css::frame::LayoutManager::create(m_xContext); + + // set information about all supported properties + impl_setPropertyChangeBroadcaster(static_cast< css::frame::XFrame* >(this)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_DISPATCHRECORDERSUPPLIER, + FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER, + cppu::UnoType<css::frame::XDispatchRecorderSupplier>::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION, + FRAME_PROPHANDLE_INDICATORINTERCEPTION, + cppu::UnoType<css::task::XStatusIndicator>::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_ISHIDDEN, + FRAME_PROPHANDLE_ISHIDDEN, + cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_LAYOUTMANAGER, + FRAME_PROPHANDLE_LAYOUTMANAGER, + cppu::UnoType<css::frame::XLayoutManager>::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_TITLE, + FRAME_PROPHANDLE_TITLE, + cppu::UnoType<OUString>::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo(css::beans::Property(FRAME_PROPNAME_ASCII_URL, FRAME_PROPHANDLE_URL, + cppu::UnoType<OUString>::get(), + css::beans::PropertyAttribute::TRANSIENT)); +} + +/*-************************************************************************************************************ + @interface XComponentLoader + @short try to load given URL into a task + @descr You can give us some information about the content, which you will load into a frame. + We search or create this target for you, make a type detection of given URL and try to load it. + As result of this operation we return the new created component or nothing, if loading failed. + @param "sURL" , URL, which represent the content + @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ... + @param "nSearchFlags" , optional arguments for frame search, if target is not a special one + @param "lArguments" , optional arguments for loading + @return A valid component reference, if loading was successful. + A null reference otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL XFrameImpl::loadComponentFromURL( + const OUString& sURL, + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + checkDisposed(); + + css::uno::Reference< css::frame::XComponentLoader > xThis(this); + + utl::MediaDescriptor aDescriptor(lArguments); + bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false); + + if (bOnMainThread) + { + // Make sure that we own the solar mutex, otherwise later + // vcl::SolarThreadExecutor::execute() will release the solar mutex, even if it's owned by + // another thread, leading to an std::abort() at the end. + SolarMutexGuard g; + + return vcl::solarthread::syncExecute(std::bind(&LoadEnv::loadComponentFromURL, xThis, + m_xContext, sURL, sTargetFrameName, + nSearchFlags, lArguments)); + } + else + return LoadEnv::loadComponentFromURL(xThis, m_xContext, sURL, sTargetFrameName, + nSearchFlags, lArguments); +} + +/*-**************************************************************************************************** + @short return access to append or remove children on desktop + @descr We don't implement these interface directly. We use a helper class to do this. + If you wish to add or delete children to/from the container, call these method to get + a reference to the helper. + + @seealso class OFrames + @return A reference to the helper which answer your queries. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrames > SAL_CALL XFrameImpl::getFrames() +{ + checkDisposed(); + + SolarMutexGuard g; + // Return access to all child frames to caller. + // Our childframe container is implemented in helper class OFrames and used as a reference m_xFramesHelper! + return m_xFramesHelper; +} + +/*-**************************************************************************************************** + @short get the current active child frame + @descr It must be a frameto. Direct children of a frame are frames only! No task or desktop is accepted. + We don't save this information directly in this class. We use our container-helper + to do that. + + @seealso class OFrameContainer + @seealso method setActiveFrame() + @return A reference to our current active childframe, if anyone exist. + @return A null reference, if nobody is active. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL XFrameImpl::getActiveFrame() +{ + checkDisposed(); + + SolarMutexGuard g; + // Return current active frame. + // This information is available on the container. + return m_aChildFrameContainer.getActive(); +} + +/*-**************************************************************************************************** + @short set the new active direct child frame + @descr It must be a frame to. Direct children of frame are frames only! No task or desktop is accepted. + We don't save this information directly in this class. We use our container-helper + to do that. + + @seealso class OFrameContainer + @seealso method getActiveFrame() + + @param "xFrame", reference to new active child. It must be an already existing child! + @onerror An assertion is thrown and element is ignored, if given frame isn't already a child of us. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // Copy necessary member for threadsafe access! + // m_aChildFrameContainer is threadsafe himself and he live if we live!!! + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + EActiveState eActiveState = m_eActiveState; + + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Don't work, if "new" active frame isn't different from current one! + // (xFrame==NULL is allowed to UNSET it!) + if( xActiveChild != xFrame ) + { + // ... otherwise set new and deactivate old one. + m_aChildFrameContainer.setActive( xFrame ); + if ( + ( eActiveState != E_INACTIVE ) && + xActiveChild.is() + ) + { + xActiveChild->deactivate(); + } + } + + if( xFrame.is() ) + { + // If last active frame had focus ... + // ... reset state to ACTIVE and send right FrameActionEvent for focus lost. + if( eActiveState == E_FOCUS ) + { + aWriteLock.reset(); + eActiveState = E_ACTIVE; + m_eActiveState = eActiveState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING ); + } + + // If last active frame was active ... + // but new one is not it ... + // ... set it as active one. + if ( eActiveState == E_ACTIVE && !xFrame->isActive() ) + { + xFrame->activate(); + } + } + else + // If this frame is active and has no active subframe anymore it is UI active too + if( eActiveState == E_ACTIVE ) + { + aWriteLock.reset(); + eActiveState = E_FOCUS; + m_eActiveState = eActiveState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED ); + } +} + +/*-**************************************************************************************************** + initialize new created layout manager +**/ +void lcl_enableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager, + const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + // Provide container window to our layout manager implementation + xLayoutManager->attachFrame(xFrame); + + xFrame->addFrameActionListener(xLayoutManager); + + rtl::Reference<DockingAreaDefaultAcceptor> xDockingAreaAcceptor = new DockingAreaDefaultAcceptor(xFrame); + xLayoutManager->setDockingAreaAcceptor(xDockingAreaAcceptor); +} + +/*-**************************************************************************************************** + deinitialize layout manager +**/ +void XFrameImpl::disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager) +{ + removeFrameActionListener(xLayoutManager); + xLayoutManager->setDockingAreaAcceptor(css::uno::Reference< css::ui::XDockingAreaAcceptor >()); + xLayoutManager->attachFrame(css::uno::Reference< css::frame::XFrame >()); +} + +/*-**************************************************************************************************** + @short initialize frame instance + @descr A frame needs a window. This method set a new one ... but should called one times only! + We use this window to listen for window events and forward it to our set component. + It's used as parent of component window too. + + @seealso method getContainerWindow() + @seealso method setComponent() + @seealso member m_xContainerWindow + + @param "xWindow", reference to new container window - must be valid! + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::initialize( const css::uno::Reference< css::awt::XWindow >& xWindow ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + if (!xWindow.is()) + throw css::uno::RuntimeException( + "XFrameImpl::initialize() called without a valid container window reference.", + static_cast< css::frame::XFrame* >(this)); + + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + if ( m_xContainerWindow.is() ) + throw css::uno::RuntimeException( + "XFrameImpl::initialized() is called more than once, which is not useful nor allowed.", + static_cast< css::frame::XFrame* >(this)); + + // This must be the first call of this method! + // We should initialize our object and open it for working. + // Set the new window. + SAL_WARN_IF( m_xContainerWindow.is(), "fwk.frame", "XFrameImpl::initialize(): Leak detected! This state should never occur ..." ); + m_xContainerWindow = xWindow; + + // if window is initially visible, we will never get a windowShowing event + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow); + if (pWindow) + { + if (pWindow->IsVisible()) + m_bIsHidden = false; + m_bDocHidden + = static_cast<bool>(pWindow->GetExtendedStyle() & WindowExtendedStyle::DocHidden); + } + + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager = m_xLayoutManager; + + // Release lock ... because we call some impl methods, which are threadsafe by himself. + // If we hold this lock - we will produce our own deadlock! + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Avoid enabling the layout manager for hidden frames: it's expensive and + // provides little value. + if (xLayoutManager.is() && !m_bDocHidden) + lcl_enableLayoutManager(xLayoutManager, this); + + // create progress helper + css::uno::Reference< css::frame::XFrame > xThis (static_cast< css::frame::XFrame* >(this), + css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::task::XStatusIndicatorFactory > xIndicatorFactory = + css::task::StatusIndicatorFactory::createWithFrame(m_xContext, xThis, + false/*DisableReschedule*/, true/*AllowParentShow*/ ); + + // SAFE -> ---------------------------------- + aWriteLock.reset(); + m_xIndicatorFactoryHelper = xIndicatorFactory; + aWriteLock.clear(); + // <- SAFE ---------------------------------- + + // Start listening for events after setting it on helper class ... + // So superfluous messages are filtered to NULL :-) + implts_startWindowListening(); + + m_pWindowCommandDispatch.reset(new WindowCommandDispatch(m_xContext, this)); + + // Initialize title functionality + m_xTitleHelper = new TitleHelper( m_xContext, xThis, nullptr ); +} + +/*-**************************************************************************************************** + @short returns current set container window + @descr The ContainerWindow property is used as a container for the component + in this frame. So this object implements a container interface too. + The instantiation of the container window is done by the user of this class. + The frame is the owner of its container window. + + @seealso method initialize() + @return A reference to current set containerwindow. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::awt::XWindow > SAL_CALL XFrameImpl::getContainerWindow() +{ + SolarMutexGuard g; + return m_xContainerWindow; +} + +/*-**************************************************************************************************** + @short set parent frame + @descr We need a parent to support some functionality! e.g. findFrame() + By the way we use the chance to set an internal information about our top state. + So we must not check this information during every isTop() call. + We are top, if our parent is the desktop instance or we haven't any parent. + + @seealso getCreator() + @seealso findFrame() + @seealso isTop() + @seealso m_bIsFrameTop + + @param xCreator + valid reference to our new owner frame, which should implement a supplier interface + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& xCreator ) +{ + checkDisposed(); + + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + m_xParent = xCreator; + } + /* } SAFE */ + + css::uno::Reference< css::frame::XDesktop > xIsDesktop( xCreator, css::uno::UNO_QUERY ); + m_bIsFrameTop = ( xIsDesktop.is() || ! xCreator.is() ); +} + +/*-**************************************************************************************************** + @short returns current parent frame + @descr The Creator is the parent frame container. If it is NULL, the frame is the uppermost one. + + @seealso method setCreator() + @return A reference to current set parent frame container. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL XFrameImpl::getCreator() +{ + checkDisposed(); + SolarMutexGuard g; + return m_xParent; +} + +/*-**************************************************************************************************** + @short returns current set name of frame + @descr This name is used to find target of findFrame() or queryDispatch() calls. + + @seealso method setName() + @return Current set name of frame. + + @onerror An empty string is returned. +*//*-*****************************************************************************************************/ +OUString SAL_CALL XFrameImpl::getName() +{ + SolarMutexGuard g; + return m_sName; +} + +/*-**************************************************************************************************** + @short set new name for frame + @descr This name is used to find target of findFrame() or queryDispatch() calls. + + @attention Special names like "_blank", "_self" aren't allowed... + "_beamer" excepts this rule! + + @seealso method getName() + + @param "sName", new frame name. + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::setName( const OUString& sName ) +{ + SolarMutexGuard g; + // Set new name... but look for invalid special target names! + // They are not allowed to set. + if (TargetHelper::isValidNameForFrame(sName)) + m_sName = sName; +} + +/*-**************************************************************************************************** + @short search for frames + @descr This method searches for a frame with the specified name. + Frames may contain other frames (e.g. a frameset) and may + be contained in other frames. This hierarchy is searched by + this method. + First some special names are taken into account, i.e. "", + "_self", "_top", "_blank" etc. The nSearchFlags are ignored + when comparing these names with sTargetFrameName, further steps are + controlled by the search flags. If allowed, the name of the frame + itself is compared with the desired one, then ( again if allowed ) + the method findFrame() is called for all children, for siblings + and as last for the parent frame. + If no frame with the given name is found until the top frames container, + a new top one is created, if this is allowed by a special + flag. The new frame also gets the desired name. + + @param sTargetFrameName + special names (_blank, _self) or real name of target frame + @param nSearchFlags + optional flags which regulate search for non special target frames + + @return A reference to found or may be new created frame. + @threadsafe yes +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL XFrameImpl::findFrame( const OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XFrame > xTarget; + + // 0) Ignore wrong parameter! + // We don't support search for following special targets. + // If we reject this requests - we must not check for such names + // in following code again and again. If we do not so -wrong + // search results can occur! + + if ( sTargetFrameName == SPECIALTARGET_DEFAULT ) // valid for dispatches - not for findFrame()! + { + return nullptr; + } + + // I) check for special defined targets first which must be handled exclusive. + // force using of "if() else if() ..." + + // get threadsafe some necessary member which are necessary for following functionality + /* SAFE { */ + SolarMutexResettableGuard aReadLock; + css::uno::Reference< css::frame::XFrame > xParent = m_xParent; + bool bIsTopFrame = m_bIsFrameTop; + bool bIsTopWindow = WindowHelper::isTopWindow(m_xContainerWindow); + aReadLock.clear(); + /* } SAFE */ + + // I.I) "_blank" + // Not allowed for a normal frame - but for the desktop. + // Use helper class to do so. It use the desktop automatically. + + if ( sTargetFrameName==SPECIALTARGET_BLANK ) + { + TaskCreator aCreator(m_xContext); + xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor()); + } + + // I.II) "_parent" + // It doesn't matter if we have a valid parent or not. User ask for him and get it. + // An empty result is a valid result too. + + else if ( sTargetFrameName==SPECIALTARGET_PARENT ) + { + xTarget = xParent; + } + + // I.III) "_top" + // If we are not the top frame in this hierarchy, we must forward request to our parent. + // Otherwise we must return ourself. + + else if ( sTargetFrameName==SPECIALTARGET_TOP ) + { + if (bIsTopFrame) + xTarget = this; + else if (xParent.is()) // If we are not top - the parent MUST exist. But may it's better to check it again .-) + xTarget = xParent->findFrame(SPECIALTARGET_TOP,0); + } + + // I.IV) "_self", "" + // This mean this frame in every case. + + else if ( + ( sTargetFrameName==SPECIALTARGET_SELF ) || + ( sTargetFrameName.isEmpty() ) + ) + { + xTarget = this; + } + + // I.V) "_beamer" + // This is a special sub frame of any task. We must return it if we found it on our direct children + // or create it there if it not already exists. + // Note: Such beamer exists for task(top) frames only! + + else if ( sTargetFrameName==SPECIALTARGET_BEAMER ) + { + // We are a task => search or create the beamer + if (bIsTopWindow) + { + xTarget = m_aChildFrameContainer.searchOnDirectChildrens(SPECIALTARGET_BEAMER); + if ( ! xTarget.is() ) + { + /* TODO + Creation not supported yet! + Wait for new layout manager service because we can't plug it + inside already opened document of this frame... + */ + } + } + // We aren't a task => forward request to our parent or ignore it. + else if (xParent.is()) + xTarget = xParent->findFrame(SPECIALTARGET_BEAMER,0); + } + + else + { + + // II) otherwise use optional given search flags + // force using of combinations of such flags. means no "else" part of use if() statements. + // But we ust break further searches if target was already found. + // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT + // TASK and CREATE are handled special. + + // get threadsafe some necessary member which are necessary for following functionality + /* SAFE { */ + aReadLock.reset(); + OUString sOwnName = m_sName; + aReadLock.clear(); + /* } SAFE */ + + // II.I) SELF + // Check for right name. If it's the searched one return ourself - otherwise + // ignore this flag. + + if ( + (nSearchFlags & css::frame::FrameSearchFlag::SELF) && + (sOwnName == sTargetFrameName ) + ) + { + xTarget = this; + } + + // II.II) CHILDREN + // Search on all children for the given target name. + // An empty name value can't occur here - because it must be already handled as "_self" + // before. Used helper function of container doesn't create any frame. + // It makes a deep search only. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + ) + { + xTarget = m_aChildFrameContainer.searchOnAllChildrens(sTargetFrameName); + } + + // II.III) TASKS + // This is a special flag. It regulate search on this task tree only or allow search on + // all other ones (which are sibling trees of us) too. + // Upper search must stop at this frame if we are the topest one and the TASK flag is not set + // or we can ignore it if we have no valid parent. + + if ( + ( bIsTopFrame && (nSearchFlags & css::frame::FrameSearchFlag::TASKS) ) || + ( ! bIsTopFrame ) + ) + { + + // II.III.I) SIBLINGS + // Search on all our direct siblings - means all children of our parent. + // Use this flag in combination with TASK. We must suppress such upper search if + // user has not set it and if we are a top frame. + // Attention: Don't forward this request to our parent as a findFrame() call. + // In such case we must protect us against recursive calls. + // Use snapshot of our parent. But don't use queryFrames() of XFrames interface. + // Because it's return all siblings and all her children including our children too + // if we call it with the CHILDREN flag. We don't need that - we need the direct container + // items of our parent only to start searches there. So we must use the container interface + // XIndexAccess instead of XFrames. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::SIBLINGS) && + ( xParent.is() ) // search on siblings is impossible without a parent + ) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY ); + if (xSupplier.is()) + { + css::uno::Reference< css::container::XIndexAccess > xContainer = xSupplier->getFrames(); + if (xContainer.is()) + { + sal_Int32 nCount = xContainer->getCount(); + for( sal_Int32 i=0; i<nCount; ++i ) + { + css::uno::Reference< css::frame::XFrame > xSibling; + if ( + // control unpacking + ( !(xContainer->getByIndex(i)>>=xSibling) ) || + // check for valid items + ( ! xSibling.is() ) || + // ignore ourself! (We are a part of this container too - but search on our children was already done.) + ( xSibling==static_cast< ::cppu::OWeakObject* >(this) ) + ) + { + continue; + } + + // Don't allow upper search here! Use right flags to regulate it. + // And allow deep search on children only - if it was allowed for us too. + sal_Int32 nRightFlags = css::frame::FrameSearchFlag::SELF; + if (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + nRightFlags |= css::frame::FrameSearchFlag::CHILDREN; + xTarget = xSibling->findFrame(sTargetFrameName, nRightFlags ); + // perform search be breaking further search if a result exist. + if (xTarget.is()) + break; + } + } + } + } + + // II.III.II) PARENT + // Forward search to our parent (if he exists.) + // To prevent us against recursive and superfluous calls (which can occur if we allow him + // to search on his children too) we must change used search flags. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::PARENT) && + ( xParent.is() ) + ) + { + if (xParent->getName() == sTargetFrameName) + xTarget = xParent; + else + { + sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CHILDREN; + xTarget = xParent->findFrame(sTargetFrameName, nRightFlags); + } + } + } + + // II.IV) CREATE + // If we haven't found any valid target frame by using normal flags - but user allowed us to create + // a new one ... we should do that. Used TaskCreator use Desktop instance automatically as parent! + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + ) + { + TaskCreator aCreator(m_xContext); + xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor()); + } + } + + return xTarget; +} + +/*-**************************************************************************************************** + @descr Returns sal_True, if this frame is a "top frame", otherwise sal_False. + The "m_bIsFrameTop" member must be set in the ctor or setCreator() method. + A top frame is a member of the top frame container or a member of the + task frame container. Both containers can create new frames if the findFrame() + method of their css::frame::XFrame interface is called with a frame name not yet known. + + @seealso ctor + @seealso method setCreator() + @seealso method findFrame() + @return true, if is it a top frame ... false otherwise. + + @onerror No error should occur! +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::isTop() +{ + checkDisposed(); + SolarMutexGuard g; + // This information is set in setCreator(). + // We are top, if our parent is a task or the desktop or if no parent exist! + return m_bIsFrameTop; +} + +/*-**************************************************************************************************** + @short activate frame in hierarchy + @descr This feature is used to mark active paths in our frame hierarchy. + You can be a listener for this event to react for it ... change some internal states or something else. + + @seealso method deactivate() + @seealso method isActivate() + @seealso enum EActiveState + @seealso listener mechanism +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::activate() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // Copy necessary member and free the lock. + // It's not necessary for m_aChildFrameContainer ... because + // he is threadsafe himself and live if we live. + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + css::uno::Reference< css::frame::XFramesSupplier > xParent = m_xParent; + css::uno::Reference< css::frame::XFrame > xThis(this); + EActiveState eState = m_eActiveState; + + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // 1) If I am not active before... + if( eState == E_INACTIVE ) + { + // ... do it then. + aWriteLock.reset(); + eState = E_ACTIVE; + m_eActiveState = eState; + aWriteLock.clear(); + // Deactivate sibling path and forward activation to parent ... if any parent exist! + if( xParent.is() ) + { + // Every time set THIS frame as active child of parent and activate it. + // We MUST have a valid path from bottom to top as active path! + // But we must deactivate the old active sibling path first. + + // Attention: Deactivation of an active path, deactivate the whole path ... from bottom to top! + // But we wish to deactivate founded sibling-tree only. + // [ see deactivate() / step 4) for further information! ] + + xParent->setActiveFrame( xThis ); + + // Then we can activate from here to top. + // Attention: We are ACTIVE now. And the parent will call activate() at us! + // But we do nothing then! We are already activated. + xParent->activate(); + } + // It's necessary to send event NOW - not before. + // Activation goes from bottom to top! + // That's the reason to activate parent first and send event now. + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_ACTIVATED ); + } + + // 2) I was active before or current activated and there is a path from here to bottom, who CAN be active. + // But our direct child of path is not active yet. + // (It can be, if activation occur in the middle of a current path!) + // In these case we activate path to bottom to set focus on right frame! + if ( eState == E_ACTIVE && xActiveChild.is() && !xActiveChild->isActive() ) + { + xActiveChild->activate(); + } + + // 3) I was active before or current activated. But if I have no active child => I will get the focus! + if ( eState == E_ACTIVE && !xActiveChild.is() ) + { + aWriteLock.reset(); + eState = E_FOCUS; + m_eActiveState = eState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED ); + } +} + +/*-**************************************************************************************************** + @short deactivate frame in hierarchy + @descr This feature is used to deactivate paths in our frame hierarchy. + You can be a listener for this event to react for it... change some internal states or something else. + + @seealso method activate() + @seealso method isActivate() + @seealso enum EActiveState + @seealso listener mechanism +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::deactivate() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // Copy necessary member and free the lock. + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + css::uno::Reference< css::frame::XFramesSupplier > xParent = m_xParent; + css::uno::Reference< css::frame::XFrame > xThis(this); + EActiveState eState = m_eActiveState; + + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Work only, if there something to do! + if( eState == E_INACTIVE ) + return; + + + // 1) Deactivate all active children. + if ( xActiveChild.is() && xActiveChild->isActive() ) + { + xActiveChild->deactivate(); + } + + // 2) If I have the focus - I will lost it now. + if( eState == E_FOCUS ) + { + // Set new state INACTIVE(!) and send message to all listener. + // Don't set ACTIVE as new state. This frame is deactivated for next time - due to activate(). + aWriteLock.reset(); + eState = E_ACTIVE; + m_eActiveState = eState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING ); + } + + // 3) If I am active - I will be deactivated now. + if( eState == E_ACTIVE ) + { + // Set new state and send message to all listener. + aWriteLock.reset(); + eState = E_INACTIVE; + m_eActiveState = eState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_DEACTIVATING ); + } + + // 4) If there is a path from here to my parent... + // ... I am on the top or in the middle of deactivated subtree and action was started here. + // I must deactivate all frames from here to top, which are members of current path. + // Stop, if THESE frame not the active frame of our parent! + if ( xParent.is() && xParent->getActiveFrame() == xThis ) + { + // We MUST break the path - otherwise we will get the focus - not our parent! ... + // Attention: Our parent don't call us again - WE ARE NOT ACTIVE YET! + // [ see step 3 and condition "if ( m_eActiveState!=INACTIVE ) ..." in this method! ] + xParent->deactivate(); + } +} + +/*-**************************************************************************************************** + @short returns active state + @descr Call it to get information about current active state of this frame. + + @seealso method activate() + @seealso method deactivate() + @seealso enum EActiveState + @return true if active, false otherwise. + + @onerror No error should occur. +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::isActive() +{ + checkDisposed(); + SolarMutexGuard g; + return m_eActiveState == E_ACTIVE || m_eActiveState == E_FOCUS; +} + +/*-**************************************************************************************************** + @short ??? +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::contextChanged() +{ + // Sometimes called during closing object... + // Impl-method is threadsafe himself! + // Send event to all listener for frame actions. + implts_sendFrameActionEvent( css::frame::FrameAction_CONTEXT_CHANGED ); +} + +/*-**************************************************************************************************** + @short set new component inside the frame + @descr A frame is a container for a component. Use this method to set, change or release it! + We accept null references! The xComponentWindow will be a child of our container window + and get all window events from us. + + @attention (a) A current set component can disagree with the suspend() request! + We don't set the new one and return with false then. + (b) It's possible to set: + (b1) a simple component here which supports the window only - no controller; + (b2) a full featured component which supports window and controller; + (b3) or both to NULL if outside code which to forget this component. + + @seealso method getComponentWindow() + @seealso method getController() + + @param xComponentWindow + valid reference to new component window which will be a child of internal container window + May <NULL/> for releasing. + @param xController + reference to new component controller + (may <NULL/> for releasing or setting of a simple component) + + @return <TRUE/> if operation was successful, <FALSE/> otherwise. + + @onerror We return <FALSE/>. + @threadsafe yes +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::setComponent(const css::uno::Reference< css::awt::XWindow >& xComponentWindow, + const css::uno::Reference< css::frame::XController >& xController ) +{ + + // Ignore this HACK of sfx2! + // He call us with a valid controller without a valid window... that's not allowed! + if ( xController.is() && ! xComponentWindow.is() ) + return true; + + checkDisposed(); + + // Get threadsafe some copies of used members. + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::awt::XWindow > xOldComponentWindow = m_xComponentWindow; + css::uno::Reference< css::frame::XController > xOldController = m_xController; + VclPtr<vcl::Window> pOwnWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + bool bHadFocus = pOwnWindow != nullptr && pOwnWindow->HasChildPathFocus(); + bool bWasConnected = m_bConnected; + aReadLock.clear(); + /* } SAFE */ + + // stop listening on old window + // May it produce some trouble. + // But don't forget to listen on new window again ... or reactivate listening + // if we reject this setComponent() request and leave this method without changing the old window. + implts_stopWindowListening(); + + // Notify all listener, that this component (if current one exist) will be unloaded. + if (bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_DETACHING ); + + // otherwise release old component first + // Always release controller before releasing window, + // because controller may want to access its window! + // But check for real changes - may the new controller is the old one. + if ( + (xOldController.is() ) && + (xOldController != xController) + ) + { + /* ATTENTION + Don't suspend the old controller here. Because the outside caller must do that + by definition. We have to dispose it here only. + */ + + // Before we dispose this controller we should hide it inside this frame instance. + // We hold it alive for next calls by using xOldController! + /* SAFE {*/ + { + SolarMutexGuard aWriteLock; + m_xController = nullptr; + + auto pInterceptionHelper = dynamic_cast<InterceptionHelper*>(m_xDispatchHelper.get()); + if (pInterceptionHelper) + { + css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider = pInterceptionHelper->GetSlave(); + auto pDispatchProvider = dynamic_cast<DispatchProvider*>(xDispatchProvider.get()); + if (pDispatchProvider) + { + pDispatchProvider->ClearProtocolHandlers(); + } + } + } + /* } SAFE */ + + if (xOldController.is()) + { + try + { + xOldController->dispose(); + } + catch(const css::lang::DisposedException&) + {} + } + xOldController = nullptr; + } + + // Now it's time to release the component window. + // If controller wasn't released successfully - this code line shouldn't be reached. + // Because in case of "suspend()==false" we return immediately with false ... + // see before + // Check for real changes too. + if ( + (xOldComponentWindow.is() ) && + (xOldComponentWindow != xComponentWindow) + ) + { + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + m_xComponentWindow = nullptr; + } + /* } SAFE */ + + if (xOldComponentWindow.is()) + { + try + { + xOldComponentWindow->dispose(); + } + catch(const css::lang::DisposedException&) + { + } + } + xOldComponentWindow = nullptr; + } + + // Now it's time to set the new component ... + // By the way - find out our new "load state" - means if we have a valid component inside. + /* SAFE { */ + SolarMutexResettableGuard aWriteLock; + m_xComponentWindow = xComponentWindow; + m_xController = xController; + + // Clear the URL on the frame itself, now that the controller has it. + m_aURL.clear(); + + m_bConnected = (m_xComponentWindow.is() || m_xController.is()); + bool bIsConnected = m_bConnected; + aWriteLock.clear(); + /* } SAFE */ + + // notifies all interest listener, that current component was changed or a new one was loaded + if (bIsConnected && bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_REATTACHED ); + else if (bIsConnected && !bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_ATTACHED ); + + // A new component window doesn't know anything about current active/focus states. + // Set this information on it! + if ( bHadFocus && xComponentWindow.is() ) + { + xComponentWindow->setFocus(); + } + + // If it was a new component window - we must resize it to fill out + // our container window. + implts_resizeComponentWindow(); + // New component should change our current icon ... + implts_setIconOnWindow(); + // OK - start listening on new window again - or do nothing if it is an empty one. + implts_startWindowListening(); + + /* SAFE { */ + aWriteLock.reset(); + impl_checkMenuCloser(); + aWriteLock.clear(); + /* } SAFE */ + + return true; +} + +/*-**************************************************************************************************** + @short returns current set component window + @descr Frames are used to display components. The actual displayed component is + held by the m_xComponentWindow property. If the component implements only a + XComponent interface, the communication between the frame and the + component is very restricted. Better integration is achievable through a + XController interface. + If the component wants other objects to be able to get information about its + ResourceDescriptor it has to implement a XModel interface. + This frame is the owner of the component window. + + @seealso method setComponent() + @return css::uno::Reference to current set component window. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::awt::XWindow > SAL_CALL XFrameImpl::getComponentWindow() +{ + checkDisposed(); + SolarMutexGuard g; + return m_xComponentWindow; +} + +/*-**************************************************************************************************** + @short returns current set controller + @descr Frames are used to display components. The actual displayed component is + held by the m_xComponentWindow property. If the component implements only a + XComponent interface, the communication between the frame and the + component is very restricted. Better integration is achievable through a + XController interface. + If the component wants other objects to be able to get information about its + ResourceDescriptor it has to implement a XModel interface. + This frame is the owner of the component window. + + @seealso method setComponent() + @return css::uno::Reference to current set controller. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XController > SAL_CALL XFrameImpl::getController() +{ + SolarMutexGuard g; + return m_xController; +} + +/*-**************************************************************************************************** + @short add/remove listener for activate/deactivate/contextChanged events + @seealso method activate() + @seealso method deactivate() + @seealso method contextChanged() + + @param "xListener" reference to your listener object + @onerror Listener is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) +{ + checkDisposed(); + m_aListenerContainer.addInterface( cppu::UnoType<css::frame::XFrameActionListener>::get(), xListener ); +} + +void SAL_CALL XFrameImpl::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType<css::frame::XFrameActionListener>::get(), xListener ); +} + +/*-**************************************************************************************************** + @short support two way mechanism to release a frame + @descr This method ask internal component (controller) if he accept this close request. + In case of <TRUE/> nothing will be happen (from point of caller of this close method). + In case of <FALSE/> a CloseVetoException is thrown. After such exception given parameter + <var>bDeliverOwnership</var> regulate which will be the new owner of this instance. + + @attention It's the replacement for XTask::close() which is marked as obsolete method. + + @param bDeliverOwnership + If parameter is set to <FALSE/> the original caller will be the owner after thrown + veto exception and must try to close this frame at later time again. Otherwise the + source of thrown exception is the right one. May it will be the frame himself. + + @throws CloseVetoException + if any internal things will not be closed + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::close( sal_Bool bDeliverOwnership ) +{ + checkDisposed(); + + // At the end of this method may we must dispose ourself... + // and may nobody from outside hold a reference to us... + // then it's a good idea to do that by ourself. + css::uno::Reference< css::uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) ); + + // Check any close listener before we look for currently running internal processes. + // Because if a listener disagree with this close() request - we have time to finish this + // internal operations too... + // Note: container is threadsafe himself. + css::lang::EventObject aSource (static_cast< ::cppu::OWeakObject*>(this)); + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::util::XCloseListener>::get()); + if (pContainer!=nullptr) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<css::util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + // Ok - no listener disagreed with this close() request + // check if this frame is used for any load process currently + if (isActionLocked()) + { + if (bDeliverOwnership) + { + SolarMutexGuard g; + m_bSelfClose = true; + } + + throw css::util::CloseVetoException("Frame in use for loading document...",static_cast< ::cppu::OWeakObject*>(this)); + } + + if ( ! setComponent(nullptr,nullptr) ) + throw css::util::CloseVetoException("Component couldn't be detached...",static_cast< ::cppu::OWeakObject*>(this)); + + // If closing is allowed... inform all listeners and dispose this frame! + pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::util::XCloseListener>::get()); + if (pContainer!=nullptr) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<css::util::XCloseListener*>(pIterator.next())->notifyClosing( aSource ); + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + m_bIsHidden = true; + } + /* } SAFE */ + impl_checkMenuCloser(); + + dispose(); +} + +/*-**************************************************************************************************** + @short be a listener for close events! + @descr Adds/remove a CloseListener at this frame instance. If the close() method is called on + this object, the such listener are informed and can disagree with that by throwing + a CloseVetoException. + + @seealso XFrameImpl::close() + + @param xListener + reference to your listener object + + @onerror Listener is ignored. + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::addCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener ) +{ + checkDisposed(); + m_aListenerContainer.addInterface( cppu::UnoType<css::util::XCloseListener>::get(), xListener ); +} + +void SAL_CALL XFrameImpl::removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType<css::util::XCloseListener>::get(), xListener ); +} + +OUString SAL_CALL XFrameImpl::getTitle() +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_SET_THROW); + aReadLock.clear(); + // <- SAFE + + return xTitle->getTitle(); +} + +void SAL_CALL XFrameImpl::setTitle( const OUString& sTitle ) +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_SET_THROW); + aReadLock.clear(); + // <- SAFE + + xTitle->setTitle(sTitle); +} + +void SAL_CALL XFrameImpl::addTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener) +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.clear(); + // <- SAFE + + xTitle->addTitleChangeListener(xListener); +} + +void SAL_CALL XFrameImpl::removeTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.clear(); + // <- SAFE + + xTitle->removeTitleChangeListener(xListener); +} + +css::uno::Reference<css::container::XNameContainer> SAL_CALL XFrameImpl::getUserDefinedAttributes() +{ + // optional attribute + return nullptr; +} + +css::uno::Reference<css::frame::XDispatchRecorderSupplier> SAL_CALL XFrameImpl::getDispatchRecorderSupplier() +{ + SolarMutexGuard g; + return m_xDispatchRecorderSupplier; +} + +void SAL_CALL XFrameImpl::setDispatchRecorderSupplier(const css::uno::Reference<css::frame::XDispatchRecorderSupplier>& p) +{ + checkDisposed(); + SolarMutexGuard g; + m_xDispatchRecorderSupplier.set(p); +} + +css::uno::Reference<css::uno::XInterface> SAL_CALL XFrameImpl::getLayoutManager() +{ + SolarMutexGuard g; + return m_xLayoutManager; +} + +void SAL_CALL XFrameImpl::setLayoutManager(const css::uno::Reference<css::uno::XInterface>& p1) +{ + checkDisposed(); + SolarMutexGuard g; + + css::uno::Reference<css::frame::XLayoutManager2> xOldLayoutManager = m_xLayoutManager; + css::uno::Reference<css::frame::XLayoutManager2> xNewLayoutManager(p1, css::uno::UNO_QUERY); + + if (xOldLayoutManager != xNewLayoutManager) + { + m_xLayoutManager = xNewLayoutManager; + if (xOldLayoutManager.is()) + disableLayoutManager(xOldLayoutManager); + if (xNewLayoutManager.is() && !m_bDocHidden) + lcl_enableLayoutManager(xNewLayoutManager, this); + } +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL XFrameImpl::getPropertySetInfo() +{ + checkDisposed(); + return css::uno::Reference< css::beans::XPropertySetInfo >(this); +} + +void SAL_CALL XFrameImpl::setPropertyValue(const OUString& sProperty, + const css::uno::Any& aValue ) +{ + // TODO look for e.g. readonly props and reject setProp() call! + + checkDisposed(); + + // SAFE -> + SolarMutexGuard g; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + + css::beans::Property aPropInfo = pIt->second; + + css::uno::Any aCurrentValue = impl_getPropertyValue(aPropInfo.Handle); + + bool bWillBeChanged = (aCurrentValue != aValue); + if (! bWillBeChanged) + return; + + css::beans::PropertyChangeEvent aEvent; + aEvent.PropertyName = aPropInfo.Name; + aEvent.Further = false; + aEvent.PropertyHandle = aPropInfo.Handle; + aEvent.OldValue = aCurrentValue; + aEvent.NewValue = aValue; + aEvent.Source.set(m_xBroadcaster.get(), css::uno::UNO_QUERY); + + if (impl_existsVeto(aEvent)) + throw css::beans::PropertyVetoException(); + + impl_setPropertyValue(aPropInfo.Handle, aValue); + + impl_notifyChangeListener(aEvent); +} + +css::uno::Any SAL_CALL XFrameImpl::getPropertyValue(const OUString& sProperty) +{ + checkDisposed(); + + // SAFE -> + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + + css::beans::Property aPropInfo = pIt->second; + + return impl_getPropertyValue(aPropInfo.Handle); +} + +void SAL_CALL XFrameImpl::addPropertyChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) +{ + checkDisposed(); + + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lSimpleChangeListener.addInterface(sProperty, xListener); +} + +void SAL_CALL XFrameImpl::removePropertyChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) +{ + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lSimpleChangeListener.removeInterface(sProperty, xListener); +} + +void SAL_CALL XFrameImpl::addVetoableChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) +{ + checkDisposed(); + + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lVetoChangeListener.addInterface(sProperty, xListener); +} + +void SAL_CALL XFrameImpl::removeVetoableChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) +{ + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lVetoChangeListener.removeInterface(sProperty, xListener); +} + +css::uno::Sequence< css::beans::Property > SAL_CALL XFrameImpl::getProperties() +{ + checkDisposed(); + + SolarMutexGuard g; + + sal_Int32 c = static_cast<sal_Int32>(m_lProps.size()); + css::uno::Sequence< css::beans::Property > lProps(c); + auto lPropsRange = asNonConstRange(lProps); + for (auto const& elem : m_lProps) + { + lPropsRange[--c] = elem.second; + } + + return lProps; +} + +css::beans::Property SAL_CALL XFrameImpl::getPropertyByName(const OUString& sName) +{ + checkDisposed(); + + SolarMutexGuard g; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sName); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sName); + + return pIt->second; +} + +sal_Bool SAL_CALL XFrameImpl::hasPropertyByName(const OUString& sName) +{ + checkDisposed(); + + SolarMutexGuard g; + + TPropInfoHash::iterator pIt = m_lProps.find(sName); + bool bExist = (pIt != m_lProps.end()); + + return bExist; +} + +/*-****************************************************************************************************/ +void XFrameImpl::implts_forgetSubFrames() +{ + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::container::XIndexAccess > xContainer(m_xFramesHelper, css::uno::UNO_QUERY_THROW); + aReadLock.clear(); + // <- SAFE + + sal_Int32 c = xContainer->getCount(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + try + { + css::uno::Reference< css::frame::XFrame > xFrame; + xContainer->getByIndex(i) >>= xFrame; + if (xFrame.is()) + xFrame->setCreator(css::uno::Reference< css::frame::XFramesSupplier >()); + } + catch(const css::uno::Exception&) + { + // Ignore errors here. + // Nobody can guarantee a stable index in multi threaded environments .-) + } + } + + SolarMutexGuard g; + m_xFramesHelper.clear(); // clear uno reference + m_aChildFrameContainer.clear(); // clear container content +} + +/*-**************************************************************************************************** + @short destroy instance + @descr The owner of this object calls the dispose method if the object + should be destroyed. All other objects and components, that are registered + as an EventListener are forced to release their references to this object. + Furthermore this frame is removed from its parent frame container to release + this reference. The reference attributes are disposed and released also. + + @attention Look for globale description at beginning of file too! + (DisposedException, FairRWLock ..., initialize, dispose) + + @seealso method initialize() + @seealso baseclass FairRWLockBase! +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::disposing() +{ + // We should hold a reference to ourself ... + // because our owner dispose us and release our reference ... + // May be we will die before we could finish this method ... + css::uno::Reference< css::frame::XFrame > xThis(this); + + SAL_INFO("fwk.frame", "[Frame] " << m_sName << " send dispose event to listener"); + + // First operation should be... "stop all listening for window events on our container window". + // These events are superfluous but can make trouble! + // We will die, die and die... + implts_stopWindowListening(); + + css::uno::Reference<css::frame::XLayoutManager2> layoutMgr; + { + SolarMutexGuard g; + layoutMgr = m_xLayoutManager; + } + if (layoutMgr.is()) { + disableLayoutManager(layoutMgr); + } + + std::unique_ptr<WindowCommandDispatch> disp; + { + SolarMutexGuard g; + std::swap(disp, m_pWindowCommandDispatch); + } + disp.reset(); + + // Send message to all listener and forget her references. + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + // set "end of live" for our property set helper + impl_disablePropertySet(); + + // interception/dispatch chain must be destructed explicitly + // Otherwise some dispatches and/or interception objects won't die. + css::uno::Reference< css::lang::XEventListener > xDispatchHelper; + { + SolarMutexGuard g; + xDispatchHelper.set(m_xDispatchHelper, css::uno::UNO_QUERY_THROW); + } + xDispatchHelper->disposing(aEvent); + xDispatchHelper.clear(); + + // Don't show any dialogs, errors or something else any more! + // If somewhere called dispose() without close() before - normally no dialogs + // should exist. Otherwise it's the problem of the outside caller. + // Note: + // (a) Do it after stopWindowListening(). May that force some active/deactivate + // notifications which we don't need here really. + // (b) Don't forget to save the old value of IsDialogCancelEnabled() to + // restore it afterwards (to not kill headless mode). + DialogCancelMode old = Application::GetDialogCancelMode(); + Application::SetDialogCancelMode( DialogCancelMode::Silent ); + + // We should be alone for ever and further dispose calls are rejected by lines before ... + // I hope it :-) + + // Free references of our frame tree. + // Force parent container to forget this frame too ... + // ( It's contained in m_xParent and so no css::lang::XEventListener for m_xParent! ) + // It's important to do that before we free some other internal structures. + // Because if our parent gets an activate and found us as last possible active frame + // he try to deactivate us ... and we run into some trouble (DisposedExceptions!). + css::uno::Reference<css::frame::XFramesSupplier> parent; + { + SolarMutexGuard g; + std::swap(parent, m_xParent); + } + if( parent.is() ) + { + parent->getFrames()->remove( xThis ); + } + + /* } SAFE */ + // Forget our internal component and her window first. + // So we can release our container window later without problems. + // Because this container window is the parent of the component window ... + // Note: Dispose it hard - because suspending must be done inside close() call! + // But try to dispose the controller first before you destroy the window. + // Because the window is used by the controller too ... + css::uno::Reference< css::lang::XComponent > xDisposableCtrl; + css::uno::Reference< css::lang::XComponent > xDisposableComp; + { + SolarMutexGuard g; + xDisposableCtrl = m_xController; + xDisposableComp = m_xComponentWindow; + } + if (xDisposableCtrl.is()) + xDisposableCtrl->dispose(); + if (xDisposableComp.is()) + xDisposableComp->dispose(); + + impl_checkMenuCloser(); + + css::uno::Reference<css::awt::XWindow> contWin; + { + SolarMutexGuard g; + std::swap(contWin, m_xContainerWindow); + } + if( contWin.is() ) + { + contWin->setVisible( false ); + // All VclComponents are XComponents; so call dispose before discarding + // a css::uno::Reference< XVclComponent >, because this frame is the owner of the window + contWin->dispose(); + } + + /*ATTENTION + Clear container after successful removing from parent container ... + because our parent could be the desktop which stand in dispose too! + If we have already cleared our own container we lost our child before this could be + remove himself at this instance ... + Release m_xFramesHelper after that ... it's the same problem between parent and child! + "m_xParent->getFrames()->remove( xThis );" needs this helper ... + Otherwise we get a null reference and could finish removing successfully. + => You see: Order of calling operations is important!!! + */ + implts_forgetSubFrames(); + + { + SolarMutexGuard g; + + // Release some other references. + // This calls should be easy ... I hope it :-) + m_xDispatchHelper.clear(); + m_xDropTargetListener.clear(); + m_xDispatchRecorderSupplier.clear(); + m_xLayoutManager.clear(); + m_xIndicatorFactoryHelper.clear(); + + // It's important to set default values here! + // If may be later somewhere change the disposed-behaviour of this implementation + // and doesn't throw any DisposedExceptions we must guarantee best matching default values ... + m_eActiveState = E_INACTIVE; + m_sName.clear(); + m_bIsFrameTop = false; + m_bConnected = false; + m_nExternalLockCount = 0; + m_bSelfClose = false; + m_bIsHidden = true; + } + + // Don't forget it restore old value - + // otherwise no dialogs can be shown anymore in other frames. + Application::SetDialogCancelMode( old ); +} + +/*-**************************************************************************************************** + @short Be a listener for dispose events! + @descr Adds/remove an EventListener to this object. If the dispose method is called on + this object, the disposing method of the listener is called. + @param "xListener" reference to your listener object. + @onerror Listener is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + checkDisposed(); + m_aListenerContainer.addInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener ); +} + +void SAL_CALL XFrameImpl::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener ); +} + +/*-**************************************************************************************************** + @short create new status indicator + @descr Use returned status indicator to show progresses and some text information. + All created objects share the same dialog! Only the last one can show his information. + + @seealso class StatusIndicatorFactory + @seealso class StatusIndicator + @return A reference to created object. + + @onerror We return a null reference. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::task::XStatusIndicator > SAL_CALL XFrameImpl::createStatusIndicator() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + + // Make snapshot of necessary member and define default return value. + css::uno::Reference< css::task::XStatusIndicator > xExternal(m_xIndicatorInterception.get(), css::uno::UNO_QUERY); + css::uno::Reference< css::task::XStatusIndicatorFactory > xFactory = m_xIndicatorFactoryHelper; + + aReadLock.clear(); + /* UNSAFE AREA ----------------------------------------------------------------------------------------- */ + + // Was set from outside to intercept any progress activities! + if (xExternal.is()) + return xExternal; + + // Or use our own factory as fallback, to create such progress. + if (xFactory.is()) + return xFactory->createStatusIndicator(); + + return css::uno::Reference< css::task::XStatusIndicator >(); +} + +/*-**************************************************************************************************** + @short search for target to load URL + @descr This method searches for a dispatch for the specified DispatchDescriptor. + The FrameSearchFlags and the FrameName of the DispatchDescriptor are + treated as described for findFrame. + + @seealso method findFrame() + @seealso method queryDispatches() + @seealso method set/getName() + @seealso class TargetFinder + + @param "aURL" , URL for loading + @param "sTargetFrameName" , name of target frame + @param "nSearchFlags" , additional flags to regulate search if sTargetFrameName is not clear + @return css::uno::Reference to dispatch handler. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL XFrameImpl::queryDispatch( const css::util::URL& aURL, + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags) +{ + // Don't check incoming parameter here! Our helper do it for us and it is not a good idea to do it more than once! + + checkDisposed(); + + // Remove uno and cmd protocol part as we want to support both of them. We store only the command part + // in our hash map. All other protocols are stored with the protocol part. + OUString aCommand( aURL.Main ); + if ( aURL.Protocol.equalsIgnoreAsciiCase(".uno:") ) + aCommand = aURL.Path; + + // Make std::unordered_map lookup if the current URL is in the disabled list + if ( m_aCommandOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aCommand ) ) + return css::uno::Reference< css::frame::XDispatch >(); + else + { + // We use a helper to support these interface and an interceptor mechanism. + css::uno::Reference<css::frame::XDispatchProvider> disp; + { + SolarMutexGuard g; + disp = m_xDispatchHelper; + } + if (!disp.is()) { + throw css::lang::DisposedException("Frame disposed"); + } + return disp->queryDispatch( aURL, sTargetFrameName, nSearchFlags ); + } +} + +/*-**************************************************************************************************** + @short handle more than ones dispatches at same call + @descr Returns a sequence of dispatches. For details see the queryDispatch method. + For failed dispatches we return empty items in list! + + @seealso method queryDispatch() + + @param "lDescriptor" list of dispatch arguments for queryDispatch()! + @return List of dispatch references. Some elements can be NULL! + + @onerror An empty list is returned. +*//*-*****************************************************************************************************/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL XFrameImpl::queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + // Don't check incoming parameter here! Our helper do it for us and it is not a good idea to do it more than ones! + + checkDisposed(); + + // We use a helper to support these interface and an interceptor mechanism. + css::uno::Reference<css::frame::XDispatchProvider> disp; + { + SolarMutexGuard g; + disp = m_xDispatchHelper; + } + if (!disp.is()) { + throw css::lang::DisposedException("Frame disposed"); + } + return disp->queryDispatches( lDescriptor ); +} + +/*-**************************************************************************************************** + @short register/unregister interceptor for dispatch calls + @descr If you wish to handle some dispatches by himself ... you should be + an interceptor for it. Please see class OInterceptionHelper for further information. + + @seealso class OInterceptionHelper + + @param "xInterceptor", reference to your interceptor implementation. + @onerror Interceptor is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::registerDispatchProviderInterceptor( + const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) +{ + // We use a helper to support these interface and an interceptor mechanism. + // This helper is threadsafe himself and check incoming parameter too. + // I think we don't need any lock here! + + checkDisposed(); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper; + { + SolarMutexGuard g; + xInterceptionHelper.set( m_xDispatchHelper, css::uno::UNO_QUERY ); + } + if (xInterceptionHelper.is()) { + xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor ); + } +} + +void SAL_CALL XFrameImpl::releaseDispatchProviderInterceptor( + const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) +{ + // We use a helper to support these interface and an interceptor mechanism. + // This helper is threadsafe himself and check incoming parameter too. + // I think we don't need any lock here! + + // Sometimes we are called during our dispose() method + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper; + { + SolarMutexGuard g; + xInterceptionHelper.set( m_xDispatchHelper, css::uno::UNO_QUERY ); + } + if (xInterceptionHelper.is()) { + xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor ); + } +} + +/*-**************************************************************************************************** + @short provides information about all possible dispatch functions + inside the current frame environment +*//*-*****************************************************************************************************/ +css::uno::Sequence< sal_Int16 > SAL_CALL XFrameImpl::getSupportedCommandGroups() +{ + return m_xDispatchInfoHelper->getSupportedCommandGroups(); +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL XFrameImpl::getConfigurableDispatchInformation( + sal_Int16 nCommandGroup) +{ + return m_xDispatchInfoHelper->getConfigurableDispatchInformation(nCommandGroup); +} + +/*-**************************************************************************************************** + @short notifications for window events + @descr We are a listener on our container window to forward it to our component window. + + @seealso method setComponent() + @seealso member m_xContainerWindow + @seealso member m_xComponentWindow + + @param "aEvent" describe source of detected event +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::windowResized( const css::awt::WindowEvent& ) +{ + // Part of dispose-mechanism + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Impl-method is threadsafe! + // If we have a current component window - we must resize it! + implts_resizeComponentWindow(); +} + +void SAL_CALL XFrameImpl::focusGained( const css::awt::FocusEvent& ) +{ + // Part of dispose() mechanism + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + // Make snapshot of member! + css::uno::Reference< css::awt::XWindow > xComponentWindow = m_xComponentWindow; + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( xComponentWindow.is() ) + { + xComponentWindow->setFocus(); + } +} + +/*-**************************************************************************************************** + @short notifications for window events + @descr We are a listener on our container window to forward it to our component window ... + but a XTopWindowListener we are only if we are a top frame! + + @seealso method setComponent() + @seealso member m_xContainerWindow + @seealso member m_xComponentWindow + + @param "aEvent" describe source of detected event +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::windowActivated( const css::lang::EventObject& ) +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + // Make snapshot of member! + EActiveState eState = m_eActiveState; + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Activate the new active path from here to top. + if( eState == E_INACTIVE ) + { + setActiveFrame( css::uno::Reference< css::frame::XFrame >() ); + activate(); + } +} + +void SAL_CALL XFrameImpl::windowDeactivated( const css::lang::EventObject& ) +{ + // Sometimes called during dispose() + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + + css::uno::Reference< css::frame::XFrame > xParent = m_xParent; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + EActiveState eActiveState = m_eActiveState; + + aReadLock.clear(); + + if( eActiveState == E_INACTIVE ) + return; + + // Deactivation is always done implicitly by activation of another frame. + // Only if no activation is done, deactivations have to be processed if the activated window + // is a parent window of the last active Window! + SolarMutexClearableGuard aSolarGuard; + vcl::Window* pFocusWindow = Application::GetFocusWindow(); + if ( !xContainerWindow.is() || !xParent.is() || + css::uno::Reference< css::frame::XDesktop >( xParent, css::uno::UNO_QUERY ).is() + ) + return; + + css::uno::Reference< css::awt::XWindow > xParentWindow = xParent->getContainerWindow(); + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xParentWindow ); + //#i70261#: dialogs opened from an OLE object will cause a deactivate on the frame of the OLE object + // on Solaris/Linux at that time pFocusWindow is still NULL because the focus handling is different; right after + // the deactivation the focus will be set into the dialog! + // currently I see no case where a sub frame could get a deactivate with pFocusWindow being NULL permanently + // so for now this case is omitted from handled deactivations + if( pFocusWindow && pParentWindow->IsChild( pFocusWindow ) ) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY ); + if( xSupplier.is() ) + { + aSolarGuard.clear(); + xSupplier->setActiveFrame( css::uno::Reference< css::frame::XFrame >() ); + } + } +} + +void SAL_CALL XFrameImpl::windowClosing( const css::lang::EventObject& ) +{ + checkDisposed(); + + // deactivate this frame ... + deactivate(); + + // ... and try to close it + // But do it asynchronous inside the main thread. + // VCL has no fun to do such things outside his main thread :-( + // Note: The used dispatch make it asynchronous for us .-) + + /*ATTENTION! + Don't try to suspend the controller here! Because it's done inside used dispatch(). + Otherwise the dialog "would you save your changes?" will be shown more than once ... + */ + + css::util::URL aURL; + aURL.Complete = ".uno:CloseFrame"; + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + xParser->parseStrict(aURL); + + css::uno::Reference< css::frame::XDispatch > xCloser = queryDispatch(aURL, SPECIALTARGET_SELF, 0); + if (xCloser.is()) + xCloser->dispatch(aURL, css::uno::Sequence< css::beans::PropertyValue >()); + + // Attention: If this dispatch works synchronous ... and fulfill its job ... + // this line of code will never be reached ... + // Or if it will be reached it will be for sure that all your member are gone .-) +} + +/*-**************************************************************************************************** + @short react for a show event for the internal container window + @descr Normally we don't need this information really. But we can use it to + implement the special feature "trigger first visible task". + + Algorithm: - first we have to check if we are a top (task) frame + It's not enough to be a top frame! Because we MUST have the desktop as parent. + But frames without a parent are top too. So it's not possible to check isTop() here! + We have to look for the type of our parent. + - if we are a task frame, then we have to check if we are the first one. + We use a static variable to do so. They will be reset to afterwards be sure + that further calls of this method doesn't do anything then. + - Then we have to trigger the right event string on the global job executor. + + @seealso css::task::JobExecutor + + @param aEvent + describes the source of this event + We are not interested on this information. We are interested on the visible state only. + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::windowShown( const css::lang::EventObject& ) +{ + static std::mutex aFirstVisibleLock; + + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XDesktop > xDesktopCheck( m_xParent, css::uno::UNO_QUERY ); + m_bIsHidden = false; + aReadLock.clear(); + /* } SAFE */ + + impl_checkMenuCloser(); + + if (!xDesktopCheck.is()) + return; + + static bool bFirstVisibleTask = true; + std::unique_lock aGuard(aFirstVisibleLock); + bool bMustBeTriggered = bFirstVisibleTask; + bFirstVisibleTask = false; + aGuard.unlock(); + + if (bMustBeTriggered) + { + css::uno::Reference< css::task::XJobExecutor > xExecutor + = css::task::theJobExecutor::get( m_xContext ); + xExecutor->trigger( "onFirstVisibleTask" ); + } +} + +void SAL_CALL XFrameImpl::windowHidden( const css::lang::EventObject& ) +{ + /* SAFE { */ + { + SolarMutexGuard aReadLock; + m_bIsHidden = true; + } + /* } SAFE */ + + impl_checkMenuCloser(); +} + +/*-**************************************************************************************************** + @short called by dispose of our windows! + @descr This object is forced to release all references to the interfaces given + by the parameter source. We are a listener at our container window and + should listen for his disposing. + + @seealso XWindowListener + @seealso XTopWindowListener + @seealso XFocusListener +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::disposing( const css::lang::EventObject& aEvent ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + if( aEvent.Source == m_xContainerWindow ) + { + // NECESSARY: Impl-method is threadsafe by himself! + aWriteLock.clear(); + implts_stopWindowListening(); + aWriteLock.reset(); + m_xContainerWindow.clear(); + } +} + +/*-************************************************************************************************************ + @interface com.sun.star.document.XActionLockable + @short implement locking of frame/task from outside + @descr Sometimes we have problems to decide if closing of task is allowed. Because; frame/task + could be used for pending loading jobs. So you can lock this object from outside and + prevent instance against closing during using! But - don't do it in a wrong or expensive manner. + Otherwise task couldn't die anymore!!! + + @seealso interface XActionLockable + @seealso method BaseDispatcher::implts_loadIt() + @seealso method Desktop::loadComponentFromURL() + @return true if frame/task is locked + false otherwise + @threadsafe yes +*//*-*************************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::isActionLocked() +{ + SolarMutexGuard g; + return( m_nExternalLockCount!=0); +} + +void SAL_CALL XFrameImpl::addActionLock() +{ + SolarMutexGuard g; + ++m_nExternalLockCount; +} + +void SAL_CALL XFrameImpl::removeActionLock() +{ + { + SolarMutexGuard g; + SAL_WARN_IF( m_nExternalLockCount<=0, "fwk.frame", "XFrameImpl::removeActionLock(): Frame is not locked! " + "Possible multithreading problem detected." ); + --m_nExternalLockCount; + } + + implts_checkSuicide(); +} + +void SAL_CALL XFrameImpl::setActionLocks( sal_Int16 nLock ) +{ + SolarMutexGuard g; + // Attention: If somewhere called resetActionLocks() before and get e.g. 5 locks ... + // and tried to set these 5 ones here after his operations ... + // we can't ignore set requests during these two calls! + // So we must add(!) these 5 locks here. + m_nExternalLockCount = m_nExternalLockCount + nLock; +} + +sal_Int16 SAL_CALL XFrameImpl::resetActionLocks() +{ + sal_Int16 nCurrentLocks = 0; + { + SolarMutexGuard g; + nCurrentLocks = m_nExternalLockCount; + m_nExternalLockCount = 0; + } + + // Attention: + // external lock count is 0 here every time... but if + // member m_bSelfClose is set to true too... we call our own close()/dispose(). + // See close() for further information + implts_checkSuicide(); + + return nCurrentLocks; +} + +void XFrameImpl::impl_setPropertyValue(sal_Int32 nHandle, + const css::uno::Any& aValue) + +{ + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "don't release it on calling us" .-) + */ + + /* Attention: You can use nHandle only, if you are sure that all supported + properties has a unique handle. That must be guaranteed + inside method initListeners()! + */ + switch (nHandle) + { + case FRAME_PROPHANDLE_TITLE : + { + OUString sExternalTitle; + aValue >>= sExternalTitle; + setTitle (sExternalTitle); + } + break; + + case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + aValue >>= m_xDispatchRecorderSupplier; + break; + + case FRAME_PROPHANDLE_LAYOUTMANAGER : + { + css::uno::Reference< css::frame::XLayoutManager2 > xOldLayoutManager = m_xLayoutManager; + css::uno::Reference< css::frame::XLayoutManager2 > xNewLayoutManager; + aValue >>= xNewLayoutManager; + + if (xOldLayoutManager != xNewLayoutManager) + { + m_xLayoutManager = xNewLayoutManager; + if (xOldLayoutManager.is()) + disableLayoutManager(xOldLayoutManager); + if (xNewLayoutManager.is() && !m_bDocHidden) + lcl_enableLayoutManager(xNewLayoutManager, this); + } + } + break; + + case FRAME_PROPHANDLE_INDICATORINTERCEPTION : + { + css::uno::Reference< css::task::XStatusIndicator > xProgress; + aValue >>= xProgress; + m_xIndicatorInterception = xProgress; + } + break; + + case FRAME_PROPHANDLE_URL: + { + aValue >>= m_aURL; + } + break; + default : + SAL_INFO("fwk.frame", "XFrameImpl::setFastPropertyValue_NoBroadcast(): Invalid handle detected!" ); + break; + } +} + +css::uno::Any XFrameImpl::impl_getPropertyValue(sal_Int32 nHandle) +{ + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "don't release it on calling us" .-) + */ + + /* Attention: You can use nHandle only, if you are sure that all supported + properties has a unique handle. That must be guaranteed + inside method initListeners()! + */ + css::uno::Any aValue; + switch (nHandle) + { + case FRAME_PROPHANDLE_TITLE : + aValue <<= getTitle (); + break; + + case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + aValue <<= m_xDispatchRecorderSupplier; + break; + + case FRAME_PROPHANDLE_ISHIDDEN : + aValue <<= m_bIsHidden; + break; + + case FRAME_PROPHANDLE_LAYOUTMANAGER : + aValue <<= m_xLayoutManager; + break; + + case FRAME_PROPHANDLE_INDICATORINTERCEPTION : + { + css::uno::Reference< css::task::XStatusIndicator > xProgress(m_xIndicatorInterception.get(), + css::uno::UNO_QUERY); + aValue <<= xProgress; + } + break; + + case FRAME_PROPHANDLE_URL: + { + aValue <<= m_aURL; + } + break; + default : + SAL_INFO("fwk.frame", "XFrameImpl::getFastPropertyValue(): Invalid handle detected!" ); + break; + } + + return aValue; +} + +void XFrameImpl::impl_setPropertyChangeBroadcaster(const css::uno::Reference< css::uno::XInterface >& xBroadcaster) +{ + SolarMutexGuard g; + m_xBroadcaster = xBroadcaster; +} + +void XFrameImpl::impl_addPropertyInfo(const css::beans::Property& aProperty) +{ + SolarMutexGuard g; + + TPropInfoHash::const_iterator pIt = m_lProps.find(aProperty.Name); + if (pIt != m_lProps.end()) + throw css::beans::PropertyExistException(); + + m_lProps[aProperty.Name] = aProperty; +} + +void XFrameImpl::impl_disablePropertySet() +{ + SolarMutexGuard g; + + css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::beans::XPropertySet* >(this), css::uno::UNO_QUERY); + css::lang::EventObject aEvent(xThis); + + m_lSimpleChangeListener.disposeAndClear(aEvent); + m_lVetoChangeListener.disposeAndClear(aEvent); + m_lProps.clear(); +} + +bool XFrameImpl::impl_existsVeto(const css::beans::PropertyChangeEvent& aEvent) +{ + /* Don't use the lock here! + The used helper is threadsafe and it lives for the whole lifetime of + our own object. + */ + ::comphelper::OInterfaceContainerHelper3<css::beans::XVetoableChangeListener>* pVetoListener = m_lVetoChangeListener.getContainer(aEvent.PropertyName); + if (! pVetoListener) + return false; + + ::comphelper::OInterfaceIteratorHelper3 pListener(*pVetoListener); + while (pListener.hasMoreElements()) + { + try + { + pListener.next()->vetoableChange(aEvent); + } + catch(const css::uno::RuntimeException&) + { pListener.remove(); } + catch(const css::beans::PropertyVetoException&) + { return true; } + } + + return false; +} + +void XFrameImpl::impl_notifyChangeListener(const css::beans::PropertyChangeEvent& aEvent) +{ + /* Don't use the lock here! + The used helper is threadsafe and it lives for the whole lifetime of + our own object. + */ + ::comphelper::OInterfaceContainerHelper3<css::beans::XPropertyChangeListener>* pSimpleListener = m_lSimpleChangeListener.getContainer(aEvent.PropertyName); + if (! pSimpleListener) + return; + + ::comphelper::OInterfaceIteratorHelper3 pListener(*pSimpleListener); + while (pListener.hasMoreElements()) + { + try + { + pListener.next()->propertyChange(aEvent); + } + catch(const css::uno::RuntimeException&) + { pListener.remove(); } + } +} + +/*-**************************************************************************************************** + @short send frame action event to our listener + @descr This method is threadsafe AND can be called by our dispose method too! + @param "aAction", describe the event for sending +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_sendFrameActionEvent( const css::frame::FrameAction& aAction ) +{ + // Sometimes used by dispose() + + // Log information about order of events to file! + // (only activated in debug version!) + SAL_INFO( "fwk.frame", + "[Frame] " << m_sName << " send event " << + (aAction == css::frame::FrameAction_COMPONENT_ATTACHED ? OUString("COMPONENT ATTACHED") : + (aAction == css::frame::FrameAction_COMPONENT_DETACHING ? OUString("COMPONENT DETACHING") : + (aAction == css::frame::FrameAction_COMPONENT_REATTACHED ? OUString("COMPONENT REATTACHED") : + (aAction == css::frame::FrameAction_FRAME_ACTIVATED ? OUString("FRAME ACTIVATED") : + (aAction == css::frame::FrameAction_FRAME_DEACTIVATING ? OUString("FRAME DEACTIVATING") : + (aAction == css::frame::FrameAction_CONTEXT_CHANGED ? OUString("CONTEXT CHANGED") : + (aAction == css::frame::FrameAction_FRAME_UI_ACTIVATED ? OUString("FRAME UI ACTIVATED") : + (aAction == css::frame::FrameAction_FRAME_UI_DEACTIVATING ? OUString("FRAME UI DEACTIVATING") : + (aAction == css::frame::FrameAction::FrameAction_MAKE_FIXED_SIZE ? OUString("MAKE_FIXED_SIZE") : + OUString("*invalid*"))))))))))); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Send css::frame::FrameAction event to all listener. + // Get container for right listener. + // FOLLOW LINES ARE THREADSAFE!!! + // ( OInterfaceContainerHelper2 is synchronized with m_aListenerContainer! ) + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( + cppu::UnoType<css::frame::XFrameActionListener>::get()); + + if( pContainer == nullptr ) + return; + + // Build action event. + css::frame::FrameActionEvent aFrameActionEvent( static_cast< ::cppu::OWeakObject* >(this), this, aAction ); + + // Get iterator for access to listener. + comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer ); + // Send message to all listener. + while( aIterator.hasMoreElements() ) + { + try + { + static_cast<css::frame::XFrameActionListener*>(aIterator.next())->frameAction( aFrameActionEvent ); + } + catch( const css::uno::RuntimeException& ) + { + aIterator.remove(); + } + } +} + +/*-**************************************************************************************************** + @short helper to resize our component window + @descr A frame contains 2 windows - a container ~ and a component window. + This method resize inner component window to full size of outer container window. + This method is threadsafe AND can be called by our dispose method too! +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_resizeComponentWindow() +{ + // usually the LayoutManager does the resizing + // in case there is no LayoutManager resizing has to be done here + if ( m_xLayoutManager.is() ) + return; + + css::uno::Reference< css::awt::XWindow > xComponentWindow( getComponentWindow() ); + if( !xComponentWindow.is() ) + return; + + css::uno::Reference< css::awt::XDevice > xDevice( getContainerWindow(), css::uno::UNO_QUERY ); + + // Convert relative size to output size. + css::awt::Rectangle aRectangle = getContainerWindow()->getPosSize(); + css::awt::DeviceInfo aInfo = xDevice->getInfo(); + css::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, css::awt::PosSize::POSSIZE ); +} + +/*-**************************************************************************************************** + @short helper to set icon on our container window (if it is a system window!) + @descr We use our internal set controller (if it exist) to specify which factory he represented. + This information can be used to find right icon. But our controller can say it us directly + too ... we should ask his optional property set first ... + + @seealso method Window::SetIcon() + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_setIconOnWindow() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of necessary members and release lock. + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::frame::XController > xController = m_xController; + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( !(xContainerWindow.is() && xController.is()) ) + return; + + + // a) set default value to an invalid one. So we can start further searches for right icon id, if + // first steps failed! + // We must reset it to any fallback value - if no search step returns a valid result. + sal_Int32 nIcon = -1; + + // b) try to find information on controller propertyset directly + // Don't forget to catch possible exceptions - because these property is an optional one! + css::uno::Reference< css::beans::XPropertySet > xSet( xController, css::uno::UNO_QUERY ); + if( xSet.is() ) + { + try + { + css::uno::Reference< css::beans::XPropertySetInfo > const xPSI( xSet->getPropertySetInfo(), + css::uno::UNO_SET_THROW ); + if ( xPSI->hasPropertyByName( "IconId" ) ) + xSet->getPropertyValue( "IconId" ) >>= nIcon; + } + catch( css::uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + // c) if b) failed... analyze argument list of currently loaded document inside the frame to find the filter. + // He can be used to detect right factory - and these can be used to match factory to icon... + if( nIcon == -1 ) + { + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + if( xModel.is() ) + { + SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByModel(xModel); + if (eFactory != SvtModuleOptions::EFactory::UNKNOWN_FACTORY) + nIcon = SvtModuleOptions().GetFactoryIcon( eFactory ); + } + } + + // d) if all steps failed - use fallback! + if( nIcon == -1 ) + { + nIcon = 0; + } + + // e) set icon on container window now + // Don't forget SolarMutex! We use vcl directly :-( + // Check window pointer for right WorkWindow class too!!! + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aSolarGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + if( + ( pWindow != nullptr ) && + ( pWindow->GetType() == WindowType::WORKWINDOW ) + ) + { + WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get()); + pWorkWindow->SetIcon( static_cast<sal_uInt16>(nIcon) ); + } + } + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ +} + +/*-************************************************************************************************************ + @short helper to start/stop listening for window events on container window + @descr If we get a new container window, we must set it on internal member ... + and stop listening at old one ... and start listening on new one! + But sometimes (in dispose() call!) it's necessary to stop listening without starting + on new connections. So we split this functionality to make it easier at use. + + @seealso method initialize() + @seealso method dispose() + @onerror We do nothing! + @threadsafe yes +*//*-*************************************************************************************************************/ +void XFrameImpl::implts_startWindowListening() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of necessary member! + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener; + css::uno::Reference< css::awt::XWindowListener > xWindowListener(this); + css::uno::Reference< css::awt::XFocusListener > xFocusListener(this); + css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener(this); + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( !xContainerWindow.is() ) + return; + + xContainerWindow->addWindowListener( xWindowListener); + xContainerWindow->addFocusListener ( xFocusListener ); + + css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY ); + if( xTopWindow.is() ) + { + xTopWindow->addTopWindowListener( xTopWindowListener ); + + css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext ); + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xToolkit->getDropTarget( xContainerWindow ); + if( xDropTarget.is() ) + { + xDropTarget->addDropTargetListener( xDragDropListener ); + xDropTarget->setActive( true ); + } + } +} + +void XFrameImpl::implts_stopWindowListening() +{ + // Sometimes used by dispose() + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of necessary member! + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener; + css::uno::Reference< css::awt::XWindowListener > xWindowListener(this); + css::uno::Reference< css::awt::XFocusListener > xFocusListener(this); + css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener(this); + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( !xContainerWindow.is() ) + return; + + xContainerWindow->removeWindowListener( xWindowListener); + xContainerWindow->removeFocusListener ( xFocusListener ); + + css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY ); + if( !xTopWindow.is() ) + return; + + xTopWindow->removeTopWindowListener( xTopWindowListener ); + + css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext ); + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = + xToolkit->getDropTarget( xContainerWindow ); + if( xDropTarget.is() ) + { + xDropTarget->removeDropTargetListener( xDragDropListener ); + xDropTarget->setActive( false ); + } +} + +/*-**************************************************************************************************** + @short helper to force broken close() request again + @descr If we self disagree with a close() request, and detect that all external locks are gone ... + then we must try to close this frame again. + + @seealso XCloseable::close() + @seealso XFrameImpl::close() + @seealso XFrameImpl::removeActionLock() + @seealso XFrameImpl::resetActionLock() + @seealso m_bSelfClose + @seealso m_nExternalLockCount + + @threadsafe yes +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_checkSuicide() +{ + /* SAFE */ + SolarMutexClearableGuard aReadLock; + // in case of lock==0 and safed state of previous close() request m_bSelfClose + // we must force close() again. Because we had disagreed with that before. + bool bSuicide = (m_nExternalLockCount==0 && m_bSelfClose); + m_bSelfClose = false; + aReadLock.clear(); + /* } SAFE */ + // force close and deliver ownership to source of possible thrown veto exception + // Attention: Because this method is not designed to throw such exception we must suppress + // it for outside code! + try + { + if (bSuicide) + close(true); + } + catch(const css::util::CloseVetoException&) + {} + catch(const css::lang::DisposedException&) + {} +} + +/** little helper to enable/disable the menu closer at the menubar of the given frame. + + @param xFrame + we use its layout manager to set/reset a special callback. + Its existence regulate visibility of this closer item. + + @param bState + <TRUE/> enable; <FALSE/> disable this state + */ + +void XFrameImpl::impl_setCloser( /*IN*/ const css::uno::Reference< css::frame::XFrame2 >& xFrame , + /*IN*/ bool bState ) +{ + // Note: If start module is not installed - no closer has to be shown! + if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) + return; + + try + { + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xFrameProps->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + css::uno::Reference< css::beans::XPropertySet > xLayoutProps(xLayoutManager, css::uno::UNO_QUERY_THROW); + xLayoutProps->setPropertyValue(LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, css::uno::Any(bState)); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +/** it checks, which of the top level task frames must have the special menu closer for + switching to the backing window mode. + + It analyze the current list of visible top level frames. Only the last real document + frame can have this symbol. Not the help frame nor the backing task itself. + Here we do anything related to this closer. We remove it from the old frame and set it + for the new one. + */ + +void XFrameImpl::impl_checkMenuCloser() +{ + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + + // only top frames, which are part of our desktop hierarchy, can + // do so! By the way - we need the desktop instance to have access + // to all other top level frames too. + css::uno::Reference< css::frame::XDesktop > xDesktop (m_xParent, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XFramesSupplier > xTaskSupplier(xDesktop , css::uno::UNO_QUERY); + if ( !xDesktop.is() || !xTaskSupplier.is() ) + return; + + aReadLock.clear(); + /* } SAFE */ + + // analyze the list of current open tasks + // Suppress search for other views to the same model ... + // It's not needed here and can be very expensive. + FrameListAnalyzer aAnalyzer( + xTaskSupplier, + this, + FrameAnalyzerFlags::Hidden | FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent); + + // specify the new frame, which must have this special state... + css::uno::Reference< css::frame::XFrame2 > xNewCloserFrame; + + // a) + // If there exist at least one other frame - there are two frames currently open. + // But we can enable this closer only, if one of these two tasks includes the help module. + // The "other frame" couldn't be the help. Because then it wouldn't be part of this "other list". + // In such case it will be separated to the reference aAnalyzer.m_xHelp! + // But we must check, if we include ourself the help... + // Check aAnalyzer.m_bReferenceIsHelp! + if ( + (aAnalyzer.m_lOtherVisibleFrames.size()==1) && + ( + (aAnalyzer.m_bReferenceIsHelp ) || + (aAnalyzer.m_bReferenceIsHidden) + ) + ) + { + // others[0] can't be the backing component! + // Because it's set at the special member aAnalyzer.m_xBackingComponent ... :-) + xNewCloserFrame.set( aAnalyzer.m_lOtherVisibleFrames[0], css::uno::UNO_QUERY_THROW ); + } + + // b) + // There is no other frame... means no other document frame. The help module + // will be handled separately and must(!) be ignored here... excepting if we include ourself the help. + else if ( + (aAnalyzer.m_lOtherVisibleFrames.empty()) && + (!aAnalyzer.m_bReferenceIsHelp) && + (!aAnalyzer.m_bReferenceIsHidden) && + (!aAnalyzer.m_bReferenceIsBacking) + ) + { + xNewCloserFrame = this; + } + + // Look for necessary actions ... + // Only if the closer state must be moved from one frame to another one + // or must be enabled/disabled at all. + SolarMutexGuard aGuard; + // Holds the only frame, which must show the special closer menu item (can be NULL!) + static css::uno::WeakReference< css::frame::XFrame2 > s_xCloserFrame; + css::uno::Reference< css::frame::XFrame2 > xCloserFrame (s_xCloserFrame.get(), css::uno::UNO_QUERY); + if (xCloserFrame!=xNewCloserFrame) + { + if (xCloserFrame.is()) + impl_setCloser(xCloserFrame, false); + if (xNewCloserFrame.is()) + impl_setCloser(xNewCloserFrame, true); + s_xCloserFrame = xNewCloserFrame; + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_Frame_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + rtl::Reference<XFrameImpl> inst = new XFrameImpl(context); + css::uno::XInterface *acquired_inst = cppu::acquire(inst.get()); + + inst->initListeners(); + + return acquired_inst; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |