diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/source/controls/tree/treecontrol.cxx | 514 | ||||
-rw-r--r-- | toolkit/source/controls/tree/treecontrol.hxx | 65 | ||||
-rw-r--r-- | toolkit/source/controls/tree/treecontrolpeer.cxx | 1579 | ||||
-rw-r--r-- | toolkit/source/controls/tree/treedatamodel.cxx | 523 |
4 files changed, 2681 insertions, 0 deletions
diff --git a/toolkit/source/controls/tree/treecontrol.cxx b/toolkit/source/controls/tree/treecontrol.cxx new file mode 100644 index 000000000..d4439a4ff --- /dev/null +++ b/toolkit/source/controls/tree/treecontrol.cxx @@ -0,0 +1,514 @@ +/* -*- 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 "treecontrol.hxx" + +#include <com/sun/star/awt/tree/XTreeControl.hpp> +#include <com/sun/star/awt/tree/XTreeDataModel.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/helper/property.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::tree; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; + +namespace toolkit +{ + + +UnoTreeModel::UnoTreeModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ) + :UnoControlModel( i_factory ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FILLCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TREE_SELECTIONTYPE ); + ImplRegisterProperty( BASEPROPERTY_TREE_EDITABLE ); + ImplRegisterProperty( BASEPROPERTY_TREE_DATAMODEL ); + ImplRegisterProperty( BASEPROPERTY_TREE_ROOTDISPLAYED ); + ImplRegisterProperty( BASEPROPERTY_TREE_SHOWSHANDLES ); + ImplRegisterProperty( BASEPROPERTY_TREE_SHOWSROOTHANDLES ); + ImplRegisterProperty( BASEPROPERTY_ROW_HEIGHT ); + ImplRegisterProperty( BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING ); + ImplRegisterProperty( BASEPROPERTY_HIDEINACTIVESELECTION ); +} + +rtl::Reference<UnoControlModel> UnoTreeModel::Clone() const +{ + return new UnoTreeModel( *this ); +} + +OUString UnoTreeModel::getServiceName() +{ + return "com.sun.star.awt.tree.TreeControlModel"; +} + +Any UnoTreeModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch( nPropId ) + { + case BASEPROPERTY_TREE_SELECTIONTYPE: + return Any( SelectionType_NONE ); + case BASEPROPERTY_ROW_HEIGHT: + return Any( sal_Int32( 0 ) ); + case BASEPROPERTY_TREE_DATAMODEL: + return Any( Reference< XTreeDataModel >( nullptr ) ); + case BASEPROPERTY_TREE_EDITABLE: + case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: + return Any( false ); + case BASEPROPERTY_TREE_ROOTDISPLAYED: + case BASEPROPERTY_TREE_SHOWSROOTHANDLES: + case BASEPROPERTY_TREE_SHOWSHANDLES: + return Any( true ); + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "com.sun.star.awt.tree.TreeControl" ) ); + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } +} + +::cppu::IPropertyArrayHelper& UnoTreeModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > UnoTreeModel::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +} + +namespace { + +typedef ::cppu::ImplInheritanceHelper< UnoControlBase, css::awt::tree::XTreeControl > UnoTreeControl_Base; +class UnoTreeControl : public UnoTreeControl_Base +{ +public: + UnoTreeControl(); + OUString GetComponentServiceName() const override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::awt::XControl + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + + // css::view::XSelectionSupplier + virtual sal_Bool SAL_CALL select( const css::uno::Any& xSelection ) override; + virtual css::uno::Any SAL_CALL getSelection( ) override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // css::view::XMultiSelectionSupplier + virtual sal_Bool SAL_CALL addSelection( const css::uno::Any& Selection ) override; + virtual void SAL_CALL removeSelection( const css::uno::Any& Selection ) override; + virtual void SAL_CALL clearSelection( ) override; + virtual ::sal_Int32 SAL_CALL getSelectionCount( ) override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSelectionEnumeration( ) override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createReverseSelectionEnumeration( ) override; + + // css::awt::XTreeControl + virtual OUString SAL_CALL getDefaultExpandedGraphicURL() override; + virtual void SAL_CALL setDefaultExpandedGraphicURL( const OUString& _defaultexpandedgraphicurl ) override; + virtual OUString SAL_CALL getDefaultCollapsedGraphicURL() override; + virtual void SAL_CALL setDefaultCollapsedGraphicURL( const OUString& _defaultcollapsedgraphicurl ) override; + virtual sal_Bool SAL_CALL isNodeExpanded( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isNodeCollapsed( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL makeNodeVisible( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isNodeVisible( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL expandNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL collapseNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL addTreeExpansionListener( const css::uno::Reference< css::awt::tree::XTreeExpansionListener >& Listener ) override; + virtual void SAL_CALL removeTreeExpansionListener( const css::uno::Reference< css::awt::tree::XTreeExpansionListener >& Listener ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getNodeForLocation( ::sal_Int32 x, ::sal_Int32 y ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getClosestNodeForLocation( ::sal_Int32 x, ::sal_Int32 y ) override; + virtual css::awt::Rectangle SAL_CALL getNodeRect( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isEditing( ) override; + virtual sal_Bool SAL_CALL stopEditing( ) override; + virtual void SAL_CALL cancelEditing( ) override; + virtual void SAL_CALL startEditingAtNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL addTreeEditListener( const css::uno::Reference< css::awt::tree::XTreeEditListener >& Listener ) override; + virtual void SAL_CALL removeTreeEditListener( const css::uno::Reference< css::awt::tree::XTreeEditListener >& Listener ) override; + + // css::lang::XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED( UnoTreeControl, UnoControlBase, "com.sun.star.awt.tree.TreeControl" ) + + using UnoControl::getPeer; +private: + TreeSelectionListenerMultiplexer maSelectionListeners; + TreeExpansionListenerMultiplexer maTreeExpansionListeners; + TreeEditListenerMultiplexer maTreeEditListeners; +}; + +UnoTreeControl::UnoTreeControl() +: maSelectionListeners( *this ) +, maTreeExpansionListeners( *this ) +, maTreeEditListeners( *this ) +{ +} + +OUString UnoTreeControl::GetComponentServiceName() const +{ + return "Tree"; +} + + +// css::view::XSelectionSupplier + + +sal_Bool SAL_CALL UnoTreeControl::select( const Any& rSelection ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->select( rSelection ); +} + + +Any SAL_CALL UnoTreeControl::getSelection() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getSelection(); +} + + +void SAL_CALL UnoTreeControl::addSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + maSelectionListeners.addInterface( xListener ); + if( getPeer().is() && (maSelectionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // add it to the peer if this is the first listener added to that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addSelectionChangeListener(&maSelectionListeners); + } +} + + +void SAL_CALL UnoTreeControl::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + if( getPeer().is() && (maSelectionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // remove it from the peer if this is the last listener removed from that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeSelectionChangeListener(&maSelectionListeners); + } + maSelectionListeners.removeInterface( xListener ); +} + + +// css::view::XMultiSelectionSupplier + + +sal_Bool SAL_CALL UnoTreeControl::addSelection( const Any& rSelection ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addSelection(rSelection); +} + + +void SAL_CALL UnoTreeControl::removeSelection( const Any& rSelection ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeSelection(rSelection); +} + + +void SAL_CALL UnoTreeControl::clearSelection() +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->clearSelection(); +} + + +sal_Int32 SAL_CALL UnoTreeControl::getSelectionCount() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getSelectionCount(); +} + + +Reference< XEnumeration > SAL_CALL UnoTreeControl::createSelectionEnumeration() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->createSelectionEnumeration(); +} + + +Reference< XEnumeration > SAL_CALL UnoTreeControl::createReverseSelectionEnumeration() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->createReverseSelectionEnumeration(); +} + + +// XTreeControl + + +OUString SAL_CALL UnoTreeControl::getDefaultExpandedGraphicURL() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getDefaultExpandedGraphicURL(); +} + + +void SAL_CALL UnoTreeControl::setDefaultExpandedGraphicURL( const OUString& _defaultexpansiongraphicurl ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->setDefaultExpandedGraphicURL(_defaultexpansiongraphicurl); +} + + +OUString SAL_CALL UnoTreeControl::getDefaultCollapsedGraphicURL() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getDefaultCollapsedGraphicURL(); +} + + +void SAL_CALL UnoTreeControl::setDefaultCollapsedGraphicURL( const OUString& _defaultcollapsedgraphicurl ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->setDefaultCollapsedGraphicURL(_defaultcollapsedgraphicurl); +} + + +sal_Bool SAL_CALL UnoTreeControl::isNodeExpanded( const Reference< XTreeNode >& xNode ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeExpanded(xNode); +} + + +sal_Bool SAL_CALL UnoTreeControl::isNodeCollapsed( const Reference< XTreeNode >& xNode ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeCollapsed(xNode); +} + + +void SAL_CALL UnoTreeControl::makeNodeVisible( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->makeNodeVisible(xNode); +} + + +sal_Bool SAL_CALL UnoTreeControl::isNodeVisible( const Reference< XTreeNode >& xNode ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeVisible(xNode); +} + + +void SAL_CALL UnoTreeControl::expandNode( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->expandNode(xNode); +} + + +void SAL_CALL UnoTreeControl::collapseNode( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->collapseNode(xNode); +} + + +void SAL_CALL UnoTreeControl::addTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + maTreeExpansionListeners.addInterface( xListener ); + if( getPeer().is() && (maTreeExpansionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // add it to the peer if this is the first listener added to that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addTreeExpansionListener(&maTreeExpansionListeners); + } +} + + +void SAL_CALL UnoTreeControl::removeTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + if( getPeer().is() && (maTreeExpansionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // remove it from the peer if this is the last listener removed from that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeTreeExpansionListener(&maTreeExpansionListeners); + } + maTreeExpansionListeners.removeInterface( xListener ); +} + + +Reference< XTreeNode > SAL_CALL UnoTreeControl::getNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getNodeForLocation(x,y); +} + + +Reference< XTreeNode > SAL_CALL UnoTreeControl::getClosestNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getClosestNodeForLocation(x,y); +} + + +awt::Rectangle SAL_CALL UnoTreeControl::getNodeRect( const Reference< XTreeNode >& Node ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getNodeRect( Node ); +} + + +sal_Bool SAL_CALL UnoTreeControl::isEditing( ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isEditing(); +} + + +sal_Bool SAL_CALL UnoTreeControl::stopEditing() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->stopEditing(); +} + + +void SAL_CALL UnoTreeControl::cancelEditing() +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->cancelEditing(); +} + + +void SAL_CALL UnoTreeControl::startEditingAtNode( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->startEditingAtNode(xNode); +} + + +void SAL_CALL UnoTreeControl::addTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + maTreeEditListeners.addInterface( xListener ); + if( getPeer().is() && (maTreeEditListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // add it to the peer if this is the first listener added to that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addTreeEditListener(&maTreeEditListeners); + } +} + + +void SAL_CALL UnoTreeControl::removeTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + if( getPeer().is() && (maTreeEditListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // remove it from the peer if this is the last listener removed from that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeTreeEditListener(&maTreeEditListeners); + } + maTreeEditListeners.removeInterface( xListener ); +} + + +// XComponent + + +void SAL_CALL UnoTreeControl::dispose( ) +{ + lang::EventObject aEvt; + aEvt.Source = static_cast< ::cppu::OWeakObject* >(this); + maSelectionListeners.disposeAndClear( aEvt ); + maTreeExpansionListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoTreeControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + Reference< XTreeControl > xTree( getPeer(), UNO_QUERY_THROW ); + if( maSelectionListeners.getLength() ) + xTree->addSelectionChangeListener( &maSelectionListeners ); + if( maTreeExpansionListeners.getLength() ) + xTree->addTreeExpansionListener( &maTreeExpansionListeners ); +} + +} + +void SAL_CALL TreeEditListenerMultiplexer::nodeEditing( const Reference< XTreeNode >& Node ) +{ + ::comphelper::OInterfaceIteratorHelper3 aIt(*this); + while( aIt.hasMoreElements() ) + { + Reference<XTreeEditListener> xListener(aIt.next()); + try + { + xListener->nodeEditing( Node ); + } + catch( const DisposedException& e ) + { + OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); + if ( e.Context == xListener || !e.Context.is() ) + aIt.remove(); + } + catch( const RuntimeException& ) + { + DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEditing ) + } + } +} + +void SAL_CALL TreeEditListenerMultiplexer::nodeEdited( const Reference< XTreeNode >& Node, const OUString& NewText ) +{ + ::comphelper::OInterfaceIteratorHelper3 aIt(*this); + while( aIt.hasMoreElements() ) + { + Reference<XTreeEditListener> xListener(aIt.next()); + try + { + xListener->nodeEdited( Node, NewText ); + } + catch( const DisposedException& e ) + { + OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); + if ( e.Context == xListener || !e.Context.is() ) + aIt.remove(); + } + catch( const RuntimeException& ) + { + DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEdited ) + } + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_TreeControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoTreeModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_TreeControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoTreeControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treecontrol.hxx b/toolkit/source/controls/tree/treecontrol.hxx new file mode 100644 index 000000000..af4793fe9 --- /dev/null +++ b/toolkit/source/controls/tree/treecontrol.hxx @@ -0,0 +1,65 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_CONTROLS_TREE_TREECONTROL_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_TREE_TREECONTROL_HXX + +#include <toolkit/controls/unocontrolmodel.hxx> + +namespace toolkit +{ +// = UnoTreeModel + +class UnoTreeModel : public UnoControlModel +{ +protected: + css::uno::Any ImplGetDefaultValue(sal_uInt16 nPropId) const override; + ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + +public: + explicit UnoTreeModel(const css::uno::Reference<css::uno::XComponentContext>& i_factory); + + rtl::Reference<UnoControlModel> Clone() const override; + + // css::beans::XMultiPropertySet + css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { + return "stardiv.Toolkit.TreeControlModel"; + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 1); + s.getArray()[s.getLength() - 1] = "com.sun.star.awt.tree.TreeControlModel"; + return s; + } +}; + +} // toolkit + +#endif // _ INCLUDED_TOOLKIT_SOURCE_CONTROLS_TREE_TREECONTROL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treecontrolpeer.cxx b/toolkit/source/controls/tree/treecontrolpeer.cxx new file mode 100644 index 000000000..fe273e0f6 --- /dev/null +++ b/toolkit/source/controls/tree/treecontrolpeer.cxx @@ -0,0 +1,1579 @@ +/* -*- 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 <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <com/sun/star/util/VetoException.hpp> +#include <o3tl/any.hxx> +#include <toolkit/helper/property.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <com/sun/star/awt/tree/XMutableTreeNode.hpp> +#include <controls/treecontrolpeer.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> + +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolkit/treelistbox.hxx> +#include <vcl/toolkit/treelistentry.hxx> +#include <vcl/toolkit/viewdataentry.hxx> +#include <vcl/toolkit/svlbitm.hxx> + +#include <map> +#include <memory> +#include <list> + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::awt::tree; +using namespace css::beans; +using namespace css::view; +using namespace css::container; +using namespace css::util; +using namespace css::graphic; + +namespace { + +struct LockGuard +{ +public: + explicit LockGuard( sal_Int32& rLock ) + : mrLock( rLock ) + { + rLock++; + } + + ~LockGuard() + { + mrLock--; + } + + sal_Int32& mrLock; +}; + + +class ImplContextGraphicItem : public SvLBoxContextBmp +{ +public: + ImplContextGraphicItem( Image const & rI1, Image const & rI2, bool bExpanded) + : SvLBoxContextBmp(rI1, rI2, bExpanded) {} + + OUString msExpandedGraphicURL; + OUString msCollapsedGraphicURL; +}; + + +} + +class UnoTreeListBoxImpl : public SvTreeListBox +{ +public: + UnoTreeListBoxImpl( TreeControlPeer* pPeer, vcl::Window* pParent, WinBits nWinStyle ); + virtual ~UnoTreeListBoxImpl() override; + virtual void dispose() override; + + void insert( SvTreeListEntry* pEntry, SvTreeListEntry* pParent, sal_uLong nPos ); + + virtual void RequestingChildren( SvTreeListEntry* pParent ) override; + + virtual bool EditingEntry( SvTreeListEntry* pEntry ) override; + virtual bool EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText ) override; + + DECL_LINK(OnSelectionChangeHdl, SvTreeListBox*, void); + DECL_LINK(OnExpandingHdl, SvTreeListBox*, bool); + DECL_LINK(OnExpandedHdl, SvTreeListBox*, void); + +private: + rtl::Reference< TreeControlPeer > mxPeer; +}; + + +namespace { + +class UnoTreeListItem : public SvLBoxString +{ +public: + UnoTreeListItem(); + + void InitViewData( SvTreeListBox*,SvTreeListEntry*,SvViewDataItem * = nullptr ) override; + void SetImage( const Image& rImage ); + const OUString& GetGraphicURL() const { return maGraphicURL;} + void SetGraphicURL( const OUString& rGraphicURL ); + virtual void Paint(const Point& rPos, SvTreeListBox& rOutDev, vcl::RenderContext& rRenderContext, + const SvViewDataEntry* pView, const SvTreeListEntry& rEntry) override; + std::unique_ptr<SvLBoxItem> Clone( SvLBoxItem const * pSource ) const override; + +private: + OUString maGraphicURL; + Image maImage; +}; + +} + +class UnoTreeListEntry : public SvTreeListEntry +{ +public: + UnoTreeListEntry( const Reference< XTreeNode >& xNode, TreeControlPeer* pPeer ); + virtual ~UnoTreeListEntry() override; + + Reference< XTreeNode > mxNode; + TreeControlPeer* mpPeer; +}; + +TreeControlPeer::TreeControlPeer() + : maSelectionListeners( *this ) + , maTreeExpansionListeners( *this ) + , maTreeEditListeners( *this ) + , mbIsRootDisplayed(false) + , mpTreeImpl( nullptr ) + , mnEditLock( 0 ) +{ +} + + +TreeControlPeer::~TreeControlPeer() +{ + if( mpTreeImpl ) + mpTreeImpl->Clear(); +} + + +void TreeControlPeer::addEntry( UnoTreeListEntry* pEntry ) +{ + if( pEntry && pEntry->mxNode.is() ) + { + if( !mpTreeNodeMap ) + { + mpTreeNodeMap.reset( new TreeNodeMap ); + } + + (*mpTreeNodeMap)[ pEntry->mxNode ] = pEntry; + } +} + + +void TreeControlPeer::removeEntry( UnoTreeListEntry const * pEntry ) +{ + if( mpTreeNodeMap && pEntry && pEntry->mxNode.is() ) + { + TreeNodeMap::iterator aIter( mpTreeNodeMap->find( pEntry->mxNode ) ); + if( aIter != mpTreeNodeMap->end() ) + { + mpTreeNodeMap->erase( aIter ); + } + } +} + + +UnoTreeListEntry* TreeControlPeer::getEntry( const Reference< XTreeNode >& xNode, bool bThrow /* = true */ ) +{ + if( mpTreeNodeMap ) + { + TreeNodeMap::iterator aIter( mpTreeNodeMap->find( xNode ) ); + if( aIter != mpTreeNodeMap->end() ) + return (*aIter).second; + } + + if( bThrow ) + throw IllegalArgumentException(); + + return nullptr; +} + + +vcl::Window* TreeControlPeer::createVclControl( vcl::Window* pParent, sal_Int64 nWinStyle ) +{ + mpTreeImpl = VclPtr<UnoTreeListBoxImpl>::Create( this, pParent, nWinStyle ); + return mpTreeImpl; +} + + +/** called from the UnoTreeListBoxImpl when it gets deleted */ +void TreeControlPeer::disposeControl() +{ + mpTreeNodeMap.reset(); + mpTreeImpl = nullptr; +} + + +UnoTreeListEntry* TreeControlPeer::createEntry( const Reference< XTreeNode >& xNode, UnoTreeListEntry* pParent, sal_uLong nPos /* = TREELIST_APPEND */ ) +{ + UnoTreeListEntry* pEntry = nullptr; + if( mpTreeImpl ) + { + Image aImage; + pEntry = new UnoTreeListEntry( xNode, this ); + pEntry->AddItem(std::make_unique<ImplContextGraphicItem>(aImage, aImage, true)); + + std::unique_ptr<UnoTreeListItem> pUnoItem(new UnoTreeListItem); + + if( !xNode->getNodeGraphicURL().isEmpty() ) + { + pUnoItem->SetGraphicURL( xNode->getNodeGraphicURL() ); + Image aNodeImage; + loadImage( xNode->getNodeGraphicURL(), aNodeImage ); + pUnoItem->SetImage( aNodeImage ); + mpTreeImpl->AdjustEntryHeight( aNodeImage ); + } + + pEntry->AddItem(std::move(pUnoItem)); + + mpTreeImpl->insert( pEntry, pParent, nPos ); + + if( !msDefaultExpandedGraphicURL.isEmpty() ) + mpTreeImpl->SetExpandedEntryBmp( pEntry, maDefaultExpandedImage ); + + if( !msDefaultCollapsedGraphicURL.isEmpty() ) + mpTreeImpl->SetCollapsedEntryBmp( pEntry, maDefaultCollapsedImage ); + + updateEntry( pEntry ); + } + return pEntry; +} + + +void TreeControlPeer::updateEntry( UnoTreeListEntry* pEntry ) +{ + bool bChanged = false; + if( !(pEntry && pEntry->mxNode.is() && mpTreeImpl) ) + return; + + const OUString aValue( getEntryString( pEntry->mxNode->getDisplayValue() ) ); + UnoTreeListItem* pUnoItem = dynamic_cast< UnoTreeListItem* >( &pEntry->GetItem( 1 ) ); + if( pUnoItem ) + { + if( aValue != pUnoItem->GetText() ) + { + pUnoItem->SetText( aValue ); + bChanged = true; + } + + if( pUnoItem->GetGraphicURL() != pEntry->mxNode->getNodeGraphicURL() ) + { + Image aImage; + if( loadImage( pEntry->mxNode->getNodeGraphicURL(), aImage ) ) + { + pUnoItem->SetGraphicURL( pEntry->mxNode->getNodeGraphicURL() ); + pUnoItem->SetImage( aImage ); + mpTreeImpl->AdjustEntryHeight( aImage ); + bChanged = true; + } + } + } + + if( bool(pEntry->mxNode->hasChildrenOnDemand()) != pEntry->HasChildrenOnDemand() ) + { + pEntry->EnableChildrenOnDemand( pEntry->mxNode->hasChildrenOnDemand() ); + bChanged = true; + } + + ImplContextGraphicItem* pContextGraphicItem = dynamic_cast< ImplContextGraphicItem* >( &pEntry->GetItem( 0 ) ); + if( pContextGraphicItem ) + { + if( pContextGraphicItem->msExpandedGraphicURL != pEntry->mxNode->getExpandedGraphicURL() ) + { + Image aImage; + if( loadImage( pEntry->mxNode->getExpandedGraphicURL(), aImage ) ) + { + pContextGraphicItem->msExpandedGraphicURL = pEntry->mxNode->getExpandedGraphicURL(); + mpTreeImpl->SetExpandedEntryBmp( pEntry, aImage ); + bChanged = true; + } + } + if( pContextGraphicItem->msCollapsedGraphicURL != pEntry->mxNode->getCollapsedGraphicURL() ) + { + Image aImage; + if( loadImage( pEntry->mxNode->getCollapsedGraphicURL(), aImage ) ) + { + pContextGraphicItem->msCollapsedGraphicURL = pEntry->mxNode->getCollapsedGraphicURL(); + mpTreeImpl->SetCollapsedEntryBmp( pEntry, aImage ); + bChanged = true; + } + } + } + + if( bChanged ) + mpTreeImpl->GetModel()->InvalidateEntry( pEntry ); +} + + +void TreeControlPeer::onSelectionChanged() +{ + Reference< XInterface > xSource( static_cast< ::cppu::OWeakObject* >( this ) ); + EventObject aEvent( xSource ); + maSelectionListeners.selectionChanged( aEvent ); +} + + +void TreeControlPeer::onRequestChildNodes( const Reference< XTreeNode >& xNode ) +{ + try + { + Reference< XInterface > xSource( static_cast< ::cppu::OWeakObject* >( this ) ); + TreeExpansionEvent aEvent( xSource, xNode ); + maTreeExpansionListeners.requestChildNodes( aEvent ); + } + catch( Exception& ) + { + } +} + + +bool TreeControlPeer::onExpanding( const Reference< XTreeNode >& xNode, bool bExpanding ) +{ + try + { + Reference< XInterface > xSource( static_cast< ::cppu::OWeakObject* >( this ) ); + TreeExpansionEvent aEvent( xSource, xNode ); + if( bExpanding ) + { + maTreeExpansionListeners.treeExpanding( aEvent ); + } + else + { + maTreeExpansionListeners.treeCollapsing( aEvent ); + } + } + catch( Exception& ) + { + return false; + } + return true; +} + + +void TreeControlPeer::onExpanded( const Reference< XTreeNode >& xNode, bool bExpanding ) +{ + try + { + Reference< XInterface > xSource( static_cast< ::cppu::OWeakObject* >( this ) ); + TreeExpansionEvent aEvent( xSource, xNode ); + + if( bExpanding ) + { + maTreeExpansionListeners.treeExpanded( aEvent ); + } + else + { + maTreeExpansionListeners.treeCollapsed( aEvent ); + } + } + catch( Exception& ) + { + } +} + + +void TreeControlPeer::fillTree( UnoTreeListBoxImpl& rTree, const Reference< XTreeDataModel >& xDataModel ) +{ + rTree.Clear(); + + if( !xDataModel.is() ) + return; + + Reference< XTreeNode > xRootNode( xDataModel->getRoot() ); + if( !xRootNode.is() ) + return; + + if( mbIsRootDisplayed ) + { + addNode( rTree, xRootNode, nullptr ); + } + else + { + const sal_Int32 nChildCount = xRootNode->getChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; nChild++ ) + addNode( rTree, xRootNode->getChildAt( nChild ), nullptr ); + } +} + + +void TreeControlPeer::addNode( UnoTreeListBoxImpl& rTree, const Reference< XTreeNode >& xNode, UnoTreeListEntry* pParentEntry ) +{ + if( xNode.is() ) + { + UnoTreeListEntry* pEntry = createEntry( xNode, pParentEntry, TREELIST_APPEND ); + const sal_Int32 nChildCount = xNode->getChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; nChild++ ) + addNode( rTree, xNode->getChildAt( nChild ), pEntry ); + } +} + + +UnoTreeListBoxImpl& TreeControlPeer::getTreeListBoxOrThrow() const +{ + if( !mpTreeImpl ) + throw DisposedException(); + return *mpTreeImpl; +} + + +void TreeControlPeer::ChangeNodesSelection( const Any& rSelection, bool bSelect, bool bSetSelection ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Reference< XTreeNode > xTempNode; + + Sequence<Reference<XTreeNode>> pNodes; + sal_Int32 nCount = 0; + + if( rSelection.hasValue() ) + { + switch( rSelection.getValueTypeClass() ) + { + case TypeClass_INTERFACE: + { + rSelection >>= xTempNode; + if( xTempNode.is() ) + { + nCount = 1; + pNodes = {xTempNode}; + } + break; + } + case TypeClass_SEQUENCE: + { + if( auto rSeq = o3tl::tryAccess<Sequence<Reference<XTreeNode>>>( + rSelection) ) + { + nCount = rSeq->getLength(); + pNodes = *rSeq; + } + break; + } + default: + break; + } + + if( nCount == 0 ) + throw IllegalArgumentException(); + } + + if( bSetSelection ) + rTree.SelectAll( false ); + + for( sal_Int32 i = 0; i != nCount; ++i ) + { + UnoTreeListEntry* pEntry = getEntry( pNodes[i] ); + rTree.Select( pEntry, bSelect ); + } +} + + +// css::view::XSelectionSupplier + + +sal_Bool SAL_CALL TreeControlPeer::select( const Any& rSelection ) +{ + SolarMutexGuard aGuard; + ChangeNodesSelection( rSelection, true, true ); + return true; +} + + +Any SAL_CALL TreeControlPeer::getSelection() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Any aRet; + + sal_uLong nSelectionCount = rTree.GetSelectionCount(); + if( nSelectionCount == 1 ) + { + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + if( pEntry && pEntry->mxNode.is() ) + aRet <<= pEntry->mxNode; + } + else if( nSelectionCount > 1 ) + { + Sequence< Reference< XTreeNode > > aSelection( nSelectionCount ); + Reference< XTreeNode >* pNodes = aSelection.getArray(); + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + while( pEntry && nSelectionCount ) + { + *pNodes++ = pEntry->mxNode; + pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.NextSelected( pEntry ) ); + --nSelectionCount; + } + + OSL_ASSERT( (pEntry == nullptr) && (nSelectionCount == 0) ); + aRet <<= aSelection; + } + + return aRet; +} + + +void SAL_CALL TreeControlPeer::addSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + maSelectionListeners.addInterface( xListener ); +} + + +void SAL_CALL TreeControlPeer::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + maSelectionListeners.addInterface( xListener ); +} + + +// css::view::XMultiSelectionSupplier + + +sal_Bool SAL_CALL TreeControlPeer::addSelection( const Any& rSelection ) +{ + ChangeNodesSelection( rSelection, true, false ); + return true; +} + + +void SAL_CALL TreeControlPeer::removeSelection( const Any& rSelection ) +{ + ChangeNodesSelection( rSelection, false, false ); +} + + +void SAL_CALL TreeControlPeer::clearSelection() +{ + SolarMutexGuard aGuard; + getTreeListBoxOrThrow().SelectAll( false ); +} + + +sal_Int32 SAL_CALL TreeControlPeer::getSelectionCount() +{ + SolarMutexGuard aGuard; + return getTreeListBoxOrThrow().GetSelectionCount(); +} + +namespace { + +class TreeSelectionEnumeration : public ::cppu::WeakImplHelper< XEnumeration > +{ +public: + explicit TreeSelectionEnumeration( std::list< Any >& rSelection ); + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual Any SAL_CALL nextElement() override; + + std::list< Any > maSelection; + std::list< Any >::iterator maIter; +}; + +} + +TreeSelectionEnumeration::TreeSelectionEnumeration( std::list< Any >& rSelection ) +{ + maSelection.swap( rSelection ); + maIter = maSelection.begin(); +} + + +sal_Bool SAL_CALL TreeSelectionEnumeration::hasMoreElements() +{ + return maIter != maSelection.end(); +} + + +Any SAL_CALL TreeSelectionEnumeration::nextElement() +{ + if( maIter == maSelection.end() ) + throw NoSuchElementException(); + + return (*maIter++); +} + + +Reference< XEnumeration > SAL_CALL TreeControlPeer::createSelectionEnumeration() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + sal_uInt32 nSelectionCount = rTree.GetSelectionCount(); + std::list< Any > aSelection( nSelectionCount ); + + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + while( pEntry && nSelectionCount ) + { + aSelection.emplace_back( pEntry->mxNode ); + pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.NextSelected( pEntry ) ); + --nSelectionCount; + } + + OSL_ASSERT( (pEntry == nullptr) && (nSelectionCount == 0) ); + + return Reference< XEnumeration >( new TreeSelectionEnumeration( aSelection ) ); +} + + +Reference< XEnumeration > SAL_CALL TreeControlPeer::createReverseSelectionEnumeration() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + sal_uInt32 nSelectionCount = rTree.GetSelectionCount(); + std::list< Any > aSelection; + + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + while( pEntry && nSelectionCount ) + { + aSelection.push_front( Any( pEntry->mxNode ) ); + pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.NextSelected( pEntry ) ); + --nSelectionCount; + } + + OSL_ASSERT( (pEntry == nullptr) && (nSelectionCount == 0) ); + + return Reference< XEnumeration >( new TreeSelectionEnumeration( aSelection ) ); +} + + +// css::awt::XTreeControl + + +OUString SAL_CALL TreeControlPeer::getDefaultExpandedGraphicURL() +{ + SolarMutexGuard aGuard; + return msDefaultExpandedGraphicURL; +} + + +void SAL_CALL TreeControlPeer::setDefaultExpandedGraphicURL( const OUString& sDefaultExpandedGraphicURL ) +{ + SolarMutexGuard aGuard; + if( msDefaultExpandedGraphicURL == sDefaultExpandedGraphicURL ) + return; + + if( !sDefaultExpandedGraphicURL.isEmpty() ) + loadImage( sDefaultExpandedGraphicURL, maDefaultExpandedImage ); + else + maDefaultExpandedImage = Image(); + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + SvTreeListEntry* pEntry = rTree.First(); + while( pEntry ) + { + ImplContextGraphicItem* pContextGraphicItem = dynamic_cast< ImplContextGraphicItem* >( &pEntry->GetItem( 0 ) ); + if( pContextGraphicItem ) + { + if( pContextGraphicItem->msExpandedGraphicURL.isEmpty() ) + rTree.SetExpandedEntryBmp( pEntry, maDefaultExpandedImage ); + } + pEntry = rTree.Next( pEntry ); + } + + msDefaultExpandedGraphicURL = sDefaultExpandedGraphicURL; +} + + +OUString SAL_CALL TreeControlPeer::getDefaultCollapsedGraphicURL() +{ + SolarMutexGuard aGuard; + return msDefaultCollapsedGraphicURL; +} + + +void SAL_CALL TreeControlPeer::setDefaultCollapsedGraphicURL( const OUString& sDefaultCollapsedGraphicURL ) +{ + SolarMutexGuard aGuard; + if( msDefaultCollapsedGraphicURL == sDefaultCollapsedGraphicURL ) + return; + + if( !sDefaultCollapsedGraphicURL.isEmpty() ) + loadImage( sDefaultCollapsedGraphicURL, maDefaultCollapsedImage ); + else + maDefaultCollapsedImage = Image(); + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + SvTreeListEntry* pEntry = rTree.First(); + while( pEntry ) + { + ImplContextGraphicItem* pContextGraphicItem = dynamic_cast< ImplContextGraphicItem* >( &pEntry->GetItem( 0 ) ); + if( pContextGraphicItem ) + { + if( pContextGraphicItem->msCollapsedGraphicURL.isEmpty() ) + rTree.SetCollapsedEntryBmp( pEntry, maDefaultCollapsedImage ); + } + pEntry = rTree.Next( pEntry ); + } + + msDefaultCollapsedGraphicURL = sDefaultCollapsedGraphicURL; +} + + +sal_Bool SAL_CALL TreeControlPeer::isNodeExpanded( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + return pEntry && rTree.IsExpanded( pEntry ); +} + + +sal_Bool SAL_CALL TreeControlPeer::isNodeCollapsed( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + return !isNodeExpanded( xNode ); +} + + +void SAL_CALL TreeControlPeer::makeNodeVisible( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + if( pEntry ) + rTree.MakeVisible( pEntry ); +} + + +sal_Bool SAL_CALL TreeControlPeer::isNodeVisible( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + return pEntry && rTree.IsEntryVisible( pEntry ); +} + + +void SAL_CALL TreeControlPeer::expandNode( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + if( pEntry ) + rTree.Expand( pEntry ); +} + + +void SAL_CALL TreeControlPeer::collapseNode( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + if( pEntry ) + rTree.Collapse( pEntry ); +} + + +void SAL_CALL TreeControlPeer::addTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + maTreeExpansionListeners.addInterface( xListener ); +} + + +void SAL_CALL TreeControlPeer::removeTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + maTreeExpansionListeners.removeInterface( xListener ); +} + + +Reference< XTreeNode > SAL_CALL TreeControlPeer::getNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Reference< XTreeNode > xNode; + + const Point aPos( x, y ); + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.GetEntry( aPos, true ) ); + if( pEntry ) + xNode = pEntry->mxNode; + + return xNode; +} + + +Reference< XTreeNode > SAL_CALL TreeControlPeer::getClosestNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Reference< XTreeNode > xNode; + + const Point aPos( x, y ); + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.GetEntry( aPos, true ) ); + if( pEntry ) + xNode = pEntry->mxNode; + + return xNode; +} + + +awt::Rectangle SAL_CALL TreeControlPeer::getNodeRect( const Reference< XTreeNode >& i_Node ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( i_Node ); + + ::tools::Rectangle aEntryRect( rTree.GetFocusRect( pEntry, rTree.GetEntryPosition( pEntry ).Y() ) ); + return VCLUnoHelper::ConvertToAWTRect( aEntryRect ); +} + + +sal_Bool SAL_CALL TreeControlPeer::isEditing( ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + return rTree.IsEditingActive(); +} + + +sal_Bool SAL_CALL TreeControlPeer::stopEditing() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + if( rTree.IsEditingActive() ) + { + rTree.EndEditing(); + return true; + } + else + { + return false; + } +} + + +void SAL_CALL TreeControlPeer::cancelEditing( ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + rTree.EndEditing(); +} + + +void SAL_CALL TreeControlPeer::startEditingAtNode( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + rTree.EditEntry( pEntry ); +} + +void SAL_CALL TreeControlPeer::addTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + maTreeEditListeners.addInterface( xListener ); +} + +void SAL_CALL TreeControlPeer::removeTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + maTreeEditListeners.removeInterface( xListener ); +} + +bool TreeControlPeer::onEditingEntry( UnoTreeListEntry const * pEntry ) +{ + if( mpTreeImpl && pEntry && pEntry->mxNode.is() && (maTreeEditListeners.getLength() > 0) ) + { + try + { + maTreeEditListeners.nodeEditing( pEntry->mxNode ); + } + catch( VetoException& ) + { + return false; + } + catch( Exception& ) + { + } + } + return true; +} + +bool TreeControlPeer::onEditedEntry( UnoTreeListEntry const * pEntry, const OUString& rNewText ) +{ + if( mpTreeImpl && pEntry && pEntry->mxNode.is() ) try + { + LockGuard aLockGuard( mnEditLock ); + if( maTreeEditListeners.getLength() > 0 ) + { + maTreeEditListeners.nodeEdited( pEntry->mxNode, rNewText ); + return false; + } + else + { + Reference< XMutableTreeNode > xMutableNode( pEntry->mxNode, UNO_QUERY ); + if( xMutableNode.is() ) + xMutableNode->setDisplayValue( Any( rNewText ) ); + else + return false; + } + + } + catch( Exception& ) + { + } + + return true; +} + + +// css::awt::tree::TreeDataModelListener + + +void SAL_CALL TreeControlPeer::treeNodesChanged( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void SAL_CALL TreeControlPeer::treeNodesInserted( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void SAL_CALL TreeControlPeer::treeNodesRemoved( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void SAL_CALL TreeControlPeer::treeStructureChanged( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void TreeControlPeer::updateTree( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Sequence< Reference< XTreeNode > > Nodes; + Reference< XTreeNode > xNode( rEvent.ParentNode ); + if( !xNode.is() && Nodes.hasElements() ) + { + xNode = Nodes[0]; + } + + if( xNode.is() ) + updateNode( rTree, xNode ); +} + +void TreeControlPeer::updateNode( UnoTreeListBoxImpl const & rTree, const Reference< XTreeNode >& xNode ) +{ + if( !xNode.is() ) + return; + + UnoTreeListEntry* pNodeEntry = getEntry( xNode, false ); + + if( !pNodeEntry ) + { + Reference< XTreeNode > xParentNode( xNode->getParent() ); + UnoTreeListEntry* pParentEntry = nullptr; + sal_uLong nChild = TREELIST_APPEND; + + if( xParentNode.is() ) + { + pParentEntry = getEntry( xParentNode ); + nChild = xParentNode->getIndex( xNode ); + } + + pNodeEntry = createEntry( xNode, pParentEntry, nChild ); + } + + updateChildNodes( rTree, xNode, pNodeEntry ); +} + +void TreeControlPeer::updateChildNodes( UnoTreeListBoxImpl const & rTree, const Reference< XTreeNode >& xParentNode, UnoTreeListEntry* pParentEntry ) +{ + if( !(xParentNode.is() && pParentEntry) ) + return; + + UnoTreeListEntry* pCurrentChild = dynamic_cast< UnoTreeListEntry* >( rTree.FirstChild( pParentEntry ) ); + + const sal_Int32 nChildCount = xParentNode->getChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; nChild++ ) + { + Reference< XTreeNode > xNode( xParentNode->getChildAt( nChild ) ); + if( !pCurrentChild || ( pCurrentChild->mxNode != xNode ) ) + { + UnoTreeListEntry* pNodeEntry = getEntry( xNode, false ); + if( pNodeEntry == nullptr ) + { + // child node is not yet part of the tree, add it + pCurrentChild = createEntry( xNode, pParentEntry, nChild ); + } + else if( pNodeEntry != pCurrentChild ) + { + // node is already part of the tree, but not on the correct position + rTree.GetModel()->Move( pNodeEntry, pParentEntry, nChild ); + pCurrentChild = pNodeEntry; + updateEntry( pCurrentChild ); + } + } + else + { + // child node has entry and entry is equal to current entry, + // so no structural changes happened + updateEntry( pCurrentChild ); + } + + pCurrentChild = dynamic_cast< UnoTreeListEntry* >( pCurrentChild->NextSibling() ); + } + + // check if we have entries without nodes left, we need to remove them + while( pCurrentChild ) + { + UnoTreeListEntry* pNextChild = dynamic_cast< UnoTreeListEntry* >( pCurrentChild->NextSibling() ); + rTree.GetModel()->Remove( pCurrentChild ); + pCurrentChild = pNextChild; + } +} + +OUString TreeControlPeer::getEntryString( const Any& rValue ) +{ + OUString sValue; + if( rValue.hasValue() ) + { + switch( rValue.getValueTypeClass() ) + { + case TypeClass_SHORT: + case TypeClass_LONG: + { + sal_Int32 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_BYTE: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_HYPER: + { + sal_Int64 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_UNSIGNED_HYPER: + { + sal_uInt64 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + { + double fValue = 0.0; + if( rValue >>= fValue ) + sValue = OUString::number( fValue ); + break; + } + case TypeClass_STRING: + rValue >>= sValue; + break; + /* + case TypeClass_INTERFACE: + // @todo + break; + case TypeClass_SEQUENCE: + { + Sequence< Any > aValues; + if( aValue >>= aValues ) + { + updateEntry( SvTreeListEntry& rEntry, aValues ); + return; + } + } + break; + */ + default: + break; + } + } + return sValue; +} + +// XEventListener +void SAL_CALL TreeControlPeer::disposing( const css::lang::EventObject& ) +{ + // model is disposed, so we clear our tree + SolarMutexGuard aGuard; + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + rTree.Clear(); + mxDataModel.clear(); +} + +void TreeControlPeer::onChangeDataModel( UnoTreeListBoxImpl& rTree, const Reference< XTreeDataModel >& xDataModel ) +{ + if( xDataModel.is() && (mxDataModel == xDataModel) ) + return; // do nothing + + Reference< XTreeDataModelListener > xListener( this ); + + if( mxDataModel.is() ) + mxDataModel->removeTreeDataModelListener( xListener ); + + mxDataModel = xDataModel; + + fillTree( rTree, mxDataModel ); + + if( mxDataModel.is() ) + mxDataModel->addTreeDataModelListener( xListener ); +} + + +// css::awt::XLayoutConstrains + + +css::awt::Size TreeControlPeer::getMinimumSize() +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; +/* todo + MultiLineEdit* pEdit = (MultiLineEdit*) GetWindow(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcMinimumSize()); +*/ + return aSz; +} + +css::awt::Size TreeControlPeer::getPreferredSize() +{ + return getMinimumSize(); +} + +css::awt::Size TreeControlPeer::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; +/* todo + MultiLineEdit* pEdit = (MultiLineEdit*) GetWindow(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcAdjustedSize( VCLSize(rNewSize ))); +*/ + return aSz; +} + + +// css::awt::XVclWindowPeer + + +void TreeControlPeer::setProperty( const OUString& PropertyName, const Any& aValue) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + switch( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + { + bool bEnabled = false; + if ( aValue >>= bEnabled ) + { + WinBits nStyle = rTree.GetStyle(); + if ( bEnabled ) + nStyle |= WB_HIDESELECTION; + else + nStyle &= ~WB_HIDESELECTION; + rTree.SetStyle( nStyle ); + } + } + break; + + case BASEPROPERTY_TREE_SELECTIONTYPE: + { + SelectionType eSelectionType; + if( aValue >>= eSelectionType ) + { + SelectionMode eSelMode; + switch( eSelectionType ) + { + case SelectionType_SINGLE: eSelMode = SelectionMode::Single; break; + case SelectionType_RANGE: eSelMode = SelectionMode::Range; break; + case SelectionType_MULTI: eSelMode = SelectionMode::Multiple; break; + // case SelectionType_NONE: + default: eSelMode = SelectionMode::NONE; break; + } + if( rTree.GetSelectionMode() != eSelMode ) + rTree.SetSelectionMode( eSelMode ); + } + break; + } + + case BASEPROPERTY_TREE_DATAMODEL: + onChangeDataModel( rTree, Reference< XTreeDataModel >( aValue, UNO_QUERY ) ); + break; + case BASEPROPERTY_ROW_HEIGHT: + { + sal_Int32 nHeight = 0; + if( aValue >>= nHeight ) + rTree.SetEntryHeight( static_cast<short>(nHeight) ); + break; + } + case BASEPROPERTY_TREE_EDITABLE: + { + bool bEnabled = false; + if( aValue >>= bEnabled ) + rTree.EnableInplaceEditing( bEnabled ); + break; + } + case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: + break; // @todo + case BASEPROPERTY_TREE_ROOTDISPLAYED: + { + bool bDisplayed = false; + if( (aValue >>= bDisplayed) && ( bDisplayed != mbIsRootDisplayed) ) + { + onChangeRootDisplayed(bDisplayed); + } + break; + } + case BASEPROPERTY_TREE_SHOWSHANDLES: + { + bool bEnabled = false; + if( aValue >>= bEnabled ) + { + WinBits nBits = rTree.GetStyle() & (~WB_HASLINES); + if( bEnabled ) + nBits |= WB_HASLINES; + if( nBits != rTree.GetStyle() ) + rTree.SetStyle( nBits ); + } + break; + } + case BASEPROPERTY_TREE_SHOWSROOTHANDLES: + { + bool bEnabled = false; + if( aValue >>= bEnabled ) + { + WinBits nBits = rTree.GetStyle() & (~WB_HASLINESATROOT); + if( bEnabled ) + nBits |= WB_HASLINESATROOT; + if( nBits != rTree.GetStyle() ) + rTree.SetStyle( nBits ); + } + break; + } + default: + VCLXWindow::setProperty( PropertyName, aValue ); + break; + } +} + +Any TreeControlPeer::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + const sal_uInt16 nPropId = GetPropertyId( PropertyName ); + if( (nPropId >= BASEPROPERTY_TREE_START) && (nPropId <= BASEPROPERTY_TREE_END) ) + { + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + switch(nPropId) + { + case BASEPROPERTY_TREE_SELECTIONTYPE: + { + SelectionType eSelectionType; + + SelectionMode eSelMode = rTree.GetSelectionMode(); + switch( eSelMode ) + { + case SelectionMode::Single: eSelectionType = SelectionType_SINGLE; break; + case SelectionMode::Range: eSelectionType = SelectionType_RANGE; break; + case SelectionMode::Multiple:eSelectionType = SelectionType_MULTI; break; +// case SelectionMode::NONE: + default: eSelectionType = SelectionType_NONE; break; + } + return Any( eSelectionType ); + } + case BASEPROPERTY_ROW_HEIGHT: + return Any( static_cast<sal_Int32>(rTree.GetEntryHeight()) ); + case BASEPROPERTY_TREE_DATAMODEL: + return Any( mxDataModel ); + case BASEPROPERTY_TREE_EDITABLE: + return Any( rTree.IsInplaceEditingEnabled() ); + case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: + return Any( true ); // @todo + case BASEPROPERTY_TREE_ROOTDISPLAYED: + return Any( mbIsRootDisplayed ); + case BASEPROPERTY_TREE_SHOWSHANDLES: + return Any( (rTree.GetStyle() & WB_HASLINES) != 0 ); + case BASEPROPERTY_TREE_SHOWSROOTHANDLES: + return Any( (rTree.GetStyle() & WB_HASLINESATROOT) != 0 ); + } + } + return VCLXWindow::getProperty( PropertyName ); +} + +void TreeControlPeer::onChangeRootDisplayed( bool bIsRootDisplayed ) +{ + if( mbIsRootDisplayed == bIsRootDisplayed ) + return; + + mbIsRootDisplayed = bIsRootDisplayed; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + if( rTree.GetEntryCount() == 0 ) + return; + + // todo + fillTree( rTree, mxDataModel ); +} + +bool TreeControlPeer::loadImage( const OUString& rURL, Image& rImage ) +{ + if( !mxGraphicProvider.is() ) + { + mxGraphicProvider = graphic::GraphicProvider::create( + comphelper::getProcessComponentContext()); + } + + try + { + css::beans::PropertyValues aProps{ comphelper::makePropertyValue("URL", rURL) }; + Reference< XGraphic > xGraphic( mxGraphicProvider->queryGraphic( aProps ) ); + + Graphic aGraphic( xGraphic ); + rImage = Image(aGraphic.GetBitmapEx()); + return true; + } + catch( Exception& ) + { + } + + return false; +} + + + + +UnoTreeListBoxImpl::UnoTreeListBoxImpl( TreeControlPeer* pPeer, vcl::Window* pParent, WinBits nWinStyle ) +: SvTreeListBox( pParent, nWinStyle ) +, mxPeer( pPeer ) +{ + SetStyle( WB_BORDER | WB_HASLINES |WB_HASBUTTONS | WB_HASLINESATROOT | WB_HASBUTTONSATROOT | WB_HSCROLL ); + SetNodeDefaultImages(); + SetSelectHdl( LINK(this, UnoTreeListBoxImpl, OnSelectionChangeHdl) ); + SetDeselectHdl( LINK(this, UnoTreeListBoxImpl, OnSelectionChangeHdl) ); + + SetExpandingHdl( LINK(this, UnoTreeListBoxImpl, OnExpandingHdl) ); + SetExpandedHdl( LINK(this, UnoTreeListBoxImpl, OnExpandedHdl) ); + +} + + +UnoTreeListBoxImpl::~UnoTreeListBoxImpl() +{ + disposeOnce(); +} + +void UnoTreeListBoxImpl::dispose() +{ + if( mxPeer.is() ) + mxPeer->disposeControl(); + mxPeer.clear(); + SvTreeListBox::dispose(); +} + + +IMPL_LINK_NOARG(UnoTreeListBoxImpl, OnSelectionChangeHdl, SvTreeListBox*, void) +{ + if( mxPeer.is() ) + mxPeer->onSelectionChanged(); +} + + +IMPL_LINK_NOARG(UnoTreeListBoxImpl, OnExpandingHdl, SvTreeListBox*, bool) +{ + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( GetHdlEntry() ); + + if( pEntry && mxPeer.is() ) + { + return mxPeer->onExpanding( pEntry->mxNode, !IsExpanded( pEntry ) ); + } + return false; +} + + +IMPL_LINK_NOARG(UnoTreeListBoxImpl, OnExpandedHdl, SvTreeListBox*, void) +{ + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( GetHdlEntry() ); + if( pEntry && mxPeer.is() ) + { + mxPeer->onExpanded( pEntry->mxNode, IsExpanded( pEntry ) ); + } +} + + +void UnoTreeListBoxImpl::insert( SvTreeListEntry* pEntry,SvTreeListEntry* pParent,sal_uLong nPos ) +{ + if( pParent ) + SvTreeListBox::Insert( pEntry, pParent, nPos ); + else + SvTreeListBox::Insert( pEntry, nPos ); +} + + +void UnoTreeListBoxImpl::RequestingChildren( SvTreeListEntry* pParent ) +{ + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( pParent ); + if( pEntry && pEntry->mxNode.is() && mxPeer.is() ) + mxPeer->onRequestChildNodes( pEntry->mxNode ); +} + + +bool UnoTreeListBoxImpl::EditingEntry( SvTreeListEntry* pEntry ) +{ + return mxPeer.is() && mxPeer->onEditingEntry( dynamic_cast< UnoTreeListEntry* >( pEntry ) ); +} + + +bool UnoTreeListBoxImpl::EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText ) +{ + return mxPeer.is() && mxPeer->onEditedEntry( dynamic_cast< UnoTreeListEntry* >( pEntry ), rNewText ); +} + + + + +UnoTreeListItem::UnoTreeListItem() +: SvLBoxString(OUString()) +{ +} + +void UnoTreeListItem::Paint( + const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext, const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry) +{ + Point aPos(rPos); + Size aSize(GetWidth(&rDev, &rEntry), GetHeight(&rDev, &rEntry)); + if (!!maImage) + { + rRenderContext.DrawImage(aPos, maImage, rDev.IsEnabled() ? DrawImageFlags::NONE : DrawImageFlags::Disable); + int nWidth = maImage.GetSizePixel().Width() + 6; + aPos.AdjustX(nWidth ); + aSize.AdjustWidth( -nWidth ); + } + rRenderContext.DrawText(tools::Rectangle(aPos,aSize),maText, rDev.IsEnabled() ? DrawTextFlags::NONE : DrawTextFlags::Disable); +} + + +std::unique_ptr<SvLBoxItem> UnoTreeListItem::Clone(SvLBoxItem const * pSource) const +{ + std::unique_ptr<UnoTreeListItem> pNew(new UnoTreeListItem); + UnoTreeListItem const * pSourceItem = static_cast< UnoTreeListItem const * >( pSource ); + pNew->maText = pSourceItem->maText; + pNew->maImage = pSourceItem->maImage; + return std::unique_ptr<SvLBoxItem>(pNew.release()); +} + + +void UnoTreeListItem::SetImage( const Image& rImage ) +{ + maImage = rImage; +} + + +void UnoTreeListItem::SetGraphicURL( const OUString& rGraphicURL ) +{ + maGraphicURL = rGraphicURL; +} + + +void UnoTreeListItem::InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData) +{ + if( !pViewData ) + pViewData = pView->GetViewDataItem( pEntry, this ); + + Size aSize(maImage.GetSizePixel()); + pViewData->mnWidth = aSize.Width(); + pViewData->mnHeight = aSize.Height(); + + const Size aTextSize(pView->GetTextWidth( maText ), pView->GetTextHeight()); + if( pViewData->mnWidth ) + { + pViewData->mnWidth += (6 + aTextSize.Width()); + if( pViewData->mnHeight < aTextSize.Height() ) + pViewData->mnHeight = aTextSize.Height(); + } + else + { + pViewData->mnWidth = aTextSize.Width(); + pViewData->mnHeight = aTextSize.Height(); + } +} + + +UnoTreeListEntry::UnoTreeListEntry( const Reference< XTreeNode >& xNode, TreeControlPeer* pPeer ) +: mxNode( xNode ) +, mpPeer( pPeer ) +{ + if( mpPeer ) + mpPeer->addEntry( this ); +} + + +UnoTreeListEntry::~UnoTreeListEntry() +{ + if( mpPeer ) + mpPeer->removeEntry( this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treedatamodel.cxx b/toolkit/source/controls/tree/treedatamodel.cxx new file mode 100644 index 000000000..df8056057 --- /dev/null +++ b/toolkit/source/controls/tree/treedatamodel.cxx @@ -0,0 +1,523 @@ +/* -*- 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 <com/sun/star/awt/tree/XMutableTreeDataModel.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase2.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ref.hxx> +#include <toolkit/helper/mutexandbroadcasthelper.hxx> +#include <mutex> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::tree; +using namespace ::com::sun::star::lang; + +namespace { + + enum broadcast_type { nodes_changed, nodes_inserted, nodes_removed, structure_changed }; + +class MutableTreeNode; +class MutableTreeDataModel; + +typedef std::vector< rtl::Reference< MutableTreeNode > > TreeNodeVector; + +class MutableTreeDataModel : public ::cppu::WeakAggImplHelper2< XMutableTreeDataModel, XServiceInfo >, + public MutexAndBroadcastHelper +{ +public: + MutableTreeDataModel(); + + void broadcast( broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ); + + // XMutableTreeDataModel + virtual css::uno::Reference< css::awt::tree::XMutableTreeNode > SAL_CALL createNode( const css::uno::Any& DisplayValue, sal_Bool ChildrenOnDemand ) override; + virtual void SAL_CALL setRoot( const css::uno::Reference< css::awt::tree::XMutableTreeNode >& RootNode ) override; + + // XTreeDataModel + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getRoot( ) override; + virtual void SAL_CALL addTreeDataModelListener( const css::uno::Reference< css::awt::tree::XTreeDataModelListener >& Listener ) override; + virtual void SAL_CALL removeTreeDataModelListener( const css::uno::Reference< css::awt::tree::XTreeDataModelListener >& Listener ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const Reference< XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& aListener ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + bool mbDisposed; + Reference< XTreeNode > mxRootNode; +}; + +class MutableTreeNode: public ::cppu::WeakAggImplHelper2< XMutableTreeNode, XServiceInfo > +{ + friend class MutableTreeDataModel; + +public: + MutableTreeNode( const rtl::Reference< MutableTreeDataModel >& xModel, const Any& rValue, bool bChildrenOnDemand ); + virtual ~MutableTreeNode() override; + + void setParent( MutableTreeNode* pParent ); + void broadcast_changes(); + void broadcast_changes(std::unique_lock<std::mutex> & rLock, + const Reference< XTreeNode >& xNode, bool bNew); + + // XMutableTreeNode + virtual css::uno::Any SAL_CALL getDataValue() override; + virtual void SAL_CALL setDataValue( const css::uno::Any& _datavalue ) override; + virtual void SAL_CALL appendChild( const css::uno::Reference< css::awt::tree::XMutableTreeNode >& ChildNode ) override; + virtual void SAL_CALL insertChildByIndex( ::sal_Int32 Index, const css::uno::Reference< css::awt::tree::XMutableTreeNode >& ChildNode ) override; + virtual void SAL_CALL removeChildByIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL setHasChildrenOnDemand( sal_Bool ChildrenOnDemand ) override; + virtual void SAL_CALL setDisplayValue( const css::uno::Any& Value ) override; + virtual void SAL_CALL setNodeGraphicURL( const OUString& URL ) override; + virtual void SAL_CALL setExpandedGraphicURL( const OUString& URL ) override; + virtual void SAL_CALL setCollapsedGraphicURL( const OUString& URL ) override; + + // XTreeNode + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getChildAt( ::sal_Int32 Index ) override; + virtual ::sal_Int32 SAL_CALL getChildCount( ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getParent( ) override; + virtual ::sal_Int32 SAL_CALL getIndex( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL hasChildrenOnDemand( ) override; + virtual css::uno::Any SAL_CALL getDisplayValue( ) override; + virtual OUString SAL_CALL getNodeGraphicURL( ) override; + virtual OUString SAL_CALL getExpandedGraphicURL( ) override; + virtual OUString SAL_CALL getCollapsedGraphicURL( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + TreeNodeVector maChildren; + Any maDisplayValue; + Any maDataValue; + bool mbHasChildrenOnDemand; + std::mutex maMutex; + MutableTreeNode* mpParent; + rtl::Reference< MutableTreeDataModel > mxModel; + OUString maNodeGraphicURL; + OUString maExpandedGraphicURL; + OUString maCollapsedGraphicURL; + bool mbIsInserted; +}; + +MutableTreeDataModel::MutableTreeDataModel() +: mbDisposed( false ) +{ +} + +void MutableTreeDataModel::broadcast( broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ) +{ + ::cppu::OInterfaceContainerHelper* pIter = BrdcstHelper.getContainer( cppu::UnoType<XTreeDataModelListener>::get() ); + if( !pIter ) + return; + + Reference< XInterface > xSource( static_cast< ::cppu::OWeakObject* >( this ) ); + const Sequence< Reference< XTreeNode > > aNodes { rNode }; + TreeDataModelEvent aEvent( xSource, aNodes, xParentNode ); + + ::cppu::OInterfaceIteratorHelper aListIter(*pIter); + while(aListIter.hasMoreElements()) + { + XTreeDataModelListener* pListener = static_cast<XTreeDataModelListener*>(aListIter.next()); + switch( eType ) + { + case nodes_changed: pListener->treeNodesChanged(aEvent); break; + case nodes_inserted: pListener->treeNodesInserted(aEvent); break; + case nodes_removed: pListener->treeNodesRemoved(aEvent); break; + case structure_changed: pListener->treeStructureChanged(aEvent); break; + } + } +} + +Reference< XMutableTreeNode > SAL_CALL MutableTreeDataModel::createNode( const Any& aValue, sal_Bool bChildrenOnDemand ) +{ + return new MutableTreeNode( this, aValue, bChildrenOnDemand ); +} + +void SAL_CALL MutableTreeDataModel::setRoot( const Reference< XMutableTreeNode >& xNode ) +{ + if( !xNode.is() ) + throw IllegalArgumentException(); + + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + if( xNode == mxRootNode ) + return; + + if( mxRootNode.is() ) + { + rtl::Reference< MutableTreeNode > xOldImpl( dynamic_cast< MutableTreeNode* >( mxRootNode.get() ) ); + if( xOldImpl.is() ) + xOldImpl->mbIsInserted = false; + } + + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xNode.get() ) ); + if( !xImpl.is() || xImpl->mbIsInserted ) + throw IllegalArgumentException(); + + xImpl->mbIsInserted = true; + mxRootNode = xImpl; + + Reference< XTreeNode > xParentNode; + broadcast( structure_changed, xParentNode, mxRootNode ); +} + +Reference< XTreeNode > SAL_CALL MutableTreeDataModel::getRoot( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + return mxRootNode; +} + +void SAL_CALL MutableTreeDataModel::addTreeDataModelListener( const Reference< XTreeDataModelListener >& xListener ) +{ + BrdcstHelper.addListener( cppu::UnoType<XTreeDataModelListener>::get(), xListener ); +} + +void SAL_CALL MutableTreeDataModel::removeTreeDataModelListener( const Reference< XTreeDataModelListener >& xListener ) +{ + BrdcstHelper.removeListener( cppu::UnoType<XTreeDataModelListener>::get(), xListener ); +} + +void SAL_CALL MutableTreeDataModel::dispose() +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + if( !mbDisposed ) + { + mbDisposed = true; + css::lang::EventObject aEvent; + aEvent.Source.set( static_cast< ::cppu::OWeakObject* >( this ) ); + BrdcstHelper.aLC.disposeAndClear( aEvent ); + } +} + +void SAL_CALL MutableTreeDataModel::addEventListener( const Reference< XEventListener >& xListener ) +{ + BrdcstHelper.addListener( cppu::UnoType<XEventListener>::get(), xListener ); +} + +void SAL_CALL MutableTreeDataModel::removeEventListener( const Reference< XEventListener >& xListener ) +{ + BrdcstHelper.removeListener( cppu::UnoType<XEventListener>::get(), xListener ); +} + +OUString SAL_CALL MutableTreeDataModel::getImplementationName( ) +{ + return "toolkit.MutableTreeDataModel"; +} + +sal_Bool SAL_CALL MutableTreeDataModel::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL MutableTreeDataModel::getSupportedServiceNames( ) +{ + Sequence<OUString> aSeq { "com.sun.star.awt.tree.MutableTreeDataModel" }; + return aSeq; +} + +MutableTreeNode::MutableTreeNode( const rtl::Reference< MutableTreeDataModel >& xModel, const Any& rValue, bool bChildrenOnDemand ) +: maDisplayValue( rValue ) +, mbHasChildrenOnDemand( bChildrenOnDemand ) +, mpParent( nullptr ) +, mxModel( xModel ) +, mbIsInserted( false ) +{ +} + +MutableTreeNode::~MutableTreeNode() +{ + for( auto& rChild : maChildren ) + rChild->setParent(nullptr); +} + +void MutableTreeNode::setParent( MutableTreeNode* pParent ) +{ + mpParent = pParent; +} + +void MutableTreeNode::broadcast_changes() +{ + if( mxModel.is() ) + { + mxModel->broadcast( nodes_changed, mpParent, this ); + } +} + +void MutableTreeNode::broadcast_changes(std::unique_lock<std::mutex> & rLock, + const Reference< XTreeNode >& xNode, bool const bNew) +{ + auto const xModel(mxModel); + rLock.unlock(); + if (xModel.is()) + { + xModel->broadcast(bNew ? nodes_inserted : nodes_removed, this, xNode); + } +} + +Any SAL_CALL MutableTreeNode::getDataValue() +{ + std::scoped_lock aGuard( maMutex ); + return maDataValue; +} + +void SAL_CALL MutableTreeNode::setDataValue( const Any& _datavalue ) +{ + std::scoped_lock aGuard( maMutex ); + maDataValue = _datavalue; +} + +void SAL_CALL MutableTreeNode::appendChild( const Reference< XMutableTreeNode >& xChildNode ) +{ + std::unique_lock aGuard( maMutex ); + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xChildNode.get() ) ); + + if( !xImpl.is() || xImpl->mbIsInserted || (this == xImpl.get()) ) + throw IllegalArgumentException(); + + maChildren.push_back( xImpl ); + xImpl->setParent(this); + xImpl->mbIsInserted = true; + + broadcast_changes(aGuard, xChildNode, true); +} + +void SAL_CALL MutableTreeNode::insertChildByIndex( sal_Int32 nChildIndex, const Reference< XMutableTreeNode >& xChildNode ) +{ + std::unique_lock aGuard( maMutex ); + + if( (nChildIndex < 0) || (o3tl::make_unsigned(nChildIndex) > maChildren.size()) ) + throw IndexOutOfBoundsException(); + + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xChildNode.get() ) ); + if( !xImpl.is() || xImpl->mbIsInserted || (this == xImpl.get()) ) + throw IllegalArgumentException(); + + xImpl->mbIsInserted = true; + + TreeNodeVector::iterator aIter( maChildren.begin() ); + std::advance(aIter, nChildIndex); + + maChildren.insert( aIter, xImpl ); + xImpl->setParent( this ); + + broadcast_changes(aGuard, xChildNode, true); +} + +void SAL_CALL MutableTreeNode::removeChildByIndex( sal_Int32 nChildIndex ) +{ + std::unique_lock aGuard( maMutex ); + + if( (nChildIndex < 0) || (o3tl::make_unsigned(nChildIndex) >= maChildren.size()) ) + throw IndexOutOfBoundsException(); + + rtl::Reference< MutableTreeNode > xImpl; + + TreeNodeVector::iterator aIter( maChildren.begin() ); + std::advance(aIter, nChildIndex); + + xImpl = *aIter; + maChildren.erase( aIter ); + + if( !xImpl.is() ) + throw IndexOutOfBoundsException(); + + xImpl->setParent(nullptr); + xImpl->mbIsInserted = false; + + broadcast_changes(aGuard, xImpl, false); +} + +void SAL_CALL MutableTreeNode::setHasChildrenOnDemand( sal_Bool bChildrenOnDemand ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = mbHasChildrenOnDemand != bool(bChildrenOnDemand); + mbHasChildrenOnDemand = bChildrenOnDemand; + } + + if( bChanged ) + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setDisplayValue( const Any& aValue ) +{ + { + std::scoped_lock aGuard( maMutex ); + maDisplayValue = aValue; + } + + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setNodeGraphicURL( const OUString& rURL ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = maNodeGraphicURL != rURL; + maNodeGraphicURL = rURL; + } + + if( bChanged ) + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setExpandedGraphicURL( const OUString& rURL ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = maExpandedGraphicURL != rURL; + maExpandedGraphicURL = rURL; + } + + if( bChanged ) + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setCollapsedGraphicURL( const OUString& rURL ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = maCollapsedGraphicURL != rURL; + maCollapsedGraphicURL = rURL; + } + + if( bChanged ) + broadcast_changes(); +} + +Reference< XTreeNode > SAL_CALL MutableTreeNode::getChildAt( sal_Int32 nChildIndex ) +{ + std::scoped_lock aGuard( maMutex ); + + if( (nChildIndex < 0) || (o3tl::make_unsigned(nChildIndex) >= maChildren.size()) ) + throw IndexOutOfBoundsException(); + return maChildren[nChildIndex]; +} + +sal_Int32 SAL_CALL MutableTreeNode::getChildCount( ) +{ + std::scoped_lock aGuard( maMutex ); + return static_cast<sal_Int32>(maChildren.size()); +} + +Reference< XTreeNode > SAL_CALL MutableTreeNode::getParent( ) +{ + std::scoped_lock aGuard( maMutex ); + return mpParent; +} + +sal_Int32 SAL_CALL MutableTreeNode::getIndex( const Reference< XTreeNode >& xNode ) +{ + std::scoped_lock aGuard( maMutex ); + + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xNode.get() ) ); + if( xImpl.is() ) + { + sal_Int32 nChildCount = maChildren.size(); + while( nChildCount-- ) + { + if( maChildren[nChildCount] == xImpl ) + return nChildCount; + } + } + + return -1; +} + +sal_Bool SAL_CALL MutableTreeNode::hasChildrenOnDemand( ) +{ + std::scoped_lock aGuard( maMutex ); + return mbHasChildrenOnDemand; +} + +Any SAL_CALL MutableTreeNode::getDisplayValue( ) +{ + std::scoped_lock aGuard( maMutex ); + return maDisplayValue; +} + +OUString SAL_CALL MutableTreeNode::getNodeGraphicURL( ) +{ + std::scoped_lock aGuard( maMutex ); + return maNodeGraphicURL; +} + +OUString SAL_CALL MutableTreeNode::getExpandedGraphicURL( ) +{ + std::scoped_lock aGuard( maMutex ); + return maExpandedGraphicURL; +} + +OUString SAL_CALL MutableTreeNode::getCollapsedGraphicURL( ) +{ + std::scoped_lock aGuard( maMutex ); + return maCollapsedGraphicURL; +} + +OUString SAL_CALL MutableTreeNode::getImplementationName( ) +{ + return "toolkit.MutableTreeNode"; +} + +sal_Bool SAL_CALL MutableTreeNode::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL MutableTreeNode::getSupportedServiceNames( ) +{ + Sequence<OUString> aSeq { "com.sun.star.awt.tree.MutableTreeNode" }; + return aSeq; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_MutableTreeDataModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new MutableTreeDataModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |