| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_toolkit.hxx" |
| |
| #ifndef _TOOLKIT_TREE_CONTROL_HXX |
| #include <treecontrol.hxx> |
| #endif |
| |
| #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
| #include <com/sun/star/view/SelectionType.hpp> |
| #include <com/sun/star/awt/tree/XTreeDataModel.hpp> |
| #include <toolkit/helper/unopropertyarrayhelper.hxx> |
| #include <toolkit/helper/property.hxx> |
| #include <com/sun/star/awt/XVclWindowPeer.hpp> |
| #include <comphelper/processfactory.hxx> |
| #include <osl/diagnose.h> |
| |
| using ::rtl::OUString; |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::uno; |
| 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 |
| { |
| // ---------------------------------------------------- |
| // class UnoTreeModel |
| // ---------------------------------------------------- |
| UnoTreeModel::UnoTreeModel( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& 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 ); |
| } |
| |
| UnoTreeModel::UnoTreeModel( const UnoTreeModel& rModel ) |
| : UnoControlModel( rModel ) |
| { |
| } |
| |
| UnoControlModel* UnoTreeModel::Clone() const |
| { |
| return new UnoTreeModel( *this ); |
| } |
| |
| OUString UnoTreeModel::getServiceName() throw(RuntimeException) |
| { |
| return OUString::createFromAscii( szServiceName_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 >( 0 ) ); |
| case BASEPROPERTY_TREE_EDITABLE: |
| case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: |
| return Any( sal_False ); |
| case BASEPROPERTY_TREE_ROOTDISPLAYED: |
| case BASEPROPERTY_TREE_SHOWSROOTHANDLES: |
| case BASEPROPERTY_TREE_SHOWSHANDLES: |
| return Any( sal_True ); |
| case BASEPROPERTY_DEFAULTCONTROL: |
| return uno::makeAny( ::rtl::OUString::createFromAscii( szServiceName_TreeControl ) ); |
| default: |
| return UnoControlModel::ImplGetDefaultValue( nPropId ); |
| } |
| } |
| |
| ::cppu::IPropertyArrayHelper& UnoTreeModel::getInfoHelper() |
| { |
| static UnoPropertyArrayHelper* pHelper = NULL; |
| if ( !pHelper ) |
| { |
| Sequence<sal_Int32> aIDs = ImplGetPropertyIds(); |
| pHelper = new UnoPropertyArrayHelper( aIDs ); |
| } |
| return *pHelper; |
| } |
| |
| // XMultiPropertySet |
| Reference< XPropertySetInfo > UnoTreeModel::getPropertySetInfo( ) throw(RuntimeException) |
| { |
| static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); |
| return xInfo; |
| } |
| |
| |
| // ---------------------------------------------------- |
| // class UnoTreeControl |
| // ---------------------------------------------------- |
| UnoTreeControl::UnoTreeControl( const Reference< XMultiServiceFactory >& i_factory ) |
| : UnoTreeControl_Base( i_factory ) |
| , maSelectionListeners( *this ) |
| , maTreeExpansionListeners( *this ) |
| , maTreeEditListeners( *this ) |
| { |
| } |
| |
| OUString UnoTreeControl::GetComponentServiceName() |
| { |
| return OUString::createFromAscii( "Tree" ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // ::com::sun::star::view::XSelectionSupplier |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::select( const Any& rSelection ) throw (IllegalArgumentException, RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->select( rSelection ); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| Any SAL_CALL UnoTreeControl::getSelection() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getSelection(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::addSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) throw (RuntimeException) |
| { |
| 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< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->addSelectionChangeListener(&maSelectionListeners); |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) throw (RuntimeException) |
| { |
| 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< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->removeSelectionChangeListener(&maSelectionListeners); |
| } |
| maSelectionListeners.removeInterface( xListener ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // ::com::sun::star::view::XMultiSelectionSupplier |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::addSelection( const Any& rSelection ) throw (IllegalArgumentException, RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addSelection(rSelection); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::removeSelection( const Any& rSelection ) throw (IllegalArgumentException, RuntimeException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->removeSelection(rSelection); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::clearSelection() throw (RuntimeException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->clearSelection(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| sal_Int32 SAL_CALL UnoTreeControl::getSelectionCount() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getSelectionCount(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| Reference< XEnumeration > SAL_CALL UnoTreeControl::createSelectionEnumeration() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->createSelectionEnumeration(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| Reference< XEnumeration > SAL_CALL UnoTreeControl::createReverseSelectionEnumeration() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->createReverseSelectionEnumeration(); |
| } |
| |
| // -------------------------------------------------------------------- |
| // XTreeControl |
| // -------------------------------------------------------------------- |
| |
| OUString SAL_CALL UnoTreeControl::getDefaultExpandedGraphicURL() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getDefaultExpandedGraphicURL(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::setDefaultExpandedGraphicURL( const OUString& _defaultexpansiongraphicurl ) throw (RuntimeException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->setDefaultExpandedGraphicURL(_defaultexpansiongraphicurl); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| OUString SAL_CALL UnoTreeControl::getDefaultCollapsedGraphicURL() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getDefaultCollapsedGraphicURL(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::setDefaultCollapsedGraphicURL( const OUString& _defaultcollapsedgraphicurl ) throw (RuntimeException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->setDefaultCollapsedGraphicURL(_defaultcollapsedgraphicurl); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::isNodeExpanded( const Reference< XTreeNode >& xNode ) throw (RuntimeException, IllegalArgumentException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeExpanded(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::isNodeCollapsed( const Reference< XTreeNode >& xNode ) throw (RuntimeException, IllegalArgumentException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeCollapsed(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::makeNodeVisible( const Reference< XTreeNode >& xNode ) throw (RuntimeException, ExpandVetoException, IllegalArgumentException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->makeNodeVisible(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::isNodeVisible( const Reference< XTreeNode >& xNode ) throw (RuntimeException, IllegalArgumentException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeVisible(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::expandNode( const Reference< XTreeNode >& xNode ) throw (RuntimeException, ExpandVetoException, IllegalArgumentException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->expandNode(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::collapseNode( const Reference< XTreeNode >& xNode ) throw (RuntimeException, ExpandVetoException, IllegalArgumentException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->collapseNode(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::addTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) throw (RuntimeException) |
| { |
| 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< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->addTreeExpansionListener(&maTreeExpansionListeners); |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::removeTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) throw (RuntimeException) |
| { |
| 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< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->removeTreeExpansionListener(&maTreeExpansionListeners); |
| } |
| maTreeExpansionListeners.removeInterface( xListener ); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| Reference< XTreeNode > SAL_CALL UnoTreeControl::getNodeForLocation( sal_Int32 x, sal_Int32 y ) throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getNodeForLocation(x,y); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| Reference< XTreeNode > SAL_CALL UnoTreeControl::getClosestNodeForLocation( sal_Int32 x, sal_Int32 y ) throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getClosestNodeForLocation(x,y); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| awt::Rectangle SAL_CALL UnoTreeControl::getNodeRect( const Reference< XTreeNode >& Node ) throw (IllegalArgumentException, RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getNodeRect( Node ); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::isEditing( ) throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isEditing(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL UnoTreeControl::stopEditing() throw (RuntimeException) |
| { |
| return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->stopEditing(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::cancelEditing() throw (RuntimeException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->cancelEditing(); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::startEditingAtNode( const Reference< XTreeNode >& xNode ) throw (IllegalArgumentException, RuntimeException) |
| { |
| Reference< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->startEditingAtNode(xNode); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::addTreeEditListener( const Reference< XTreeEditListener >& xListener ) throw (RuntimeException) |
| { |
| 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< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->addTreeEditListener(&maTreeEditListeners); |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::removeTreeEditListener( const Reference< XTreeEditListener >& xListener ) throw (RuntimeException) |
| { |
| 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< XWindowPeer > xGcc3WorkaroundTemporary( getPeer()); |
| Reference< XTreeControl >( xGcc3WorkaroundTemporary, UNO_QUERY_THROW )->removeTreeEditListener(&maTreeEditListeners); |
| } |
| maTreeEditListeners.removeInterface( xListener ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // XComponent |
| // ------------------------------------------------------------------- |
| |
| void SAL_CALL UnoTreeControl::dispose( ) throw(RuntimeException) |
| { |
| 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 ) throw(uno::RuntimeException) |
| { |
| UnoControlBase::createPeer( rxToolkit, rParentPeer ); |
| |
| Reference< XTreeControl > xTree( getPeer(), UNO_QUERY_THROW ); |
| if( maSelectionListeners.getLength() ) |
| xTree->addSelectionChangeListener( &maSelectionListeners ); |
| if( maTreeExpansionListeners.getLength() ) |
| xTree->addTreeExpansionListener( &maTreeExpansionListeners ); |
| } |
| |
| } |
| |
| Reference< XInterface > SAL_CALL TreeControl_CreateInstance( const Reference< XMultiServiceFactory >& i_factory ) |
| { |
| return Reference < XInterface >( ( ::cppu::OWeakObject* ) new ::toolkit::UnoTreeControl( i_factory ) ); |
| } |
| |
| Reference< XInterface > SAL_CALL TreeControlModel_CreateInstance( const Reference< XMultiServiceFactory >& i_factory ) |
| { |
| return Reference < XInterface >( ( ::cppu::OWeakObject* ) new ::toolkit::UnoTreeModel( i_factory ) ); |
| } |
| |
| void SAL_CALL TreeEditListenerMultiplexer::nodeEditing( const Reference< XTreeNode >& Node ) throw (RuntimeException, ::com::sun::star::util::VetoException) |
| { |
| ::cppu::OInterfaceIteratorHelper aIt( *this ); |
| while( aIt.hasMoreElements() ) |
| { |
| Reference< XTreeEditListener > xListener(static_cast< XTreeEditListener* >( aIt.next() ) ); |
| try |
| { |
| xListener->nodeEditing( Node ); |
| } |
| catch( DisposedException& e ) |
| { |
| OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); |
| if ( e.Context == xListener || !e.Context.is() ) |
| aIt.remove(); |
| } |
| catch( RuntimeException& e ) |
| { |
| (void)e; |
| DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEditing, e ) |
| } |
| } |
| } |
| |
| void SAL_CALL TreeEditListenerMultiplexer::nodeEdited( const Reference< XTreeNode >& Node, const OUString& NewText ) throw (RuntimeException) |
| { |
| ::cppu::OInterfaceIteratorHelper aIt( *this ); |
| while( aIt.hasMoreElements() ) |
| { |
| Reference< XTreeEditListener > xListener( static_cast< XTreeEditListener* >( aIt.next() ) ); |
| try |
| { |
| xListener->nodeEdited( Node, NewText ); |
| } |
| catch( DisposedException& e ) |
| { |
| OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); |
| if ( e.Context == xListener || !e.Context.is() ) |
| aIt.remove(); |
| } |
| catch( RuntimeException& e ) |
| { |
| (void)e; |
| DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEdited, e ) |
| } |
| } |
| } |