diff options
Diffstat (limited to 'toolkit/source/controls')
55 files changed, 31118 insertions, 0 deletions
diff --git a/toolkit/source/controls/accessiblecontrolcontext.cxx b/toolkit/source/controls/accessiblecontrolcontext.cxx new file mode 100644 index 0000000000..761821bce6 --- /dev/null +++ b/toolkit/source/controls/accessiblecontrolcontext.cxx @@ -0,0 +1,343 @@ +/* -*- 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 <controls/accessiblecontrolcontext.hxx> +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <vcl/svapp.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/accessiblecontexthelper.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/window.hxx> + + +namespace toolkit +{ + + + using ::comphelper::OContextEntryGuard; + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::accessibility; + + + //= OAccessibleControlContext + + + OAccessibleControlContext::OAccessibleControlContext() + { + // nothing to do here, we have a late ctor + } + + + OAccessibleControlContext::~OAccessibleControlContext() + { + ensureDisposed(); + } + + + void OAccessibleControlContext::Init( const Reference< XAccessible >& _rxCreator ) + { + OContextEntryGuard aGuard( this ); + + // retrieve the model of the control + OSL_ENSURE( !m_xControlModel.is(), "OAccessibleControlContext::Init: already know a control model...!???" ); + + Reference< awt::XControl > xControl( _rxCreator, UNO_QUERY ); + if ( xControl.is() ) + m_xControlModel.set(xControl->getModel(), css::uno::UNO_QUERY); + OSL_ENSURE( m_xControlModel.is(), "OAccessibleControlContext::Init: invalid creator (no control, or control without model!" ); + if ( !m_xControlModel.is() ) + throw DisposedException(); // caught by the caller (the create method) + + // start listening at the model + startModelListening(); + + // announce the XAccessible to our base class + OAccessibleControlContext_Base::lateInit( _rxCreator ); + } + + + rtl::Reference<OAccessibleControlContext> OAccessibleControlContext::create( const Reference< XAccessible >& _rxCreator ) + { + rtl::Reference<OAccessibleControlContext> pNew; + try + { + pNew = new OAccessibleControlContext; + pNew->Init( _rxCreator ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "OAccessibleControlContext::create: caught an exception from the late ctor!" ); + } + return pNew; + } + + + void OAccessibleControlContext::startModelListening( ) + { + Reference< XComponent > xModelComp( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xModelComp.is(), "OAccessibleControlContext::startModelListening: invalid model!" ); + if ( xModelComp.is() ) + xModelComp->addEventListener( this ); + } + + + void OAccessibleControlContext::stopModelListening( ) + { + Reference< XComponent > xModelComp( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xModelComp.is(), "OAccessibleControlContext::stopModelListening: invalid model!" ); + if ( xModelComp.is() ) + xModelComp->removeEventListener( this ); + } + + + sal_Int64 SAL_CALL OAccessibleControlContext::getAccessibleChildCount( ) + { + // we do not have children + return 0; + } + + + Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleChild( sal_Int64 ) + { + // we do not have children + throw IndexOutOfBoundsException(); + } + + + Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleParent( ) + { + return Reference< XAccessible >(); + } + + + sal_Int16 SAL_CALL OAccessibleControlContext::getAccessibleRole( ) + { + return AccessibleRole::SHAPE; + } + + + OUString SAL_CALL OAccessibleControlContext::getAccessibleDescription( ) + { + OContextEntryGuard aGuard( this ); + return getModelStringProperty( "HelpText" ); + } + + + OUString SAL_CALL OAccessibleControlContext::getAccessibleName( ) + { + OContextEntryGuard aGuard( this ); + return getModelStringProperty( "Name" ); + } + + + Reference< XAccessibleRelationSet > SAL_CALL OAccessibleControlContext::getAccessibleRelationSet( ) + { + return nullptr; + } + + + sal_Int64 SAL_CALL OAccessibleControlContext::getAccessibleStateSet( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + // no OContextEntryGuard here, as we do not want to throw an exception in case we're not alive anymore + + sal_Int64 nStateSet = 0; + if ( isAlive() ) + { + // no own states, only the ones which are foreign controlled + } + else + { // only the DEFUNC state if we're already disposed + nStateSet |= AccessibleStateType::DEFUNC; + } + return nStateSet; + } + + + void SAL_CALL OAccessibleControlContext::disposing( const EventObject& _rSource ) + { + OSL_ENSURE( Reference< XPropertySet >( _rSource.Source, UNO_QUERY ).get() == m_xControlModel.get(), + "OAccessibleControlContext::disposing: where did this come from?" ); + + stopModelListening( ); + m_xControlModel.clear(); + m_xModelPropsInfo.clear(); + + OAccessibleControlContext_Base::disposing(); + } + + + OUString OAccessibleControlContext::getModelStringProperty( const char* _pPropertyName ) + { + OUString sReturn; + try + { + if ( !m_xModelPropsInfo.is() && m_xControlModel.is() ) + m_xModelPropsInfo = m_xControlModel->getPropertySetInfo(); + + OUString sPropertyName( OUString::createFromAscii( _pPropertyName ) ); + if ( m_xModelPropsInfo.is() && m_xModelPropsInfo->hasPropertyByName( sPropertyName ) ) + m_xControlModel->getPropertyValue( sPropertyName ) >>= sReturn; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "OAccessibleControlContext::getModelStringProperty" ); + } + return sReturn; + } + + + vcl::Window* OAccessibleControlContext::implGetWindow( Reference< awt::XWindow >* _pxUNOWindow ) const + { + Reference< awt::XControl > xControl( getAccessibleCreator(), UNO_QUERY ); + Reference< awt::XWindow > xWindow; + if ( xControl.is() ) + xWindow.set(xControl->getPeer(), css::uno::UNO_QUERY); + + vcl::Window* pWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow ) : nullptr; + + if ( _pxUNOWindow ) + *_pxUNOWindow = xWindow; + + return pWindow; + } + + + awt::Rectangle OAccessibleControlContext::implGetBounds( ) + { + SolarMutexGuard aSolarGuard; + // want to do some VCL stuff here ... + OContextEntryGuard aGuard( this ); + + OSL_FAIL( "OAccessibleControlContext::implGetBounds: performance issue: forced to calc the size myself!" ); + // In design mode (and this is what this class is for), the surrounding shape (if any) should handle this call + // The problem is that in design mode, our size may not be correct (in the drawing layer, controls are + // positioned/sized for painting only), and that calculation of our position is expensive + + // what we know (or can obtain from somewhere): + // * the PosSize of our peer, relative to its parent window + // * the parent window which the PosSize is relative to + // * our foreign controlled accessible parent + // from this info, we can determine the position of our peer relative to the foreign parent + + // our control + Reference< awt::XWindow > xWindow; + VclPtr< vcl::Window > pVCLWindow = implGetWindow( &xWindow ); + + awt::Rectangle aBounds( 0, 0, 0, 0 ); + if ( xWindow.is() ) + { + // ugly, but... though the XWindow has a getPosSize, it is impossible to determine the + // parent which this position/size is relative to. This means we must tunnel UNO and ask the + // implementation + vcl::Window* pVCLParent = pVCLWindow ? pVCLWindow->GetParent() : nullptr; + + // the relative location of the window + ::Point aWindowRelativePos( 0, 0); + if ( pVCLWindow ) + aWindowRelativePos = pVCLWindow->GetPosPixel(); + + // the screen position of the "window parent" of the control + ::Point aVCLParentScreenPos( 0, 0 ); + if ( pVCLParent ) + aVCLParentScreenPos = pVCLParent->GetPosPixel(); + + // now the size of the control + aBounds = xWindow->getPosSize(); + + // correct the pos + aBounds.X = aWindowRelativePos.X() + aVCLParentScreenPos.X(); + aBounds.Y = aWindowRelativePos.Y() + aVCLParentScreenPos.Y(); + } + + return aBounds; + } + + + Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleAtPoint( const awt::Point& /* _rPoint */ ) + { + // no children at all + return nullptr; + } + + + void SAL_CALL OAccessibleControlContext::grabFocus( ) + { + OSL_FAIL( "OAccessibleControlContext::grabFocus: !isFocusTraversable, but grabFocus!" ); + } + + + sal_Int32 SAL_CALL OAccessibleControlContext::getForeground( ) + { + SolarMutexGuard aSolarGuard; + // want to do some VCL stuff here ... + OContextEntryGuard aGuard( this ); + + VclPtr< vcl::Window > pWindow = implGetWindow(); + Color nColor; + if ( pWindow ) + { + if ( pWindow->IsControlForeground() ) + nColor = pWindow->GetControlForeground(); + else + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + nColor = aFont.GetColor(); + } + } + return sal_Int32(nColor); + } + + + sal_Int32 SAL_CALL OAccessibleControlContext::getBackground( ) + { + SolarMutexGuard aSolarGuard; + // want to do some VCL stuff here ... + OContextEntryGuard aGuard( this ); + + VclPtr< vcl::Window > pWindow = implGetWindow(); + Color nColor; + if ( pWindow ) + { + if ( pWindow->IsControlBackground() ) + nColor = pWindow->GetControlBackground(); + else + nColor = pWindow->GetBackground().GetColor(); + } + + return sal_Int32(nColor); + } + + +} //namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/animatedimages.cxx b/toolkit/source/controls/animatedimages.cxx new file mode 100644 index 0000000000..dec4a8c533 --- /dev/null +++ b/toolkit/source/controls/animatedimages.cxx @@ -0,0 +1,491 @@ +/* -*- 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 <controls/animatedimages.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> +#include <com/sun/star/awt/XAnimation.hpp> +#include <com/sun/star/awt/XAnimatedImages.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <o3tl/safeint.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> + +#include <cppuhelper/implbase2.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace css::awt; +using namespace css::container; +using namespace css::lang; +using namespace css::uno; + +namespace { + +typedef ::cppu::AggImplInheritanceHelper2 < UnoControlBase + , css::awt::XAnimation + , css::container::XContainerListener + > AnimatedImagesControl_Base; + +class AnimatedImagesControl : public AnimatedImagesControl_Base +{ +public: + AnimatedImagesControl(); + OUString GetComponentServiceName() const override; + + // XAnimation + virtual void SAL_CALL startAnimation( ) override; + virtual void SAL_CALL stopAnimation( ) override; + virtual sal_Bool SAL_CALL isAnimationRunning( ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XControl + sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& i_rModel ) override; + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& i_toolkit, const css::uno::Reference< css::awt::XWindowPeer >& i_parentPeer ) override; + + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; +}; + + AnimatedImagesControl::AnimatedImagesControl() + { + } + + + OUString AnimatedImagesControl::GetComponentServiceName() const + { + return "AnimatedImages"; + } + + + void SAL_CALL AnimatedImagesControl::startAnimation( ) + { + Reference< XAnimation > xAnimation( getPeer(), UNO_QUERY ); + if ( xAnimation.is() ) + xAnimation->startAnimation(); + } + + + void SAL_CALL AnimatedImagesControl::stopAnimation( ) + { + Reference< XAnimation > xAnimation( getPeer(), UNO_QUERY ); + if ( xAnimation.is() ) + xAnimation->stopAnimation(); + } + + + sal_Bool SAL_CALL AnimatedImagesControl::isAnimationRunning( ) + { + Reference< XAnimation > xAnimation( getPeer(), UNO_QUERY ); + if ( xAnimation.is() ) + return xAnimation->isAnimationRunning(); + return false; + } + + + OUString SAL_CALL AnimatedImagesControl::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.AnimatedImagesControl"; + } + + + Sequence< OUString > SAL_CALL AnimatedImagesControl::getSupportedServiceNames() + { + Sequence< OUString > aServices( AnimatedImagesControl_Base::getSupportedServiceNames() ); + aServices.realloc( aServices.getLength() + 1 ); + aServices.getArray()[ aServices.getLength() - 1 ] = "com.sun.star.awt.AnimatedImagesControl"; + return aServices; + } + + void lcl_updatePeer( Reference< XWindowPeer > const& i_peer, Reference< XControlModel > const& i_model ) + { + const Reference< css::util::XModifyListener > xPeerModify( i_peer, UNO_QUERY ); + if ( xPeerModify.is() ) + { + EventObject aEvent; + aEvent.Source = i_model; + xPeerModify->modified( aEvent ); + } + } + + sal_Bool SAL_CALL AnimatedImagesControl::setModel( const Reference< XControlModel >& i_rModel ) + { + const Reference< XAnimatedImages > xOldContainer( getModel(), UNO_QUERY ); + const Reference< XAnimatedImages > xNewContainer( i_rModel, UNO_QUERY ); + + if ( !AnimatedImagesControl_Base::setModel( i_rModel ) ) + return false; + + if ( xOldContainer.is() ) + xOldContainer->removeContainerListener( this ); + + if ( xNewContainer.is() ) + xNewContainer->addContainerListener( this ); + + lcl_updatePeer( getPeer(), getModel() ); + + return true; + } + + + void SAL_CALL AnimatedImagesControl::createPeer( const Reference< XToolkit >& i_toolkit, const Reference< XWindowPeer >& i_parentPeer ) + { + AnimatedImagesControl_Base::createPeer( i_toolkit, i_parentPeer ); + + lcl_updatePeer( getPeer(), getModel() ); + } + + + void SAL_CALL AnimatedImagesControl::elementInserted( const ContainerEvent& i_event ) + { + const Reference< XContainerListener > xPeerListener( getPeer(), UNO_QUERY ); + if ( xPeerListener.is() ) + xPeerListener->elementInserted( i_event ); + } + + + void SAL_CALL AnimatedImagesControl::elementRemoved( const ContainerEvent& i_event ) + { + const Reference< XContainerListener > xPeerListener( getPeer(), UNO_QUERY ); + if ( xPeerListener.is() ) + xPeerListener->elementRemoved( i_event ); + } + + + void SAL_CALL AnimatedImagesControl::elementReplaced( const ContainerEvent& i_event ) + { + const Reference< XContainerListener > xPeerListener( getPeer(), UNO_QUERY ); + if ( xPeerListener.is() ) + xPeerListener->elementReplaced( i_event ); + } + + + void SAL_CALL AnimatedImagesControl::disposing( const EventObject& i_event ) + { + UnoControlBase::disposing( i_event ); + } + +} + +namespace toolkit { + + namespace + { + void lcl_checkIndex( const std::vector< css::uno::Sequence< OUString > > & rImageSets, const sal_Int32 i_index, const Reference< XInterface >& i_context, + const bool i_forInsert = false ) + { + if ( ( i_index < 0 ) || ( o3tl::make_unsigned( i_index ) > rImageSets.size() + ( i_forInsert ? 1 : 0 ) ) ) + throw IndexOutOfBoundsException( OUString(), i_context ); + } + + void lcl_notify( std::unique_lock<std::mutex>& i_guard, comphelper::OInterfaceContainerHelper4<XContainerListener>& rContainer, + void ( SAL_CALL XContainerListener::*i_notificationMethod )( const ContainerEvent& ), + const sal_Int32 i_accessor, const Sequence< OUString >& i_imageURLs, const Reference< XInterface >& i_context ) + { + if ( !rContainer.getLength(i_guard) ) + return; + + ContainerEvent aEvent; + aEvent.Source = i_context; + aEvent.Accessor <<= i_accessor; + aEvent.Element <<= i_imageURLs; + + rContainer.notifyEach( i_guard, i_notificationMethod, aEvent ); + } + } + + + AnimatedImagesControlModel::AnimatedImagesControlModel( Reference< css::uno::XComponentContext > const & i_factory ) + :AnimatedImagesControlModel_Base( i_factory ) + { + ImplRegisterProperty( BASEPROPERTY_AUTO_REPEAT ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_IMAGE_SCALE_MODE ); + ImplRegisterProperty( BASEPROPERTY_STEP_TIME ); + } + + + AnimatedImagesControlModel::AnimatedImagesControlModel( const AnimatedImagesControlModel& i_copySource ) + :AnimatedImagesControlModel_Base( i_copySource ) + ,maImageSets( i_copySource.maImageSets ) + { + } + + + AnimatedImagesControlModel::~AnimatedImagesControlModel() + { + } + + + rtl::Reference<UnoControlModel> AnimatedImagesControlModel::Clone() const + { + return new AnimatedImagesControlModel( *this ); + } + + + Reference< css::beans::XPropertySetInfo > SAL_CALL AnimatedImagesControlModel::getPropertySetInfo( ) + { + static Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + OUString SAL_CALL AnimatedImagesControlModel::getServiceName() + { + return "com.sun.star.awt.AnimatedImagesControlModel"; + } + + + OUString SAL_CALL AnimatedImagesControlModel::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.AnimatedImagesControlModel"; + } + + + Sequence< OUString > SAL_CALL AnimatedImagesControlModel::getSupportedServiceNames() + { + return { "com.sun.star.awt.AnimatedImagesControlModel", "com.sun.star.awt.UnoControlModel" }; + } + + + void AnimatedImagesControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 i_handle, const Any& i_value ) + { + switch ( i_handle ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + sal_Int16 nImageScaleMode( ImageScaleMode::ANISOTROPIC ); + OSL_VERIFY( i_value >>= nImageScaleMode ); // convertFastPropertyValue ensures that this has the proper type + if ( ( nImageScaleMode != ImageScaleMode::NONE ) + && ( nImageScaleMode != ImageScaleMode::ISOTROPIC ) + && ( nImageScaleMode != ImageScaleMode::ANISOTROPIC ) + ) + throw IllegalArgumentException( OUString(), *this, 1 ); + } + break; + } + + AnimatedImagesControlModel_Base::setFastPropertyValue_NoBroadcast( rGuard, i_handle, i_value ); + } + + + Any AnimatedImagesControlModel::ImplGetDefaultValue( sal_uInt16 i_propertyId ) const + { + switch ( i_propertyId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return Any( OUString("com.sun.star.awt.AnimatedImagesControl") ); + + case BASEPROPERTY_BORDER: + return Any( css::awt::VisualEffect::NONE ); + + case BASEPROPERTY_STEP_TIME: + return Any( sal_Int32(100) ); + + case BASEPROPERTY_AUTO_REPEAT: + return Any( true ); + + case BASEPROPERTY_IMAGE_SCALE_MODE: + return Any( ImageScaleMode::NONE ); + + default: + return UnoControlModel::ImplGetDefaultValue( i_propertyId ); + } + } + + + ::cppu::IPropertyArrayHelper& AnimatedImagesControlModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + ::sal_Int32 SAL_CALL AnimatedImagesControlModel::getStepTime() + { + sal_Int32 nStepTime( 100 ); + OSL_VERIFY( getPropertyValue( GetPropertyName( BASEPROPERTY_STEP_TIME ) ) >>= nStepTime ); + return nStepTime; + } + + + void SAL_CALL AnimatedImagesControlModel::setStepTime( ::sal_Int32 i_stepTime ) + { + setPropertyValue( GetPropertyName( BASEPROPERTY_STEP_TIME ), Any( i_stepTime ) ); + } + + + sal_Bool SAL_CALL AnimatedImagesControlModel::getAutoRepeat() + { + bool bAutoRepeat( true ); + OSL_VERIFY( getPropertyValue( GetPropertyName( BASEPROPERTY_AUTO_REPEAT ) ) >>= bAutoRepeat ); + return bAutoRepeat; + } + + + void SAL_CALL AnimatedImagesControlModel::setAutoRepeat( sal_Bool i_autoRepeat ) + { + setPropertyValue( GetPropertyName( BASEPROPERTY_AUTO_REPEAT ), Any( i_autoRepeat ) ); + } + + + ::sal_Int16 SAL_CALL AnimatedImagesControlModel::getScaleMode() + { + sal_Int16 nImageScaleMode( ImageScaleMode::ANISOTROPIC ); + OSL_VERIFY( getPropertyValue( GetPropertyName( BASEPROPERTY_IMAGE_SCALE_MODE ) ) >>= nImageScaleMode ); + return nImageScaleMode; + } + + + void SAL_CALL AnimatedImagesControlModel::setScaleMode( ::sal_Int16 i_scaleMode ) + { + setPropertyValue( GetPropertyName( BASEPROPERTY_IMAGE_SCALE_MODE ), Any( i_scaleMode ) ); + } + + + ::sal_Int32 SAL_CALL AnimatedImagesControlModel::getImageSetCount( ) + { + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw DisposedException(); + + return maImageSets.size(); + } + + + Sequence< OUString > SAL_CALL AnimatedImagesControlModel::getImageSet( ::sal_Int32 i_index ) + { + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this ); + + return maImageSets[ i_index ]; + } + + + void SAL_CALL AnimatedImagesControlModel::insertImageSet( ::sal_Int32 i_index, const Sequence< OUString >& i_imageURLs ) + { + std::unique_lock aGuard( m_aMutex ); + // sanity checks + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this, true ); + + // actual insertion + maImageSets.insert( maImageSets.begin() + i_index, i_imageURLs ); + + // listener notification + lcl_notify( aGuard, maContainerListeners, &XContainerListener::elementInserted, i_index, i_imageURLs, *this ); + } + + + void SAL_CALL AnimatedImagesControlModel::replaceImageSet( ::sal_Int32 i_index, const Sequence< OUString >& i_imageURLs ) + { + std::unique_lock aGuard( m_aMutex ); + // sanity checks + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this ); + + // actual insertion + maImageSets[ i_index ] = i_imageURLs; + + // listener notification + lcl_notify( aGuard, maContainerListeners, &XContainerListener::elementReplaced, i_index, i_imageURLs, *this ); + } + + + void SAL_CALL AnimatedImagesControlModel::removeImageSet( ::sal_Int32 i_index ) + { + std::unique_lock aGuard( m_aMutex ); + // sanity checks + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this ); + + // actual removal + ::std::vector< Sequence< OUString > >::iterator removalPos = maImageSets.begin() + i_index; + Sequence< OUString > aRemovedElement( *removalPos ); + maImageSets.erase( removalPos ); + + // listener notification + lcl_notify( aGuard, maContainerListeners, &XContainerListener::elementRemoved, i_index, aRemovedElement, *this ); + } + + + void SAL_CALL AnimatedImagesControlModel::addContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard( m_aMutex ); + maContainerListeners.addInterface( aGuard, i_listener ); + } + + + void SAL_CALL AnimatedImagesControlModel::removeContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard( m_aMutex ); + maContainerListeners.removeInterface( aGuard, i_listener ); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_AnimatedImagesControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new AnimatedImagesControl()); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_AnimatedImagesControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::AnimatedImagesControlModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/controlmodelcontainerbase.cxx b/toolkit/source/controls/controlmodelcontainerbase.cxx new file mode 100644 index 0000000000..8c9bba0890 --- /dev/null +++ b/toolkit/source/controls/controlmodelcontainerbase.cxx @@ -0,0 +1,1789 @@ +/* -*- 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 <controls/controlmodelcontainerbase.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/safeint.hxx> +#include <osl/mutex.hxx> +#include <helper/property.hxx> +#include <helper/servicenames.hxx> +#include <controls/geometrycontrolmodel.hxx> +#include <toolkit/controls/unocontrols.hxx> +#include <controls/formattedcontrol.hxx> +#include <controls/roadmapcontrol.hxx> +#include <controls/tkscrollbar.hxx> +#include <controls/tabpagemodel.hxx> +#include <controls/stdtabcontroller.hxx> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakagg.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/outdev.hxx> +#include <comphelper/types.hxx> + +#include "tree/treecontrol.hxx" +#include "grid/gridcontrol.hxx" +#include <controls/tabpagecontainer.hxx> + +#include <map> +#include <algorithm> +#include <tools/urlobj.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> +#include <controls/dialogcontrol.hxx> + +#include <helper/unopropertyarrayhelper.hxx> +#include "controlmodelcontainerbase_internal.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace toolkit; + +constexpr OUString PROPERTY_RESOURCERESOLVER = u"ResourceResolver"_ustr; + + +namespace +{ + const Sequence< OUString >& lcl_getLanguageDependentProperties() + { + // note: properties must be sorted + static Sequence<OUString> s_aLanguageDependentProperties{ "HelpText", "Title" }; + return s_aLanguageDependentProperties; + } + +// functor for disposing a control model +struct DisposeControlModel +{ + void operator()( Reference< XControlModel >& _rxModel ) + { + try + { + ::comphelper::disposeComponent( _rxModel ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while disposing a component" ); + } + } +}; + +} + + +// functor for cloning a control model, and insertion into a target list +struct CloneControlModel +{ +private: + ControlModelContainerBase::UnoControlModelHolderVector& m_rTargetVector; + +public: + explicit CloneControlModel( ControlModelContainerBase::UnoControlModelHolderVector& _rTargetVector ) + :m_rTargetVector( _rTargetVector ) + { + } + + void operator()( const ControlModelContainerBase::UnoControlModelHolder& _rSource ) + { + // clone the source object + Reference< XCloneable > xCloneSource( _rSource.first, UNO_QUERY ); + Reference< XControlModel > xClone( xCloneSource->createClone(), UNO_QUERY ); + // add to target list + m_rTargetVector.emplace_back( xClone, _rSource.second ); + } +}; + + +// functor for comparing a XControlModel with a given reference +struct CompareControlModel +{ +private: + Reference< XControlModel > m_xReference; +public: + explicit CompareControlModel( const Reference< XControlModel >& _rxReference ) : m_xReference( _rxReference ) { } + + bool operator()( const ControlModelContainerBase::UnoControlModelHolder& _rCompare ) + { + return _rCompare.first.get() == m_xReference.get(); + } +}; + +constexpr OUString aTabIndexPropertyNameStr( u"TabIndex"_ustr ); + +ControlModelContainerBase::ControlModelContainerBase( const Reference< XComponentContext >& rxContext ) + :ControlModelContainer_IBase( rxContext ) + ,maContainerListeners( *this ) + ,mbGroupsUpToDate( false ) + ,m_nTabPageId(0) +{ + ImplRegisterProperty(BASEPROPERTY_ENABLED); +} + +ControlModelContainerBase::ControlModelContainerBase( const ControlModelContainerBase& rModel ) + : ControlModelContainer_IBase( rModel ) + , maContainerListeners( *this ) + , mbGroupsUpToDate( false ) + , m_nTabPageId( rModel.m_nTabPageId ) +{ +} + +ControlModelContainerBase::~ControlModelContainerBase() +{ + maModels.clear(); + mbGroupsUpToDate = false; +} + +Any ControlModelContainerBase::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString::createFromAscii( szServiceName_UnoControlDialog ); + break; + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& ControlModelContainerBase::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +void SAL_CALL ControlModelContainerBase::dispose( ) +{ + + // tell our listeners + { + std::unique_lock aGuard( m_aMutex ); + + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< XAggregation* >( static_cast< ::cppu::OWeakAggObject* >( this ) ); + + maContainerListeners.disposeAndClear( aGuard, aDisposeEvent ); + maChangeListeners.disposeAndClear( aGuard, aDisposeEvent ); + } + + + // call the base class + UnoControlModel::dispose(); + + + // dispose our child models + // for this, collect the models (we collect them from maModels, and this is modified when disposing children) + ::std::vector< Reference< XControlModel > > aChildModels( maModels.size() ); + + ::std::transform( + maModels.begin(), maModels.end(), // source range + aChildModels.begin(), // target location + []( const UnoControlModelHolder& rUnoControlModelHolder ) + { return rUnoControlModelHolder.first; } // operation to apply -> select the XControlModel part + ); + + // now dispose + ::std::for_each( aChildModels.begin(), aChildModels.end(), DisposeControlModel() ); + aChildModels.clear(); + + mbGroupsUpToDate = false; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > ControlModelContainerBase::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +void ControlModelContainerBase::Clone_Impl(ControlModelContainerBase& _rClone) const +{ + // clone all children + ::std::for_each( + maModels.begin(), maModels.end(), + CloneControlModel( _rClone.maModels ) + ); +} +rtl::Reference<UnoControlModel> ControlModelContainerBase::Clone() const +{ + // clone the container itself + rtl::Reference<ControlModelContainerBase> pClone = new ControlModelContainerBase( *this ); + Clone_Impl(*pClone); + + return pClone; +} + +ControlModelContainerBase::UnoControlModelHolderVector::iterator ControlModelContainerBase::ImplFindElement( std::u16string_view rName ) +{ + return ::std::find_if( maModels.begin(), maModels.end(), [&](const UnoControlModelHolder& elem) { return elem.second == rName; }); +} + +// ::XMultiServiceFactory +Reference< XInterface > ControlModelContainerBase::createInstance( const OUString& aServiceSpecifier ) +{ + SolarMutexGuard aGuard; + + rtl::Reference<OGeometryControlModel_Base> pNewModel; + + if ( aServiceSpecifier == "com.sun.star.awt.UnoControlEditModel" ) + pNewModel = new OGeometryControlModel< UnoControlEditModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFormattedFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlFormattedFieldModel >( m_xContext); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFileControlModel" ) + pNewModel = new OGeometryControlModel< UnoControlFileControlModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlButtonModel" ) + pNewModel = new OGeometryControlModel< UnoControlButtonModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlImageControlModel" ) + pNewModel = new OGeometryControlModel< UnoControlImageControlModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRadioButtonModel" ) + pNewModel = new OGeometryControlModel< UnoControlRadioButtonModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCheckBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlCheckBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedHyperlinkModel" ) + pNewModel = new OGeometryControlModel< UnoControlFixedHyperlinkModel >( m_xContext ); + else if ( aServiceSpecifier == "stardiv.vcl.controlmodel.FixedText" ) + pNewModel = new OGeometryControlModel< UnoControlFixedTextModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlGroupBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlGroupBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlListBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlListBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlComboBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlComboBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlDateFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlDateFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlTimeFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlTimeFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlNumericFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlNumericFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCurrencyFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlCurrencyFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlPatternFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlPatternFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlProgressBarModel" ) + pNewModel = new OGeometryControlModel< UnoControlProgressBarModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlScrollBarModel" ) + pNewModel = new OGeometryControlModel< UnoControlScrollBarModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedLineModel" ) + pNewModel = new OGeometryControlModel< UnoControlFixedLineModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRoadmapModel" ) + pNewModel = new OGeometryControlModel< UnoControlRoadmapModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tree.TreeControlModel" ) + pNewModel = new OGeometryControlModel< UnoTreeModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.grid.UnoControlGridModel" ) + pNewModel = new OGeometryControlModel< UnoGridModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageContainerModel" ) + pNewModel = new OGeometryControlModel< UnoControlTabPageContainerModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoMultiPageModel" ) + pNewModel = new OGeometryControlModel< UnoMultiPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageModel" ) + pNewModel = new OGeometryControlModel< UnoControlTabPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoPageModel" ) + pNewModel = new OGeometryControlModel< UnoPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoFrameModel" ) + pNewModel = new OGeometryControlModel< UnoFrameModel >( m_xContext ); + + if ( !pNewModel ) + { + Reference< XInterface > xObject = m_xContext->getServiceManager()->createInstanceWithContext(aServiceSpecifier, m_xContext); + Reference< XServiceInfo > xSI( xObject, UNO_QUERY ); + Reference< XCloneable > xCloneAccess( xSI, UNO_QUERY ); + Reference< XAggregation > xAgg( xCloneAccess, UNO_QUERY ); + if ( xAgg.is() ) + { + if ( xSI->supportsService("com.sun.star.awt.UnoControlModel") ) + { + // release 3 of the 4 references we have to the object + xAgg.clear(); + xSI.clear(); + xObject.clear(); + + pNewModel = new OCommonGeometryControlModel( xCloneAccess, aServiceSpecifier ); + } + } + } + + return cppu::getXWeak(pNewModel.get()); +} + +Reference< XInterface > ControlModelContainerBase::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& i_arguments ) +{ + const Reference< XInterface > xInstance( createInstance( ServiceSpecifier ) ); + const Reference< XInitialization > xInstanceInit( xInstance, UNO_QUERY ); + ENSURE_OR_RETURN( xInstanceInit.is(), "ControlModelContainerBase::createInstanceWithArguments: can't pass the arguments!", xInstance ); + xInstanceInit->initialize( i_arguments ); + return xInstance; +} + +Sequence< OUString > ControlModelContainerBase::getAvailableServiceNames() +{ + return { "com.sun.star.awt.UnoControlEditModel", + "com.sun.star.awt.UnoControlFormattedFieldModel", + "com.sun.star.awt.UnoControlFileControlModel", + "com.sun.star.awt.UnoControlButtonModel", + "com.sun.star.awt.UnoControlImageControlModel", + "com.sun.star.awt.UnoControlRadioButtonModel", + "com.sun.star.awt.UnoControlCheckBoxModel", + "com.sun.star.awt.UnoControlFixedTextModel", + "com.sun.star.awt.UnoControlGroupBoxModel", + "com.sun.star.awt.UnoControlListBoxModel", + "com.sun.star.awt.UnoControlComboBoxModel", + "com.sun.star.awt.UnoControlDateFieldModel", + "com.sun.star.awt.UnoControlTimeFieldModel", + "com.sun.star.awt.UnoControlNumericFieldModel", + "com.sun.star.awt.UnoControlCurrencyFieldModel", + "com.sun.star.awt.UnoControlPatternFieldModel", + "com.sun.star.awt.UnoControlProgressBarModel", + "com.sun.star.awt.UnoControlScrollBarModel", + "com.sun.star.awt.UnoControlFixedLineModel", + "com.sun.star.awt.UnoControlRoadmapModel", + "com.sun.star.awt.tree.TreeControlModel", + "com.sun.star.awt.grid.UnoControlGridModel", + "com.sun.star.awt.UnoControlFixedHyperlinkModel", + "com.sun.star.awt.tab.UnoControlTabPageContainerModel", + "com.sun.star.awt.tab.UnoControlTabPageModel", + "com.sun.star.awt.UnoMultiPageModel", + "com.sun.star.awt.UnoFrameModel" + }; +} + +// XContainer +void ControlModelContainerBase::addContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void ControlModelContainerBase::removeContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + +// XElementAccess +Type ControlModelContainerBase::getElementType() +{ + Type aType = cppu::UnoType<XControlModel>::get(); + return aType; +} + +sal_Bool ControlModelContainerBase::hasElements() +{ + return !maModels.empty(); +} + +// XNameContainer, XNameReplace, XNameAccess +void ControlModelContainerBase::replaceByName( const OUString& aName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xNewModel; + aElement >>= xNewModel; + if ( !xNewModel.is() ) + throw IllegalArgumentException(); + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + throw NoSuchElementException(); + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // add this to an existing container, in this case a name nested in the containment + // hierarchy of the added control could contain a name clash, if we have access to the + // list of global names then recursively check for previously existing names (we need + // to do this obviously before the 'this' objects container is updated) + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + if ( xAllChildren.is() ) + { + // remove old control (and children) from global list of containers + updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() ); + // Add new control (and containers if they exist) + updateUserFormChildren( xAllChildren, aName, Insert, xNewModel ); + } + // stop listening at the old model + stopControlListening( aElementPos->first ); + Reference< XControlModel > xReplaced( aElementPos->first ); + // remember the new model, and start listening + aElementPos->first = xNewModel; + startControlListening( xNewModel ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.ReplacedElement <<= xReplaced; + aEvent.Accessor <<= aName; + + // notify the container listener + maContainerListeners.elementReplaced( aEvent ); + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + +Any ControlModelContainerBase::getByName( const OUString& aName ) +{ + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + throw NoSuchElementException(); + + return Any( aElementPos->first ); +} + +Sequence< OUString > ControlModelContainerBase::getElementNames() +{ + Sequence< OUString > aNames( maModels.size() ); + + ::std::transform( + maModels.begin(), maModels.end(), // source range + aNames.getArray(), // target range + []( const UnoControlModelHolder& rUnoControlModelHolder ) + { return rUnoControlModelHolder.second; } // operator to apply: select the second element (the name) + ); + + return aNames; +} + +sal_Bool ControlModelContainerBase::hasByName( const OUString& aName ) +{ + return maModels.end() != ImplFindElement( aName ); +} + +void ControlModelContainerBase::insertByName( const OUString& aName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xM; + aElement >>= xM; + + if ( xM.is() ) + { + Reference< beans::XPropertySet > xProps( xM, UNO_QUERY ); + if ( xProps.is() ) + { + + Reference< beans::XPropertySetInfo > xPropInfo = xProps->getPropertySetInfo(); + + const OUString& sImageSourceProperty = GetPropertyName( BASEPROPERTY_IMAGEURL ); + if ( xPropInfo->hasPropertyByName( sImageSourceProperty ) && ImplHasProperty(BASEPROPERTY_DIALOGSOURCEURL) ) + { + Any aUrl = xProps->getPropertyValue( sImageSourceProperty ); + + OUString absoluteUrl = + getPhysicalLocation( getPropertyValue( GetPropertyName( BASEPROPERTY_DIALOGSOURCEURL ) ), aUrl ); + + aUrl <<= absoluteUrl; + + xProps->setPropertyValue( sImageSourceProperty , aUrl ); + } + } + } + + + if ( aName.isEmpty() || !xM.is() ) + throw IllegalArgumentException(); + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() != aElementPos ) + throw ElementExistException(); + + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // add this to an existing container, in this case a name nested in the containment + // hierarchy of the added control could contain a name clash, if we have access to the + // list of global names then we need to recursively check for previously existing + // names (we need to do this obviously before the 'this' objects container is updated) + // remove old control (and children) from global list of containers + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + + if ( xAllChildren.is() ) + updateUserFormChildren( xAllChildren, aName, Insert, xM ); + maModels.emplace_back( xM, aName ); + mbGroupsUpToDate = false; + startControlListening( xM ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementInserted( aEvent ); + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + +void ControlModelContainerBase::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + throw NoSuchElementException(); + + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // removing this control from an existing container, in this case all nested names in + // the containment hierarchy of the control to be removed need to be removed from the global + // names cache (we need to do this obviously before the 'this' objects container is updated) + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + if ( xAllChildren.is() ) + updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element <<= aElementPos->first; + aEvent.Accessor <<= aName; + maContainerListeners.elementRemoved( aEvent ); + + stopControlListening( aElementPos->first ); + Reference< XPropertySet > xPS( aElementPos->first, UNO_QUERY ); + maModels.erase( aElementPos ); + mbGroupsUpToDate = false; + + if ( xPS.is() ) + { + try + { + xPS->setPropertyValue( PROPERTY_RESOURCERESOLVER, Any( Reference< resource::XStringResourceResolver >() ) ); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + + +sal_Bool SAL_CALL ControlModelContainerBase::getGroupControl( ) +{ + return true; +} + + +void SAL_CALL ControlModelContainerBase::setGroupControl( sal_Bool ) +{ + SAL_WARN("toolkit", "explicit grouping not supported" ); +} + + +void SAL_CALL ControlModelContainerBase::setControlModels( const Sequence< Reference< XControlModel > >& _rControls ) +{ + SolarMutexGuard aGuard; + + // set the tab indexes according to the order of models in the sequence + + sal_Int16 nTabIndex = 1; + + for ( auto const & control : _rControls ) + { + // look up the control in our own structure. This is to prevent invalid arguments + UnoControlModelHolderVector::const_iterator aPos = + ::std::find_if( + maModels.begin(), maModels.end(), + CompareControlModel( control ) + ); + if ( maModels.end() != aPos ) + { + // okay, this is an existent model + // now set the TabIndex property (if applicable) + Reference< XPropertySet > xProps( aPos->first, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xProps.is() ) + xPSI = xProps->getPropertySetInfo(); + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + xProps->setPropertyValue( aTabIndexPropertyNameStr, Any( nTabIndex++ ) ); + } + mbGroupsUpToDate = false; + } +} + + +typedef ::std::multimap< sal_Int32, Reference< XControlModel > > MapIndexToModel; + + +Sequence< Reference< XControlModel > > SAL_CALL ControlModelContainerBase::getControlModels( ) +{ + SolarMutexGuard aGuard; + + MapIndexToModel aSortedModels; + // will be the sorted container of all models which have a tab index property + ::std::vector< Reference< XControlModel > > aUnindexedModels; + // will be the container of all models which do not have a tab index property + + for ( const auto& rModel : maModels ) + { + Reference< XControlModel > xModel( rModel.first ); + + // see if the model has a TabIndex property + Reference< XPropertySet > xControlProps( xModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xControlProps.is() ) + xPSI = xControlProps->getPropertySetInfo( ); + DBG_ASSERT( xPSI.is(), "ControlModelContainerBase::getControlModels: invalid child model!" ); + + // has it? + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + { // yes + sal_Int32 nTabIndex = -1; + xControlProps->getPropertyValue( aTabIndexPropertyNameStr ) >>= nTabIndex; + + aSortedModels.emplace( nTabIndex, xModel ); + } + else if ( xModel.is() ) + // no, it hasn't, but we have to include it, anyway + aUnindexedModels.push_back( xModel ); + } + + // okay, here we have a container of all our models, sorted by tab index, + // plus a container of "unindexed" models + // -> merge them + Sequence< Reference< XControlModel > > aReturn( aUnindexedModels.size() + aSortedModels.size() ); + ::std::transform( + aSortedModels.begin(), aSortedModels.end(), + ::std::copy( aUnindexedModels.begin(), aUnindexedModels.end(), aReturn.getArray() ), + [] ( const MapIndexToModel::value_type& entryIndexToModel ) + { return entryIndexToModel.second; } + ); + + return aReturn; +} + + +void SAL_CALL ControlModelContainerBase::setGroup( const Sequence< Reference< XControlModel > >&, const OUString& ) +{ + // not supported. We have only implicit grouping: + // We only have a sequence of control models, and we _know_ (yes, that's a HACK relying on + // implementation details) that VCL does grouping according to the order of controls automatically + // At least VCL does this for all we're interested in: Radio buttons. + SAL_WARN("toolkit", "grouping not supported" ); +} + +////----- XInitialization ------------------------------------------------------------------- +void SAL_CALL ControlModelContainerBase::initialize (const Sequence<Any>& rArguments) +{ + if ( rArguments.getLength() == 1 ) + { + sal_Int16 nPageId = -1; + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + } + else + m_nTabPageId = -1; +} +::sal_Int16 SAL_CALL ControlModelContainerBase::getTabPageID() +{ + return m_nTabPageId; +} +sal_Bool SAL_CALL ControlModelContainerBase::getEnabled() +{ + SolarMutexGuard aGuard; + bool bEnabled = false; + getPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED)) >>= bEnabled; + return bEnabled; +} +void SAL_CALL ControlModelContainerBase::setEnabled( sal_Bool _enabled ) +{ + SolarMutexGuard aGuard; + setPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED), Any(_enabled)); +} +OUString SAL_CALL ControlModelContainerBase::getTitle() +{ + SolarMutexGuard aGuard; + OUString sTitle; + getPropertyValue(GetPropertyName(BASEPROPERTY_TITLE)) >>= sTitle; + return sTitle; +} +void SAL_CALL ControlModelContainerBase::setTitle( const OUString& _title ) +{ + SolarMutexGuard aGuard; + setPropertyValue(GetPropertyName(BASEPROPERTY_TITLE),Any(_title)); +} +OUString SAL_CALL ControlModelContainerBase::getImageURL() +{ + return m_sImageURL; +} +void SAL_CALL ControlModelContainerBase::setImageURL( const OUString& _imageurl ) +{ + m_sImageURL = _imageurl; + SolarMutexGuard aGuard; + setPropertyValue(GetPropertyName(BASEPROPERTY_IMAGEURL), Any(_imageurl)); +} +OUString SAL_CALL ControlModelContainerBase::getToolTip() +{ + return m_sTooltip; +} +void SAL_CALL ControlModelContainerBase::setToolTip( const OUString& _tooltip ) +{ + m_sTooltip = _tooltip; +} + + +namespace +{ + enum GroupingMachineState + { + eLookingForGroup, + eExpandingGroup + }; + + + sal_Int32 lcl_getDialogStep( const Reference< XControlModel >& _rxModel ) + { + sal_Int32 nStep = 0; + try + { + Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY ); + xModelProps->getPropertyValue( u"Step"_ustr ) >>= nStep; + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while determining the dialog page" ); + } + return nStep; + } +} + + +sal_Int32 SAL_CALL ControlModelContainerBase::getGroupCount( ) +{ + SolarMutexGuard aGuard; + + implUpdateGroupStructure(); + + return maGroups.size(); +} + + +void SAL_CALL ControlModelContainerBase::getGroup( sal_Int32 _nGroup, Sequence< Reference< XControlModel > >& _rGroup, OUString& _rName ) +{ + SolarMutexGuard aGuard; + + implUpdateGroupStructure(); + + if ( ( _nGroup < 0 ) || ( o3tl::make_unsigned(_nGroup) >= maGroups.size() ) ) + { + SAL_WARN("toolkit", "invalid argument and I am not allowed to throw exception!" ); + _rGroup.realloc( 0 ); + _rName.clear(); + } + else + { + AllGroups::const_iterator aGroupPos = maGroups.begin() + _nGroup; + _rGroup.realloc( aGroupPos->size() ); + // copy the models + ::std::copy( aGroupPos->begin(), aGroupPos->end(), _rGroup.getArray() ); + // give the group a name + _rName = OUString::number( _nGroup ); + } +} + + +void SAL_CALL ControlModelContainerBase::getGroupByName( const OUString& _rName, Sequence< Reference< XControlModel > >& _rGroup ) +{ + SolarMutexGuard aGuard; + + OUString sDummyName; + getGroup( _rName.toInt32( ), _rGroup, sDummyName ); +} + + +void SAL_CALL ControlModelContainerBase::addChangesListener( const Reference< XChangesListener >& _rxListener ) +{ + std::unique_lock g(m_aMutex); + maChangeListeners.addInterface( g, _rxListener ); +} + + +void SAL_CALL ControlModelContainerBase::removeChangesListener( const Reference< XChangesListener >& _rxListener ) +{ + std::unique_lock g(m_aMutex); + maChangeListeners.removeInterface( g, _rxListener ); +} + + +void ControlModelContainerBase::implNotifyTabModelChange( const OUString& _rAccessor ) +{ + // multiplex to our change listeners: + // the changes event + ChangesEvent aEvent; + aEvent.Source = *this; + aEvent.Base <<= aEvent.Source; // the "base of the changes root" is also ourself + aEvent.Changes.realloc( 1 ); // exactly one change + aEvent.Changes.getArray()[ 0 ].Accessor <<= _rAccessor; + + + std::unique_lock g(m_aMutex); + std::vector< Reference< css::util::XChangesListener > > aChangeListeners( maChangeListeners.getElements(g) ); + g.unlock(); + for ( const auto& rListener : aChangeListeners ) + rListener->changesOccurred( aEvent ); +} + + +void ControlModelContainerBase::implUpdateGroupStructure() +{ + if ( mbGroupsUpToDate ) + // nothing to do + return; + + // conditions for a group: + // * all elements of the group are radio buttons + // * all elements of the group are on the same dialog page + // * in the overall control order (determined by the tab index), all elements are subsequent + + maGroups.clear(); + + const Sequence< Reference< XControlModel > > aControlModels = getControlModels(); + + // in extreme we have as much groups as controls + maGroups.reserve( aControlModels.getLength() ); + + GroupingMachineState eState = eLookingForGroup; // the current state of our machine + Reference< XServiceInfo > xModelSI; // for checking for a radio button + AllGroups::iterator aCurrentGroup = maGroups.end(); // the group which we're currently building + sal_Int32 nCurrentGroupStep = -1; // the step which all controls of the current group belong to + + + for ( const Reference< XControlModel >& rControlModel : aControlModels ) + { + // we'll need this in every state + xModelSI.set(rControlModel, css::uno::UNO_QUERY); + // is it a radio button? + bool bIsRadioButton = xModelSI.is() && xModelSI->supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" ); + + switch ( eState ) + { + case eLookingForGroup: + { + if ( !bIsRadioButton ) + // this is no radio button -> still looking for the beginning of a group + continue; + // the current model is a radio button + // -> we found the beginning of a new group + // create the place for this group + size_t nGroups = maGroups.size(); + maGroups.resize( nGroups + 1 ); + aCurrentGroup = maGroups.begin() + nGroups; + // and add the (only, til now) member + aCurrentGroup->push_back( rControlModel ); + + // get the step which all controls of this group now have to belong to + nCurrentGroupStep = lcl_getDialogStep( rControlModel ); + // new state: looking for further members + eState = eExpandingGroup; + + } + break; + + case eExpandingGroup: + { + if ( !bIsRadioButton ) + { // no radio button -> the group is done + aCurrentGroup = maGroups.end(); + eState = eLookingForGroup; + continue; + } + + // it is a radio button - is it on the proper page? + const sal_Int32 nThisModelStep = lcl_getDialogStep( rControlModel ); + if ( ( nThisModelStep == nCurrentGroupStep ) // the current button is on the same dialog page + || ( 0 == nThisModelStep ) // the current button appears on all pages + ) + { + // -> it belongs to the same group + aCurrentGroup->push_back( rControlModel ); + // state still is eExpandingGroup - we're looking for further elements + eState = eExpandingGroup; + + continue; + } + + // it's a radio button, but on a different page + // -> we open a new group for it + + + // open a new group + size_t nGroups = maGroups.size(); + maGroups.resize( nGroups + 1 ); + aCurrentGroup = maGroups.begin() + nGroups; + // and add the (only, til now) member + aCurrentGroup->push_back( rControlModel ); + + nCurrentGroupStep = nThisModelStep; + + // state is the same: we still are looking for further elements of the current group + eState = eExpandingGroup; + } + break; + } + } + + mbGroupsUpToDate = true; +} + + +void SAL_CALL ControlModelContainerBase::propertyChange( const PropertyChangeEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + + DBG_ASSERT( _rEvent.PropertyName == "TabIndex", + "ControlModelContainerBase::propertyChange: not listening for this property!" ); + + // the accessor for the changed element + OUString sAccessor; + UnoControlModelHolderVector::const_iterator aPos = + ::std::find_if( + maModels.begin(), maModels.end(), + CompareControlModel( Reference< XControlModel >( _rEvent.Source, UNO_QUERY ) ) + ); + OSL_ENSURE( maModels.end() != aPos, "ControlModelContainerBase::propertyChange: don't know this model!" ); + if ( maModels.end() != aPos ) + sAccessor = aPos->second; + + // our groups are not up-to-date + mbGroupsUpToDate = false; + + // notify + implNotifyTabModelChange( sAccessor ); +} + + +void SAL_CALL ControlModelContainerBase::disposing( const EventObject& /*rEvent*/ ) +{ +} + + +void ControlModelContainerBase::startControlListening( const Reference< XControlModel >& _rxChildModel ) +{ + SolarMutexGuard aGuard; + + Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xModelProps.is() ) + xPSI = xModelProps->getPropertySetInfo(); + + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + xModelProps->addPropertyChangeListener( aTabIndexPropertyNameStr, this ); +} + + +void ControlModelContainerBase::stopControlListening( const Reference< XControlModel >& _rxChildModel ) +{ + SolarMutexGuard aGuard; + + Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xModelProps.is() ) + xPSI = xModelProps->getPropertySetInfo(); + + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + xModelProps->removePropertyChangeListener( aTabIndexPropertyNameStr, this ); +} + + +// = class ResourceListener + + +ResourceListener::ResourceListener( + const Reference< util::XModifyListener >& rListener ) : + m_xListener( rListener ), + m_bListening( false ) +{ +} + +ResourceListener::~ResourceListener() +{ +} + +// XInterface +Any SAL_CALL ResourceListener::queryInterface( const Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< XModifyListener* >( this ), + static_cast< XEventListener* >( this )); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ResourceListener::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ResourceListener::release() noexcept +{ + OWeakObject::release(); +} + +void ResourceListener::startListening( + const Reference< resource::XStringResourceResolver >& rResource ) +{ + { + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + bool bListening( m_bListening ); + bool bResourceSet( m_xResource.is() ); + aGuard.unlock(); + // --- SAFE --- + + if ( bListening && bResourceSet ) + stopListening(); + + // --- SAFE --- + aGuard.lock(); + m_xResource = rResource; + aGuard.unlock(); + // --- SAFE --- + } + + if ( !rResource.is() ) + return; + + try + { + rResource->addModifyListener( this ); + + // --- SAFE --- + std::scoped_lock aGuard( m_aMutex ); + m_bListening = true; + // --- SAFE --- + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +void ResourceListener::stopListening() +{ + Reference< util::XModifyBroadcaster > xModifyBroadcaster; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + if ( m_bListening && m_xResource.is() ) + xModifyBroadcaster = m_xResource; + aGuard.unlock(); + // --- SAFE --- + + if ( !xModifyBroadcaster.is() ) + return; + + try + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + m_xResource.clear(); + aGuard.unlock(); + // --- SAFE --- + + xModifyBroadcaster->removeModifyListener( this ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +// XModifyListener +void SAL_CALL ResourceListener::modified( + const lang::EventObject& aEvent ) +{ + Reference< util::XModifyListener > xListener; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + xListener = m_xListener; + aGuard.unlock(); + // --- SAFE --- + + if ( !xListener.is() ) + return; + + try + { + xListener->modified( aEvent ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +// XEventListener +void SAL_CALL ResourceListener::disposing( + const EventObject& Source ) +{ + Reference< lang::XEventListener > xListener; + Reference< resource::XStringResourceResolver > xResource; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + Reference< XInterface > xIfacRes( m_xResource, UNO_QUERY ); + Reference< XInterface > xIfacList( m_xListener, UNO_QUERY ); + aGuard.unlock(); + // --- SAFE --- + + if ( Source.Source == xIfacRes ) + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + xResource = m_xResource; + xListener = m_xListener; + m_xResource.clear(); + aGuard.unlock(); + // --- SAFE --- + + if ( xListener.is() ) + { + try + { + xListener->disposing( Source ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } + else if ( Source.Source == xIfacList ) + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + xListener = m_xListener; + xResource = m_xResource; + m_xResource.clear(); + m_xListener.clear(); + aGuard.unlock(); + // --- SAFE --- + + // Remove ourself as listener from resource resolver + if ( xResource.is() ) + { + try + { + xResource->removeModifyListener( this ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } +} + + + +ControlContainerBase::ControlContainerBase( const Reference< XComponentContext >& rxContext ) + :m_xContext(rxContext) + ,mbSizeModified(false) + ,mbPosModified(false) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; + mxListener = new ResourceListener( Reference< util::XModifyListener >(this) ); +} + +ControlContainerBase::~ControlContainerBase() +{ +} + +void ControlContainerBase::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); +} + +void ControlContainerBase::ImplInsertControl( Reference< XControlModel > const & rxModel, const OUString& rName ) +{ + Reference< XPropertySet > xP( rxModel, UNO_QUERY ); + + OUString aDefCtrl; + xP->getPropertyValue( GetPropertyName( BASEPROPERTY_DEFAULTCONTROL ) ) >>= aDefCtrl; + Reference < XControl > xCtrl( m_xContext->getServiceManager()->createInstanceWithContext(aDefCtrl, m_xContext), UNO_QUERY ); + + DBG_ASSERT( xCtrl.is(), "ControlContainerBase::ImplInsertControl: could not create the control!" ); + if ( xCtrl.is() ) + { + xCtrl->setModel( rxModel ); + addControl( rName, xCtrl ); + // will implicitly call addingControl, where we can add the PropertiesChangeListener to the model + // (which we formerly did herein) + // 08.01.2001 - 96008 - fs@openoffice.org + + ImplSetPosSize( xCtrl ); + } +} + +void ControlContainerBase::ImplRemoveControl( Reference< XControlModel > const & rxModel ) +{ + Sequence< Reference< XControl > > aControls = getControls(); + Reference< XControl > xCtrl = StdTabController::FindControl( aControls, rxModel ); + if ( xCtrl.is() ) + { + removeControl( xCtrl ); + try + { + xCtrl->dispose(); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + +void ControlContainerBase::ImplSetPosSize( Reference< XControl >& rxCtrl ) +{ + Reference< XPropertySet > xP( rxCtrl->getModel(), UNO_QUERY ); + + sal_Int32 nX = 0, nY = 0, nWidth = 0, nHeight = 0; + xP->getPropertyValue("PositionX") >>= nX; + xP->getPropertyValue("PositionY") >>= nY; + xP->getPropertyValue("Width") >>= nWidth; + xP->getPropertyValue("Height") >>= nHeight; + MapMode aMode( MapUnit::MapAppFont ); + OutputDevice*pOutDev = Application::GetDefaultDevice(); + if ( pOutDev ) + { + ::Size aTmp( nX, nY ); + aTmp = pOutDev->LogicToPixel( aTmp, aMode ); + nX = aTmp.Width(); + nY = aTmp.Height(); + aTmp = ::Size( nWidth, nHeight ); + aTmp = pOutDev->LogicToPixel( aTmp, aMode ); + nWidth = aTmp.Width(); + nHeight = aTmp.Height(); + } + else + { + Reference< XWindowPeer > xPeer = ImplGetCompatiblePeer(); + Reference< XDevice > xD( xPeer, UNO_QUERY ); + + SimpleFontMetric aFM; + FontDescriptor aFD; + Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_FONTDESCRIPTOR ) ); + aVal >>= aFD; + if ( !aFD.StyleName.isEmpty() ) + { + Reference< XFont > xFont = xD->getFont( aFD ); + aFM = xFont->getFontMetric(); + } + else + { + Reference< XGraphics > xG = xD->createGraphics(); + aFM = xG->getFontMetric(); + } + + sal_Int16 nH = aFM.Ascent + aFM.Descent; + sal_Int16 nW = nH/2; // calculate average width?! + + nX *= nW; + nX /= 4; + nWidth *= nW; + nWidth /= 4; + nY *= nH; + nY /= 8; + nHeight *= nH; + nHeight /= 8; + } + Reference < XWindow > xW( rxCtrl, UNO_QUERY ); + xW->setPosSize( nX, nY, nWidth, nHeight, PosSize::POSSIZE ); +} + +void ControlContainerBase::dispose() +{ + EventObject aEvt; + aEvt.Source = getXWeak(); + // Notify our listener helper about dispose + // --- SAFE --- + + SolarMutexClearableGuard aGuard; + Reference< XEventListener > xListener = mxListener; + mxListener.clear(); + aGuard.clear(); + // --- SAFE --- + + if ( xListener.is() ) + xListener->disposing( aEvt ); + UnoControlContainer::dispose(); +} + +void SAL_CALL ControlContainerBase::disposing( + const EventObject& Source ) +{ + UnoControlContainer::disposing( Source ); +} + +sal_Bool ControlContainerBase::setModel( const Reference< XControlModel >& rxModel ) +{ + SolarMutexGuard aGuard; + + // destroy the old tab controller, if existent + if ( mxTabController.is() ) + { + mxTabController->setModel( nullptr ); // just to be sure, should not be necessary + removeTabController( mxTabController ); + ::comphelper::disposeComponent( mxTabController ); // just to be sure, should not be necessary + mxTabController.clear(); + } + + if ( getModel().is() ) + { + const Sequence< Reference< XControl > > aControls = getControls(); + + for ( const Reference< XControl >& rCtrl : aControls ) + removeControl( rCtrl ); + // will implicitly call removingControl, which will remove the PropertyChangeListener + // (which we formerly did herein) + // 08.01.2001 - 96008 - fs@openoffice.org + + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->removeContainerListener( this ); + + Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY ); + if ( xChangeNotifier.is() ) + xChangeNotifier->removeChangesListener( this ); + } + + bool bRet = UnoControl::setModel( rxModel ); + + if ( getModel().is() ) + { + Reference< XNameAccess > xNA( getModel(), UNO_QUERY ); + if ( xNA.is() ) + { + const Sequence< OUString > aNames = xNA->getElementNames(); + + Reference< XControlModel > xCtrlModel; + for( const OUString& rName : aNames ) + { + xNA->getByName( rName ) >>= xCtrlModel; + ImplInsertControl( xCtrlModel, rName ); + } + } + + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->addContainerListener( this ); + + Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY ); + if ( xChangeNotifier.is() ) + xChangeNotifier->addChangesListener( this ); + } + + Reference< XTabControllerModel > xTabbing( getModel(), UNO_QUERY ); + if ( xTabbing.is() ) + { + mxTabController = new StdTabController; + mxTabController->setModel( xTabbing ); + addTabController( mxTabController ); + } + ImplStartListingForResourceEvents(); + + return bRet; +} +void ControlContainerBase::setDesignMode( sal_Bool bOn ) +{ + SolarMutexGuard aGuard; + + UnoControl::setDesignMode( bOn ); + + Sequence< Reference< XControl > > xCtrls = getControls(); + for ( Reference< XControl >& rControl : asNonConstRange(xCtrls) ) + rControl->setDesignMode( bOn ); + + // #109067# in design mode the tab controller is not notified about + // tab index changes, therefore the tab order must be activated + // when switching from design mode to live mode + if ( mxTabController.is() && !bOn ) + mxTabController->activateTabOrder(); +} + +void ControlContainerBase::elementInserted( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + OUString aName; + + Event.Accessor >>= aName; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementInserted: illegal element!" ); + try + { + ImplInsertControl( xModel, aName ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +void ControlContainerBase::elementRemoved( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementRemoved: illegal element!" ); + try + { + ImplRemoveControl( xModel ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +void ControlContainerBase::elementReplaced( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + Event.ReplacedElement >>= xModel; + try + { + OSL_ENSURE( xModel.is(), "ControlContainerBase::elementReplaced: invalid ReplacedElement!" ); + if ( xModel.is() ) + ImplRemoveControl( xModel ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + OUString aName; + Event.Accessor >>= aName; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementReplaced: invalid new element!" ); + try + { + ImplInsertControl( xModel, aName ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +// XPropertiesChangeListener +void ControlContainerBase::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + if( !isDesignMode() && !mbCreatingCompatiblePeer ) + { + auto pEvt = std::find_if(rEvents.begin(), rEvents.end(), + [](const PropertyChangeEvent& rEvt) { + return rEvt.PropertyName == "PositionX" + || rEvt.PropertyName == "PositionY" + || rEvt.PropertyName == "Width" + || rEvt.PropertyName == "Height"; + }); + if (pEvt != rEvents.end()) + { + Reference< XControlModel > xModel( pEvt->Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == getModel().get(); + if ( bOwnModel ) + { + if ( !mbPosModified && !mbSizeModified ) + { + // Don't set new pos/size if we get new values from window listener + Reference< XControl > xThis(this); + ImplSetPosSize( xThis ); + } + } + else + { + Sequence<Reference<XControl> > aControlSequence(getControls()); + Reference<XControl> aControlRef( StdTabController::FindControl( aControlSequence, xModel ) ); + ImplSetPosSize( aControlRef ); + } + } + } + + UnoControlContainer::ImplModelPropertiesChanged( rEvents ); +} + +void ControlContainerBase::addingControl( const Reference< XControl >& _rxControl ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::addingControl( _rxControl ); + + if ( !_rxControl.is() ) + return; + + Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY ); + if ( xProps.is() ) + { + const Sequence< OUString > aNames { + "PositionX", + "PositionY", + "Width", + "Height" + }; + + xProps->addPropertiesChangeListener( aNames, this ); + } +} + +void ControlContainerBase::removingControl( const Reference< XControl >& _rxControl ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::removingControl( _rxControl ); + + if ( _rxControl.is() ) + { + Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY ); + if ( xProps.is() ) + xProps->removePropertiesChangeListener( this ); + } + +} + +void SAL_CALL ControlContainerBase::changesOccurred( const ChangesEvent& ) +{ + SolarMutexGuard aGuard; + // a tab controller model may have changed + + // #109067# in design mode don't notify the tab controller + // about tab index changes + if ( mxTabController.is() && !mbDesignMode ) + mxTabController->activateTabOrder(); +} +static void lcl_ApplyResolverToNestedContainees( const Reference< resource::XStringResourceResolver >& xStringResourceResolver, const Reference< XControlContainer >& xContainer ) +{ + OUString aPropName( PROPERTY_RESOURCERESOLVER ); + + Any aNewStringResourceResolver; + aNewStringResourceResolver <<= xStringResourceResolver; + + Sequence< OUString > aPropNames { aPropName }; + + const Sequence< Reference< awt::XControl > > aSeq = xContainer->getControls(); + for ( const Reference< XControl >& xControl : aSeq ) + { + Reference< XPropertySet > xPropertySet; + + if ( xControl.is() ) + xPropertySet.set( xControl->getModel(), UNO_QUERY ); + + if ( !xPropertySet.is() ) + continue; + + try + { + Reference< resource::XStringResourceResolver > xCurrStringResourceResolver; + Any aOldValue = xPropertySet->getPropertyValue( aPropName ); + if ( ( aOldValue >>= xCurrStringResourceResolver ) + && ( xStringResourceResolver == xCurrStringResourceResolver ) + ) + { + Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY ); + Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY ); + xMultiPropSet->firePropertiesChangeEvent( aPropNames, xListener ); + } + else + xPropertySet->setPropertyValue( aPropName, aNewStringResourceResolver ); + } + catch (const Exception&) + { + } + + uno::Reference< XControlContainer > xNestedContainer( xControl, uno::UNO_QUERY ); + if ( xNestedContainer.is() ) + lcl_ApplyResolverToNestedContainees( xStringResourceResolver, xNestedContainer ); + + } + +} +void ControlContainerBase::ImplStartListingForResourceEvents() +{ + Reference< resource::XStringResourceResolver > xStringResourceResolver; + + if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) ) + return; + + ImplGetPropertyValue( PROPERTY_RESOURCERESOLVER ) >>= xStringResourceResolver; + + // Add our helper as listener to retrieve notifications about changes + Reference< util::XModifyListener > rListener( mxListener ); + ResourceListener* pResourceListener = static_cast< ResourceListener* >( rListener.get() ); + + // resource listener will stop listening if resolver reference is empty + if ( pResourceListener ) + pResourceListener->startListening( xStringResourceResolver ); + ImplUpdateResourceResolver(); +} + +void ControlContainerBase::ImplUpdateResourceResolver() +{ + Reference< resource::XStringResourceResolver > xStringResourceResolver; + + if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) ) + return; + + ImplGetPropertyValue(PROPERTY_RESOURCERESOLVER) >>= xStringResourceResolver; + + if ( !xStringResourceResolver.is() ) + return; + + lcl_ApplyResolverToNestedContainees( xStringResourceResolver, this ); + + // propagate resource resolver changes to language dependent props of the dialog + Reference< XPropertySet > xPropertySet( getModel(), UNO_QUERY ); + if ( xPropertySet.is() ) + { + Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY ); + Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY ); + xMultiPropSet->firePropertiesChangeEvent( lcl_getLanguageDependentProperties(), xListener ); + } +} + +//// ---------------------------------------------------- +//// Helper Method to convert relative url to physical location +//// ---------------------------------------------------- + +OUString getPhysicalLocation( const css::uno::Any& rbase, const css::uno::Any& rUrl ) +{ + + OUString baseLocation; + OUString url; + + rbase >>= baseLocation; + rUrl >>= url; + + OUString absoluteURL( url ); + if ( !url.isEmpty() ) + { + INetURLObject urlObj(baseLocation); + urlObj.removeSegment(); + baseLocation = urlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + const INetURLObject protocolCheck( url ); + const INetProtocol protocol = protocolCheck.GetProtocol(); + if ( protocol == INetProtocol::NotValid ) + { + OUString testAbsoluteURL; + if ( ::osl::FileBase::E_None == ::osl::FileBase::getAbsoluteFileURL( baseLocation, url, testAbsoluteURL ) ) + absoluteURL = testAbsoluteURL; + } + } + + return absoluteURL; +} + +void +ControlModelContainerBase::updateUserFormChildren( const Reference< XNameContainer >& xAllChildren, const OUString& aName, ChildOperation Operation, const css::uno::Reference< css::awt::XControlModel >& xTarget ) +{ + if ( Operation < Insert || Operation > Remove ) + throw IllegalArgumentException(); + + if ( !xAllChildren.is() ) + throw IllegalArgumentException(); + + if ( Operation == Remove ) + { + Reference< XControlModel > xOldModel( xAllChildren->getByName( aName ), UNO_QUERY ); + xAllChildren->removeByName( aName ); + + Reference< XNameContainer > xChildContainer( xOldModel, UNO_QUERY ); + if ( xChildContainer.is() ) + { + Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY ); + // container control is being removed from this container, reset the + // global list of containers + if ( xProps.is() ) + xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( uno::Reference< XNameContainer >() ) ); + const Sequence< OUString > aChildNames = xChildContainer->getElementNames(); + for ( const auto& rName : aChildNames ) + updateUserFormChildren( xAllChildren, rName, Operation, Reference< XControlModel > () ); + } + } + else if ( Operation == Insert ) + { + xAllChildren->insertByName( aName, uno::Any( xTarget ) ); + Reference< XNameContainer > xChildContainer( xTarget, UNO_QUERY ); + if ( xChildContainer.is() ) + { + // container control is being added from this container, reset the + // global list of containers to point to the correct global list + Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY ); + if ( xProps.is() ) + xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( xAllChildren ) ); + const Sequence< OUString > aChildNames = xChildContainer->getElementNames(); + for ( const auto& rName : aChildNames ) + { + Reference< XControlModel > xChildTarget( xChildContainer->getByName( rName ), UNO_QUERY ); + updateUserFormChildren( xAllChildren, rName, Operation, xChildTarget ); + } + } + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/controlmodelcontainerbase_internal.hxx b/toolkit/source/controls/controlmodelcontainerbase_internal.hxx new file mode 100644 index 0000000000..c65cabc8ad --- /dev/null +++ b/toolkit/source/controls/controlmodelcontainerbase_internal.hxx @@ -0,0 +1,30 @@ +/* -*- 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_CONTROLMODELCONTAINERBASE_INTERNAL_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_CONTROLMODELCONTAINERBASE_INTERNAL_HXX + +#include <com/sun/star/uno/Any.hxx> + +////HELPER +OUString getPhysicalLocation(const css::uno::Any& rbase, const css::uno::Any& rUrl); + +#endif // INCLUDED_TOOLKIT_SOURCE_CONTROLS_CONTROLMODELCONTAINERBASE_INTERNAL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/dialogcontrol.cxx b/toolkit/source/controls/dialogcontrol.cxx new file mode 100644 index 0000000000..ba954a1541 --- /dev/null +++ b/toolkit/source/controls/dialogcontrol.cxx @@ -0,0 +1,1245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> + +#include <sal/types.h> +#include <vcl/svapp.hxx> +#include <controls/dialogcontrol.hxx> +#include <controls/geometrycontrolmodel.hxx> +#include <helper/property.hxx> +#include <helper/servicenames.hxx> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <tools/debug.hxx> +#include <comphelper/sequence.hxx> +#include <vcl/outdev.hxx> + +#include <vcl/image.hxx> +#include <cppuhelper/implbase.hxx> +#include <unordered_map> + +#include <vcl/tabctrl.hxx> +#include <toolkit/controls/unocontrols.hxx> + +#include <awt/vclxwindows.hxx> +#include <helper/unopropertyarrayhelper.hxx> +#include "controlmodelcontainerbase_internal.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::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; + +constexpr OUStringLiteral PROPERTY_DIALOGSOURCEURL = u"DialogSourceURL"; +constexpr OUStringLiteral PROPERTY_IMAGEURL = u"ImageURL"; +constexpr OUStringLiteral PROPERTY_GRAPHIC = u"Graphic"; + + +// we probably will need both a hash of control models and hash of controls +// => use some template magic + +namespace { + +template< typename T > +class SimpleNamedThingContainer : public ::cppu::WeakImplHelper< container::XNameContainer > +{ + std::unordered_map< OUString, Reference< T > > things; + std::mutex m_aMutex; +public: + // css::container::XNameContainer, XNameReplace, XNameAccess + virtual void SAL_CALL replaceByName( const OUString& aName, const Any& aElement ) override + { + std::scoped_lock aGuard( m_aMutex ); + auto it = things.find( aName ); + if ( it == things.end() ) + throw NoSuchElementException(); + Reference< T > xElement; + if ( ! ( aElement >>= xElement ) ) + throw IllegalArgumentException(); + it->second = xElement; + } + virtual Any SAL_CALL getByName( const OUString& aName ) override + { + std::scoped_lock aGuard( m_aMutex ); + auto it = things.find( aName ); + if ( it == things.end() ) + throw NoSuchElementException(); + return uno::Any( it->second ); + } + virtual Sequence< OUString > SAL_CALL getElementNames( ) override + { + std::scoped_lock aGuard( m_aMutex ); + return comphelper::mapKeysToSequence( things ); + } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + std::scoped_lock aGuard( m_aMutex ); + return ( things.find( aName ) != things.end() ); + } + virtual void SAL_CALL insertByName( const OUString& aName, const Any& aElement ) override + { + std::scoped_lock aGuard( m_aMutex ); + auto it = things.find( aName ); + if ( it != things.end() ) + throw ElementExistException(); + Reference< T > xElement; + if ( ! ( aElement >>= xElement ) ) + throw IllegalArgumentException(); + things[ aName ] = xElement; + } + virtual void SAL_CALL removeByName( const OUString& aName ) override + { + std::scoped_lock aGuard( m_aMutex ); + if ( things.erase( aName ) == 0 ) + throw NoSuchElementException(); + } + virtual Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<T>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + std::scoped_lock aGuard( m_aMutex ); + return !things.empty(); + } +}; + +class UnoControlDialogModel : public ControlModelContainerBase +{ +protected: + css::uno::Reference< css::graphic::XGraphicObject > mxGrfObj; + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + // ::comphelper::OPropertySetHelper + void setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) override; +public: + explicit UnoControlDialogModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + UnoControlDialogModel( const UnoControlDialogModel& rModel ); + + 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.UnoControlDialogModel"; } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(ControlModelContainerBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlDialogModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.Dialog"; + return s; + } +}; + +UnoControlDialogModel::UnoControlDialogModel( const Reference< XComponentContext >& rxContext ) + :ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); +// ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); +// ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_TITLE ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); + ImplRegisterProperty( BASEPROPERTY_DESKTOP_AS_PARENT ); + ImplRegisterProperty( BASEPROPERTY_DECORATION ); + ImplRegisterProperty( BASEPROPERTY_DIALOGSOURCEURL ); + ImplRegisterProperty( BASEPROPERTY_GRAPHIC ); + ImplRegisterProperty( BASEPROPERTY_IMAGEURL ); + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_SCROLLWIDTH ); + ImplRegisterProperty( BASEPROPERTY_SCROLLHEIGHT ); + ImplRegisterProperty( BASEPROPERTY_SCROLLTOP ); + ImplRegisterProperty( BASEPROPERTY_SCROLLLEFT ); + + Any aBool; + aBool <<= true; + ImplRegisterProperty( BASEPROPERTY_MOVEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_CLOSEABLE, aBool ); + // #TODO separate class for 'UserForm' ( instead of re-using Dialog ? ) + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoControlDialogModel::UnoControlDialogModel( const UnoControlDialogModel& rModel ) + : ControlModelContainerBase( rModel ) +{ + // need to clone BASEPROPERTY_USERFORMCONTAINEES too + Reference< XNameContainer > xSrcNameCont( const_cast< UnoControlDialogModel& >(rModel).getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + Reference<XNameContainer > xNameCont( new SimpleNamedThingContainer< XControlModel > ); + + const uno::Sequence< OUString > sNames = xSrcNameCont->getElementNames(); + for ( OUString const & name : sNames ) + { + if ( xSrcNameCont->hasByName( name ) ) + xNameCont->insertByName( name, xSrcNameCont->getByName( name ) ); + } + std::unique_lock aGuard(m_aMutex); + setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_USERFORMCONTAINEES, Any( xNameCont ) ); +} + +rtl::Reference<UnoControlModel> UnoControlDialogModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoControlDialogModel> pClone = new UnoControlDialogModel( *this ); + + Clone_Impl(*pClone); + + return pClone; +} + + +OUString UnoControlDialogModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.Dialog"; +} + +Any UnoControlDialogModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString::createFromAscii( szServiceName_UnoControlDialog ); + break; + case BASEPROPERTY_SCROLLWIDTH: + case BASEPROPERTY_SCROLLHEIGHT: + case BASEPROPERTY_SCROLLTOP: + case BASEPROPERTY_SCROLLLEFT: + aAny <<= sal_Int32(0); + break; + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& UnoControlDialogModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > UnoControlDialogModel::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void UnoControlDialogModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + ControlModelContainerBase::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + try + { + if ( nHandle == BASEPROPERTY_IMAGEURL && ImplHasProperty( BASEPROPERTY_GRAPHIC ) ) + { + OUString sImageURL; + uno::Reference<graphic::XGraphic> xGraphic; + if (rValue >>= sImageURL) + { + setFastPropertyValueImpl(rGuard, + BASEPROPERTY_GRAPHIC, + uno::Any(ImageHelper::getGraphicAndGraphicObjectFromURL_nothrow( + mxGrfObj, sImageURL))); + } + else if (rValue >>= xGraphic) + { + setFastPropertyValueImpl(rGuard, BASEPROPERTY_GRAPHIC, uno::Any(xGraphic)); + } + } + } + catch( const css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "caught an exception while setting ImageURL properties" ); + } +} + +} + + +// = class UnoDialogControl + + +UnoDialogControl::UnoDialogControl( const uno::Reference< uno::XComponentContext >& rxContext ) + :UnoDialogControl_Base( rxContext ) + ,maTopWindowListeners( *this ) + ,mbWindowListener(false) +{ + maComponentInfos.nWidth = 300; + maComponentInfos.nHeight = 450; + } + +UnoDialogControl::~UnoDialogControl() +{ +} + +OUString UnoDialogControl::GetComponentServiceName() const +{ + + bool bDecoration( true ); + ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_DECORATION )) >>= bDecoration; + if ( bDecoration ) + return "Dialog"; + else + return "TabPage"; +} + +void UnoDialogControl::dispose() +{ + SolarMutexGuard aGuard; + + EventObject aEvt; + aEvt.Source = getXWeak(); + maTopWindowListeners.disposeAndClear( aEvt ); + ControlContainerBase::dispose(); +} + +void SAL_CALL UnoDialogControl::disposing( + const EventObject& Source ) +{ + ControlContainerBase::disposing( Source ); +} + +sal_Bool UnoDialogControl::setModel( const Reference< XControlModel >& rxModel ) +{ + // #Can we move all the Resource stuff to the ControlContainerBase ? + SolarMutexGuard aGuard; + bool bRet = ControlContainerBase::setModel( rxModel ); + ImplStartListingForResourceEvents(); + return bRet; +} + +void UnoDialogControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aGuard; + + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); + + Reference < XTopWindow > xTW( getPeer(), UNO_QUERY ); + if ( !xTW.is() ) + return; + + xTW->setMenuBar( mxMenuBar ); + + if ( !mbWindowListener ) + { + Reference< XWindowListener > xWL(this); + addWindowListener( xWL ); + mbWindowListener = true; + } + + if ( maTopWindowListeners.getLength() ) + xTW->addTopWindowListener( &maTopWindowListeners ); + // there must be a better way than doing this, we can't + // process the scrolltop & scrollleft in XDialog because + // the children haven't been added when those props are applied + ImplSetPeerProperty( GetPropertyName( BASEPROPERTY_SCROLLTOP ), ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLTOP ) ) ); + ImplSetPeerProperty( GetPropertyName( BASEPROPERTY_SCROLLLEFT ), ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLLEFT ) ) ); +} + +OUString UnoDialogControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoDialogControl"; +} + +sal_Bool UnoDialogControl::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> UnoDialogControl::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.UnoControlDialog", + "stardiv.vcl.control.Dialog"}; +} + +void UnoDialogControl::PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) +{ + UnoControlContainer::PrepareWindowDescriptor( rDesc ); + bool bDecoration( true ); + ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_DECORATION )) >>= bDecoration; + if ( !bDecoration ) + { + // Now we have to manipulate the WindowDescriptor + rDesc.WindowAttributes = rDesc.WindowAttributes | css::awt::WindowAttribute::NODECORATION; + } + + // We have to set the graphic property before the peer + // will be created. Otherwise the properties will be copied + // into the peer via propertiesChangeEvents. As the order of + // can lead to overwrites we have to set the graphic property + // before the propertiesChangeEvents are sent! + OUString aImageURL; + Reference< graphic::XGraphic > xGraphic; + if (( ImplGetPropertyValue( PROPERTY_IMAGEURL ) >>= aImageURL ) && + ( !aImageURL.isEmpty() )) + { + OUString absoluteUrl = getPhysicalLocation(ImplGetPropertyValue(PROPERTY_DIALOGSOURCEURL), uno::Any(aImageURL)); + xGraphic = ImageHelper::getGraphicFromURL_nothrow( absoluteUrl ); + ImplSetPropertyValue( PROPERTY_GRAPHIC, uno::Any( xGraphic ), true ); + } +} + +void UnoDialogControl::addTopWindowListener( const Reference< XTopWindowListener >& rxListener ) +{ + maTopWindowListeners.addInterface( rxListener ); + if( getPeer().is() && maTopWindowListeners.getLength() == 1 ) + { + Reference < XTopWindow > xTW( getPeer(), UNO_QUERY ); + xTW->addTopWindowListener( &maTopWindowListeners ); + } +} + +void UnoDialogControl::removeTopWindowListener( const Reference< XTopWindowListener >& rxListener ) +{ + if( getPeer().is() && maTopWindowListeners.getLength() == 1 ) + { + Reference < XTopWindow > xTW( getPeer(), UNO_QUERY ); + xTW->removeTopWindowListener( &maTopWindowListeners ); + } + maTopWindowListeners.removeInterface( rxListener ); +} + +void UnoDialogControl::toFront( ) +{ + SolarMutexGuard aGuard; + if ( getPeer().is() ) + { + Reference< XTopWindow > xTW( getPeer(), UNO_QUERY ); + if( xTW.is() ) + xTW->toFront(); + } +} + +void UnoDialogControl::toBack( ) +{ + SolarMutexGuard aGuard; + if ( getPeer().is() ) + { + Reference< XTopWindow > xTW( getPeer(), UNO_QUERY ); + if( xTW.is() ) + xTW->toBack(); + } +} + +void UnoDialogControl::setMenuBar( const Reference< XMenuBar >& rxMenuBar ) +{ + SolarMutexGuard aGuard; + mxMenuBar = rxMenuBar; + if ( getPeer().is() ) + { + Reference< XTopWindow > xTW( getPeer(), UNO_QUERY ); + if( xTW.is() ) + xTW->setMenuBar( mxMenuBar ); + } +} +static ::Size ImplMapPixelToAppFont( OutputDevice const * pOutDev, const ::Size& aSize ) +{ + ::Size aTmp = pOutDev->PixelToLogic(aSize, MapMode(MapUnit::MapAppFont)); + return aTmp; +} +// css::awt::XWindowListener +void SAL_CALL UnoDialogControl::windowResized( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbSizeModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aAppFontSize( e.Width, e.Height ); + + Reference< XControl > xDialogControl( *this, UNO_QUERY_THROW ); + Reference< XDevice > xDialogDevice( xDialogControl->getPeer(), UNO_QUERY ); + OSL_ENSURE( xDialogDevice.is(), "UnoDialogControl::windowResized: no peer, but a windowResized event?" ); + + // #i87592 In design mode the drawing layer works with sizes with decoration. + // Therefore we have to subtract them before writing back to the properties (model). + if ( xDialogDevice.is() && mbDesignMode ) + { + DeviceInfo aDeviceInfo( xDialogDevice->getInfo() ); + aAppFontSize.AdjustWidth( -(aDeviceInfo.LeftInset + aDeviceInfo.RightInset) ); + aAppFontSize.AdjustHeight( -(aDeviceInfo.TopInset + aDeviceInfo.BottomInset) ); + } + + aAppFontSize = ImplMapPixelToAppFont( pOutDev, aAppFontSize ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbSizeModified = true; + // Properties in a sequence must be sorted! + Sequence< OUString > aProps{ "Height", "Width" }; + Sequence< Any > aValues{ + Any(sal_Int32( + std::clamp(aAppFontSize.Height(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))), + Any(sal_Int32( + std::clamp(aAppFontSize.Width(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))) + }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbSizeModified = false; + +} + +void SAL_CALL UnoDialogControl::windowMoved( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbPosModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aTmp( e.X, e.Y ); + aTmp = ImplMapPixelToAppFont( pOutDev, aTmp ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbPosModified = true; + Sequence< OUString > aProps{ "PositionX", "PositionY" }; + Sequence< Any > aValues{ + Any(sal_Int32( + std::clamp(aTmp.Width(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))), + Any(sal_Int32( + std::clamp(aTmp.Height(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))) + }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbPosModified = false; + +} + +void SAL_CALL UnoDialogControl::windowShown( const EventObject& ) {} + +void SAL_CALL UnoDialogControl::windowHidden( const EventObject& ) {} + +void SAL_CALL UnoDialogControl::endDialog( ::sal_Int32 i_result ) +{ + Reference< XDialog2 > xPeerDialog( getPeer(), UNO_QUERY ); + if ( xPeerDialog.is() ) + xPeerDialog->endDialog( i_result ); +} + +void SAL_CALL UnoDialogControl::setHelpId( const OUString& i_id ) +{ + Reference< XDialog2 > xPeerDialog( getPeer(), UNO_QUERY ); + if ( xPeerDialog.is() ) + xPeerDialog->setHelpId( i_id ); +} + +void UnoDialogControl::setTitle( const OUString& Title ) +{ + SolarMutexGuard aGuard; + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TITLE ), uno::Any(Title), true ); +} + +OUString UnoDialogControl::getTitle() +{ + SolarMutexGuard aGuard; + return ImplGetPropertyValue_UString( BASEPROPERTY_TITLE ); +} + +sal_Int16 UnoDialogControl::execute() +{ + SolarMutexGuard aGuard; + sal_Int16 nDone = -1; + if ( getPeer().is() ) + { + Reference< XDialog > xDlg( getPeer(), UNO_QUERY ); + if( xDlg.is() ) + { + GetComponentInfos().bVisible = true; + nDone = xDlg->execute(); + GetComponentInfos().bVisible = false; + } + } + return nDone; +} + +void UnoDialogControl::endExecute() +{ + SolarMutexGuard aGuard; + if ( getPeer().is() ) + { + Reference< XDialog > xDlg( getPeer(), UNO_QUERY ); + if( xDlg.is() ) + { + xDlg->endExecute(); + GetComponentInfos().bVisible = false; + } + } +} + +// XModifyListener +void SAL_CALL UnoDialogControl::modified( + const lang::EventObject& /*rEvent*/ ) +{ + ImplUpdateResourceResolver(); +} + +void UnoDialogControl::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + for( const PropertyChangeEvent& rEvt : rEvents ) + { + Reference< XControlModel > xModel( rEvt.Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == getModel().get(); + if (bOwnModel && rEvt.PropertyName == "ImageURL" && !ImplHasProperty(BASEPROPERTY_GRAPHIC)) + { + OUString aImageURL; + Reference< graphic::XGraphic > xGraphic; + if (( ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_IMAGEURL ) ) >>= aImageURL ) && + ( !aImageURL.isEmpty() )) + { + OUString absoluteUrl = getPhysicalLocation(ImplGetPropertyValue(GetPropertyName(BASEPROPERTY_DIALOGSOURCEURL)), uno::Any(aImageURL)); + xGraphic = ImageHelper::getGraphicFromURL_nothrow( absoluteUrl ); + } + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_GRAPHIC), uno::Any( xGraphic ), true ); + break; + } + else if (bOwnModel && rEvt.PropertyName == "Graphic") + { + uno::Reference<graphic::XGraphic> xGraphic; + if (ImplGetPropertyValue("Graphic") >>= xGraphic) + { + ImplSetPropertyValue("Graphic", uno::Any(xGraphic), true); + } + break; + } + } + ControlContainerBase::ImplModelPropertiesChanged(rEvents); +} + + + +UnoMultiPageControl::UnoMultiPageControl( const uno::Reference< uno::XComponentContext >& rxContext ) : ControlContainerBase(rxContext), maTabListeners( *this ) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} + +UnoMultiPageControl::~UnoMultiPageControl() +{ +} +// XTabListener + +void SAL_CALL UnoMultiPageControl::inserted( SAL_UNUSED_PARAMETER ::sal_Int32 ) +{ +} +void SAL_CALL UnoMultiPageControl::removed( SAL_UNUSED_PARAMETER ::sal_Int32 ) +{ +} +void SAL_CALL UnoMultiPageControl::changed( SAL_UNUSED_PARAMETER ::sal_Int32, + SAL_UNUSED_PARAMETER const Sequence< NamedValue >& ) +{ +} +void SAL_CALL UnoMultiPageControl::activated( ::sal_Int32 ID ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ), uno::Any( ID ), false ); + +} +void SAL_CALL UnoMultiPageControl::deactivated( SAL_UNUSED_PARAMETER ::sal_Int32 ) +{ +} +void SAL_CALL UnoMultiPageControl::disposing(const EventObject&) +{ +} + +void SAL_CALL UnoMultiPageControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maTabListeners.disposeAndClear( aEvt ); + ControlContainerBase::dispose(); +} + +// css::awt::XSimpleTabController +::sal_Int32 SAL_CALL UnoMultiPageControl::insertTab() +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + return xMultiPage->insertTab(); +} + +void SAL_CALL UnoMultiPageControl::removeTab( ::sal_Int32 ID ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + xMultiPage->removeTab( ID ); +} + +void SAL_CALL UnoMultiPageControl::setTabProps( ::sal_Int32 ID, const Sequence< NamedValue >& Properties ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + xMultiPage->setTabProps( ID, Properties ); +} + +Sequence< NamedValue > SAL_CALL UnoMultiPageControl::getTabProps( ::sal_Int32 ID ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + return xMultiPage->getTabProps( ID ); +} + +void SAL_CALL UnoMultiPageControl::activateTab( ::sal_Int32 ID ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + xMultiPage->activateTab( ID ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ), uno::Any( ID ), true ); + +} + +::sal_Int32 SAL_CALL UnoMultiPageControl::getActiveTabID() +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + return xMultiPage->getActiveTabID(); +} + +void SAL_CALL UnoMultiPageControl::addTabListener( const Reference< XTabListener >& Listener ) +{ + maTabListeners.addInterface( Listener ); + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY ); + if ( xMultiPage.is() && maTabListeners.getLength() == 1 ) + xMultiPage->addTabListener( &maTabListeners ); +} + +void SAL_CALL UnoMultiPageControl::removeTabListener( const Reference< XTabListener >& Listener ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY ); + if ( xMultiPage.is() && maTabListeners.getLength() == 1 ) + xMultiPage->removeTabListener( &maTabListeners ); + maTabListeners.removeInterface( Listener ); +} + +IMPL_IMPLEMENTATION_ID( UnoMultiPageControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoMultiPageControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XSimpleTabController>::get(), + cppu::UnoType<awt::XTabListener>::get(), + ControlContainerBase::getTypes() + ); + return aTypeList.getTypes(); +} + +// uno::XInterface +uno::Any UnoMultiPageControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XTabListener* >(this), + static_cast< awt::XSimpleTabController* >(this) ); + return (aRet.hasValue() ? aRet : ControlContainerBase::queryAggregation( rType )); +} + +OUString UnoMultiPageControl::GetComponentServiceName() const +{ + bool bDecoration( true ); + ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_DECORATION )) >>= bDecoration; + if ( bDecoration ) + return "tabcontrol"; + // Hopefully we can tweak the tabcontrol to display without tabs + return "tabcontrolnotabs"; +} + +void UnoMultiPageControl::bindPage( const uno::Reference< awt::XControl >& _rxControl ) +{ + uno::Reference< awt::XWindowPeer > xPage( _rxControl->getPeer() ); + uno::Reference< awt::XSimpleTabController > xTabCntrl( getPeer(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xProps( _rxControl->getModel(), uno::UNO_QUERY ); + + VCLXTabPage* pXPage = dynamic_cast< VCLXTabPage* >( xPage.get() ); + TabPage* pPage = pXPage ? pXPage->getTabPage() : nullptr; + if ( xTabCntrl.is() && pPage ) + { + VCLXMultiPage* pXTab = dynamic_cast< VCLXMultiPage* >( xTabCntrl.get() ); + if ( pXTab ) + { + OUString sTitle; + xProps->getPropertyValue( GetPropertyName( BASEPROPERTY_TITLE ) ) >>= sTitle; + pXTab->insertTab( pPage, sTitle); + } + } + +} + +void UnoMultiPageControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aSolarGuard; + + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); + + const uno::Sequence< uno::Reference< awt::XControl > > aCtrls = getControls(); + for( const auto& rCtrl : aCtrls ) + bindPage( rCtrl ); + sal_Int32 nActiveTab(0); + Reference< XPropertySet > xMultiProps( getModel(), UNO_QUERY ); + xMultiProps->getPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ) ) >>= nActiveTab; + + uno::Reference< awt::XSimpleTabController > xTabCntrl( getPeer(), uno::UNO_QUERY ); + if ( xTabCntrl.is() ) + { + xTabCntrl->addTabListener( this ); + if ( nActiveTab && aCtrls.hasElements() ) // Ensure peer is initialise with correct activated tab + { + xTabCntrl->activateTab( nActiveTab ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ), uno::Any( nActiveTab ), true ); + } + } +} + +void UnoMultiPageControl::impl_createControlPeerIfNecessary( const uno::Reference< awt::XControl >& _rxControl) +{ + OSL_PRECOND( _rxControl.is(), "UnoMultiPageControl::impl_createControlPeerIfNecessary: invalid control, this will crash!" ); + + // if the container already has a peer, then also create a peer for the control + uno::Reference< awt::XWindowPeer > xMyPeer( getPeer() ); + + if( xMyPeer.is() ) + { + _rxControl->createPeer( nullptr, xMyPeer ); + bindPage( _rxControl ); + ImplActivateTabControllers(); + } + +} + +// ------------- UnoMultiPageModel ----------------- + +UnoMultiPageModel::UnoMultiPageModel( const Reference< XComponentContext >& rxContext ) : ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); + //ImplRegisterProperty( BASEPROPERTY_DIALOGSOURCEURL ); + ImplRegisterProperty( BASEPROPERTY_MULTIPAGEVALUE ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); + + Any aBool; + aBool <<= true; + ImplRegisterProperty( BASEPROPERTY_MOVEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_CLOSEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_DECORATION, aBool ); + // MultiPage Control has the tab stop property. And the default value is True. + ImplRegisterProperty( BASEPROPERTY_TABSTOP, aBool ); + + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoMultiPageModel::~UnoMultiPageModel() +{ +} + +rtl::Reference<UnoControlModel> UnoMultiPageModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoMultiPageModel> pClone = new UnoMultiPageModel( *this ); + Clone_Impl( *pClone ); + return pClone; +} + +OUString UnoMultiPageModel::getServiceName() +{ + return "com.sun.star.awt.UnoMultiPageModel"; +} + +uno::Any UnoMultiPageModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlMultiPage" ) ); + } + return ControlModelContainerBase::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoMultiPageModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoMultiPageModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void UnoMultiPageModel::insertByName( const OUString& aName, const Any& aElement ) +{ + Reference< XServiceInfo > xInfo; + aElement >>= xInfo; + + if ( !xInfo.is() ) + throw IllegalArgumentException(); + + // Only a Page model can be inserted into the multipage + if ( !xInfo->supportsService( "com.sun.star.awt.UnoPageModel" ) ) + throw IllegalArgumentException(); + + return ControlModelContainerBase::insertByName( aName, aElement ); +} + + +sal_Bool SAL_CALL UnoMultiPageModel::getGroupControl( ) +{ + return true; +} + + + +UnoPageControl::UnoPageControl( const uno::Reference< uno::XComponentContext >& rxContext ) : ControlContainerBase(rxContext) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} + +UnoPageControl::~UnoPageControl() +{ +} + +OUString UnoPageControl::GetComponentServiceName() const +{ + return "tabpage"; +} + + +// ------------- UnoPageModel ----------------- + +UnoPageModel::UnoPageModel( const Reference< XComponentContext >& rxContext ) : ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_TITLE ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); +// ImplRegisterProperty( BASEPROPERTY_DIALOGSOURCEURL ); + + Any aBool; + aBool <<= true; + ImplRegisterProperty( BASEPROPERTY_MOVEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_CLOSEABLE, aBool ); + //ImplRegisterProperty( BASEPROPERTY_TABSTOP, aBool ); + + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoPageModel::~UnoPageModel() +{ +} + +rtl::Reference<UnoControlModel> UnoPageModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoPageModel> pClone = new UnoPageModel( *this ); + Clone_Impl( *pClone ); + return pClone; +} + +OUString UnoPageModel::getServiceName() +{ + return "com.sun.star.awt.UnoPageModel"; +} + +uno::Any UnoPageModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlPage" ) ); + } + return ControlModelContainerBase::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoPageModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoPageModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +sal_Bool SAL_CALL UnoPageModel::getGroupControl( ) +{ + return false; +} + +// Frame control + + + +UnoFrameControl::UnoFrameControl( const uno::Reference< uno::XComponentContext >& rxContext ) : ControlContainerBase(rxContext) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} + +UnoFrameControl::~UnoFrameControl() +{ +} + +OUString UnoFrameControl::GetComponentServiceName() const +{ + return "frame"; +} + +void UnoFrameControl::ImplSetPosSize( Reference< XControl >& rxCtrl ) +{ + bool bOwnCtrl = false; + OUString sTitle; + if ( rxCtrl.get() == Reference<XControl>( this ).get() ) + bOwnCtrl = true; + Reference< XPropertySet > xProps( getModel(), UNO_QUERY ); + //xProps->getPropertyValue( GetPropertyName( BASEPROPERTY_TITLE ) ) >>= sTitle; + xProps->getPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ) ) >>= sTitle; + + ControlContainerBase::ImplSetPosSize( rxCtrl ); + Reference < XWindow > xW( rxCtrl, UNO_QUERY ); + if ( bOwnCtrl || !xW.is() || sTitle.isEmpty() ) + return; + + awt::Rectangle aSizePos = xW->getPosSize(); + + sal_Int32 nX = aSizePos.X, nY = aSizePos.Y, nWidth = aSizePos.Width, nHeight = aSizePos.Height; + // Retrieve the values set by the base class + OutputDevice*pOutDev = Application::GetDefaultDevice(); + if ( pOutDev ) + { + // Adjust Y based on height of Title + ::tools::Rectangle aRect = pOutDev->GetTextRect( {}, sTitle ); + nY = nY + ( aRect.GetHeight() / 2 ); + } + else + { + Reference< XWindowPeer > xPeer = ImplGetCompatiblePeer(); + Reference< XDevice > xD( xPeer, UNO_QUERY ); + + SimpleFontMetric aFM; + FontDescriptor aFD; + Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_FONTDESCRIPTOR ) ); + + aVal >>= aFD; + if ( !aFD.StyleName.isEmpty() ) + { + Reference< XFont > xFont = xD->getFont( aFD ); + aFM = xFont->getFontMetric(); + } + else + { + Reference< XGraphics > xG = xD->createGraphics(); + aFM = xG->getFontMetric(); + } + + sal_Int16 nH = aFM.Ascent + aFM.Descent; + // offset y based on height of font ( not sure if my guess at the correct calculation is correct here ) + nY = nY + ( nH / 8); // how do I test this + } + xW->setPosSize( nX, nY, nWidth, nHeight, PosSize::POSSIZE ); +} + +// ------------- UnoFrameModel ----------------- + +UnoFrameModel::UnoFrameModel( const Reference< XComponentContext >& rxContext ) : ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_LABEL ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_SCROLLWIDTH ); + ImplRegisterProperty( BASEPROPERTY_SCROLLHEIGHT ); + ImplRegisterProperty( BASEPROPERTY_SCROLLTOP ); + ImplRegisterProperty( BASEPROPERTY_SCROLLLEFT ); + + + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoFrameModel::~UnoFrameModel() +{ +} + +rtl::Reference<UnoControlModel> UnoFrameModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoFrameModel> pClone = new UnoFrameModel( *this ); + Clone_Impl( *pClone ); + return pClone; +} + +OUString UnoFrameModel::getServiceName() +{ + return "com.sun.star.awt.UnoFrameModel"; +} + +uno::Any UnoFrameModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlFrame" ) ); + } + case BASEPROPERTY_SCROLLWIDTH: + case BASEPROPERTY_SCROLLHEIGHT: + case BASEPROPERTY_SCROLLTOP: + case BASEPROPERTY_SCROLLLEFT: + return uno::Any( sal_Int32(0) ); + case BASEPROPERTY_USERFORMCONTAINEES: + { + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + return Any( xNameCont ); + } + } + return ControlModelContainerBase::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoFrameModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoFrameModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlDialogModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OGeometryControlModel<UnoControlDialogModel>(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoDialogControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoDialogControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoMultiPageControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoMultiPageControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoMultiPageModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoMultiPageModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoPageControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoPageControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoPageModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoPageModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFrameControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFrameControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFrameModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFrameModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/eventcontainer.cxx b/toolkit/source/controls/eventcontainer.cxx new file mode 100644 index 0000000000..1b57e984d7 --- /dev/null +++ b/toolkit/source/controls/eventcontainer.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <cppuhelper/factory.hxx> + +#include <controls/eventcontainer.hxx> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> + + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::registry; +using namespace com::sun::star::script; +using namespace cppu; + +namespace toolkit +{ + +// Methods XElementAccess +Type ScriptEventContainer::getElementType() +{ + return mType; +} + +sal_Bool ScriptEventContainer::hasElements() +{ + return !mHashMap.empty(); +} + +// Methods XNameAccess +Any ScriptEventContainer::getByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + sal_Int32 iHashResult = (*aIt).second; + Any aRetAny = mValues[ iHashResult ]; + return aRetAny; +} + +Sequence< OUString > ScriptEventContainer::getElementNames() +{ + return mNames; +} + +sal_Bool ScriptEventContainer::hasByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + bool bRet = ( aIt != mHashMap.end() ); + return bRet; +} + + +// Methods XNameReplace +void ScriptEventContainer::replaceByName( const OUString& aName, const Any& aElement ) +{ + const Type& aAnyType = aElement.getValueType(); + if( mType != aAnyType ) + throw IllegalArgumentException(); + + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + sal_Int32 iHashResult = (*aIt).second; + Any aOldElement = mValues[ iHashResult ]; + mValues[ iHashResult ] = aElement; + + // Fire event + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.ReplacedElement = aOldElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementReplaced( aEvent ); +} + + +// Methods XNameContainer +void ScriptEventContainer::insertByName( const OUString& aName, const Any& aElement ) +{ + const Type& aAnyType = aElement.getValueType(); + if( mType != aAnyType ) + throw IllegalArgumentException(); + + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt != mHashMap.end() ) + { + throw ElementExistException(); + } + + sal_Int32 nCount = mNames.getLength(); + mNames.realloc( nCount + 1 ); + mValues.resize( nCount + 1 ); + mNames.getArray()[ nCount ] = aName; + mValues[ nCount ] = aElement; + mHashMap[ aName ] = nCount; + + // Fire event + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementInserted( aEvent ); +} + +void ScriptEventContainer::removeByName( const OUString& Name ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( Name ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + + sal_Int32 iHashResult = (*aIt).second; + Any aOldElement = mValues[ iHashResult ]; + + // Fire event + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aOldElement; + aEvent.Accessor <<= Name; + maContainerListeners.elementRemoved( aEvent ); + + mHashMap.erase( aIt ); + sal_Int32 iLast = mNames.getLength() - 1; + if( iLast != iHashResult ) + { + OUString* pNames = mNames.getArray(); + pNames[ iHashResult ] = pNames[ iLast ]; + mValues[ iHashResult ] = mValues[ iLast ]; + mHashMap[ pNames[ iHashResult ] ] = iHashResult; + } + mNames.realloc( iLast ); + mValues.resize( iLast ); +} + +// Methods XContainer +void ScriptEventContainer::addContainerListener( const css::uno::Reference< css::container::XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void ScriptEventContainer::removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + + +ScriptEventContainer::ScriptEventContainer() + : mType( cppu::UnoType<ScriptEventDescriptor>::get() ), + maContainerListeners( *this ) +{ +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/filectrl.cxx b/toolkit/source/controls/filectrl.cxx new file mode 100644 index 0000000000..f1b476220b --- /dev/null +++ b/toolkit/source/controls/filectrl.cxx @@ -0,0 +1,244 @@ +/* -*- 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 <controls/filectrl.hxx> + +#include <com/sun/star/ui/dialogs/FilePicker.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <comphelper/processfactory.hxx> +#include <osl/file.h> +#include <svl/svlresid.hxx> +#include <svl/svl.hrc> +#include <comphelper/diagnose_ex.hxx> +#include <tools/urlobj.hxx> +#include <vcl/toolkit/edit.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + + +FileControl::FileControl( vcl::Window* pParent, WinBits nStyle ) : + Window( pParent, nStyle|WB_DIALOGCONTROL ), + maEdit( VclPtr<Edit>::Create(this, (nStyle&(~WB_BORDER))|WB_NOTABSTOP) ), + maButton( VclPtr<PushButton>::Create( this, (nStyle&(~WB_BORDER))|WB_NOLIGHTBORDER|WB_NOPOINTERFOCUS|WB_NOTABSTOP ) ), + maButtonText( SvlResId(STR_FILECTRL_BUTTONTEXT) ), + mnInternalFlags( FileControlMode_Internal::ORIGINALBUTTONTEXT ) +{ + maButton->SetClickHdl( LINK( this, FileControl, ButtonHdl ) ); + + maButton->Show(); + maEdit->Show(); + + SetCompoundControl( true ); + + SetStyle( ImplInitStyle( GetStyle() ) ); +} + + +WinBits FileControl::ImplInitStyle( WinBits nStyle ) +{ + if ( !( nStyle & WB_NOTABSTOP ) ) + { + maEdit->SetStyle( (maEdit->GetStyle()|WB_TABSTOP)&(~WB_NOTABSTOP) ); + maButton->SetStyle( (maButton->GetStyle()|WB_TABSTOP)&(~WB_NOTABSTOP) ); + } + else + { + maEdit->SetStyle( (maEdit->GetStyle()|WB_NOTABSTOP)&(~WB_TABSTOP) ); + maButton->SetStyle( (maButton->GetStyle()|WB_NOTABSTOP)&(~WB_TABSTOP) ); + } + + const WinBits nAlignmentStyle = ( WB_TOP | WB_VCENTER | WB_BOTTOM ); + maEdit->SetStyle( ( maEdit->GetStyle() & ~nAlignmentStyle ) | ( nStyle & nAlignmentStyle ) ); + + if ( !(nStyle & WB_NOGROUP) ) + nStyle |= WB_GROUP; + + if ( !(nStyle & WB_NOBORDER ) ) + nStyle |= WB_BORDER; + + nStyle &= ~WB_TABSTOP; + + return nStyle; +} + + +FileControl::~FileControl() +{ + disposeOnce(); +} + +void FileControl::dispose() +{ + maEdit.disposeAndClear(); + maButton.disposeAndClear(); + Window::dispose(); +} + +void FileControl::SetText( const OUString& rStr ) +{ + maEdit->SetText( rStr ); +} + + +OUString FileControl::GetText() const +{ + return maEdit->GetText(); +} + + +void FileControl::StateChanged( StateChangedType nType ) +{ + if ( nType == StateChangedType::Enable ) + { + maEdit->Enable( IsEnabled() ); + maButton->Enable( IsEnabled() ); + } + else if ( nType == StateChangedType::Zoom ) + { + GetEdit().SetZoom( GetZoom() ); + GetButton().SetZoom( GetZoom() ); + } + else if ( nType == StateChangedType::Style ) + { + SetStyle( ImplInitStyle( GetStyle() ) ); + } + else if ( nType == StateChangedType::ControlFont ) + { + GetEdit().SetControlFont( GetControlFont() ); + // Only use height of the button, as in HTML + // always Courier is used + vcl::Font aFont = GetButton().GetControlFont(); + aFont.SetFontSize( GetControlFont().GetFontSize() ); + GetButton().SetControlFont( aFont ); + } + else if ( nType == StateChangedType::ControlForeground ) + { + GetEdit().SetControlForeground( GetControlForeground() ); + GetButton().SetControlForeground( GetControlForeground() ); + } + else if ( nType == StateChangedType::ControlBackground ) + { + GetEdit().SetControlBackground( GetControlBackground() ); + GetButton().SetControlBackground( GetControlBackground() ); + } + Window::StateChanged( nType ); +} + + +void FileControl::Resize() +{ + static const tools::Long ButtonBorder = 10; + + if( mnInternalFlags & FileControlMode_Internal::INRESIZE ) + return; + mnInternalFlags |= FileControlMode_Internal::INRESIZE;//InResize = sal_True + + Size aOutSz = GetOutputSizePixel(); + tools::Long nButtonTextWidth = maButton->GetTextWidth( maButtonText ); + if ( !(mnInternalFlags & FileControlMode_Internal::ORIGINALBUTTONTEXT) || + ( nButtonTextWidth < aOutSz.Width()/3 ) ) + { + maButton->SetText( maButtonText ); + } + else + { + OUString aSmallText( "..." ); + maButton->SetText( aSmallText ); + nButtonTextWidth = maButton->GetTextWidth( aSmallText ); + } + + tools::Long nButtonWidth = nButtonTextWidth+ButtonBorder; + maEdit->setPosSizePixel( 0, 0, aOutSz.Width()-nButtonWidth, aOutSz.Height() ); + maButton->setPosSizePixel( aOutSz.Width()-nButtonWidth, 0, nButtonWidth, aOutSz.Height() ); + + mnInternalFlags &= ~FileControlMode_Internal::INRESIZE; //InResize = sal_False +} + + +void FileControl::GetFocus() +{ + if (!maEdit || maEdit->isDisposed()) + return; + maEdit->GrabFocus(); +} + +void FileControl::SetEditModifyHdl( const Link<Edit&,void>& rLink ) +{ + if (!maEdit || maEdit->isDisposed()) + return; + maEdit->SetModifyHdl(rLink); +} + +void FileControl::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags ) +{ + WinBits nOldEditStyle = GetEdit().GetStyle(); + if ( GetStyle() & WB_BORDER ) + GetEdit().SetStyle( nOldEditStyle|WB_BORDER ); + Size aOrigSize(GetEdit().GetSizePixel()); + GetEdit().SetSizePixel(GetSizePixel()); + GetEdit().Draw( pDev, rPos, nFlags ); + GetEdit().SetSizePixel(aOrigSize); + if ( GetStyle() & WB_BORDER ) + GetEdit().SetStyle( nOldEditStyle ); +} + +IMPL_LINK_NOARG(FileControl, ButtonHdl, Button*, void) +{ + try + { + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + Reference < dialogs::XFilePicker3 > xFilePicker = dialogs::FilePicker::createWithMode( xContext, dialogs::TemplateDescription::FILEOPEN_SIMPLE ); + // transform the system notation text into a file URL + OUString sSystemNotation = GetText(), sFileURL; + oslFileError nError = osl_getFileURLFromSystemPath( sSystemNotation.pData, &sFileURL.pData ); + if ( nError == osl_File_E_INVAL ) + sFileURL = GetText(); // #97709# Maybe URL is already a file URL... + + //#90430# Check if URL is really a file URL + OUString aTmp; + if ( osl_getSystemPathFromFileURL( sFileURL.pData, &aTmp.pData ) == osl_File_E_None ) + { + // initially set this directory + xFilePicker->setDisplayDirectory( sFileURL ); + } + + if ( xFilePicker->execute() ) + { + Sequence < OUString > aPathSeq = xFilePicker->getSelectedFiles(); + + if ( aPathSeq.hasElements() ) + { + OUString aNewText = aPathSeq[0]; + INetURLObject aObj( aNewText ); + if ( aObj.GetProtocol() == INetProtocol::File ) + aNewText = aObj.PathToFileName(); + SetText( aNewText ); + maEdit->GetModifyHdl().Call( *maEdit ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "FileControl::ImplBrowseFile: caught an exception while executing the file picker!" ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/formattedcontrol.cxx b/toolkit/source/controls/formattedcontrol.cxx new file mode 100644 index 0000000000..cf3094c358 --- /dev/null +++ b/toolkit/source/controls/formattedcontrol.cxx @@ -0,0 +1,478 @@ +/* -*- 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 <controls/formattedcontrol.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> + +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> + +#include <helper/unopropertyarrayhelper.hxx> +#include <mutex> + +namespace toolkit +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + + + namespace + { + + std::mutex& getDefaultFormatsMutex() + { + static std::mutex s_aDefaultFormatsMutex; + return s_aDefaultFormatsMutex; + } + + Reference< XNumberFormatsSupplier > s_xDefaultFormats; + bool s_bTriedCreation = false; + oslInterlockedCount s_refCount(0); + + const Reference< XNumberFormatsSupplier >& lcl_getDefaultFormats_throw() + { + std::scoped_lock aGuard( getDefaultFormatsMutex() ); + + if ( !s_xDefaultFormats.is() && !s_bTriedCreation ) + { + s_bTriedCreation = true; + s_xDefaultFormats = NumberFormatsSupplier::createWithDefaultLocale( ::comphelper::getProcessComponentContext() ); + } + if ( !s_xDefaultFormats.is() ) + throw RuntimeException(); + + return s_xDefaultFormats; + } + + void lcl_registerDefaultFormatsClient() + { + osl_atomic_increment( &s_refCount ); + } + + void lcl_revokeDefaultFormatsClient() + { + Reference< XNumberFormatsSupplier > xReleasePotentialLastReference; + { + std::scoped_lock aGuard( getDefaultFormatsMutex() ); + if ( 0 != osl_atomic_decrement( &s_refCount ) ) + return; + + xReleasePotentialLastReference = std::move(s_xDefaultFormats); + s_bTriedCreation = false; + } + xReleasePotentialLastReference.clear(); + } + } + + + // = UnoControlFormattedFieldModel + + + UnoControlFormattedFieldModel::UnoControlFormattedFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) + ,m_bRevokedAsClient( false ) + ,m_bSettingValueAndText( false ) + { + ImplRegisterProperty( BASEPROPERTY_ALIGN ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_DEFAULT ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_VALUE ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_MAX ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_MIN ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_FORMATKEY ); + ImplRegisterProperty( BASEPROPERTY_FORMATSSUPPLIER ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_MAXTEXTLEN ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_REPEAT ); + ImplRegisterProperty( BASEPROPERTY_REPEAT_DELAY ); + ImplRegisterProperty( BASEPROPERTY_READONLY ); + ImplRegisterProperty( BASEPROPERTY_SPIN ); + ImplRegisterProperty( BASEPROPERTY_STRICTFORMAT ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); + ImplRegisterProperty( BASEPROPERTY_TEXTCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HIDEINACTIVESELECTION ); + ImplRegisterProperty( BASEPROPERTY_ENFORCE_FORMAT ); + ImplRegisterProperty( BASEPROPERTY_VERTICALALIGN ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR ); + ImplRegisterProperty( BASEPROPERTY_HIGHLIGHT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_HIGHLIGHT_TEXT_COLOR ); + + Any aTreatAsNumber; + aTreatAsNumber <<= true; + ImplRegisterProperty( BASEPROPERTY_TREATASNUMBER, aTreatAsNumber ); + + lcl_registerDefaultFormatsClient(); + } + + + UnoControlFormattedFieldModel::~UnoControlFormattedFieldModel() + { + } + + + OUString UnoControlFormattedFieldModel::getServiceName() + { + return "stardiv.vcl.controlmodel.FormattedField"; + } + + + void UnoControlFormattedFieldModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const Any& rValue ) + { + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + switch ( nHandle ) + { + case BASEPROPERTY_EFFECTIVE_VALUE: + if ( !m_bSettingValueAndText ) + impl_updateTextFromValue_nothrow(rGuard); + break; + case BASEPROPERTY_FORMATSSUPPLIER: + impl_updateCachedFormatter_nothrow(rGuard); + impl_updateTextFromValue_nothrow(rGuard); + break; + case BASEPROPERTY_FORMATKEY: + impl_updateCachedFormatKey_nothrow(rGuard); + impl_updateTextFromValue_nothrow(rGuard); + break; + } + } + + + void UnoControlFormattedFieldModel::impl_updateTextFromValue_nothrow( std::unique_lock<std::mutex>& rGuard) + { + if ( !m_xCachedFormatter.is() ) + impl_updateCachedFormatter_nothrow(rGuard); + if ( !m_xCachedFormatter.is() ) + return; + + try + { + Any aEffectiveValue; + getFastPropertyValue( rGuard, aEffectiveValue, BASEPROPERTY_EFFECTIVE_VALUE ); + + OUString sStringValue; + if ( !( aEffectiveValue >>= sStringValue ) ) + { + double nDoubleValue(0); + if ( aEffectiveValue >>= nDoubleValue ) + { + sal_Int32 nFormatKey( 0 ); + if ( m_aCachedFormat.hasValue() ) + m_aCachedFormat >>= nFormatKey; + sStringValue = m_xCachedFormatter->convertNumberToString( nFormatKey, nDoubleValue ); + } + } + + setFastPropertyValueImpl( rGuard, BASEPROPERTY_TEXT, Any( sStringValue ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + + void UnoControlFormattedFieldModel::impl_updateCachedFormatter_nothrow(std::unique_lock<std::mutex>& rGuard) + { + Any aFormatsSupplier; + getFastPropertyValue( rGuard, aFormatsSupplier, BASEPROPERTY_FORMATSSUPPLIER ); + try + { + Reference< XNumberFormatsSupplier > xSupplier( aFormatsSupplier, UNO_QUERY ); + if ( !xSupplier.is() ) + xSupplier = lcl_getDefaultFormats_throw(); + + if ( !m_xCachedFormatter.is() ) + { + m_xCachedFormatter.set( + NumberFormatter::create(::comphelper::getProcessComponentContext()), + UNO_QUERY_THROW + ); + } + m_xCachedFormatter->attachNumberFormatsSupplier( xSupplier ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + + void UnoControlFormattedFieldModel::impl_updateCachedFormatKey_nothrow(std::unique_lock<std::mutex>& rGuard) + { + Any aFormatKey; + getFastPropertyValue( rGuard, aFormatKey, BASEPROPERTY_FORMATKEY ); + m_aCachedFormat = aFormatKey; + } + + + void UnoControlFormattedFieldModel::dispose( ) + { + UnoControlModel::dispose(); + + std::unique_lock aGuard( m_aMutex ); + if ( !m_bRevokedAsClient ) + { + lcl_revokeDefaultFormatsClient(); + m_bRevokedAsClient = true; + } + } + + + void UnoControlFormattedFieldModel::ImplNormalizePropertySequence( const sal_Int32 _nCount, sal_Int32* _pHandles, + Any* _pValues, sal_Int32* _pValidHandles ) const + { + ImplEnsureHandleOrder( _nCount, _pHandles, _pValues, BASEPROPERTY_EFFECTIVE_VALUE, BASEPROPERTY_TEXT ); + + UnoControlModel::ImplNormalizePropertySequence( _nCount, _pHandles, _pValues, _pValidHandles ); + } + + + namespace + { + class ResetFlagOnExit + { + private: + bool& m_rFlag; + + public: + explicit ResetFlagOnExit( bool& _rFlag ) + :m_rFlag( _rFlag ) + { + } + ~ResetFlagOnExit() + { + m_rFlag = false; + } + }; + } + + + void SAL_CALL UnoControlFormattedFieldModel::setPropertyValues( const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues ) + { + bool bSettingValue = false; + bool bSettingText = false; + for ( auto const & propertyName : _rPropertyNames ) + { + if ( BASEPROPERTY_EFFECTIVE_VALUE == GetPropertyId( propertyName ) ) + bSettingValue = true; + + if ( BASEPROPERTY_TEXT == GetPropertyId( propertyName ) ) + bSettingText = true; + } + + m_bSettingValueAndText = ( bSettingValue && bSettingText ); + ResetFlagOnExit aResetFlag( m_bSettingValueAndText ); + UnoControlModel::setPropertyValues( _rPropertyNames, _rValues ); + } + + + bool UnoControlFormattedFieldModel::convertFastPropertyValue( + std::unique_lock<std::mutex>& rGuard, + Any& rConvertedValue, Any& rOldValue, sal_Int32 nPropId, + const Any& rValue ) + { + if ( BASEPROPERTY_EFFECTIVE_DEFAULT == nPropId && rValue.hasValue() ) + { + double dVal = 0; + bool bStreamed = (rValue >>= dVal); + if ( bStreamed ) + { + rConvertedValue <<= dVal; + } + else + { + sal_Int32 nVal = 0; + bStreamed = (rValue >>= nVal); + if ( bStreamed ) + { + rConvertedValue <<= static_cast<double>(nVal); + } + else + { + OUString sVal; + bStreamed = (rValue >>= sVal); + if ( bStreamed ) + { + rConvertedValue <<= sVal; + } + } + } + + if ( bStreamed ) + { + getFastPropertyValue( rGuard, rOldValue, nPropId ); + return !CompareProperties( rConvertedValue, rOldValue ); + } + + throw IllegalArgumentException( + ("Unable to convert the given value for the property " + + GetPropertyName(static_cast<sal_uInt16>(nPropId)) + + " (double, integer, or string expected)."), + static_cast< XPropertySet* >(this), + 1); + } + + return UnoControlModel::convertFastPropertyValue( rGuard, rConvertedValue, rOldValue, nPropId, rValue ); + } + + + Any UnoControlFormattedFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + Any aReturn; + switch (nPropId) + { + case BASEPROPERTY_DEFAULTCONTROL: aReturn <<= OUString("stardiv.vcl.control.FormattedField"); break; + + case BASEPROPERTY_TREATASNUMBER: aReturn <<= true; break; + + case BASEPROPERTY_EFFECTIVE_DEFAULT: + case BASEPROPERTY_EFFECTIVE_VALUE: + case BASEPROPERTY_EFFECTIVE_MAX: + case BASEPROPERTY_EFFECTIVE_MIN: + case BASEPROPERTY_FORMATKEY: + case BASEPROPERTY_FORMATSSUPPLIER: + // (void) + break; + + default : aReturn = UnoControlModel::ImplGetDefaultValue( nPropId ); break; + } + + return aReturn; + } + + + ::cppu::IPropertyArrayHelper& UnoControlFormattedFieldModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + // beans::XMultiPropertySet + + Reference< XPropertySetInfo > UnoControlFormattedFieldModel::getPropertySetInfo( ) + { + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + OUString UnoControlFormattedFieldModel::getImplementationName() + { + return "stardiv.Toolkit.UnoControlFormattedFieldModel"; + } + + css::uno::Sequence<OUString> + UnoControlFormattedFieldModel::getSupportedServiceNames() + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlFormattedFieldModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.FormattedField"; + return s; + } + + // = UnoFormattedFieldControl + + + UnoFormattedFieldControl::UnoFormattedFieldControl() + { + } + + + OUString UnoFormattedFieldControl::GetComponentServiceName() const + { + return "FormattedField"; + } + + + void UnoFormattedFieldControl::textChanged(const TextEvent& e) + { + Reference< XVclWindowPeer > xPeer(getPeer(), UNO_QUERY); + OSL_ENSURE(xPeer.is(), "UnoFormattedFieldControl::textChanged : what kind of peer do I have ?"); + + Sequence< OUString > aNames{ GetPropertyName( BASEPROPERTY_EFFECTIVE_VALUE ), + GetPropertyName( BASEPROPERTY_TEXT ) }; + + Sequence< Any > aValues{ xPeer->getProperty( aNames[0] ), + xPeer->getProperty( aNames[1] ) }; + + ImplSetPropertyValues( aNames, aValues, false ); + + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); + } + + OUString UnoFormattedFieldControl::getImplementationName() + { + return "stardiv.Toolkit.UnoFormattedFieldControl"; + } + + css::uno::Sequence<OUString> + UnoFormattedFieldControl::getSupportedServiceNames() + { + auto s(UnoEditControl::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlFormattedField"; + ps[s.getLength() - 1] = "stardiv.vcl.control.FormattedField"; + return s; + } +} // namespace toolkit + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFormattedFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoControlFormattedFieldModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFormattedFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoFormattedFieldControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/geometrycontrolmodel.cxx b/toolkit/source/controls/geometrycontrolmodel.cxx new file mode 100644 index 0000000000..70e8817da2 --- /dev/null +++ b/toolkit/source/controls/geometrycontrolmodel.cxx @@ -0,0 +1,609 @@ +/* -*- 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 <controls/geometrycontrolmodel.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <osl/diagnose.h> +#include <comphelper/sequence.hxx> +#include <controls/eventcontainer.hxx> +#include <helper/property.hxx> +#include <algorithm> +#include <functional> +#include <utility> + + +#define GCM_PROPERTY_ID_POS_X 1 +#define GCM_PROPERTY_ID_POS_Y 2 +#define GCM_PROPERTY_ID_WIDTH 3 +#define GCM_PROPERTY_ID_HEIGHT 4 +#define GCM_PROPERTY_ID_NAME 5 +#define GCM_PROPERTY_ID_TABINDEX 6 +#define GCM_PROPERTY_ID_STEP 7 +#define GCM_PROPERTY_ID_TAG 8 +#define GCM_PROPERTY_ID_RESOURCERESOLVER 9 + +constexpr OUStringLiteral GCM_PROPERTY_POS_X = u"PositionX"; +constexpr OUStringLiteral GCM_PROPERTY_POS_Y = u"PositionY"; +constexpr OUStringLiteral GCM_PROPERTY_WIDTH = u"Width"; +constexpr OUStringLiteral GCM_PROPERTY_HEIGHT = u"Height"; +constexpr OUStringLiteral GCM_PROPERTY_NAME = u"Name"; +constexpr OUStringLiteral GCM_PROPERTY_TABINDEX = u"TabIndex"; +constexpr OUStringLiteral GCM_PROPERTY_STEP = u"Step"; +constexpr OUStringLiteral GCM_PROPERTY_TAG = u"Tag"; +constexpr OUStringLiteral GCM_PROPERTY_RESOURCERESOLVER = u"ResourceResolver"; + +#define DEFAULT_ATTRIBS() PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT + + +// namespace toolkit +// { + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::container; + using namespace ::comphelper; + + + //= OGeometryControlModel_Base + + + OGeometryControlModel_Base::OGeometryControlModel_Base(css::uno::XAggregation* _pAggregateInstance) + :OPropertySetAggregationHelper( m_aBHelper ) + ,OPropertyContainer( m_aBHelper ) + ,OGCM_Base( m_aMutex ) + ,m_nPosX(0) + ,m_nPosY(0) + ,m_nWidth(0) + ,m_nHeight(0) + ,m_nTabIndex(-1) + ,m_nStep(0) + ,m_bCloneable(false) + { + OSL_ENSURE(nullptr != _pAggregateInstance, "OGeometryControlModel_Base::OGeometryControlModel_Base: invalid aggregate!"); + + osl_atomic_increment(&m_refCount); + { + m_xAggregate = _pAggregateInstance; + + { // check if the aggregate is clonable + Reference< XCloneable > xCloneAccess(m_xAggregate, UNO_QUERY); + m_bCloneable = xCloneAccess.is(); + } + + setAggregation(m_xAggregate); + m_xAggregate->setDelegator(getXWeak()); + } + osl_atomic_decrement(&m_refCount); + + registerProperties(); + } + + + OGeometryControlModel_Base::OGeometryControlModel_Base(Reference< XCloneable >& _rxAggregateInstance) + :OPropertySetAggregationHelper( m_aBHelper ) + ,OPropertyContainer( m_aBHelper ) + ,OGCM_Base( m_aMutex ) + ,m_nPosX(0) + ,m_nPosY(0) + ,m_nWidth(0) + ,m_nHeight(0) + ,m_nTabIndex(-1) + ,m_nStep(0) + ,m_bCloneable(_rxAggregateInstance.is()) + { + osl_atomic_increment(&m_refCount); + { + { + // ensure that the temporary gets destructed NOW + m_xAggregate.set(_rxAggregateInstance, UNO_QUERY); + } + OSL_ENSURE(m_xAggregate.is(), "OGeometryControlModel_Base::OGeometryControlModel_Base: invalid object given!"); + + // now the aggregate has a ref count of 2, but before setting the delegator it must be 1 + _rxAggregateInstance.clear(); + // now it should be the 1 we need here ... + + setAggregation(m_xAggregate); + m_xAggregate->setDelegator(getXWeak()); + } + osl_atomic_decrement(&m_refCount); + + registerProperties(); + } + + + Sequence< Type > SAL_CALL OGeometryControlModel_Base::getTypes( ) + { + // our own types + Sequence< Type > aTypes = ::comphelper::concatSequences( + OPropertySetAggregationHelper::getTypes(), + getBaseTypes(), + OGCM_Base::getTypes() + ); + + if ( m_xAggregate.is() ) + { + // retrieve the types of the aggregate + Reference< XTypeProvider > xAggregateTypeProv; + m_xAggregate->queryAggregation( cppu::UnoType<decltype(xAggregateTypeProv)>::get() ) >>= xAggregateTypeProv; + OSL_ENSURE( xAggregateTypeProv.is(), "OGeometryControlModel_Base::getTypes: aggregate should be a type provider!" ); + Sequence< Type > aAggTypes; + if ( xAggregateTypeProv.is() ) + aAggTypes = xAggregateTypeProv->getTypes(); + + // concat the sequences + sal_Int32 nOldSize = aTypes.getLength(); + aTypes.realloc( nOldSize + aAggTypes.getLength() ); + ::std::copy( + std::cbegin(aAggTypes), + std::cend(aAggTypes), + aTypes.getArray() + nOldSize + ); + } + + return aTypes; + } + + + void OGeometryControlModel_Base::registerProperties() + { + // register our members for the property handling of the OPropertyContainer + registerProperty(GCM_PROPERTY_POS_X, GCM_PROPERTY_ID_POS_X, DEFAULT_ATTRIBS(), &m_nPosX, cppu::UnoType<decltype(m_nPosX)>::get()); + registerProperty(GCM_PROPERTY_POS_Y, GCM_PROPERTY_ID_POS_Y, DEFAULT_ATTRIBS(), &m_nPosY, cppu::UnoType<decltype(m_nPosY)>::get()); + registerProperty(GCM_PROPERTY_WIDTH, GCM_PROPERTY_ID_WIDTH, DEFAULT_ATTRIBS(), &m_nWidth, cppu::UnoType<decltype(m_nWidth)>::get()); + registerProperty(GCM_PROPERTY_HEIGHT, GCM_PROPERTY_ID_HEIGHT, DEFAULT_ATTRIBS(), &m_nHeight, cppu::UnoType<decltype(m_nHeight)>::get()); + registerProperty(GCM_PROPERTY_NAME, GCM_PROPERTY_ID_NAME, DEFAULT_ATTRIBS(), &m_aName, cppu::UnoType<decltype(m_aName)>::get()); + registerProperty(GCM_PROPERTY_TABINDEX, GCM_PROPERTY_ID_TABINDEX, DEFAULT_ATTRIBS(), &m_nTabIndex, cppu::UnoType<decltype(m_nTabIndex)>::get()); + registerProperty(GCM_PROPERTY_STEP, GCM_PROPERTY_ID_STEP, DEFAULT_ATTRIBS(), &m_nStep, cppu::UnoType<decltype(m_nStep)>::get()); + registerProperty(GCM_PROPERTY_TAG, GCM_PROPERTY_ID_TAG, DEFAULT_ATTRIBS(), &m_aTag, cppu::UnoType<decltype(m_aTag)>::get()); + registerProperty(GCM_PROPERTY_RESOURCERESOLVER, GCM_PROPERTY_ID_RESOURCERESOLVER, DEFAULT_ATTRIBS(), &m_xStrResolver, cppu::UnoType<decltype(m_xStrResolver)>::get()); + } + + + css::uno::Any OGeometryControlModel_Base::ImplGetDefaultValueByHandle(sal_Int32 nHandle) + { + css::uno::Any aDefault; + + switch ( nHandle ) + { + case GCM_PROPERTY_ID_POS_X: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_POS_Y: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_WIDTH: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_HEIGHT: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_NAME: aDefault <<= OUString(); break; + case GCM_PROPERTY_ID_TABINDEX: aDefault <<= sal_Int16(-1); break; + case GCM_PROPERTY_ID_STEP: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_TAG: aDefault <<= OUString(); break; + case GCM_PROPERTY_ID_RESOURCERESOLVER: aDefault <<= Reference< resource::XStringResourceResolver >(); break; + default: OSL_FAIL( "ImplGetDefaultValueByHandle - unknown Property" ); + } + + return aDefault; + } + + + css::uno::Any OGeometryControlModel_Base::ImplGetPropertyValueByHandle(sal_Int32 nHandle) const + { + css::uno::Any aValue; + + switch ( nHandle ) + { + case GCM_PROPERTY_ID_POS_X: aValue <<= m_nPosX; break; + case GCM_PROPERTY_ID_POS_Y: aValue <<= m_nPosY; break; + case GCM_PROPERTY_ID_WIDTH: aValue <<= m_nWidth; break; + case GCM_PROPERTY_ID_HEIGHT: aValue <<= m_nHeight; break; + case GCM_PROPERTY_ID_NAME: aValue <<= m_aName; break; + case GCM_PROPERTY_ID_TABINDEX: aValue <<= m_nTabIndex; break; + case GCM_PROPERTY_ID_STEP: aValue <<= m_nStep; break; + case GCM_PROPERTY_ID_TAG: aValue <<= m_aTag; break; + case GCM_PROPERTY_ID_RESOURCERESOLVER: aValue <<= m_xStrResolver; break; + default: OSL_FAIL( "ImplGetPropertyValueByHandle - unknown Property" ); + } + + return aValue; + } + + + void OGeometryControlModel_Base::ImplSetPropertyValueByHandle(sal_Int32 nHandle, const css::uno::Any& aValue) + { + switch ( nHandle ) + { + case GCM_PROPERTY_ID_POS_X: aValue >>= m_nPosX; break; + case GCM_PROPERTY_ID_POS_Y: aValue >>= m_nPosY; break; + case GCM_PROPERTY_ID_WIDTH: aValue >>= m_nWidth; break; + case GCM_PROPERTY_ID_HEIGHT: aValue >>= m_nHeight; break; + case GCM_PROPERTY_ID_NAME: aValue >>= m_aName; break; + case GCM_PROPERTY_ID_TABINDEX: aValue >>= m_nTabIndex; break; + case GCM_PROPERTY_ID_STEP: aValue >>= m_nStep; break; + case GCM_PROPERTY_ID_TAG: aValue >>= m_aTag; break; + case GCM_PROPERTY_ID_RESOURCERESOLVER: aValue >>= m_xStrResolver; break; + default: OSL_FAIL( "ImplSetPropertyValueByHandle - unknown Property" ); + } + } + + + Any SAL_CALL OGeometryControlModel_Base::queryAggregation( const Type& _rType ) + { + Any aReturn; + if (_rType.equals(cppu::UnoType<XCloneable>::get()) && !m_bCloneable) + // somebody is asking for the XCloneable interface, but our aggregate does not support it + // -> outta here + // (need this extra check, cause OGCM_Base::queryAggregation would return this interface + // in every case) + return aReturn; + + aReturn = OGCM_Base::queryAggregation(_rType); + // the basic interfaces (XInterface, XAggregation etc) + + if (!aReturn.hasValue()) + aReturn = OPropertySetAggregationHelper::queryInterface(_rType); + // the property set related interfaces + + if (!aReturn.hasValue() && m_xAggregate.is()) + aReturn = m_xAggregate->queryAggregation(_rType); + // the interfaces our aggregate can provide + + return aReturn; + } + + + Any SAL_CALL OGeometryControlModel_Base::queryInterface( const Type& _rType ) + { + return OGCM_Base::queryInterface(_rType); + } + + + void SAL_CALL OGeometryControlModel_Base::acquire( ) noexcept + { + OGCM_Base::acquire(); + } + + + void SAL_CALL OGeometryControlModel_Base::release( ) noexcept + { + OGCM_Base::release(); + } + + + void OGeometryControlModel_Base::releaseAggregation() + { + // release the aggregate (_before_ clearing m_xAggregate) + if (m_xAggregate.is()) + m_xAggregate->setDelegator(nullptr); + setAggregation(nullptr); + } + + + OGeometryControlModel_Base::~OGeometryControlModel_Base() + { + releaseAggregation(); + } + + + sal_Bool SAL_CALL OGeometryControlModel_Base::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue, + sal_Int32 _nHandle, const Any& _rValue) + { + return OPropertyContainer::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue); + } + + + void SAL_CALL OGeometryControlModel_Base::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) + { + OPropertyContainer::setFastPropertyValue_NoBroadcast(_nHandle, _rValue); + } + + + void SAL_CALL OGeometryControlModel_Base::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const + { + OPropertyArrayAggregationHelper& rPH = static_cast<OPropertyArrayAggregationHelper&>(const_cast<OGeometryControlModel_Base*>(this)->getInfoHelper()); + OUString sPropName; + sal_Int32 nOriginalHandle = -1; + + if (rPH.fillAggregatePropertyInfoByHandle(&sPropName, &nOriginalHandle, _nHandle)) + OPropertySetAggregationHelper::getFastPropertyValue(_rValue, _nHandle); + else + OPropertyContainer::getFastPropertyValue(_rValue, _nHandle); + } + + + css::beans::PropertyState OGeometryControlModel_Base::getPropertyStateByHandle(sal_Int32 nHandle) + { + css::uno::Any aValue = ImplGetPropertyValueByHandle( nHandle ); + css::uno::Any aDefault = ImplGetDefaultValueByHandle( nHandle ); + + return CompareProperties( aValue, aDefault ) ? css::beans::PropertyState_DEFAULT_VALUE : css::beans::PropertyState_DIRECT_VALUE; + } + + + void OGeometryControlModel_Base::setPropertyToDefaultByHandle(sal_Int32 nHandle) + { + ImplSetPropertyValueByHandle( nHandle , ImplGetDefaultValueByHandle( nHandle ) ); + } + + + css::uno::Any OGeometryControlModel_Base::getPropertyDefaultByHandle( sal_Int32 nHandle ) const + { + return ImplGetDefaultValueByHandle( nHandle ); + } + + + Reference< XPropertySetInfo> SAL_CALL OGeometryControlModel_Base::getPropertySetInfo() + { + return OPropertySetAggregationHelper::createPropertySetInfo(getInfoHelper()); + } + + + Reference< XCloneable > SAL_CALL OGeometryControlModel_Base::createClone( ) + { + OSL_ENSURE(m_bCloneable, "OGeometryControlModel_Base::createClone: invalid call!"); + if (!m_bCloneable) + return Reference< XCloneable >(); + + // let the aggregate create its own clone + // the interface + Reference< XCloneable > xCloneAccess; + m_xAggregate->queryAggregation(cppu::UnoType<decltype(xCloneAccess)>::get()) >>= xCloneAccess; + OSL_ENSURE(xCloneAccess.is(), "OGeometryControlModel_Base::createClone: suspicious aggregate!"); + if (!xCloneAccess.is()) + return Reference< XCloneable >(); + // the aggregate's clone + Reference< XCloneable > xAggregateClone = xCloneAccess->createClone(); + OSL_ENSURE(xAggregateClone.is(), "OGeometryControlModel_Base::createClone: suspicious return of the aggregate!"); + + // create a new wrapper aggregating this return value + rtl::Reference<OGeometryControlModel_Base> pOwnClone = createClone_Impl(xAggregateClone); + OSL_ENSURE(pOwnClone, "OGeometryControlModel_Base::createClone: invalid derivee behaviour!"); + OSL_ENSURE(!xAggregateClone.is(), "OGeometryControlModel_Base::createClone: invalid ctor behaviour!"); + // should have been reset + + // set properties + pOwnClone->m_nPosX = m_nPosX; + pOwnClone->m_nPosY = m_nPosY; + pOwnClone->m_nWidth = m_nWidth; + pOwnClone->m_nHeight = m_nHeight; + pOwnClone->m_aName = m_aName; + pOwnClone->m_nTabIndex = m_nTabIndex; + pOwnClone->m_nStep = m_nStep; + pOwnClone->m_aTag = m_aTag; + + + // Clone event container + Reference< css::script::XScriptEventsSupplier > xEventsSupplier = + static_cast< css::script::XScriptEventsSupplier* >( this ); + + if( xEventsSupplier.is() ) + { + Reference< XNameContainer > xEventCont = xEventsSupplier->getEvents(); + Reference< XNameContainer > xCloneEventCont = pOwnClone->getEvents(); + + const css::uno::Sequence< OUString > aNames = + xEventCont->getElementNames(); + + for( const OUString& aName : aNames ) + { + css::uno::Any aElement = xEventCont->getByName( aName ); + xCloneEventCont->insertByName( aName, aElement ); + } + } + + return pOwnClone; + } + + + Reference< XNameContainer > SAL_CALL OGeometryControlModel_Base::getEvents() + { + if( !mxEventContainer.is() ) + mxEventContainer = new toolkit::ScriptEventContainer(); + return mxEventContainer; + } + + + void SAL_CALL OGeometryControlModel_Base::disposing() + { + OGCM_Base::disposing(); + OPropertySetAggregationHelper::disposing(); + + Reference<XComponent> xComp; + if ( query_aggregation( m_xAggregate, xComp ) ) + xComp->dispose(); + } + + + //= OCommonGeometryControlModel + + + typedef std::unordered_map< OUString, sal_Int32 > HashMapString2Int; + typedef std::vector< css::uno::Sequence< css::beans::Property > > PropSeqArray; + typedef std::vector< ::std::vector< sal_Int32 > > IntArrayArray; + + // for creating class-unique PropertySetInfo's, we need some info: + namespace { HashMapString2Int gServiceSpecifierMap; } + // this one maps from a String, which is the service specifier for our + // aggregate, to a unique id + + namespace { PropSeqArray gAggregateProperties; } + // this one contains the properties which belong to all the unique ids + // in ServiceSpecifierMap + + namespace { IntArrayArray gAmbiguousPropertyIds; } + // the ids of the properties which we as well as our aggregate supply + // For such props, we let our base class handle them, and whenever such + // a prop is set, we forward this to our aggregate. + + // With this, we can ensure that two instances of this class share the + // same PropertySetInfo if and only if both aggregates have the same + // service specifier. + + + OCommonGeometryControlModel::OCommonGeometryControlModel( Reference< XCloneable >& _rxAgg, OUString _aServiceSpecifier ) + :OGeometryControlModel_Base( _rxAgg ) + ,m_sServiceSpecifier(std::move( _aServiceSpecifier )) + ,m_nPropertyMapId( 0 ) + { + Reference< XPropertySetInfo > xPI; + if ( m_xAggregateSet.is() ) + xPI = m_xAggregateSet->getPropertySetInfo(); + if ( !xPI.is() ) + { + releaseAggregation(); + throw IllegalArgumentException(); + } + + HashMapString2Int::iterator aPropMapIdPos = gServiceSpecifierMap.find( m_sServiceSpecifier ); + if ( gServiceSpecifierMap.end() == aPropMapIdPos ) + { + m_nPropertyMapId = gAggregateProperties.size(); + gAggregateProperties.push_back( xPI->getProperties() ); + gAmbiguousPropertyIds.emplace_back( ); + + gServiceSpecifierMap[ m_sServiceSpecifier ] = m_nPropertyMapId; + } + else + m_nPropertyMapId = aPropMapIdPos->second; + } + + namespace { + + struct PropertyNameLess + { + bool operator()( const Property& _rLHS, const Property& _rRHS ) + { + return _rLHS.Name < _rRHS.Name; + } + }; + + + struct PropertyNameEqual + { + const OUString& m_rCompare; + explicit PropertyNameEqual( const OUString& _rCompare ) : m_rCompare( _rCompare ) { } + + bool operator()( const Property& _rLHS ) + { + return _rLHS.Name == m_rCompare; + } + }; + + } + + ::cppu::IPropertyArrayHelper* OCommonGeometryControlModel::createArrayHelper( sal_Int32 _nId ) const + { + OSL_ENSURE( _nId == m_nPropertyMapId, "OCommonGeometryControlModel::createArrayHelper: invalid argument!" ); + OSL_ENSURE( _nId < static_cast<sal_Int32>(gAggregateProperties.size()), "OCommonGeometryControlModel::createArrayHelper: invalid status info (1)!" ); + OSL_ENSURE( _nId < static_cast<sal_Int32>(gAmbiguousPropertyIds.size()), "OCommonGeometryControlModel::createArrayHelper: invalid status info (2)!" ); + + // our own properties + Sequence< Property > aProps; + OPropertyContainer::describeProperties( aProps ); + + // the aggregate properties + Sequence< Property > aAggregateProps = gAggregateProperties[ _nId ]; + + // look for duplicates, and remember them + IntArrayArray::value_type& rDuplicateIds = gAmbiguousPropertyIds[ _nId ]; + // for this, sort the aggregate properties + auto [begin, end] = asNonConstRange(aAggregateProps); + ::std::sort( + begin, + end, + PropertyNameLess() + ); + + // now loop through our own props + for ( const Property& rProp : std::as_const(aProps) ) + { + // look for the current property in the properties of our aggregate + const Property* pAggPropPos = ::std::find_if( std::cbegin(aAggregateProps), std::cend(aAggregateProps), PropertyNameEqual( rProp.Name ) ); + if ( pAggPropPos != std::cend(aAggregateProps) ) + { // found a duplicate + // -> remove from the aggregate property sequence + ::comphelper::removeElementAt( aAggregateProps, pAggPropPos - std::cbegin(aAggregateProps) ); + + // and additionally, remember the id of this property + rDuplicateIds.push_back( rProp.Handle ); + } + } + + // now, finally, sort the duplicates + ::std::sort( rDuplicateIds.begin(), rDuplicateIds.end(), ::std::less< sal_Int32 >() ); + + return new OPropertyArrayAggregationHelper(aProps, aAggregateProps); + } + + + ::cppu::IPropertyArrayHelper& SAL_CALL OCommonGeometryControlModel::getInfoHelper() + { + return *getArrayHelper( m_nPropertyMapId ); + } + + + rtl::Reference<OGeometryControlModel_Base> OCommonGeometryControlModel::createClone_Impl( Reference< XCloneable >& _rxAggregateInstance ) + { + return new OCommonGeometryControlModel( _rxAggregateInstance, m_sServiceSpecifier ); + } + + Sequence< sal_Int8 > SAL_CALL OCommonGeometryControlModel::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + namespace { + + struct Int32Equal + { + sal_Int32 m_nCompare; + explicit Int32Equal( sal_Int32 _nCompare ) : m_nCompare( _nCompare ) { } + + bool operator()( sal_Int32 _nLHS ) + { + return _nLHS == m_nCompare; + } + }; + + } + + void SAL_CALL OCommonGeometryControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) + { + OGeometryControlModel_Base::setFastPropertyValue_NoBroadcast( _nHandle, _rValue ); + + // look if this id is one we recognized as duplicate + IntArrayArray::value_type& rDuplicateIds = gAmbiguousPropertyIds[ m_nPropertyMapId ]; + + if ( std::any_of(rDuplicateIds.begin(), rDuplicateIds.end(), Int32Equal( _nHandle )) ) + { + // yes, it is such a property + OUString sPropName; + sal_Int16 nAttributes(0); + static_cast< OPropertyArrayAggregationHelper* >( getArrayHelper( m_nPropertyMapId ) )->fillPropertyMembersByHandle( &sPropName, &nAttributes, _nHandle ); + + if ( m_xAggregateSet.is() && !sPropName.isEmpty() ) + m_xAggregateSet->setPropertyValue( sPropName, _rValue ); + } + } + + +// } // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/defaultgridcolumnmodel.cxx b/toolkit/source/controls/grid/defaultgridcolumnmodel.cxx new file mode 100644 index 0000000000..5e1a085ba0 --- /dev/null +++ b/toolkit/source/controls/grid/defaultgridcolumnmodel.cxx @@ -0,0 +1,375 @@ +/* -*- 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 "gridcolumn.hxx" + +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/componentguard.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <comphelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <vector> + +using namespace css::awt; +using namespace css::awt::grid; +using namespace css::container; +using namespace css::lang; +using namespace css::uno; +using namespace toolkit; + +namespace { + +typedef ::comphelper::WeakComponentImplHelper < css::awt::grid::XGridColumnModel + , css::lang::XServiceInfo + > DefaultGridColumnModel_Base; + +class DefaultGridColumnModel : public DefaultGridColumnModel_Base +{ +public: + DefaultGridColumnModel(); + DefaultGridColumnModel( DefaultGridColumnModel const & i_copySource ); + + // XGridColumnModel + virtual ::sal_Int32 SAL_CALL getColumnCount() override; + virtual css::uno::Reference< css::awt::grid::XGridColumn > SAL_CALL createColumn( ) override; + virtual ::sal_Int32 SAL_CALL addColumn(const css::uno::Reference< css::awt::grid::XGridColumn > & column) override; + virtual void SAL_CALL removeColumn( ::sal_Int32 i_columnIndex ) override; + virtual css::uno::Sequence< css::uno::Reference< css::awt::grid::XGridColumn > > SAL_CALL getColumns() override; + virtual css::uno::Reference< css::awt::grid::XGridColumn > SAL_CALL getColumn(::sal_Int32 index) override; + virtual void SAL_CALL setDefaultColumns(sal_Int32 rowElements) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XContainer + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // OComponentHelper + virtual void disposing( std::unique_lock<std::mutex>& ) override; + +private: + typedef ::std::vector< rtl::Reference< GridColumn > > Columns; + + ::comphelper::OInterfaceContainerHelper4<XContainerListener> m_aContainerListeners; + Columns m_aColumns; +}; + + DefaultGridColumnModel::DefaultGridColumnModel() + { + } + + DefaultGridColumnModel::DefaultGridColumnModel( DefaultGridColumnModel const & i_copySource ) + { + Columns aColumns; + aColumns.reserve( i_copySource.m_aColumns.size() ); + try + { + for ( Columns::const_iterator col = i_copySource.m_aColumns.begin(); + col != i_copySource.m_aColumns.end(); + ++col + ) + { + rtl::Reference< GridColumn > const xClone( new GridColumn(**col) ); + + xClone->setIndex( col - i_copySource.m_aColumns.begin() ); + + aColumns.push_back( xClone ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + if ( aColumns.size() == i_copySource.m_aColumns.size() ) + m_aColumns.swap( aColumns ); + } + + ::sal_Int32 SAL_CALL DefaultGridColumnModel::getColumnCount() + { + return m_aColumns.size(); + } + + + Reference< XGridColumn > SAL_CALL DefaultGridColumnModel::createColumn( ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return new GridColumn(); + } + + + ::sal_Int32 SAL_CALL DefaultGridColumnModel::addColumn( const Reference< XGridColumn > & i_column ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + GridColumn* const pGridColumn = dynamic_cast<GridColumn*>( i_column.get() ); + if ( pGridColumn == nullptr ) + throw css::lang::IllegalArgumentException( "invalid column implementation", *this, 1 ); + + m_aColumns.push_back( pGridColumn ); + sal_Int32 index = m_aColumns.size() - 1; + pGridColumn->setIndex( index ); + + // fire insertion notifications + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= index; + aEvent.Element <<= i_column; + + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementInserted, aEvent ); + + return index; + } + + + void SAL_CALL DefaultGridColumnModel::removeColumn( ::sal_Int32 i_columnIndex ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_columnIndex < 0 ) || ( o3tl::make_unsigned( i_columnIndex ) >= m_aColumns.size() ) ) + throw css::lang::IndexOutOfBoundsException( OUString(), *this ); + + Columns::iterator const pos = m_aColumns.begin() + i_columnIndex; + Reference< XGridColumn > const xColumn( *pos ); + m_aColumns.erase( pos ); + + // update indexes of all subsequent columns + sal_Int32 columnIndex( i_columnIndex ); + for ( Columns::iterator updatePos = m_aColumns.begin() + columnIndex; + updatePos != m_aColumns.end(); + ++updatePos, ++columnIndex + ) + { + GridColumn* pColumnImpl = updatePos->get(); + pColumnImpl->setIndex( columnIndex ); + } + + // fire removal notifications + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= i_columnIndex; + aEvent.Element <<= xColumn; + + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementRemoved, aEvent ); + + aGuard.unlock(); + + // dispose the removed column + try + { + xColumn->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + + Sequence< Reference< XGridColumn > > SAL_CALL DefaultGridColumnModel::getColumns() + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return ::comphelper::containerToSequence<Reference<XGridColumn>>( m_aColumns ); + } + + + Reference< XGridColumn > SAL_CALL DefaultGridColumnModel::getColumn(::sal_Int32 index) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( index >=0 && o3tl::make_unsigned(index) < m_aColumns.size()) + return m_aColumns[index]; + + throw css::lang::IndexOutOfBoundsException(); + } + + + void SAL_CALL DefaultGridColumnModel::setDefaultColumns(sal_Int32 rowElements) + { + ::std::vector< ContainerEvent > aRemovedColumns; + ::std::vector< ContainerEvent > aInsertedColumns; + + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + // remove existing columns + while ( !m_aColumns.empty() ) + { + const size_t lastColIndex = m_aColumns.size() - 1; + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= sal_Int32( lastColIndex ); + aEvent.Element <<= Reference<XGridColumn>(m_aColumns[ lastColIndex ]); + aRemovedColumns.push_back( aEvent ); + + m_aColumns.erase( m_aColumns.begin() + lastColIndex ); + } + + // add new columns + for ( sal_Int32 i=0; i<rowElements; ++i ) + { + ::rtl::Reference< GridColumn > const pGridColumn = new GridColumn(); + OUString colTitle = "Column " + OUString::number( i + 1 ); + pGridColumn->setTitle( colTitle ); + pGridColumn->setColumnWidth( 80 /* APPFONT */ ); + pGridColumn->setFlexibility( 1 ); + pGridColumn->setResizeable( true ); + pGridColumn->setDataColumnIndex( i ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= i; + aEvent.Element <<= Reference<XGridColumn>(pGridColumn); + aInsertedColumns.push_back( aEvent ); + + m_aColumns.push_back( pGridColumn ); + pGridColumn->setIndex( i ); + } + + // fire removal notifications + for (const auto& rEvent : aRemovedColumns) + { + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementRemoved, rEvent ); + } + + // fire insertion notifications + for (const auto& rEvent : aInsertedColumns) + { + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementInserted, rEvent ); + } + + aGuard.unlock(); + + // dispose removed columns + for (const auto& rEvent : aRemovedColumns) + { + try + { + const Reference< XComponent > xColComp( rEvent.Element, UNO_QUERY ); + if (xColComp) + xColComp->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + } + + + OUString SAL_CALL DefaultGridColumnModel::getImplementationName( ) + { + return "stardiv.Toolkit.DefaultGridColumnModel"; + } + + sal_Bool SAL_CALL DefaultGridColumnModel::supportsService( const OUString& i_serviceName ) + { + return cppu::supportsService(this, i_serviceName); + } + + Sequence< OUString > SAL_CALL DefaultGridColumnModel::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.DefaultGridColumnModel" }; + } + + + void SAL_CALL DefaultGridColumnModel::addContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + if ( i_listener.is() ) + m_aContainerListeners.addInterface( aGuard, i_listener ); + } + + + void SAL_CALL DefaultGridColumnModel::removeContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + if ( i_listener.is() ) + m_aContainerListeners.removeInterface( aGuard, i_listener ); + } + + + void DefaultGridColumnModel::disposing( std::unique_lock<std::mutex>& rGuard ) + { + DefaultGridColumnModel_Base::disposing(rGuard); + + EventObject aEvent( *this ); + m_aContainerListeners.disposeAndClear( rGuard, aEvent ); + + // remove, dispose and clear columns + while ( !m_aColumns.empty() ) + { + try + { + m_aColumns[ 0 ]->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + m_aColumns.erase( m_aColumns.begin() ); + } + + Columns().swap(m_aColumns); + } + + + Reference< css::util::XCloneable > SAL_CALL DefaultGridColumnModel::createClone( ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return new DefaultGridColumnModel( *this ); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_DefaultGridColumnModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DefaultGridColumnModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/defaultgriddatamodel.cxx b/toolkit/source/controls/grid/defaultgriddatamodel.cxx new file mode 100644 index 0000000000..3b803d4a2e --- /dev/null +++ b/toolkit/source/controls/grid/defaultgriddatamodel.cxx @@ -0,0 +1,512 @@ +/* -*- 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/grid/XMutableGridDataModel.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 <comphelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <algorithm> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::grid; +using namespace ::com::sun::star::lang; + +namespace { + +typedef ::comphelper::WeakComponentImplHelper < XMutableGridDataModel + , XServiceInfo + > DefaultGridDataModel_Base; + +class DefaultGridDataModel: public DefaultGridDataModel_Base +{ +public: + DefaultGridDataModel(); + DefaultGridDataModel( DefaultGridDataModel const & i_copySource ); + + // XMutableGridDataModel + virtual void SAL_CALL addRow( const Any& i_heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL addRows( const css::uno::Sequence< css::uno::Any>& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL insertRow( ::sal_Int32 i_index, const css::uno::Any& i_heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL insertRows( ::sal_Int32 i_index, const css::uno::Sequence< css::uno::Any>& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL removeRow( ::sal_Int32 RowIndex ) override; + virtual void SAL_CALL removeAllRows( ) override; + virtual void SAL_CALL updateCellData( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowData( const css::uno::Sequence< ::sal_Int32 >& ColumnIndexes, ::sal_Int32 RowIndex, const css::uno::Sequence< css::uno::Any >& Values ) override; + virtual void SAL_CALL updateRowHeading( ::sal_Int32 RowIndex, const css::uno::Any& Heading ) override; + virtual void SAL_CALL updateCellToolTip( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowToolTip( ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL addGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + virtual void SAL_CALL removeGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + + // XGridDataModel + virtual ::sal_Int32 SAL_CALL getRowCount() override; + virtual ::sal_Int32 SAL_CALL getColumnCount() override; + virtual css::uno::Any SAL_CALL getCellData( ::sal_Int32 Column, ::sal_Int32 Row ) override; + virtual css::uno::Any SAL_CALL getCellToolTip( ::sal_Int32 Column, ::sal_Int32 Row ) override; + virtual css::uno::Any SAL_CALL getRowHeading( ::sal_Int32 RowIndex ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getRowData( ::sal_Int32 RowIndex ) override; + + // OComponentHelper + virtual void disposing( std::unique_lock<std::mutex>& ) override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + typedef ::std::pair< Any, Any > CellData; + typedef ::std::vector< CellData > RowData; + typedef ::std::vector< RowData > GridData; + + void broadcast( + GridDataEvent const & i_event, + void ( SAL_CALL css::awt::grid::XGridDataListener::*i_listenerMethod )( css::awt::grid::GridDataEvent const & ), + std::unique_lock<std::mutex>& i_instanceLock + ); + + void impl_insertRow( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_position, Any const & i_heading, Sequence< Any > const & i_rowData, sal_Int32 const i_assumedColCount = -1 ); + + ::sal_Int32 impl_getRowCount(std::unique_lock<std::mutex>&) const { return sal_Int32( m_aData.size() ); } + + CellData const & impl_getCellData_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex ) const; + CellData& impl_getCellDataAccess_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex ); + RowData& impl_getRowDataAccess_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_rowIndex, size_t const i_requiredColumnCount ); + + GridData m_aData; + ::std::vector< css::uno::Any > m_aRowHeaders; + sal_Int32 m_nColumnCount; + comphelper::OInterfaceContainerHelper4<XGridDataListener> maGridDataListeners; +}; + + DefaultGridDataModel::DefaultGridDataModel() + :m_nColumnCount(0) + { + } + + + DefaultGridDataModel::DefaultGridDataModel( DefaultGridDataModel const & i_copySource ) + :m_aData( i_copySource.m_aData ) + ,m_aRowHeaders( i_copySource.m_aRowHeaders ) + ,m_nColumnCount( i_copySource.m_nColumnCount ) + { + } + + void DefaultGridDataModel::broadcast( GridDataEvent const & i_event, + void ( SAL_CALL XGridDataListener::*i_listenerMethod )( GridDataEvent const & ), std::unique_lock<std::mutex>& i_instanceLock ) + { + maGridDataListeners.notifyEach( i_instanceLock, i_listenerMethod, i_event ); + } + + + ::sal_Int32 SAL_CALL DefaultGridDataModel::getRowCount() + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return impl_getRowCount(aGuard); + } + + + ::sal_Int32 SAL_CALL DefaultGridDataModel::getColumnCount() + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return m_nColumnCount; + } + + + DefaultGridDataModel::CellData const & DefaultGridDataModel::impl_getCellData_throw( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 const i_column, sal_Int32 const i_row ) const + { + if ( ( i_row < 0 ) || ( o3tl::make_unsigned( i_row ) > m_aData.size() ) + || ( i_column < 0 ) || ( i_column > m_nColumnCount ) + ) + throw IndexOutOfBoundsException( OUString(), *const_cast< DefaultGridDataModel* >( this ) ); + + RowData const & rRow( m_aData[ i_row ] ); + if ( o3tl::make_unsigned( i_column ) < rRow.size() ) + return rRow[ i_column ]; + + static CellData s_aEmpty; + return s_aEmpty; + } + + + DefaultGridDataModel::RowData& DefaultGridDataModel::impl_getRowDataAccess_throw( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 const i_rowIndex, size_t const i_requiredColumnCount ) + { + OSL_ENSURE( i_requiredColumnCount <= o3tl::make_unsigned( m_nColumnCount ), "DefaultGridDataModel::impl_getRowDataAccess_throw: invalid column count!" ); + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aData.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + RowData& rRowData( m_aData[ i_rowIndex ] ); + if ( rRowData.size() < i_requiredColumnCount ) + rRowData.resize( i_requiredColumnCount ); + return rRowData; + } + + + DefaultGridDataModel::CellData& DefaultGridDataModel::impl_getCellDataAccess_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex ) + { + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= m_nColumnCount ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + RowData& rRowData( impl_getRowDataAccess_throw( rGuard, i_rowIndex, size_t( i_columnIndex + 1 ) ) ); + return rRowData[ i_columnIndex ]; + } + + + Any SAL_CALL DefaultGridDataModel::getCellData( ::sal_Int32 i_column, ::sal_Int32 i_row ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return impl_getCellData_throw( aGuard, i_column, i_row ).first; + } + + + Any SAL_CALL DefaultGridDataModel::getCellToolTip( ::sal_Int32 i_column, ::sal_Int32 i_row ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return impl_getCellData_throw( aGuard, i_column, i_row ).second; + } + + + Any SAL_CALL DefaultGridDataModel::getRowHeading( ::sal_Int32 i_row ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_row < 0 ) || ( o3tl::make_unsigned( i_row ) >= m_aRowHeaders.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + return m_aRowHeaders[ i_row ]; + } + + + Sequence< Any > SAL_CALL DefaultGridDataModel::getRowData( ::sal_Int32 i_rowIndex ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + Sequence< Any > resultData( m_nColumnCount ); + RowData& rRowData = impl_getRowDataAccess_throw( aGuard, i_rowIndex, m_nColumnCount ); + + ::std::transform( rRowData.begin(), rRowData.end(), resultData.getArray(), + [] ( const CellData& rCellData ) + { return rCellData.first; }); + return resultData; + } + + + void DefaultGridDataModel::impl_insertRow( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 const i_position, Any const & i_heading, Sequence< Any > const & i_rowData, sal_Int32 const i_assumedColCount ) + { + OSL_PRECOND( ( i_assumedColCount <= 0 ) || ( i_assumedColCount >= i_rowData.getLength() ), + "DefaultGridDataModel::impl_insertRow: invalid column count!" ); + + // insert heading + m_aRowHeaders.insert( m_aRowHeaders.begin() + i_position, i_heading ); + + // create new data row + RowData newRow( i_assumedColCount > 0 ? i_assumedColCount : i_rowData.getLength() ); + RowData::iterator cellData = newRow.begin(); + for ( const Any& rData : i_rowData ) + { + cellData->first = rData; + ++cellData; + } + + // insert data row + m_aData.insert( m_aData.begin() + i_position, newRow ); + } + + + void SAL_CALL DefaultGridDataModel::addRow( const Any& i_heading, const Sequence< Any >& i_data ) + { + insertRow( getRowCount(), i_heading, i_data ); + } + + + void SAL_CALL DefaultGridDataModel::addRows( const Sequence< Any >& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + insertRows( getRowCount(), i_headings, i_data ); + } + + + void SAL_CALL DefaultGridDataModel::insertRow( ::sal_Int32 i_index, const Any& i_heading, const Sequence< Any >& i_data ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_index < 0 ) || ( i_index > impl_getRowCount(aGuard) ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + // actually insert the row + impl_insertRow( aGuard, i_index, i_heading, i_data ); + + // update column count + sal_Int32 const columnCount = i_data.getLength(); + if ( columnCount > m_nColumnCount ) + m_nColumnCount = columnCount; + + broadcast( + GridDataEvent( *this, -1, -1, i_index, i_index ), + &XGridDataListener::rowsInserted, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::insertRows( ::sal_Int32 i_index, const Sequence< Any>& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + if ( i_headings.getLength() != i_data.getLength() ) + throw IllegalArgumentException( OUString(), *this, -1 ); + + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_index < 0 ) || ( i_index > impl_getRowCount(aGuard) ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + sal_Int32 const rowCount = i_headings.getLength(); + if ( rowCount == 0 ) + return; + + // determine max col count in the new data + auto pData = std::max_element(i_data.begin(), i_data.end(), + [](const Sequence< Any >& a, const Sequence< Any >& b) { return a.getLength() < b.getLength(); }); + sal_Int32 maxColCount = pData->getLength(); + + if ( maxColCount < m_nColumnCount ) + maxColCount = m_nColumnCount; + + for ( sal_Int32 row=0; row<rowCount; ++row ) + { + impl_insertRow( aGuard, i_index + row, i_headings[row], i_data[row], maxColCount ); + } + + if ( maxColCount > m_nColumnCount ) + m_nColumnCount = maxColCount; + + broadcast( + GridDataEvent( *this, -1, -1, i_index, i_index + rowCount - 1 ), + &XGridDataListener::rowsInserted, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::removeRow( ::sal_Int32 i_rowIndex ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aData.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + m_aRowHeaders.erase( m_aRowHeaders.begin() + i_rowIndex ); + m_aData.erase( m_aData.begin() + i_rowIndex ); + + broadcast( + GridDataEvent( *this, -1, -1, i_rowIndex, i_rowIndex ), + &XGridDataListener::rowsRemoved, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::removeAllRows( ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + m_aRowHeaders.clear(); + m_aData.clear(); + + broadcast( + GridDataEvent( *this, -1, -1, -1, -1 ), + &XGridDataListener::rowsRemoved, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + impl_getCellDataAccess_throw( aGuard, i_columnIndex, i_rowIndex ).first = i_value; + + broadcast( + GridDataEvent( *this, i_columnIndex, i_columnIndex, i_rowIndex, i_rowIndex ), + &XGridDataListener::dataChanged, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateRowData( const Sequence< ::sal_Int32 >& i_columnIndexes, ::sal_Int32 i_rowIndex, const Sequence< Any >& i_values ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aData.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + if ( i_columnIndexes.getLength() != i_values.getLength() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + sal_Int32 const columnCount = i_columnIndexes.getLength(); + if ( columnCount == 0 ) + return; + + for ( sal_Int32 const columnIndex : i_columnIndexes ) + { + if ( ( columnIndex < 0 ) || ( columnIndex > m_nColumnCount ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + } + + RowData& rDataRow = m_aData[ i_rowIndex ]; + for ( sal_Int32 col = 0; col < columnCount; ++col ) + { + sal_Int32 const columnIndex = i_columnIndexes[ col ]; + if ( o3tl::make_unsigned( columnIndex ) >= rDataRow.size() ) + rDataRow.resize( columnIndex + 1 ); + + rDataRow[ columnIndex ].first = i_values[ col ]; + } + + auto aPair = ::std::minmax_element( i_columnIndexes.begin(), i_columnIndexes.end() ); + sal_Int32 const firstAffectedColumn = *aPair.first; + sal_Int32 const lastAffectedColumn = *aPair.second; + broadcast( + GridDataEvent( *this, firstAffectedColumn, lastAffectedColumn, i_rowIndex, i_rowIndex ), + &XGridDataListener::dataChanged, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateRowHeading( ::sal_Int32 i_rowIndex, const Any& i_heading ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aRowHeaders.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + m_aRowHeaders[ i_rowIndex ] = i_heading; + + broadcast( + GridDataEvent( *this, -1, -1, i_rowIndex, i_rowIndex ), + &XGridDataListener::rowHeadingChanged, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + impl_getCellDataAccess_throw( aGuard, i_columnIndex, i_rowIndex ).second = i_value; + } + + + void SAL_CALL DefaultGridDataModel::updateRowToolTip( ::sal_Int32 i_rowIndex, const Any& i_value ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + RowData& rRowData = impl_getRowDataAccess_throw( aGuard, i_rowIndex, m_nColumnCount ); + for ( auto& rCell : rRowData ) + rCell.second = i_value; + } + + + void SAL_CALL DefaultGridDataModel::addGridDataListener( const Reference< grid::XGridDataListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + maGridDataListeners.addInterface( aGuard, i_listener ); + } + + + void SAL_CALL DefaultGridDataModel::removeGridDataListener( const Reference< grid::XGridDataListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + maGridDataListeners.removeInterface( aGuard, i_listener ); + } + + + void DefaultGridDataModel::disposing(std::unique_lock<std::mutex>& rGuard) + { + css::lang::EventObject aEvent; + aEvent.Source.set( *this ); + maGridDataListeners.disposeAndClear(rGuard, aEvent); + + GridData().swap(m_aData); + std::vector<Any>().swap(m_aRowHeaders); + m_nColumnCount = 0; + } + + + OUString SAL_CALL DefaultGridDataModel::getImplementationName( ) + { + return "stardiv.Toolkit.DefaultGridDataModel"; + } + + sal_Bool SAL_CALL DefaultGridDataModel::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + + Sequence< OUString > SAL_CALL DefaultGridDataModel::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.DefaultGridDataModel" }; + } + + + Reference< css::util::XCloneable > SAL_CALL DefaultGridDataModel::createClone( ) + { + return new DefaultGridDataModel( *this ); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_DefaultGridDataModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DefaultGridDataModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcolumn.cxx b/toolkit/source/controls/grid/gridcolumn.cxx new file mode 100644 index 0000000000..92d28ce9c8 --- /dev/null +++ b/toolkit/source/controls/grid/gridcolumn.cxx @@ -0,0 +1,287 @@ +/* -*- 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 "gridcolumn.hxx" + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> + +namespace toolkit +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::awt::grid; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::style; + + + //= DefaultGridColumnModel + + + GridColumn::GridColumn() + :m_nIndex(-1) + ,m_nDataColumnIndex(-1) + ,m_nColumnWidth(4) + ,m_nMaxWidth(0) + ,m_nMinWidth(0) + ,m_nFlexibility(1) + ,m_bResizeable(true) + ,m_eHorizontalAlign( HorizontalAlignment_LEFT ) + { + } + + + GridColumn::GridColumn( GridColumn const & i_copySource ) + :m_aIdentifier( i_copySource.m_aIdentifier ) + ,m_nIndex( -1 ) + ,m_nDataColumnIndex( i_copySource.m_nDataColumnIndex ) + ,m_nColumnWidth( i_copySource.m_nColumnWidth ) + ,m_nMaxWidth( i_copySource.m_nMaxWidth ) + ,m_nMinWidth( i_copySource.m_nMinWidth ) + ,m_nFlexibility( i_copySource.m_nFlexibility ) + ,m_bResizeable( i_copySource.m_bResizeable ) + ,m_sTitle( i_copySource.m_sTitle ) + ,m_sHelpText( i_copySource.m_sHelpText ) + ,m_eHorizontalAlign( i_copySource.m_eHorizontalAlign ) + { + } + + + GridColumn::~GridColumn() + { + } + + + void GridColumn::broadcast_changed( char const * const i_asciiAttributeName, const Any& i_oldValue, const Any& i_newValue, + std::unique_lock<std::mutex>& i_Guard ) + { + Reference< XInterface > const xSource( getXWeak() ); + GridColumnEvent const aEvent( + xSource, OUString::createFromAscii( i_asciiAttributeName ), + i_oldValue, i_newValue, m_nIndex + ); + + maGridColumnListeners.notifyEach( i_Guard, &XGridColumnListener::columnChanged, aEvent ); + } + + + css::uno::Any SAL_CALL GridColumn::getIdentifier() + { + std::unique_lock aGuard( m_aMutex ); + return m_aIdentifier; + } + + + void SAL_CALL GridColumn::setIdentifier(const css::uno::Any & value) + { + std::unique_lock aGuard( m_aMutex ); + m_aIdentifier = value; + } + + + ::sal_Int32 SAL_CALL GridColumn::getColumnWidth() + { + std::unique_lock aGuard( m_aMutex ); + return m_nColumnWidth; + } + + + void SAL_CALL GridColumn::setColumnWidth(::sal_Int32 value) + { + impl_set( m_nColumnWidth, value, "ColumnWidth" ); + } + + + ::sal_Int32 SAL_CALL GridColumn::getMaxWidth() + { + std::unique_lock aGuard( m_aMutex ); + return m_nMaxWidth; + } + + + void SAL_CALL GridColumn::setMaxWidth(::sal_Int32 value) + { + impl_set( m_nMaxWidth, value, "MaxWidth" ); + } + + + ::sal_Int32 SAL_CALL GridColumn::getMinWidth() + { + std::unique_lock aGuard( m_aMutex ); + return m_nMinWidth; + } + + + void SAL_CALL GridColumn::setMinWidth(::sal_Int32 value) + { + impl_set( m_nMinWidth, value, "MinWidth" ); + } + + + OUString SAL_CALL GridColumn::getTitle() + { + std::unique_lock aGuard( m_aMutex ); + return m_sTitle; + } + + + void SAL_CALL GridColumn::setTitle(const OUString & value) + { + impl_set( m_sTitle, value, "Title" ); + } + + + OUString SAL_CALL GridColumn::getHelpText() + { + std::unique_lock aGuard( m_aMutex ); + return m_sHelpText; + } + + + void SAL_CALL GridColumn::setHelpText( const OUString & value ) + { + impl_set( m_sHelpText, value, "HelpText" ); + } + + + sal_Bool SAL_CALL GridColumn::getResizeable() + { + std::unique_lock aGuard( m_aMutex ); + return m_bResizeable; + } + + + void SAL_CALL GridColumn::setResizeable(sal_Bool value) + { + impl_set( m_bResizeable, bool(value), "Resizeable" ); + } + + + ::sal_Int32 SAL_CALL GridColumn::getFlexibility() + { + std::unique_lock aGuard( m_aMutex ); + return m_nFlexibility; + } + + + void SAL_CALL GridColumn::setFlexibility( ::sal_Int32 i_value ) + { + if ( i_value < 0 ) + throw IllegalArgumentException( OUString(), *this, 1 ); + impl_set( m_nFlexibility, i_value, "Flexibility" ); + } + + + HorizontalAlignment SAL_CALL GridColumn::getHorizontalAlign() + { + std::unique_lock aGuard( m_aMutex ); + return m_eHorizontalAlign; + } + + + void SAL_CALL GridColumn::setHorizontalAlign(HorizontalAlignment align) + { + impl_set( m_eHorizontalAlign, align, "HorizontalAlign" ); + } + + + void SAL_CALL GridColumn::addGridColumnListener( const Reference< XGridColumnListener >& xListener ) + { + std::unique_lock aGuard( m_aMutex ); + maGridColumnListeners.addInterface( aGuard, xListener ); + } + + + void SAL_CALL GridColumn::removeGridColumnListener( const Reference< XGridColumnListener >& xListener ) + { + std::unique_lock aGuard( m_aMutex ); + maGridColumnListeners.removeInterface( aGuard, xListener ); + } + + + void GridColumn::disposing(std::unique_lock<std::mutex>&) + { + m_aIdentifier.clear(); + m_sTitle.clear(); + m_sHelpText.clear(); + } + + + ::sal_Int32 SAL_CALL GridColumn::getIndex() + { + std::unique_lock aGuard( m_aMutex ); + return m_nIndex; + } + + + void GridColumn::setIndex( sal_Int32 const i_index ) + { + std::unique_lock aGuard( m_aMutex ); + m_nIndex = i_index; + } + + + ::sal_Int32 SAL_CALL GridColumn::getDataColumnIndex() + { + std::unique_lock aGuard( m_aMutex ); + return m_nDataColumnIndex; + } + + + void SAL_CALL GridColumn::setDataColumnIndex( ::sal_Int32 i_dataColumnIndex ) + { + impl_set( m_nDataColumnIndex, i_dataColumnIndex, "DataColumnIndex" ); + } + + + OUString SAL_CALL GridColumn::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.GridColumn"; + } + + sal_Bool SAL_CALL GridColumn::supportsService( const OUString& i_serviceName ) + { + return cppu::supportsService(this, i_serviceName); + } + + css::uno::Sequence< OUString > SAL_CALL GridColumn::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.GridColumn" }; + } + + + Reference< XCloneable > SAL_CALL GridColumn::createClone( ) + { + return new GridColumn( *this ); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_GridColumn_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::GridColumn()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcolumn.hxx b/toolkit/source/controls/grid/gridcolumn.hxx new file mode 100644 index 0000000000..13c8792271 --- /dev/null +++ b/toolkit/source/controls/grid/gridcolumn.hxx @@ -0,0 +1,122 @@ +/* -*- 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_GRID_GRIDCOLUMN_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCOLUMN_HXX + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> + +#include <comphelper/compbase.hxx> +#include <comphelper/interfacecontainer4.hxx> + +namespace toolkit +{ + +typedef comphelper::WeakComponentImplHelper < css::awt::grid::XGridColumn + , css::lang::XServiceInfo + > GridColumn_Base; +class GridColumn final : public GridColumn_Base +{ +public: + GridColumn(); + GridColumn( GridColumn const & i_copySource ); + virtual ~GridColumn() override; + + // css::awt::grid::XGridColumn + virtual css::uno::Any SAL_CALL getIdentifier() override; + virtual void SAL_CALL setIdentifier(const css::uno::Any & value) override; + virtual ::sal_Int32 SAL_CALL getColumnWidth() override; + virtual void SAL_CALL setColumnWidth(::sal_Int32 the_value) override; + virtual ::sal_Int32 SAL_CALL getMaxWidth() override; + virtual void SAL_CALL setMaxWidth(::sal_Int32 the_value) override; + virtual ::sal_Int32 SAL_CALL getMinWidth() override; + virtual void SAL_CALL setMinWidth(::sal_Int32 the_value) override; + virtual sal_Bool SAL_CALL getResizeable() override; + virtual void SAL_CALL setResizeable(sal_Bool the_value) override; + virtual ::sal_Int32 SAL_CALL getFlexibility() override; + virtual void SAL_CALL setFlexibility( ::sal_Int32 _flexibility ) override; + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL setTitle(const OUString & value) override; + virtual OUString SAL_CALL getHelpText() override; + virtual void SAL_CALL setHelpText(const OUString & value) override; + virtual ::sal_Int32 SAL_CALL getIndex() override; + virtual ::sal_Int32 SAL_CALL getDataColumnIndex() override; + virtual void SAL_CALL setDataColumnIndex( ::sal_Int32 i_dataColumnIndex ) override; + virtual css::style::HorizontalAlignment SAL_CALL getHorizontalAlign() override; + virtual void SAL_CALL setHorizontalAlign(css::style::HorizontalAlignment align) override; + virtual void SAL_CALL addGridColumnListener( const css::uno::Reference< css::awt::grid::XGridColumnListener >& xListener ) override; + virtual void SAL_CALL removeGridColumnListener( const css::uno::Reference< css::awt::grid::XGridColumnListener >& xListener ) override; + + // OComponentHelper + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XCloneable (base of XGridColumn) + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // attribute access + void setIndex( sal_Int32 const i_index ); + +private: + void broadcast_changed( + char const * const i_asciiAttributeName, + const css::uno::Any& i_oldValue, + const css::uno::Any& i_newValue, + std::unique_lock<std::mutex>& i_Guard + ); + + template< class TYPE > + void impl_set( TYPE & io_attribute, TYPE const & i_newValue, char const * i_attributeName ) + { + std::unique_lock aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( OUString(), getXWeak() ); + if ( io_attribute == i_newValue ) + return; + + TYPE const aOldValue( io_attribute ); + io_attribute = i_newValue; + broadcast_changed( i_attributeName, css::uno::Any( aOldValue ), css::uno::Any( io_attribute ), aGuard ); + } + + css::uno::Any m_aIdentifier; + sal_Int32 m_nIndex; + sal_Int32 m_nDataColumnIndex; + sal_Int32 m_nColumnWidth; + sal_Int32 m_nMaxWidth; + sal_Int32 m_nMinWidth; + sal_Int32 m_nFlexibility; + bool m_bResizeable; + OUString m_sTitle; + OUString m_sHelpText; + css::style::HorizontalAlignment m_eHorizontalAlign; + comphelper::OInterfaceContainerHelper4<css::awt::grid::XGridColumnListener> maGridColumnListeners; +}; + +} + +#endif // INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCOLUMN_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcontrol.cxx b/toolkit/source/controls/grid/gridcontrol.cxx new file mode 100644 index 0000000000..39f4abf531 --- /dev/null +++ b/toolkit/source/controls/grid/gridcontrol.cxx @@ -0,0 +1,462 @@ +/* -*- 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 "gridcontrol.hxx" +#include "grideventforwarder.hxx" + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <com/sun/star/awt/grid/XGridControl.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridRowSelection.hpp> +#include <com/sun/star/awt/grid/XMutableGridDataModel.hpp> +#include <com/sun/star/awt/grid/DefaultGridDataModel.hpp> +#include <com/sun/star/awt/grid/SortableGridDataModel.hpp> +#include <com/sun/star/awt/grid/DefaultGridColumnModel.hpp> +#include <helper/property.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <memory> + +#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::grid; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::util; + +namespace toolkit { + +namespace +{ + Reference< XGridDataModel > lcl_getDefaultDataModel_throw( const Reference<XComponentContext> & i_context ) + { + Reference< XMutableGridDataModel > const xDelegatorModel( DefaultGridDataModel::create( i_context ), UNO_SET_THROW ); + Reference< XGridDataModel > const xDataModel( SortableGridDataModel::create( i_context, xDelegatorModel ), UNO_QUERY_THROW ); + return xDataModel; + } + + Reference< XGridColumnModel > lcl_getDefaultColumnModel_throw( const Reference<XComponentContext> & i_context ) + { + Reference< XGridColumnModel > const xColumnModel = DefaultGridColumnModel::create( i_context ); + return xColumnModel; + } +} + + +UnoGridModel::UnoGridModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_FILLCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); // resizable + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_GRID_SHOWROWHEADER ); + ImplRegisterProperty( BASEPROPERTY_ROW_HEADER_WIDTH ); + ImplRegisterProperty( BASEPROPERTY_GRID_SHOWCOLUMNHEADER ); + ImplRegisterProperty( BASEPROPERTY_COLUMN_HEADER_HEIGHT ); + ImplRegisterProperty( BASEPROPERTY_ROW_HEIGHT ); + ImplRegisterProperty( BASEPROPERTY_GRID_DATAMODEL, Any( lcl_getDefaultDataModel_throw( m_xContext ) ) ); + ImplRegisterProperty( BASEPROPERTY_GRID_COLUMNMODEL, Any( lcl_getDefaultColumnModel_throw( m_xContext ) ) ); + ImplRegisterProperty( BASEPROPERTY_GRID_SELECTIONMODE ); + ImplRegisterProperty( BASEPROPERTY_FONTRELIEF ); + ImplRegisterProperty( BASEPROPERTY_FONTEMPHASISMARK ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_TEXTCOLOR ); + ImplRegisterProperty( BASEPROPERTY_TEXTLINECOLOR ); + ImplRegisterProperty( BASEPROPERTY_USE_GRID_LINES ); + ImplRegisterProperty( BASEPROPERTY_GRID_LINE_COLOR ); + ImplRegisterProperty( BASEPROPERTY_GRID_HEADER_BACKGROUND ); + ImplRegisterProperty( BASEPROPERTY_GRID_HEADER_TEXT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS ); + ImplRegisterProperty( BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR ); + ImplRegisterProperty( BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR ); + ImplRegisterProperty( BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_VERTICALALIGN ); +} + + +UnoGridModel::UnoGridModel( const UnoGridModel& rModel ) + :UnoControlModel( rModel ) +{ + osl_atomic_increment( &m_refCount ); + { + Reference< XGridDataModel > xDataModel; + // clone the data model + const Reference< XFastPropertySet > xCloneSource( &const_cast< UnoGridModel& >( rModel ) ); + try + { + const Reference< XCloneable > xCloneable( xCloneSource->getFastPropertyValue( BASEPROPERTY_GRID_DATAMODEL ), UNO_QUERY_THROW ); + xDataModel.set( xCloneable->createClone(), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + if ( !xDataModel.is() ) + xDataModel = lcl_getDefaultDataModel_throw( m_xContext ); + std::unique_lock aGuard(m_aMutex); + UnoControlModel::setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_GRID_DATAMODEL, Any( xDataModel ) ); + // do *not* use setFastPropertyValue here: The UnoControlModel ctor made a simple copy of all property values, + // so before this call here, we share our data model with the own of the clone source. setFastPropertyValue, + // then, disposes the old data model - which means the data model which in fact belongs to the clone source. + // so, call the UnoControlModel's impl-method for setting the value. + + // clone the column model + Reference< XGridColumnModel > xColumnModel; + try + { + const Reference< XCloneable > xCloneable( xCloneSource->getFastPropertyValue( BASEPROPERTY_GRID_COLUMNMODEL ), UNO_QUERY_THROW ); + xColumnModel.set( xCloneable->createClone(), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + if ( !xColumnModel.is() ) + xColumnModel = lcl_getDefaultColumnModel_throw( m_xContext ); + UnoControlModel::setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_GRID_COLUMNMODEL, Any( xColumnModel ) ); + // same comment as above: do not use our own setPropertyValue here. + } + osl_atomic_decrement( &m_refCount ); +} + + +rtl::Reference<UnoControlModel> UnoGridModel::Clone() const +{ + return new UnoGridModel( *this ); +} + + +namespace +{ + void lcl_dispose_nothrow( const Any& i_component ) + { + try + { + const Reference< XComponent > xComponent( i_component, UNO_QUERY ); + if (xComponent) + xComponent->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + + +void SAL_CALL UnoGridModel::dispose( ) +{ + lcl_dispose_nothrow( getFastPropertyValue( BASEPROPERTY_GRID_COLUMNMODEL ) ); + lcl_dispose_nothrow( getFastPropertyValue( BASEPROPERTY_GRID_DATAMODEL ) ); + + UnoControlModel::dispose(); +} + + +void UnoGridModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const Any& rValue ) +{ + Any aOldSubModel; + if ( ( nHandle == BASEPROPERTY_GRID_COLUMNMODEL ) || ( nHandle == BASEPROPERTY_GRID_DATAMODEL ) ) + { + getFastPropertyValue( rGuard, aOldSubModel, nHandle ); + if ( aOldSubModel == rValue ) + { + OSL_ENSURE( false, "UnoGridModel::setFastPropertyValue_NoBroadcast: setting the same value, again!" ); + // shouldn't this have been caught by convertFastPropertyValue? + aOldSubModel.clear(); + } + } + + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + if ( aOldSubModel.hasValue() ) + lcl_dispose_nothrow( aOldSubModel ); +} + + +OUString UnoGridModel::getServiceName() +{ + return "com.sun.star.awt.grid.UnoControlGridModel"; +} + + +Any UnoGridModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString("com.sun.star.awt.grid.UnoControlGrid") ); + case BASEPROPERTY_GRID_SELECTIONMODE: + return uno::Any( SelectionType(1) ); + case BASEPROPERTY_GRID_SHOWROWHEADER: + case BASEPROPERTY_USE_GRID_LINES: + return uno::Any( false ); + case BASEPROPERTY_ROW_HEADER_WIDTH: + return uno::Any( sal_Int32( 10 ) ); + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + return uno::Any( true ); + case BASEPROPERTY_COLUMN_HEADER_HEIGHT: + case BASEPROPERTY_ROW_HEIGHT: + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + case BASEPROPERTY_GRID_LINE_COLOR: + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + return Any(); + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } + +} + + +::cppu::IPropertyArrayHelper& UnoGridModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + + +// XMultiPropertySet +Reference< XPropertySetInfo > UnoGridModel::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +//= UnoGridControl + +UnoGridControl::UnoGridControl() + :m_aSelectionListeners( *this ) + ,m_pEventForwarder( new toolkit::GridEventForwarder( *this ) ) +{ +} + + +UnoGridControl::~UnoGridControl() +{ +} + + +OUString UnoGridControl::GetComponentServiceName() const +{ + return "Grid"; +} + + +void SAL_CALL UnoGridControl::dispose( ) +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + m_aSelectionListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + + +void SAL_CALL UnoGridControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + const Reference< XGridRowSelection > xGrid( getPeer(), UNO_QUERY_THROW ); + xGrid->addSelectionListener( &m_aSelectionListeners ); +} + + +namespace +{ + void lcl_setEventForwarding( const Reference< XControlModel >& i_gridControlModel, const std::unique_ptr< toolkit::GridEventForwarder >& i_listener, + bool const i_add ) + { + const Reference< XPropertySet > xModelProps( i_gridControlModel, UNO_QUERY ); + if ( !xModelProps.is() ) + return; + + try + { + Reference< XContainer > const xColModel( + xModelProps->getPropertyValue("ColumnModel"), + UNO_QUERY_THROW ); + if ( i_add ) + xColModel->addContainerListener( i_listener.get() ); + else + xColModel->removeContainerListener( i_listener.get() ); + + Reference< XGridDataModel > const xDataModel( + xModelProps->getPropertyValue("GridDataModel"), + UNO_QUERY_THROW + ); + Reference< XMutableGridDataModel > const xMutableDataModel( xDataModel, UNO_QUERY ); + if ( xMutableDataModel.is() ) + { + if ( i_add ) + xMutableDataModel->addGridDataListener( i_listener.get() ); + else + xMutableDataModel->removeGridDataListener( i_listener.get() ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + + +sal_Bool SAL_CALL UnoGridControl::setModel( const Reference< XControlModel >& i_model ) +{ + lcl_setEventForwarding( getModel(), m_pEventForwarder, false ); + if ( !UnoGridControl_Base::setModel( i_model ) ) + return false; + lcl_setEventForwarding( getModel(), m_pEventForwarder, true ); + return true; +} + + +::sal_Int32 UnoGridControl::getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getRowAtPoint( x, y ); +} + + +::sal_Int32 UnoGridControl::getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getColumnAtPoint( x, y ); +} + + +::sal_Int32 SAL_CALL UnoGridControl::getCurrentColumn( ) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getCurrentColumn(); +} + + +::sal_Int32 SAL_CALL UnoGridControl::getCurrentRow( ) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getCurrentRow(); +} + + +void SAL_CALL UnoGridControl::goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + xGrid->goToCell( i_columnIndex, i_rowIndex ); +} + + +void SAL_CALL UnoGridControl::selectRow( ::sal_Int32 i_rowIndex ) +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->selectRow( i_rowIndex ); +} + + +void SAL_CALL UnoGridControl::selectAllRows() +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->selectAllRows(); +} + + +void SAL_CALL UnoGridControl::deselectRow( ::sal_Int32 i_rowIndex ) +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->deselectRow( i_rowIndex ); +} + + +void SAL_CALL UnoGridControl::deselectAllRows() +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->deselectAllRows(); +} + + +css::uno::Sequence< ::sal_Int32 > SAL_CALL UnoGridControl::getSelectedRows() +{ + return Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->getSelectedRows(); +} + + +sal_Bool SAL_CALL UnoGridControl::hasSelectedRows() +{ + return Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->hasSelectedRows(); +} + + +sal_Bool SAL_CALL UnoGridControl::isRowSelected(::sal_Int32 index) +{ + return Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->isRowSelected( index ); +} + + +void SAL_CALL UnoGridControl::addSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) +{ + m_aSelectionListeners.addInterface( listener ); +} + + +void SAL_CALL UnoGridControl::removeSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) +{ + m_aSelectionListeners.removeInterface( listener ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_GridControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoGridControl()); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_GridControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoGridModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcontrol.hxx b/toolkit/source/controls/grid/gridcontrol.hxx new file mode 100644 index 0000000000..9b7eae0eaa --- /dev/null +++ b/toolkit/source/controls/grid/gridcontrol.hxx @@ -0,0 +1,142 @@ +/* -*- 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_GRID_GRIDCONTROL_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCONTROL_HXX + +#include <com/sun/star/awt/grid/XGridControl.hpp> +#include <com/sun/star/awt/grid/XGridRowSelection.hpp> + +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <cppuhelper/implbase2.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <memory> + +namespace toolkit +{ + +class GridEventForwarder; + + +// = UnoGridModel + +class UnoGridModel : public UnoControlModel +{ +protected: + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +public: + explicit UnoGridModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoGridModel( const UnoGridModel& rModel ); + + rtl::Reference<UnoControlModel> Clone() const override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // OPropertySetHelper + void setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "stardiv.Toolkit.GridControlModel"; } + + 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.grid.UnoControlGridModel"; + return s; + } +}; + + +// = UnoGridControl + +typedef ::cppu::AggImplInheritanceHelper2 < UnoControlBase + , css::awt::grid::XGridControl + , css::awt::grid::XGridRowSelection + > UnoGridControl_Base; +class UnoGridControl : public UnoGridControl_Base +{ +public: + UnoGridControl(); + 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; + sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& rxModel ) override; + + // css::awt::grid::XGridControl + virtual ::sal_Int32 SAL_CALL getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getCurrentColumn( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentRow( ) override; + virtual void SAL_CALL goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) override; + + // css::awt::grid::XGridRowSelection + virtual void SAL_CALL selectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL selectAllRows() override; + virtual void SAL_CALL deselectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL deselectAllRows() override; + virtual css::uno::Sequence< ::sal_Int32 > SAL_CALL getSelectedRows() override; + virtual sal_Bool SAL_CALL hasSelectedRows() override; + virtual sal_Bool SAL_CALL isRowSelected(::sal_Int32 index) override; + virtual void SAL_CALL addSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + virtual void SAL_CALL removeSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "stardiv.Toolkit.GridControl"; } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 1); + s.getArray()[s.getLength() - 1] = "com.sun.star.awt.grid.UnoControlGrid"; + return s; + } + + using UnoControl::getPeer; + +protected: + virtual ~UnoGridControl() override; + +private: + SelectionListenerMultiplexer m_aSelectionListeners; + std::unique_ptr< GridEventForwarder > m_pEventForwarder; +}; + +} // toolkit + +#endif // _TOOLKIT_TREE_CONTROL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/grideventforwarder.cxx b/toolkit/source/controls/grid/grideventforwarder.cxx new file mode 100644 index 0000000000..5baf5fdda3 --- /dev/null +++ b/toolkit/source/controls/grid/grideventforwarder.cxx @@ -0,0 +1,128 @@ +/* -*- 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 "grideventforwarder.hxx" +#include "gridcontrol.hxx" + + +namespace toolkit +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::awt::grid::GridDataEvent; + using ::com::sun::star::container::ContainerEvent; + using ::com::sun::star::lang::EventObject; + + + //= GridEventForwarder + + + GridEventForwarder::GridEventForwarder( UnoGridControl& i_parent ) + :m_parent( i_parent ) + { + } + + + GridEventForwarder::~GridEventForwarder() + { + } + + + void SAL_CALL GridEventForwarder::acquire() noexcept + { + m_parent.acquire(); + } + + + void SAL_CALL GridEventForwarder::release() noexcept + { + m_parent.release(); + } + + + void SAL_CALL GridEventForwarder::rowsInserted( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->rowsInserted( i_event ); + } + + + void SAL_CALL GridEventForwarder::rowsRemoved( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->rowsRemoved( i_event ); + } + + + void SAL_CALL GridEventForwarder::dataChanged( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->dataChanged( i_event ); + } + + + void SAL_CALL GridEventForwarder::rowHeadingChanged( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->rowHeadingChanged( i_event ); + } + + + void SAL_CALL GridEventForwarder::elementInserted( const ContainerEvent& i_event ) + { + Reference< XContainerListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->elementInserted( i_event ); + } + + + void SAL_CALL GridEventForwarder::elementRemoved( const ContainerEvent& i_event ) + { + Reference< XContainerListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->elementRemoved( i_event ); + } + + + void SAL_CALL GridEventForwarder::elementReplaced( const ContainerEvent& i_event ) + { + Reference< XContainerListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->elementReplaced( i_event ); + } + + + void SAL_CALL GridEventForwarder::disposing( const EventObject& i_event ) + { + Reference< XEventListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->disposing( i_event ); + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/grideventforwarder.hxx b/toolkit/source/controls/grid/grideventforwarder.hxx new file mode 100644 index 0000000000..9578e62ee0 --- /dev/null +++ b/toolkit/source/controls/grid/grideventforwarder.hxx @@ -0,0 +1,77 @@ +/* -*- 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_GRID_GRIDEVENTFORWARDER_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDEVENTFORWARDER_HXX + +#include <com/sun/star/awt/grid/XGridDataListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> + +#include <cppuhelper/implbase2.hxx> + + +namespace toolkit +{ + + + class UnoGridControl; + + + //= GridEventForwarder + + typedef ::cppu::ImplHelper2 < css::awt::grid::XGridDataListener + , css::container::XContainerListener + > GridEventForwarder_Base; + + class GridEventForwarder : public GridEventForwarder_Base + { + public: + explicit GridEventForwarder( UnoGridControl& i_parent ); + virtual ~GridEventForwarder(); + + public: + // XInterface + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XGridDataListener + virtual void SAL_CALL rowsInserted( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowsRemoved( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL dataChanged( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowHeadingChanged( const css::awt::grid::GridDataEvent& Event ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; + + private: + UnoGridControl& m_parent; + }; + + +} // namespace toolkit + + +#endif // INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDEVENTFORWARDER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/sortablegriddatamodel.cxx b/toolkit/source/controls/grid/sortablegriddatamodel.cxx new file mode 100644 index 0000000000..5eac49f475 --- /dev/null +++ b/toolkit/source/controls/grid/sortablegriddatamodel.cxx @@ -0,0 +1,928 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <com/sun/star/i18n/Collator.hpp> +#include <com/sun/star/i18n/XCollator.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/NotInitializedException.hpp> +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/grid/XGridDataListener.hpp> +#include <com/sun/star/awt/grid/XSortableMutableGridDataModel.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase1.hxx> +#include <comphelper/anycompare.hxx> +#include <comphelper/componentguard.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +using namespace css::awt; +using namespace css::awt::grid; +using namespace css::i18n; +using namespace css::lang; +using namespace css::ucb; +using namespace css::uno; + +namespace { + +class SortableGridDataModel; +class MethodGuard; + +typedef ::cppu::WeakComponentImplHelper < css::awt::grid::XSortableMutableGridDataModel + , css::lang::XServiceInfo + , css::lang::XInitialization + > SortableGridDataModel_Base; +typedef ::cppu::ImplHelper1 < css::awt::grid::XGridDataListener + > SortableGridDataModel_PrivateBase; +class SortableGridDataModel :public ::cppu::BaseMutex + ,public SortableGridDataModel_Base + ,public SortableGridDataModel_PrivateBase +{ +public: + explicit SortableGridDataModel( const css::uno::Reference< css::uno::XComponentContext > & rxContext ); + SortableGridDataModel( SortableGridDataModel const & i_copySource ); + + bool isInitialized() const { return m_isInitialized; } + +protected: + virtual ~SortableGridDataModel() override; + +public: + // XSortableGridData + virtual void SAL_CALL sortByColumn( ::sal_Int32 ColumnIndex, sal_Bool SortAscending ) override; + virtual void SAL_CALL removeColumnSort( ) override; + virtual css::beans::Pair< ::sal_Int32, sal_Bool > SAL_CALL getCurrentSortOrder( ) override; + + // XMutableGridDataModel + virtual void SAL_CALL addRow( const css::uno::Any& Heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL addRows( const css::uno::Sequence< css::uno::Any >& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL insertRow( ::sal_Int32 i_index, const css::uno::Any& i_heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL insertRows( ::sal_Int32 i_index, const css::uno::Sequence< css::uno::Any>& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL removeRow( ::sal_Int32 RowIndex ) override; + virtual void SAL_CALL removeAllRows( ) override; + virtual void SAL_CALL updateCellData( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowData( const css::uno::Sequence< ::sal_Int32 >& ColumnIndexes, ::sal_Int32 RowIndex, const css::uno::Sequence< css::uno::Any >& Values ) override; + virtual void SAL_CALL updateRowHeading( ::sal_Int32 RowIndex, const css::uno::Any& Heading ) override; + virtual void SAL_CALL updateCellToolTip( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowToolTip( ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL addGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + virtual void SAL_CALL removeGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + + // XGridDataModel + virtual ::sal_Int32 SAL_CALL getRowCount() override; + virtual ::sal_Int32 SAL_CALL getColumnCount() override; + virtual css::uno::Any SAL_CALL getCellData( ::sal_Int32 Column, ::sal_Int32 RowIndex ) override; + virtual css::uno::Any SAL_CALL getCellToolTip( ::sal_Int32 Column, ::sal_Int32 RowIndex ) override; + virtual css::uno::Any SAL_CALL getRowHeading( ::sal_Int32 RowIndex ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getRowData( ::sal_Int32 RowIndex ) override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XGridDataListener + virtual void SAL_CALL rowsInserted( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowsRemoved( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL dataChanged( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowHeadingChanged( const css::awt::grid::GridDataEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept final override; + virtual void SAL_CALL release( ) noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL getImplementationId( ) override; + +private: + /** translates the given public index into one to be passed to our delegator + @throws css::lang::IndexOutOfBoundsException + if the given index does not denote a valid row + */ + ::sal_Int32 impl_getPrivateRowIndex_throw( ::sal_Int32 const i_publicRowIndex ) const; + + /** translates the given private row index to a public one + */ + ::sal_Int32 impl_getPublicRowIndex_nothrow( ::sal_Int32 const i_privateRowIndex ) const; + + bool impl_isSorted_nothrow() const + { + return m_currentSortColumn >= 0; + } + + /** rebuilds the index translation structure. + + Neither <member>m_currentSortColumn</member> nor <member>m_sortAscending</member> are touched by this method. + Also, the given column index is not checked, this is the responsibility of the caller. + */ + bool impl_reIndex_nothrow( ::sal_Int32 const i_columnIndex, bool const i_sortAscending ); + + /** translates the given event, obtained from our delegator, to a version which can be broadcasted to our own + clients. + */ + css::awt::grid::GridDataEvent + impl_createPublicEvent( css::awt::grid::GridDataEvent const & i_originalEvent ) const; + + /** broadcasts the given event to our registered XGridDataListeners + */ + void impl_broadcast( + void ( SAL_CALL css::awt::grid::XGridDataListener::*i_listenerMethod )( const css::awt::grid::GridDataEvent & ), + css::awt::grid::GridDataEvent const & i_publicEvent, + MethodGuard& i_instanceLock + ); + + /** rebuilds our indexes, notifying row removal and row addition events + + First, a rowsRemoved event is notified to our registered listeners. Then, the index translation tables are + rebuilt, and a rowsInserted event is notified. + + Only to be called when we're sorted. + */ + void impl_rebuildIndexesAndNotify( MethodGuard& i_instanceLock ); + + /** removes the current sorting, and notifies a change of all data + */ + void impl_removeColumnSort( MethodGuard& i_instanceLock ); + + /** removes the current sorting, without any broadcast + */ + void impl_removeColumnSort_noBroadcast(); + +private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + bool m_isInitialized; + css::uno::Reference< css::awt::grid::XMutableGridDataModel > m_delegator; + css::uno::Reference< css::i18n::XCollator > m_collator; + ::sal_Int32 m_currentSortColumn; + bool m_sortAscending; + ::std::vector< ::sal_Int32 > m_publicToPrivateRowIndex; + ::std::vector< ::sal_Int32 > m_privateToPublicRowIndex; +}; + +class MethodGuard : public ::comphelper::ComponentGuard +{ +public: + MethodGuard( SortableGridDataModel& i_component, ::cppu::OBroadcastHelper & i_broadcastHelper ) + :comphelper::ComponentGuard( i_component, i_broadcastHelper ) + { + if ( !i_component.isInitialized() ) + throw css::lang::NotInitializedException( OUString(), i_component ); + } +}; + +template< class STLCONTAINER > +void lcl_clear( STLCONTAINER& i_container ) +{ + STLCONTAINER().swap(i_container); +} + + SortableGridDataModel::SortableGridDataModel( Reference< XComponentContext > const & rxContext ) + :SortableGridDataModel_Base( m_aMutex ) + ,SortableGridDataModel_PrivateBase() + ,m_xContext( rxContext ) + ,m_isInitialized( false ) + ,m_delegator() + ,m_collator() + ,m_currentSortColumn( -1 ) + ,m_sortAscending( true ) + ,m_publicToPrivateRowIndex() + ,m_privateToPublicRowIndex() + { + } + + + SortableGridDataModel::SortableGridDataModel( SortableGridDataModel const & i_copySource ) + :cppu::BaseMutex() + ,SortableGridDataModel_Base( m_aMutex ) + ,SortableGridDataModel_PrivateBase() + ,m_xContext( i_copySource.m_xContext ) + ,m_isInitialized( true ) + ,m_delegator() + ,m_collator( i_copySource.m_collator ) + ,m_currentSortColumn( i_copySource.m_currentSortColumn ) + ,m_sortAscending( i_copySource.m_sortAscending ) + ,m_publicToPrivateRowIndex( i_copySource.m_publicToPrivateRowIndex ) + ,m_privateToPublicRowIndex( i_copySource.m_privateToPublicRowIndex ) + { + ENSURE_OR_THROW( i_copySource.m_delegator.is(), + "not expected to be called for a disposed copy source!" ); + m_delegator.set( i_copySource.m_delegator->createClone(), UNO_QUERY_THROW ); + } + + + SortableGridDataModel::~SortableGridDataModel() + { + if ( !rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + } + + + Any SAL_CALL SortableGridDataModel::queryInterface( const Type& aType ) + { + Any aReturn( SortableGridDataModel_Base::queryInterface( aType ) ); + if ( !aReturn.hasValue() ) + aReturn = SortableGridDataModel_PrivateBase::queryInterface( aType ); + return aReturn; + } + + + void SAL_CALL SortableGridDataModel::acquire( ) noexcept + { + SortableGridDataModel_Base::acquire(); + } + + + void SAL_CALL SortableGridDataModel::release( ) noexcept + { + SortableGridDataModel_Base::release(); + } + + + Sequence< Type > SAL_CALL SortableGridDataModel::getTypes( ) + { + return SortableGridDataModel_Base::getTypes(); + // don't expose the types got via SortableGridDataModel_PrivateBase - they're private, after all + } + + + Sequence< ::sal_Int8 > SAL_CALL SortableGridDataModel::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + Reference< XCollator > lcl_loadDefaultCollator_throw( const Reference<XComponentContext> & rxContext ) + { + Reference< XCollator > const xCollator = Collator::create( rxContext ); + xCollator->loadDefaultCollator( Application::GetSettings().GetLanguageTag().getLocale(), 0 ); + return xCollator; + } + + void SAL_CALL SortableGridDataModel::initialize( const Sequence< Any >& i_arguments ) + { + ::comphelper::ComponentGuard aGuard( *this, rBHelper ); + + if ( m_delegator.is() ) + throw AlreadyInitializedException( OUString(), *this ); + + Reference< XMutableGridDataModel > xDelegator; + Reference< XCollator > xCollator; + switch ( i_arguments.getLength() ) + { + case 1: // SortableGridDataModel.create( XMutableGridDataModel ) + xDelegator.set( i_arguments[0], UNO_QUERY ); + xCollator = lcl_loadDefaultCollator_throw( m_xContext ); + break; + + case 2: // SortableGridDataModel.createWithCollator( XMutableGridDataModel, XCollator ) + xDelegator.set( i_arguments[0], UNO_QUERY ); + xCollator.set( i_arguments[1], UNO_QUERY ); + if ( !xCollator.is() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + break; + } + if ( !xDelegator.is() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + m_delegator = xDelegator; + m_collator = xCollator; + + m_delegator->addGridDataListener( this ); + + m_isInitialized = true; + } + + + GridDataEvent SortableGridDataModel::impl_createPublicEvent( GridDataEvent const & i_originalEvent ) const + { + GridDataEvent aEvent( i_originalEvent ); + aEvent.Source = *const_cast< SortableGridDataModel* >( this ); + aEvent.FirstRow = impl_getPublicRowIndex_nothrow( aEvent.FirstRow ); + aEvent.LastRow = impl_getPublicRowIndex_nothrow( aEvent.LastRow ); + return aEvent; + } + + + void SortableGridDataModel::impl_broadcast( void ( SAL_CALL XGridDataListener::*i_listenerMethod )( const GridDataEvent & ), + GridDataEvent const & i_publicEvent, MethodGuard& i_instanceLock ) + { + ::cppu::OInterfaceContainerHelper* pListeners = rBHelper.getContainer( cppu::UnoType<XGridDataListener>::get() ); + if ( pListeners == nullptr ) + return; + + i_instanceLock.clear(); + pListeners->notifyEach( i_listenerMethod, i_publicEvent ); + } + + + void SAL_CALL SortableGridDataModel::rowsInserted( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + if ( impl_isSorted_nothrow() ) + { + // no infrastructure is in place currently to sort the new row to its proper location, + // so we remove the sorting here. + impl_removeColumnSort( aGuard ); + aGuard.reset(); + } + + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::rowsInserted, aEvent, aGuard ); + } + + void lcl_decrementValuesGreaterThan( ::std::vector< ::sal_Int32 > & io_indexMap, sal_Int32 const i_threshold ) + { + for ( auto& rIndex : io_indexMap ) + { + if ( rIndex >= i_threshold ) + --rIndex; + } + } + + void SortableGridDataModel::impl_rebuildIndexesAndNotify( MethodGuard& i_instanceLock ) + { + OSL_PRECOND( impl_isSorted_nothrow(), "SortableGridDataModel::impl_rebuildIndexesAndNotify: illegal call!" ); + + // clear the indexes + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + + // rebuild the index + if ( !impl_reIndex_nothrow( m_currentSortColumn, m_sortAscending ) ) + { + impl_removeColumnSort( i_instanceLock ); + return; + } + + // broadcast an artificial event, saying that all rows have been removed + GridDataEvent const aRemovalEvent( *this, -1, -1, -1, -1 ); + impl_broadcast( &XGridDataListener::rowsRemoved, aRemovalEvent, i_instanceLock ); + i_instanceLock.reset(); + + // broadcast an artificial event, saying that n rows have been added + GridDataEvent const aAdditionEvent( *this, -1, -1, 0, m_delegator->getRowCount() - 1 ); + impl_broadcast( &XGridDataListener::rowsInserted, aAdditionEvent, i_instanceLock ); + } + + + void SAL_CALL SortableGridDataModel::rowsRemoved( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + // if the data is not sorted, broadcast the event unchanged + if ( !impl_isSorted_nothrow() ) + { + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard ); + return; + } + + // if all rows have been removed, also simply multiplex to own listeners + if ( i_event.FirstRow < 0 ) + { + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + GridDataEvent aEvent( i_event ); + aEvent.Source = *this; + impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard ); + return; + } + + bool needReIndex = false; + if ( i_event.FirstRow != i_event.LastRow ) + { + OSL_ENSURE( false, "SortableGridDataModel::rowsRemoved: missing implementation - removal of multiple rows!" ); + needReIndex = true; + } + else if ( o3tl::make_unsigned( i_event.FirstRow ) >= m_privateToPublicRowIndex.size() ) + { + OSL_ENSURE( false, "SortableGridDataModel::rowsRemoved: inconsistent/wrong data!" ); + needReIndex = true; + } + + if ( needReIndex ) + { + impl_rebuildIndexesAndNotify( aGuard ); + return; + } + + // build public event version + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + + // remove the entries from the index maps + sal_Int32 const privateIndex = i_event.FirstRow; + sal_Int32 const publicIndex = aEvent.FirstRow; + + m_publicToPrivateRowIndex.erase( m_publicToPrivateRowIndex.begin() + publicIndex ); + m_privateToPublicRowIndex.erase( m_privateToPublicRowIndex.begin() + privateIndex ); + + // adjust remaining entries in the index maps + lcl_decrementValuesGreaterThan( m_publicToPrivateRowIndex, privateIndex ); + lcl_decrementValuesGreaterThan( m_privateToPublicRowIndex, publicIndex ); + + // broadcast the event + impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard ); + } + + + void SAL_CALL SortableGridDataModel::dataChanged( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::dataChanged, aEvent, aGuard ); + } + + + void SAL_CALL SortableGridDataModel::rowHeadingChanged( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::rowHeadingChanged, aEvent, aGuard ); + } + + + void SAL_CALL SortableGridDataModel::disposing( const EventObject& ) + { + } + + class CellDataLessComparison + { + public: + CellDataLessComparison( + ::std::vector< Any > const & i_data, + ::comphelper::IKeyPredicateLess const & i_predicate, + bool const i_sortAscending + ) + :m_data( i_data ) + ,m_predicate( i_predicate ) + ,m_sortAscending( i_sortAscending ) + { + } + + bool operator()( sal_Int32 const i_lhs, sal_Int32 const i_rhs ) const + { + Any const & lhs = m_data[ i_lhs ]; + Any const & rhs = m_data[ i_rhs ]; + // <VOID/> is less than everything else + if ( !lhs.hasValue() ) + return m_sortAscending; + if ( !rhs.hasValue() ) + return !m_sortAscending; + + // actually compare + if ( m_sortAscending ) + return m_predicate.isLess( lhs, rhs ); + else + return m_predicate.isLess( rhs, lhs ); + } + + private: + ::std::vector< Any > const & m_data; + ::comphelper::IKeyPredicateLess const & m_predicate; + bool const m_sortAscending; + }; + + bool SortableGridDataModel::impl_reIndex_nothrow( ::sal_Int32 const i_columnIndex, bool const i_sortAscending ) + { + ::sal_Int32 const rowCount( getRowCount() ); + ::std::vector< ::sal_Int32 > aPublicToPrivate( rowCount ); + + try + { + // build an unsorted translation table, and retrieve the unsorted data + ::std::vector< Any > aColumnData( rowCount ); + Type dataType; + for ( ::sal_Int32 rowIndex = 0; rowIndex < rowCount; ++rowIndex ) + { + aColumnData[ rowIndex ] = m_delegator->getCellData( i_columnIndex, rowIndex ); + aPublicToPrivate[ rowIndex ] = rowIndex; + + // determine the data types we assume for the complete column + if ( ( dataType.getTypeClass() == TypeClass_VOID ) && aColumnData[ rowIndex ].hasValue() ) + dataType = aColumnData[ rowIndex ].getValueType(); + } + + // get predicate object + ::std::unique_ptr< ::comphelper::IKeyPredicateLess > const pPredicate( ::comphelper::getStandardLessPredicate( dataType, m_collator ) ); + ENSURE_OR_RETURN_FALSE( + pPredicate, "SortableGridDataModel::impl_reIndex_nothrow: no sortable data found!"); + + // then sort + CellDataLessComparison const aComparator( aColumnData, *pPredicate, i_sortAscending ); + ::std::sort( aPublicToPrivate.begin(), aPublicToPrivate.end(), aComparator ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + return false; + } + + // also build the "private to public" mapping + ::std::vector< sal_Int32 > aPrivateToPublic( aPublicToPrivate.size() ); + for ( size_t i=0; i<aPublicToPrivate.size(); ++i ) + aPrivateToPublic[ aPublicToPrivate[i] ] = i; + + m_publicToPrivateRowIndex.swap( aPublicToPrivate ); + m_privateToPublicRowIndex.swap( aPrivateToPublic ); + + return true; + } + + + void SAL_CALL SortableGridDataModel::sortByColumn( ::sal_Int32 i_columnIndex, sal_Bool i_sortAscending ) + { + MethodGuard aGuard( *this, rBHelper ); + + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= getColumnCount() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + if ( !impl_reIndex_nothrow( i_columnIndex, i_sortAscending ) ) + return; + + m_currentSortColumn = i_columnIndex; + m_sortAscending = i_sortAscending; + + impl_broadcast( + &XGridDataListener::dataChanged, + GridDataEvent( *this, -1, -1, -1, -1 ), + aGuard + ); + } + + + void SortableGridDataModel::impl_removeColumnSort_noBroadcast() + { + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + + m_currentSortColumn = -1; + m_sortAscending = true; + } + + + void SortableGridDataModel::impl_removeColumnSort( MethodGuard& i_instanceLock ) + { + impl_removeColumnSort_noBroadcast(); + impl_broadcast( + &XGridDataListener::dataChanged, + GridDataEvent( *this, -1, -1, -1, -1 ), + i_instanceLock + ); + } + + + void SAL_CALL SortableGridDataModel::removeColumnSort( ) + { + MethodGuard aGuard( *this, rBHelper ); + impl_removeColumnSort( aGuard ); + } + + + css::beans::Pair< ::sal_Int32, sal_Bool > SAL_CALL SortableGridDataModel::getCurrentSortOrder( ) + { + MethodGuard aGuard( *this, rBHelper ); + + return css::beans::Pair< ::sal_Int32, sal_Bool >( m_currentSortColumn, m_sortAscending ); + } + + + void SAL_CALL SortableGridDataModel::addRow( const Any& i_heading, const Sequence< Any >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->addRow( i_heading, i_data ); + } + + + void SAL_CALL SortableGridDataModel::addRows( const Sequence< Any >& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->addRows( i_headings, i_data ); + } + + + void SAL_CALL SortableGridDataModel::insertRow( ::sal_Int32 i_index, const Any& i_heading, const Sequence< Any >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = i_index == getRowCount() ? i_index : impl_getPrivateRowIndex_throw( i_index ); + // note that |RowCount| is a valid index in this method, but not for impl_getPrivateRowIndex_throw + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->insertRow( rowIndex, i_heading, i_data ); + } + + + void SAL_CALL SortableGridDataModel::insertRows( ::sal_Int32 i_index, const Sequence< Any>& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = i_index == getRowCount() ? i_index : impl_getPrivateRowIndex_throw( i_index ); + // note that |RowCount| is a valid index in this method, but not for impl_getPrivateRowIndex_throw + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->insertRows( rowIndex, i_headings, i_data ); + } + + + void SAL_CALL SortableGridDataModel::removeRow( ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->removeRow( rowIndex ); + } + + + void SAL_CALL SortableGridDataModel::removeAllRows( ) + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->removeAllRows(); + } + + + void SAL_CALL SortableGridDataModel::updateCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateCellData( i_columnIndex, rowIndex, i_value ); + } + + + void SAL_CALL SortableGridDataModel::updateRowData( const Sequence< ::sal_Int32 >& i_columnIndexes, ::sal_Int32 i_rowIndex, const Sequence< Any >& i_values ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateRowData( i_columnIndexes, rowIndex, i_values ); + } + + + void SAL_CALL SortableGridDataModel::updateRowHeading( ::sal_Int32 i_rowIndex, const Any& i_heading ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateRowHeading( rowIndex, i_heading ); + } + + + void SAL_CALL SortableGridDataModel::updateCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateCellToolTip( i_columnIndex, rowIndex, i_value ); + } + + + void SAL_CALL SortableGridDataModel::updateRowToolTip( ::sal_Int32 i_rowIndex, const Any& i_value ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateRowToolTip( rowIndex, i_value ); + } + + + void SAL_CALL SortableGridDataModel::addGridDataListener( const Reference< XGridDataListener >& i_listener ) + { + rBHelper.addListener( cppu::UnoType<XGridDataListener>::get(), i_listener ); + } + + + void SAL_CALL SortableGridDataModel::removeGridDataListener( const Reference< XGridDataListener >& i_listener ) + { + rBHelper.removeListener( cppu::UnoType<XGridDataListener>::get(), i_listener ); + } + + + ::sal_Int32 SAL_CALL SortableGridDataModel::getRowCount() + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getRowCount(); + } + + + ::sal_Int32 SAL_CALL SortableGridDataModel::getColumnCount() + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getColumnCount(); + } + + + Any SAL_CALL SortableGridDataModel::getCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getCellData( i_columnIndex, rowIndex ); + } + + + Any SAL_CALL SortableGridDataModel::getCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getCellToolTip( i_columnIndex, rowIndex ); + } + + + Any SAL_CALL SortableGridDataModel::getRowHeading( ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getRowHeading( rowIndex ); + } + + + Sequence< Any > SAL_CALL SortableGridDataModel::getRowData( ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getRowData( rowIndex ); + } + + + void SAL_CALL SortableGridDataModel::disposing() + { + m_currentSortColumn = -1; + + Reference< XComponent > const delegatorComponent( m_delegator ); + m_delegator->removeGridDataListener( this ); + m_delegator.clear(); + delegatorComponent->dispose(); + + Reference< XComponent > const collatorComponent( m_collator, UNO_QUERY ); + m_collator.clear(); + if ( collatorComponent.is() ) + collatorComponent->dispose(); + + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + } + + + Reference< css::util::XCloneable > SAL_CALL SortableGridDataModel::createClone( ) + { + MethodGuard aGuard( *this, rBHelper ); + + return new SortableGridDataModel( *this ); + } + + + OUString SAL_CALL SortableGridDataModel::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.SortableGridDataModel"; + } + + sal_Bool SAL_CALL SortableGridDataModel::supportsService( const OUString& i_serviceName ) + { + return cppu::supportsService(this, i_serviceName); + } + + Sequence< OUString > SAL_CALL SortableGridDataModel::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.SortableGridDataModel" }; + } + + + ::sal_Int32 SortableGridDataModel::impl_getPrivateRowIndex_throw( ::sal_Int32 const i_publicRowIndex ) const + { + if ( ( i_publicRowIndex < 0 ) || ( i_publicRowIndex >= m_delegator->getRowCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SortableGridDataModel* >( this ) ); + + if ( !impl_isSorted_nothrow() ) + // no need to translate anything + return i_publicRowIndex; + + ENSURE_OR_RETURN( o3tl::make_unsigned( i_publicRowIndex ) < m_publicToPrivateRowIndex.size(), + "SortableGridDataModel::impl_getPrivateRowIndex_throw: inconsistency!", i_publicRowIndex ); + // obviously the translation table contains too few elements - it should have exactly |getRowCount()| + // elements + + return m_publicToPrivateRowIndex[ i_publicRowIndex ]; + } + + + ::sal_Int32 SortableGridDataModel::impl_getPublicRowIndex_nothrow( ::sal_Int32 const i_privateRowIndex ) const + { + if ( !impl_isSorted_nothrow() ) + // no need to translate anything + return i_privateRowIndex; + + if ( i_privateRowIndex < 0 ) + return i_privateRowIndex; + + ENSURE_OR_RETURN( o3tl::make_unsigned( i_privateRowIndex ) < m_privateToPublicRowIndex.size(), + "SortableGridDataModel::impl_getPublicRowIndex_nothrow: invalid index!", i_privateRowIndex ); + + return m_privateToPublicRowIndex[ i_privateRowIndex ]; + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_SortableGridDataModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SortableGridDataModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/roadmapcontrol.cxx b/toolkit/source/controls/roadmapcontrol.cxx new file mode 100644 index 0000000000..e46a607ef3 --- /dev/null +++ b/toolkit/source/controls/roadmapcontrol.cxx @@ -0,0 +1,504 @@ +/* -*- 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 <controls/roadmapcontrol.hxx> +#include <controls/roadmapentry.hxx> +#include <helper/property.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <helper/unopropertyarrayhelper.hxx> + +namespace toolkit +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + + +// helper + + // = UnoControlRoadmapModel + + + UnoControlRoadmapModel::UnoControlRoadmapModel( const Reference< XComponentContext >& i_factory ) + :UnoControlRoadmapModel_Base( i_factory ) + ,maContainerListeners( *this ) + { + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_IMAGEURL ); + ImplRegisterProperty( BASEPROPERTY_GRAPHIC ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_COMPLETE ); + ImplRegisterProperty( BASEPROPERTY_ACTIVATED ); + ImplRegisterProperty( BASEPROPERTY_CURRENTITEMID ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); + } + + + OUString UnoControlRoadmapModel::getServiceName() + { + return "stardiv.vcl.controlmodel.Roadmap"; + } + + OUString UnoControlRoadmapModel::getImplementationName() + { + return "stardiv.Toolkit.UnoControlRoadmapModel"; + } + + css::uno::Sequence<OUString> + UnoControlRoadmapModel::getSupportedServiceNames() + { + auto s(UnoControlRoadmapModel_Base::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlRoadmapModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.Roadmap"; + return s; + } + + Any UnoControlRoadmapModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + Any aReturn; + switch (nPropId) + { + case BASEPROPERTY_COMPLETE: + aReturn <<= true; + break; + case BASEPROPERTY_ACTIVATED: + aReturn <<= true; + break; + case BASEPROPERTY_CURRENTITEMID: + aReturn <<= sal_Int16(-1); + break; + case BASEPROPERTY_TEXT: + break; + case BASEPROPERTY_BORDER: + aReturn <<= sal_Int16(2); // No Border + break; + case BASEPROPERTY_DEFAULTCONTROL: + aReturn <<= OUString( "stardiv.vcl.control.Roadmap" ); + break; + default : aReturn = UnoControlRoadmapModel_Base::ImplGetDefaultValue( nPropId ); break; + } + + return aReturn; + } + + + Reference< XInterface > SAL_CALL UnoControlRoadmapModel::createInstance( ) + { + return cppu::getXWeak(new ORoadmapEntry()); + } + + + Reference< XInterface > SAL_CALL UnoControlRoadmapModel::createInstanceWithArguments( const Sequence< Any >& /*aArguments*/ ) + { + // Todo: implementation of the arguments handling + return cppu::getXWeak(new ORoadmapEntry()); + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoControlRoadmapModel, UnoControlRoadmapModel_Base, UnoControlRoadmapModel_IBase ) + + + css::uno::Any SAL_CALL UnoControlRoadmapModel::queryAggregation( const css::uno::Type & rType ) + { + Any aRet = UnoControlRoadmapModel_Base::queryAggregation( rType ); + if ( !aRet.hasValue() ) + aRet = UnoControlRoadmapModel_IBase::queryInterface( rType ); + return aRet; + } + + + ::cppu::IPropertyArrayHelper& UnoControlRoadmapModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + // beans::XMultiPropertySet + + Reference< XPropertySetInfo > UnoControlRoadmapModel::getPropertySetInfo( ) + { + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + sal_Int32 SAL_CALL UnoControlRoadmapModel::getCount() + { + return maRoadmapItems.size(); + } + + Any SAL_CALL UnoControlRoadmapModel::getByIndex( sal_Int32 Index ) + { + if ((Index < 0) || ( o3tl::make_unsigned(Index) >= maRoadmapItems.size())) + throw IndexOutOfBoundsException(); + Any aAny( maRoadmapItems.at( Index ) ); + return aAny; + } + + + void UnoControlRoadmapModel::MakeRMItemValidation( sal_Int32 Index, const Reference< XInterface >& xRoadmapItem ) + { + if (( Index < 0 ) || (o3tl::make_unsigned(Index) > maRoadmapItems.size()) ) + throw IndexOutOfBoundsException(); + if ( !xRoadmapItem.is() ) + throw IllegalArgumentException(); + Reference< XServiceInfo > xServiceInfo( xRoadmapItem, UNO_QUERY ); + bool bIsRoadmapItem = xServiceInfo->supportsService("com.sun.star.awt.RoadmapItem"); + if ( !bIsRoadmapItem ) + throw IllegalArgumentException(); + } + + + void UnoControlRoadmapModel::SetRMItemDefaultProperties( const Reference< XInterface >& xRoadmapItem) + { + Reference< XPropertySet > xPropertySet( xRoadmapItem, UNO_QUERY ); + Reference< XPropertySet > xProps( xRoadmapItem, UNO_QUERY ); + if ( xProps.is() ) + { + sal_Int32 LocID = 0; + Any aValue = xPropertySet->getPropertyValue("ID"); + aValue >>= LocID; + if (LocID < 0) // index may not be smaller than zero + { + xPropertySet->setPropertyValue("ID", Any(GetUniqueID()) ); + } + } + } + + +// The performance of this method could certainly be improved. +// As long as only vectors with up to 10 elements are +// involved it should be sufficient + sal_Int32 UnoControlRoadmapModel::GetUniqueID() + { + Any aAny; + bool bIncrement = true; + sal_Int32 CurID = 0; + sal_Int32 n_CurItemID = 0; + Reference< XInterface > CurRoadmapItem; + while ( bIncrement ) + { + bIncrement = false; + for ( const auto& rRoadmapItem : maRoadmapItems ) + { + CurRoadmapItem = rRoadmapItem; + Reference< XPropertySet > xPropertySet( CurRoadmapItem, UNO_QUERY ); + aAny = xPropertySet->getPropertyValue("ID"); + aAny >>= n_CurItemID; + if (n_CurItemID == CurID) + { + bIncrement = true; + CurID++; + break; + } + } + } + return CurID; + } + + + ContainerEvent UnoControlRoadmapModel::GetContainerEvent(sal_Int32 Index, const Reference< XInterface >& xRoadmapItem) + { + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element <<= xRoadmapItem; + aEvent.Accessor <<= Index; + return aEvent; + } + + + sal_Int16 UnoControlRoadmapModel::GetCurrentItemID( const Reference< XPropertySet >& xPropertySet ) + { + Any aAny = xPropertySet->getPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ) ); + sal_Int16 n_CurrentItemID = 0; + aAny >>= n_CurrentItemID; + return n_CurrentItemID; + } + + + void SAL_CALL UnoControlRoadmapModel::insertByIndex( const sal_Int32 Index, const Any& Element) + { + if ( ( Index >= ( static_cast<sal_Int32>(maRoadmapItems.size()) + 1 ) ) || (Index < 0)) + throw IndexOutOfBoundsException(); + Reference< XInterface > xRoadmapItem; + Element >>= xRoadmapItem; + MakeRMItemValidation( Index, xRoadmapItem); + SetRMItemDefaultProperties( xRoadmapItem ); + maRoadmapItems.insert( maRoadmapItems.begin() + Index, xRoadmapItem); + ContainerEvent aEvent = GetContainerEvent(Index, xRoadmapItem); + maContainerListeners.elementInserted( aEvent ); + Reference< XPropertySet > xPropertySet( this ); + sal_Int16 n_CurrentItemID = GetCurrentItemID( xPropertySet ); + if ( Index <= n_CurrentItemID ) + { + Any aAny(static_cast<sal_Int16>( n_CurrentItemID + 1 ) ); + xPropertySet->setPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ), aAny ); + } + } + + + void SAL_CALL UnoControlRoadmapModel::removeByIndex( sal_Int32 Index) + { + if ((Index < 0) || ( o3tl::make_unsigned(Index) > maRoadmapItems.size())) + throw IndexOutOfBoundsException(); + Reference< XInterface > xRoadmapItem; + maRoadmapItems.erase( maRoadmapItems.begin() + Index ); + ContainerEvent aEvent = GetContainerEvent(Index, xRoadmapItem); + maContainerListeners.elementRemoved( aEvent ); + Reference< XPropertySet > xPropertySet( this ); + sal_Int16 n_CurrentItemID = GetCurrentItemID( xPropertySet ); + Any aAny; + if ( Index > n_CurrentItemID ) + return; + + if ( n_CurrentItemID >= static_cast<sal_Int32>(maRoadmapItems.size()) ) + { + n_CurrentItemID = sal::static_int_cast< sal_Int16 >( + maRoadmapItems.size()-1); + if ( n_CurrentItemID < 0 ) + return; + aAny <<= n_CurrentItemID; + } + else if (Index == n_CurrentItemID) + aAny <<= sal_Int16(-1); + else if( Index < n_CurrentItemID) + aAny <<= static_cast<sal_Int16>( n_CurrentItemID - 1 ); + xPropertySet->setPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ), aAny ); + } + + + void SAL_CALL UnoControlRoadmapModel::replaceByIndex( const sal_Int32 Index, const Any& Element) + { + Reference< XInterface > xRoadmapItem; + Element >>= xRoadmapItem; + MakeRMItemValidation( Index, xRoadmapItem); + SetRMItemDefaultProperties( xRoadmapItem ); + maRoadmapItems.erase( maRoadmapItems.begin() + Index ); + maRoadmapItems.insert( maRoadmapItems.begin() + Index, xRoadmapItem); //push_back( xRoadmapItem ); + ContainerEvent aEvent = GetContainerEvent(Index, xRoadmapItem); + maContainerListeners.elementReplaced( aEvent ); + } + + + Type SAL_CALL UnoControlRoadmapModel::getElementType() + { + Type aType = cppu::UnoType<XPropertySet>::get(); + return aType; + } + + + sal_Bool SAL_CALL UnoControlRoadmapModel::hasElements() + { + return !maRoadmapItems.empty(); + } + + + void SAL_CALL UnoControlRoadmapModel::addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) + { + maContainerListeners.addInterface( xListener ); + } + + void SAL_CALL UnoControlRoadmapModel::removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) + { + maContainerListeners.removeInterface( xListener ); + } + + + // = UnoRoadmapControl + + + UnoRoadmapControl::UnoRoadmapControl() + :maItemListeners( *this ) + { + } + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoRoadmapControl, UnoControlRoadmap_Base, UnoControlRoadmap_IBase ) + +css::uno::Any UnoRoadmapControl::queryAggregation(css::uno::Type const & aType) { + auto ret = UnoControlRoadmap_Base::queryAggregation(aType); + if (!ret.hasValue()) { + ret = UnoControlRoadmap_IBase::queryInterface(aType); + } + return ret; +} + + +sal_Bool SAL_CALL UnoRoadmapControl::setModel(const Reference< XControlModel >& _rModel) + { + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->removeContainerListener( this ); + + bool bReturn = UnoControlBase::setModel( _rModel ); + + xC.set(getModel(), css::uno::UNO_QUERY); + if ( xC.is() ) + xC->addContainerListener( this ); + + return bReturn; + } + + + OUString UnoRoadmapControl::GetComponentServiceName() const + { + return "Roadmap"; + } + + + void UnoRoadmapControl::dispose() + { + EventObject aEvt; + aEvt.Source = getXWeak(); + maItemListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); + } + + +void UnoRoadmapControl::elementInserted( const ContainerEvent& rEvent ) +{ + Reference< XInterface > xRoadmapItem; + rEvent.Element >>= xRoadmapItem; + Reference< XPropertySet > xRoadmapPropertySet( xRoadmapItem, UNO_QUERY ); + if ( xRoadmapPropertySet.is() ) + xRoadmapPropertySet->addPropertyChangeListener( OUString(), this ); + + Reference< XContainerListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + { + xPeer->elementInserted( rEvent ); + Reference < XPropertySet > xPropertySet( xPeer, UNO_QUERY ); + if ( xPropertySet.is() ) + xPropertySet->addPropertyChangeListener( OUString(), this ); + } +} + + +void UnoRoadmapControl::elementRemoved( const ContainerEvent& rEvent ) +{ + Reference< XContainerListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + xPeer->elementRemoved( rEvent ); + Reference< XInterface > xRoadmapItem; + rEvent.Element >>= xRoadmapItem; + Reference< XPropertySet > xPropertySet( xRoadmapItem, UNO_QUERY ); + if ( xPropertySet.is() ) + xPropertySet->removePropertyChangeListener( OUString(), this ); +} + + +void UnoRoadmapControl::elementReplaced( const ContainerEvent& rEvent ) +{ + Reference< XContainerListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + xPeer->elementReplaced( rEvent ); +} + + +void SAL_CALL UnoRoadmapControl::itemStateChanged( const ItemEvent& rEvent ) +{ + sal_Int16 CurItemIndex = sal::static_int_cast< sal_Int16 >(rEvent.ItemId); + Reference< XControlModel > xModel = getModel( ); + Reference< XPropertySet > xPropertySet( xModel, UNO_QUERY ); + xPropertySet->setPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ), Any(CurItemIndex) ); + if ( maItemListeners.getLength() ) + maItemListeners.itemStateChanged( rEvent ); +} + + +void SAL_CALL UnoRoadmapControl::addItemListener( const Reference< XItemListener >& l ) +{ + maItemListeners.addInterface( l ); + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + Reference < XItemEventBroadcaster > xRoadmap( getPeer(), UNO_QUERY ); + xRoadmap->addItemListener( this ); + } +} + + +void SAL_CALL UnoRoadmapControl::removeItemListener( const Reference< XItemListener >& l ) +{ + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + Reference < XItemEventBroadcaster > xRoadmap( getPeer(), UNO_QUERY ); + xRoadmap->removeItemListener( this ); + } + + maItemListeners.removeInterface( l ); +} + + +void SAL_CALL UnoRoadmapControl::propertyChange( const PropertyChangeEvent& evt ) +{ + Reference< XPropertyChangeListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + xPeer->propertyChange( evt ); +} + +OUString UnoRoadmapControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoRoadmapControl"; +} + +css::uno::Sequence<OUString> UnoRoadmapControl::getSupportedServiceNames() +{ + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlRoadmap"; + ps[s.getLength() - 1] = "stardiv.vcl.control.Roadmap"; + return s; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlRoadmapModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoControlRoadmapModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoRoadmapControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoRoadmapControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/roadmapentry.cxx b/toolkit/source/controls/roadmapentry.cxx new file mode 100644 index 0000000000..3de60f7071 --- /dev/null +++ b/toolkit/source/controls/roadmapentry.cxx @@ -0,0 +1,105 @@ +/* -*- 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 <controls/roadmapentry.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <cppuhelper/supportsservice.hxx> + + +ORoadmapEntry::ORoadmapEntry() : OPropertyContainer( GetBroadcastHelper() ) +{ + // registerProperty or registerMayBeVoidProperty or registerPropertyNoMember + + registerProperty( "Label", RM_PROPERTY_ID_LABEL, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::CONSTRAINED, + & m_sLabel, cppu::UnoType<decltype(m_sLabel)>::get() ); + m_nID = -1; + registerProperty( "ID", RM_PROPERTY_ID_ID, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::CONSTRAINED, + & m_nID, cppu::UnoType<decltype(m_nID)>::get() ); + m_bEnabled = true; + registerProperty( "Enabled", RM_PROPERTY_ID_ENABLED, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::MAYBEDEFAULT, + & m_bEnabled, cppu::UnoType<decltype(m_bEnabled)>::get() ); + + registerProperty( "Interactive", RM_PROPERTY_ID_INTERACTIVE, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::MAYBEDEFAULT, + & m_bInteractive, cppu::UnoType<decltype(m_bInteractive)>::get() ); + + + // Note that the list of registered properties has to be fixed: Different + // instances of this class have to register the same set of properties with + // the same attributes. + + // This is because all instances of the class share the same PropertySetInfo + // which has been built from the registered property of _one_ instance. +} + + +IMPLEMENT_FORWARD_XINTERFACE2( ORoadmapEntry, ORoadmapEntry_Base, ::comphelper::OPropertyContainer ); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORoadmapEntry, ORoadmapEntry_Base, ::comphelper::OPropertyContainer ); + // order matters: + // the first is the class name + // the second is the class which implements the ref-counting + // the third up to n-th (when using IMPLEMENT_FORWARD_*3 and so on) are other base classes + // whose XInterface and XTypeProvider implementations should be merged + + +css::uno::Reference< css:: beans::XPropertySetInfo > SAL_CALL + ORoadmapEntry::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ); +} + +OUString SAL_CALL ORoadmapEntry::getImplementationName( ) +{ + return "com.sun.star.comp.toolkit.RoadmapItem"; +} + +sal_Bool SAL_CALL ORoadmapEntry::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ORoadmapEntry::getSupportedServiceNames( ) +{ + return { "com.sun.star.awt.RoadmapItem" }; +} + +::cppu::IPropertyArrayHelper& ORoadmapEntry::getInfoHelper() +{ + return *getArrayHelper(); +} + + +::cppu::IPropertyArrayHelper* ORoadmapEntry::createArrayHelper() const +{ + css::uno::Sequence< css::beans::Property > aProps; + // describes all properties which have been registered in the ctor + describeProperties( aProps ); + + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/spinningprogress.cxx b/toolkit/source/controls/spinningprogress.cxx new file mode 100644 index 0000000000..851d624a3d --- /dev/null +++ b/toolkit/source/controls/spinningprogress.cxx @@ -0,0 +1,128 @@ +/* -*- 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/uno/XComponentContext.hpp> +#include <controls/animatedimages.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/toolkit/throbber.hxx> + +using namespace css; +using namespace css::uno; + +namespace { + +typedef toolkit::AnimatedImagesControlModel SpinningProgressControlModel_Base; +class SpinningProgressControlModel : public SpinningProgressControlModel_Base +{ +public: + explicit SpinningProgressControlModel( css::uno::Reference< css::uno::XComponentContext > const & i_factory ); + SpinningProgressControlModel(const SpinningProgressControlModel& rOther) : SpinningProgressControlModel_Base(rOther) {} + + virtual rtl::Reference<UnoControlModel> Clone() const override; + + // XPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +protected: + virtual ~SpinningProgressControlModel() override; +}; + + SpinningProgressControlModel::SpinningProgressControlModel( Reference< XComponentContext > const & i_factory ) + :SpinningProgressControlModel_Base( i_factory ) + { + // default image sets + osl_atomic_increment( &m_refCount ); + { + try + { + Throbber::ImageSet aImageSets[] = + { + Throbber::ImageSet::N16px, Throbber::ImageSet::N32px, Throbber::ImageSet::N64px + }; + for ( size_t i=0; i < SAL_N_ELEMENTS(aImageSets); ++i ) + { + const ::std::vector< OUString > aDefaultURLs( Throbber::getDefaultImageURLs( aImageSets[i] ) ); + const Sequence< OUString > aImageURLs( aDefaultURLs.data(), aDefaultURLs.size() ); + insertImageSet( i, aImageURLs ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + osl_atomic_decrement( &m_refCount ); + } + + + SpinningProgressControlModel::~SpinningProgressControlModel() + { + } + + + rtl::Reference<UnoControlModel> SpinningProgressControlModel::Clone() const + { + return new SpinningProgressControlModel( *this ); + } + + + Reference< beans::XPropertySetInfo > SAL_CALL SpinningProgressControlModel::getPropertySetInfo( ) + { + static Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + OUString SAL_CALL SpinningProgressControlModel::getServiceName() + { + return "com.sun.star.awt.SpinningProgressControlModel"; + } + + + OUString SAL_CALL SpinningProgressControlModel::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.SpinningProgressControlModel"; + } + + + Sequence< OUString > SAL_CALL SpinningProgressControlModel::getSupportedServiceNames() + { + return { "com.sun.star.awt.SpinningProgressControlModel", + "com.sun.star.awt.AnimatedImagesControlModel", + "com.sun.star.awt.UnoControlModel" }; + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_SpinningProgressControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SpinningProgressControlModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/stdtabcontroller.cxx b/toolkit/source/controls/stdtabcontroller.cxx new file mode 100644 index 0000000000..bac4aea585 --- /dev/null +++ b/toolkit/source/controls/stdtabcontroller.cxx @@ -0,0 +1,415 @@ +/* -*- 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/beans/XPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/XVclContainerPeer.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include <controls/stdtabcontroller.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/macros.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <comphelper/sequence.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; + + + +StdTabController::StdTabController() +{ +} + +StdTabController::~StdTabController() +{ +} + +bool StdTabController::ImplCreateComponentSequence( + Sequence< Reference< XControl > >& rControls, + const Sequence< Reference< XControlModel > >& rModels, + Sequence< Reference< XWindow > >& rComponents, + Sequence< Any>* pTabStops, + bool bPeerComponent ) +{ + // Get only the requested controls + sal_Int32 nModels = rModels.getLength(); + if (nModels != rControls.getLength()) + { + Sequence< Reference< XControl > > aSeq( nModels ); + auto aSeqRange = asNonConstRange(aSeq); + Reference< XControl > xCurrentControl; + + sal_Int32 nRealControls = 0; + for (const Reference< XControlModel >& rModel : rModels) + { + xCurrentControl = FindControl(rControls, rModel); + if (xCurrentControl.is()) + aSeqRange[nRealControls++] = xCurrentControl; + } + aSeq.realloc(nRealControls); + rControls = aSeq; + } + + // there may be less controls than models, but never more controls than models + assert(rControls.getLength() <= rModels.getLength()); + + sal_Int32 nCtrls = rControls.getLength(); + rComponents.realloc( nCtrls ); + Reference< XWindow > * pComps = rComponents.getArray(); + Any* pTabs = nullptr; + + + if ( pTabStops ) + { + *pTabStops = Sequence< Any>( nCtrls ); + pTabs = pTabStops->getArray(); + } + + bool bOK = true; + for ( const Reference< XControl >& xCtrl : std::as_const(rControls) ) + { + // Get the matching control for this model + if ( !xCtrl.is() ) + { + SAL_WARN("toolkit", "Control not found" ); + bOK = false; + break; + } + + if (bPeerComponent) + pComps->set(xCtrl->getPeer(), UNO_QUERY); + else + pComps->set(xCtrl, UNO_QUERY); + + // TabStop-Property + if ( pTabs ) + { + // opt: Constant String for TabStop name + static constexpr OUString aTabStopName = u"Tabstop"_ustr; + + Reference< XPropertySet > xPSet( xCtrl->getModel(), UNO_QUERY ); + Reference< XPropertySetInfo > xInfo = xPSet->getPropertySetInfo(); + if( xInfo->hasPropertyByName( aTabStopName ) ) + *pTabs++ = xPSet->getPropertyValue( aTabStopName ); + else + ++pTabs; + } + + ++pComps; + } + return bOK; +} + +void StdTabController::ImplActivateControl( bool bFirst ) const +{ + // HACK due to bug #53688#, map controls onto an interface if remote controls may occur + Sequence< Reference< XControl > > aCtrls = const_cast<StdTabController*>(this)->getControls(); + const Reference< XControl > * pControls = aCtrls.getConstArray(); + sal_uInt32 nCount = aCtrls.getLength(); + + for ( sal_uInt32 n = bFirst ? 0 : nCount; bFirst ? n < nCount : n != 0; ) + { + sal_uInt32 nCtrl = bFirst ? n++ : --n; + DBG_ASSERT( pControls[nCtrl].is(), "Control not in Container!" ); + if ( pControls[nCtrl].is() ) + { + Reference< XWindowPeer > xCP = pControls[nCtrl]->getPeer(); + if ( xCP.is() ) + { + VCLXWindow* pC = dynamic_cast<VCLXWindow*>( xCP.get() ); + if ( pC && pC->GetWindow() && ( pC->GetWindow()->GetStyle() & WB_TABSTOP ) ) + { + pC->GetWindow()->GrabFocus(); + break; + } + } + } + } +} + +// XInterface +Any StdTabController::queryAggregation( const Type & rType ) +{ + Any aRet = ::cppu::queryInterface( rType, + static_cast< XTabController* >(this), + static_cast< XServiceInfo* >(this), + static_cast< XTypeProvider* >(this) ); + return (aRet.hasValue() ? aRet : OWeakAggObject::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( StdTabController ) + +// XTypeProvider +css::uno::Sequence< css::uno::Type > StdTabController::getTypes() +{ + static const css::uno::Sequence< css::uno::Type > aTypeList { + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<XTabController>::get(), + cppu::UnoType<XServiceInfo>::get() + }; + return aTypeList; +} + +void StdTabController::setModel( const Reference< XTabControllerModel >& Model ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + mxModel = Model; +} + +Reference< XTabControllerModel > StdTabController::getModel( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return mxModel; +} + +void StdTabController::setContainer( const Reference< XControlContainer >& Container ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + mxControlContainer = Container; +} + +Reference< XControlContainer > StdTabController::getContainer( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return mxControlContainer; +} + +Sequence< Reference< XControl > > StdTabController::getControls( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + Sequence< Reference< XControl > > aSeq; + + if ( mxControlContainer.is() ) + { + const Sequence< Reference< XControlModel > > aModels = mxModel->getControlModels(); + + Sequence< Reference< XControl > > xCtrls = mxControlContainer->getControls(); + + sal_Int32 nCtrls = aModels.getLength(); + aSeq = Sequence< Reference< XControl > >( nCtrls ); + std::transform(aModels.begin(), aModels.end(), aSeq.getArray(), + [&xCtrls](const Reference< XControlModel >& xCtrlModel) -> Reference< XControl > { + return FindControl( xCtrls, xCtrlModel ); }); + } + return aSeq; +} + +namespace { + +struct ComponentEntry +{ + css::awt::XWindow* pComponent; + ::Point aPos; +}; + +} + +void StdTabController::autoTabOrder( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + DBG_ASSERT( mxControlContainer.is(), "autoTabOrder: No ControlContainer!" ); + if ( !mxControlContainer.is() ) + return; + + Sequence< Reference< XControlModel > > aSeq = mxModel->getControlModels(); + Sequence< Reference< XWindow > > aCompSeq; + + // This may return a TabController, which returns desired list of controls faster + Sequence< Reference< XControl > > aControls = getControls(); + + // #58317# Some Models may be missing from the Container. Plus there is a + // autoTabOrder call later on. + if( !ImplCreateComponentSequence( aControls, aSeq, aCompSeq, nullptr, false ) ) + return; + + sal_uInt32 nCtrls = aCompSeq.getLength(); + + // insert sort algorithm + std::vector< ComponentEntry > aCtrls; + aCtrls.reserve(nCtrls); + for ( const Reference< XWindow >& rComponent : std::as_const(aCompSeq) ) + { + XWindow* pC = rComponent.get(); + ComponentEntry newEntry; + newEntry.pComponent = pC; + awt::Rectangle aPosSize = pC->getPosSize(); + newEntry.aPos.setX( aPosSize.X ); + newEntry.aPos.setY( aPosSize.Y ); + + decltype(aCtrls)::size_type nPos; + for ( nPos = 0; nPos < aCtrls.size(); nPos++ ) + { + ComponentEntry& rEntry = aCtrls[ nPos ]; + if ( ( rEntry.aPos.Y() > newEntry.aPos.Y() ) || + ( ( rEntry.aPos.Y() == newEntry.aPos.Y() ) && ( rEntry.aPos.X() > newEntry.aPos.X() ) ) ) + break; + } + if ( nPos < aCtrls.size() ) { + aCtrls.insert( aCtrls.begin() + nPos, newEntry ); + } else { + aCtrls.push_back( newEntry ); + } + } + + Sequence< Reference< XControlModel > > aNewSeq( nCtrls ); + std::transform(aCtrls.begin(), aCtrls.end(), aNewSeq.getArray(), + [](const ComponentEntry& rEntry) -> Reference< XControlModel > { + Reference< XControl > xUC( rEntry.pComponent, UNO_QUERY ); + return xUC->getModel(); + }); + + mxModel->setControlModels( aNewSeq ); +} + +void StdTabController::activateTabOrder( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // Activate tab order for the control container + + Reference< XControl > xC( mxControlContainer, UNO_QUERY ); + Reference< XVclContainerPeer > xVclContainerPeer; + if ( xC.is() ) + xVclContainerPeer.set(xC->getPeer(), css::uno::UNO_QUERY); + if ( !xC.is() || !xVclContainerPeer.is() ) + return; + + // This may return a TabController, which returns desired list of controls faster + // (the dreaded UNO aggregation, retrieve the thing that we are part of) + Reference<XTabController> xTabController( static_cast<XTabController*>(this), UNO_QUERY ); + + // Get a flattened list of controls sequences + Sequence< Reference< XControlModel > > aModels = mxModel->getControlModels(); + Sequence< Reference< XWindow > > aCompSeq; + Sequence< Any> aTabSeq; + + // DG: For the sake of optimization, retrieve Controls from getControls(), + // this may sound counterproductive, but leads to performance improvements + // in practical scenarios (Forms) + Sequence< Reference< XControl > > aControls = xTabController->getControls(); + + // #58317# Some Models may be missing from the Container. Plus there is a + // autoTabOrder call later on. + if( !ImplCreateComponentSequence( aControls, aModels, aCompSeq, &aTabSeq, true ) ) + return; + + xVclContainerPeer->setTabOrder( aCompSeq, aTabSeq, mxModel->getGroupControl() ); + + OUString aName; + Sequence< Reference< XControlModel > > aThisGroupModels; + Sequence< Reference< XWindow > > aControlComponents; + + sal_uInt32 nGroups = mxModel->getGroupCount(); + for ( sal_uInt32 nG = 0; nG < nGroups; nG++ ) + { + mxModel->getGroup( nG, aThisGroupModels, aName ); + + aControls = xTabController->getControls(); + // ImplCreateComponentSequence has a really strange semantics regarding it's first parameter: + // upon method entry, it expects a super set of the controls which it returns + // this means we need to completely fill this sequence with all available controls before + // calling into ImplCreateComponentSequence + + aControlComponents.realloc( 0 ); + + ImplCreateComponentSequence( aControls, aThisGroupModels, aControlComponents, nullptr, true ); + xVclContainerPeer->setGroup( aControlComponents ); + } +} + +void StdTabController::activateFirst( ) +{ + SolarMutexGuard aSolarGuard; + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); //TODO: necessary? + + ImplActivateControl( true ); +} + +void StdTabController::activateLast( ) +{ + SolarMutexGuard aSolarGuard; + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); //TODO: necessary? + + ImplActivateControl( false ); +} + +OUString StdTabController::getImplementationName() +{ + return "stardiv.Toolkit.StdTabController"; +} + +sal_Bool StdTabController::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> StdTabController::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.TabController", + "stardiv.vcl.control.TabController"}; +} + +Reference< XControl > StdTabController::FindControl( Sequence< Reference< XControl > >& rCtrls, + const Reference< XControlModel > & rxCtrlModel ) +{ + if (!rxCtrlModel.is()) + throw lang::IllegalArgumentException("No valid XControlModel", + uno::Reference<uno::XInterface>(), 0); + + auto pCtrl = std::find_if(std::cbegin(rCtrls), std::cend(rCtrls), + [&rxCtrlModel](const Reference< XControl >& rCtrl) { + Reference< XControlModel > xModel(rCtrl.is() ? rCtrl->getModel() : Reference< XControlModel > ()); + return xModel.get() == rxCtrlModel.get(); + }); + if (pCtrl != std::cend(rCtrls)) + { + auto n = static_cast<sal_Int32>(std::distance(std::cbegin(rCtrls), pCtrl)); + Reference< XControl > xCtrl( *pCtrl ); + ::comphelper::removeElementAt( rCtrls, n ); + return xCtrl; + } + return Reference< XControl > (); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_StdTabController_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new StdTabController()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/stdtabcontrollermodel.cxx b/toolkit/source/controls/stdtabcontrollermodel.cxx new file mode 100644 index 0000000000..43fd80f170 --- /dev/null +++ b/toolkit/source/controls/stdtabcontrollermodel.cxx @@ -0,0 +1,439 @@ +/* -*- 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/io/XMarkableStream.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <controls/stdtabcontrollermodel.hxx> +#include <toolkit/helper/macros.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <tools/debug.hxx> + +#define UNOCONTROL_STREAMVERSION short(2) + + + +UnoControlModelEntryList::UnoControlModelEntryList() +{ +} + +UnoControlModelEntryList::~UnoControlModelEntryList() +{ + Reset(); +} + +void UnoControlModelEntryList::Reset() +{ + for ( size_t n = maList.size(); n; ) + DestroyEntry( --n ); +} + +void UnoControlModelEntryList::DestroyEntry( size_t nEntry ) +{ + UnoControlModelEntryListBase::iterator it = maList.begin(); + ::std::advance( it, nEntry ); + + if ( (*it)->bGroup ) + delete (*it)->pGroup; + else + delete (*it)->pxControl; + + delete *it; + maList.erase( it ); +} + +size_t UnoControlModelEntryList::size() const { + return maList.size(); +} + +UnoControlModelEntry* UnoControlModelEntryList::operator[]( size_t i ) const { + return ( i < maList.size() ) ? maList[ i ] : nullptr; +} + +void UnoControlModelEntryList::push_back( UnoControlModelEntry* item ) { + maList.push_back( item ); +} + +void UnoControlModelEntryList::insert( size_t i, UnoControlModelEntry* item ) { + if ( i < maList.size() ) { + UnoControlModelEntryListBase::iterator it = maList.begin(); + ::std::advance( it, i ); + maList.insert( it, item ); + } else { + maList.push_back( item ); + } +} + + + +StdTabControllerModel::StdTabControllerModel() +{ + mbGroupControl = true; +} + +StdTabControllerModel::~StdTabControllerModel() +{ +} + +sal_uInt32 StdTabControllerModel::ImplGetControlCount( const UnoControlModelEntryList& rList ) const +{ + sal_uInt32 nCount = 0; + size_t nEntries = rList.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = rList[ n ]; + if ( pEntry->bGroup ) + nCount += ImplGetControlCount( *pEntry->pGroup ); + else + nCount++; + } + return nCount; +} + +void StdTabControllerModel::ImplGetControlModels( css::uno::Reference< css::awt::XControlModel > ** ppRefs, const UnoControlModelEntryList& rList ) const +{ + size_t nEntries = rList.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = rList[ n ]; + if ( pEntry->bGroup ) + ImplGetControlModels( ppRefs, *pEntry->pGroup ); + else + { + **ppRefs = *pEntry->pxControl; + (*ppRefs)++; + } + } +} + +void StdTabControllerModel::ImplSetControlModels( UnoControlModelEntryList& rList, const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ) +{ + for ( const css::uno::Reference< css::awt::XControlModel >& rRef : Controls ) + { + UnoControlModelEntry* pNewEntry = new UnoControlModelEntry; + pNewEntry->bGroup = false; + pNewEntry->pxControl = new css::uno::Reference< css::awt::XControlModel > ; + *pNewEntry->pxControl = rRef; + rList.push_back( pNewEntry ); + } +} + +sal_uInt32 StdTabControllerModel::ImplGetControlPos( const css::uno::Reference< css::awt::XControlModel >& rCtrl, const UnoControlModelEntryList& rList ) +{ + for ( size_t n = rList.size(); n; ) + { + UnoControlModelEntry* pEntry = rList[ --n ]; + if ( !pEntry->bGroup && ( *pEntry->pxControl == rCtrl ) ) + return n; + } + return CONTROLPOS_NOTFOUND; +} + +static void ImplWriteControls( const css::uno::Reference< css::io::XObjectOutputStream > & OutStream, const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rCtrls ) +{ + css::uno::Reference< css::io::XMarkableStream > xMark( OutStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no XMarkableStream!" ); + + sal_uInt32 nStoredControls = 0; + sal_Int32 nDataBeginMark = xMark->createMark(); + + OutStream->writeLong( 0 ); // DataLen + OutStream->writeLong( 0 ); // nStoredControls + + for ( const css::uno::Reference< css::awt::XControlModel >& xI : rCtrls ) + { + css::uno::Reference< css::io::XPersistObject > xPO( xI, css::uno::UNO_QUERY ); + DBG_ASSERT( xPO.is(), "write: Control doesn't support XPersistObject" ); + if ( xPO.is() ) + { + OutStream->writeObject( xPO ); + nStoredControls++; + } + } + sal_Int32 nDataLen = xMark->offsetToMark( nDataBeginMark ); + xMark->jumpToMark( nDataBeginMark ); + OutStream->writeLong( nDataLen ); + OutStream->writeLong( nStoredControls ); + xMark->jumpToFurthest(); + xMark->deleteMark(nDataBeginMark); +} + +static css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > ImplReadControls( const css::uno::Reference< css::io::XObjectInputStream > & InStream ) +{ + css::uno::Reference< css::io::XMarkableStream > xMark( InStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no XMarkableStream!" ); + + sal_Int32 nDataBeginMark = xMark->createMark(); + + sal_Int32 nDataLen = InStream->readLong(); + sal_uInt32 nCtrls = InStream->readLong(); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq( nCtrls ); + for ( sal_uInt32 n = 0; n < nCtrls; n++ ) + { + css::uno::Reference< css::io::XPersistObject > xObj = InStream->readObject(); + css::uno::Reference< css::awt::XControlModel > xI( xObj, css::uno::UNO_QUERY ); + aSeq.getArray()[n] = xI; + } + + // Skip remainder if more data exists than this version recognizes + xMark->jumpToMark( nDataBeginMark ); + InStream->skipBytes( nDataLen ); + xMark->deleteMark(nDataBeginMark); + return aSeq; +} + + +// css::uno::XInterface +css::uno::Any StdTabControllerModel::queryAggregation( const css::uno::Type & rType ) +{ + css::uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< css::awt::XTabControllerModel* >(this), + static_cast< css::lang::XServiceInfo* >(this), + static_cast< css::io::XPersistObject* >(this), + static_cast< css::lang::XTypeProvider* >(this) ); + return (aRet.hasValue() ? aRet : OWeakAggObject::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( StdTabControllerModel ) + +// css::lang::XTypeProvider +css::uno::Sequence< css::uno::Type > StdTabControllerModel::getTypes() +{ + static const css::uno::Sequence< css::uno::Type > aTypeList { + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<css::awt::XTabControllerModel>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get(), + cppu::UnoType<css::io::XPersistObject>::get() + }; + return aTypeList; +} + +sal_Bool StdTabControllerModel::getGroupControl( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return mbGroupControl; +} + +void StdTabControllerModel::setGroupControl( sal_Bool GroupControl ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + mbGroupControl = GroupControl; +} + +void StdTabControllerModel::setControlModels( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maControls.Reset(); + ImplSetControlModels( maControls, Controls ); +} + +css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > StdTabControllerModel::getControlModels( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq( ImplGetControlCount( maControls ) ); + css::uno::Reference< css::awt::XControlModel > * pRefs = aSeq.getArray(); + ImplGetControlModels( &pRefs, maControls ); + return aSeq; +} + +void StdTabControllerModel::setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group, const OUString& GroupName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // The controls might occur as a flat list and will be grouped. + // Nested groups are not possible. + // The first element of a group determines its position. + UnoControlModelEntry* pNewEntry = new UnoControlModelEntry; + pNewEntry->bGroup = true; + pNewEntry->pGroup = new UnoControlModelEntryList; + pNewEntry->pGroup->SetName( GroupName ); + ImplSetControlModels( *pNewEntry->pGroup, Group ); + + bool bInserted = false; + size_t nElements = pNewEntry->pGroup->size(); + for ( size_t n = 0; n < nElements; n++ ) + { + UnoControlModelEntry* pEntry = (*pNewEntry->pGroup)[ n ]; + if ( !pEntry->bGroup ) + { + sal_uInt32 nPos = ImplGetControlPos( *pEntry->pxControl, maControls ); + // At the beginning, all Controls should be in a flattened list + DBG_ASSERT( nPos != CONTROLPOS_NOTFOUND, "setGroup - Element not found" ); + if ( nPos != CONTROLPOS_NOTFOUND ) + { + maControls.DestroyEntry( nPos ); + if ( !bInserted ) + { + maControls.insert( nPos, pNewEntry ); + bInserted = true; + } + } + } + } + if ( !bInserted ) + maControls.push_back( pNewEntry ); +} + +sal_Int32 StdTabControllerModel::getGroupCount( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // Start with only one group layer, even though Model and Impl-methods + // work recursively, this is not presented to the outside. + + sal_Int32 nGroups = 0; + size_t nEntries = maControls.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = maControls[ n ]; + if ( pEntry->bGroup ) + nGroups++; + } + return nGroups; +} + +void StdTabControllerModel::getGroup( sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rGroup, OUString& rName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq; + sal_uInt32 nG = 0; + size_t nEntries = maControls.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = maControls[ n ]; + if ( pEntry->bGroup ) + { + if ( nG == static_cast<sal_uInt32>(nGroup) ) + { + sal_uInt32 nCount = ImplGetControlCount( *pEntry->pGroup ); + aSeq = css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >( nCount ); + css::uno::Reference< css::awt::XControlModel > * pRefs = aSeq.getArray(); + ImplGetControlModels( &pRefs, *pEntry->pGroup ); + rName = pEntry->pGroup->GetName(); + break; + } + nG++; + } + } + rGroup = aSeq; +} + +void StdTabControllerModel::getGroupByName( const OUString& rName, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rGroup ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + sal_uInt32 nGroup = 0; + size_t nEntries = maControls.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = maControls[ n ]; + if ( pEntry->bGroup ) + { + if ( pEntry->pGroup->GetName() == rName ) + { + OUString Dummy; + getGroup( nGroup, rGroup, Dummy ); + break; + } + nGroup++; + } + } +} + + +// css::io::XPersistObject +OUString StdTabControllerModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.TabController"; +} + +void StdTabControllerModel::write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Reference< css::io::XMarkableStream > xMark( OutStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no XMarkableStream!" ); + + OutStream->writeShort( UNOCONTROL_STREAMVERSION ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aCtrls = getControlModels(); + ImplWriteControls( OutStream, aCtrls ); + + sal_uInt32 nGroups = getGroupCount(); + OutStream->writeLong( nGroups ); + for ( sal_uInt32 n = 0; n < nGroups; n++ ) + { + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aGroupCtrls; + OUString aGroupName; + getGroup( n, aGroupCtrls, aGroupName ); + OutStream->writeUTF( aGroupName ); + ImplWriteControls( OutStream, aGroupCtrls ); + } +} + +void StdTabControllerModel::read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq = ImplReadControls( InStream ); + setControlModels( aSeq ); + + sal_uInt32 nGroups = InStream->readLong(); + for ( sal_uInt32 n = 0; n < nGroups; n++ ) + { + OUString aGroupName = InStream->readUTF(); + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aCtrlSeq = ImplReadControls( InStream ); + setGroup( aCtrlSeq, aGroupName ); + } +} + +OUString StdTabControllerModel::getImplementationName() +{ + return "stardiv.Toolkit.StdTabControllerModel"; +} + +sal_Bool StdTabControllerModel::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> StdTabControllerModel::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.TabControllerModel", + "stardiv.vcl.controlmodel.TabController"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_StdTabControllerModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new StdTabControllerModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/svmedit.cxx b/toolkit/source/controls/svmedit.cxx new file mode 100644 index 0000000000..1bc51155f7 --- /dev/null +++ b/toolkit/source/controls/svmedit.cxx @@ -0,0 +1,43 @@ +/* -*- 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 <awt/vclxwindows.hxx> +#include <controls/svmedit.hxx> + +MultiLineEdit::MultiLineEdit(vcl::Window* pParent, WinBits nWinStyle) + : VclMultiLineEdit(pParent, nWinStyle) +{ +} + +// virtual +css::uno::Reference<css::awt::XVclWindowPeer> MultiLineEdit::GetComponentInterface(bool bCreate) +{ + css::uno::Reference<css::awt::XVclWindowPeer> xPeer( + VclMultiLineEdit::GetComponentInterface(false)); + if (!xPeer.is() && bCreate) + { + rtl::Reference<VCLXMultiLineEdit> xVCLMEdit(new VCLXMultiLineEdit); + xVCLMEdit->SetWindow(this); + xPeer = xVCLMEdit.get(); + SetComponentInterface(xPeer); + } + return xPeer; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/svtxgridcontrol.cxx b/toolkit/source/controls/svtxgridcontrol.cxx new file mode 100644 index 0000000000..1ca789db9a --- /dev/null +++ b/toolkit/source/controls/svtxgridcontrol.cxx @@ -0,0 +1,911 @@ +/* -*- 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 <awt/vclxwindows.hxx> +#include <cppuhelper/implbase.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> +#include <com/sun/star/view/SelectionType.hpp> +#include <controls/table/tablecontrol.hxx> +#include <controls/table/tablecontrolinterface.hxx> +#include <controls/table/gridtablerenderer.hxx> +#include "unocontroltablemodel.hxx" +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <helper/property.hxx> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/GridInvalidDataException.hpp> +#include <com/sun/star/awt/grid/GridInvalidModelException.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/util/Color.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include <vcl/svapp.hxx> + +#include <algorithm> + +using css::uno::Reference; +using css::uno::Exception; +using css::uno::UNO_QUERY; +using css::uno::UNO_QUERY_THROW; +using css::uno::Any; +using css::uno::Sequence; +using css::awt::grid::XGridSelectionListener; +using css::style::VerticalAlignment; +using css::style::VerticalAlignment_TOP; +using css::view::SelectionType; +using css::view::SelectionType_NONE; +using css::view::SelectionType_RANGE; +using css::view::SelectionType_SINGLE; +using css::view::SelectionType_MULTI; +using css::awt::grid::XGridDataModel; +using css::awt::grid::GridInvalidDataException; +using css::lang::EventObject; +using css::lang::IndexOutOfBoundsException; +using css::awt::grid::XGridColumnModel; +using css::awt::grid::GridSelectionEvent; +using css::awt::grid::XGridColumn; +using css::container::ContainerEvent; +using css::awt::grid::GridDataEvent; +using css::awt::grid::GridInvalidModelException; + +namespace AccessibleEventId = css::accessibility::AccessibleEventId; +namespace AccessibleStateType = css::accessibility::AccessibleStateType; + +using namespace ::svt::table; + + +SVTXGridControl::SVTXGridControl() + :m_xTableModel( std::make_shared<UnoControlTableModel>() ) + ,m_bTableModelInitCompleted( false ) + ,m_aSelectionListeners( *this ) +{ +} + + +SVTXGridControl::~SVTXGridControl() +{ +} + + +void SVTXGridControl::SetWindow( const VclPtr< vcl::Window > &pWindow ) +{ + SVTXGridControl_Base::SetWindow( pWindow ); + impl_checkTableModelInit(); +} + + +void SVTXGridControl::impl_checkColumnIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_columnIndex ) const +{ + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= i_table.GetColumnCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SVTXGridControl* >( this ) ); +} + + +void SVTXGridControl::impl_checkRowIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_rowIndex ) const +{ + if ( ( i_rowIndex < 0 ) || ( i_rowIndex >= i_table.GetRowCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SVTXGridControl* >( this ) ); +} + + +sal_Int32 SAL_CALL SVTXGridControl::getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getRowAtPoint: no control (anymore)!", -1 ); + + TableCell const tableCell = pTable->getTableControlInterface().hitTest( Point( x, y ) ); + return ( tableCell.nRow >= 0 ) ? tableCell.nRow : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getColumnAtPoint: no control (anymore)!", -1 ); + + TableCell const tableCell = pTable->getTableControlInterface().hitTest( Point( x, y ) ); + return ( tableCell.nColumn >= 0 ) ? tableCell.nColumn : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getCurrentColumn( ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getCurrentColumn: no control (anymore)!", -1 ); + + sal_Int32 const nColumn = pTable->GetCurrentColumn(); + return ( nColumn >= 0 ) ? nColumn : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getCurrentRow( ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getCurrentRow: no control (anymore)!", -1 ); + + sal_Int32 const nRow = pTable->GetCurrentRow(); + return ( nRow >= 0 ) ? nRow : -1; +} + + +void SAL_CALL SVTXGridControl::goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::getCurrentRow: no control (anymore)!" ); + + impl_checkColumnIndex_throw( *pTable, i_columnIndex ); + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->GoTo( i_columnIndex, i_rowIndex ); +} + + +void SAL_CALL SVTXGridControl::addSelectionListener(const Reference< XGridSelectionListener > & listener) +{ + m_aSelectionListeners.addInterface(listener); +} + + +void SAL_CALL SVTXGridControl::removeSelectionListener(const Reference< XGridSelectionListener > & listener) +{ + m_aSelectionListeners.removeInterface(listener); +} + + +void SVTXGridControl::setProperty( const OUString& PropertyName, const Any& aValue) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::setProperty: no control (anymore)!" ); + + switch( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_ROW_HEADER_WIDTH: + { + sal_Int32 rowHeaderWidth( -1 ); + aValue >>= rowHeaderWidth; + if ( rowHeaderWidth <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal row header width!" ); + break; + } + + m_xTableModel->setRowHeaderWidth( rowHeaderWidth ); + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_COLUMN_HEADER_HEIGHT: + { + sal_Int32 columnHeaderHeight = 0; + if ( !aValue.hasValue() ) + { + columnHeaderHeight = pTable->PixelToLogic(Size(0, pTable->GetTextHeight() + 3), MapMode(MapUnit::MapAppFont)).Height(); + } + else + { + aValue >>= columnHeaderHeight; + } + if ( columnHeaderHeight <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal column header width!" ); + break; + } + + m_xTableModel->setColumnHeaderHeight( columnHeaderHeight ); + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_USE_GRID_LINES: + { + GridTableRenderer* pGridRenderer = dynamic_cast< GridTableRenderer* >( + m_xTableModel->getRenderer().get() ); + if ( !pGridRenderer ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty(UseGridLines): invalid renderer!" ); + break; + } + + bool bUseGridLines = false; + OSL_VERIFY( aValue >>= bUseGridLines ); + pGridRenderer->useGridLines( bUseGridLines ); + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_ROW_HEIGHT: + { + sal_Int32 rowHeight = 0; + if ( !aValue.hasValue() ) + { + rowHeight = pTable->PixelToLogic(Size(0, pTable->GetTextHeight() + 3), MapMode(MapUnit::MapAppFont)).Height(); + } + else + { + aValue >>= rowHeight; + } + m_xTableModel->setRowHeight( rowHeight ); + if ( rowHeight <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal row height!" ); + break; + } + + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // let the base class handle this for the TableControl + VCLXWindow::setProperty( PropertyName, aValue ); + // and forward to the grid control's data window + if ( pTable->IsBackground() ) + pTable->getDataWindow().SetBackground( pTable->GetBackground() ); + else + pTable->getDataWindow().SetBackground(); + } + break; + + case BASEPROPERTY_GRID_SELECTIONMODE: + { + 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; + default: eSelMode = SelectionMode::NONE; break; + } + if( pTable->getSelEngine()->GetSelectionMode() != eSelMode ) + pTable->getSelEngine()->SetSelectionMode( eSelMode ); + } + break; + } + case BASEPROPERTY_HSCROLL: + { + bool bHScroll = true; + if( aValue >>= bHScroll ) + m_xTableModel->setHorizontalScrollbarVisibility( bHScroll ? ScrollbarShowAlways : ScrollbarShowSmart ); + break; + } + + case BASEPROPERTY_VSCROLL: + { + bool bVScroll = true; + if( aValue >>= bVScroll ) + { + m_xTableModel->setVerticalScrollbarVisibility( bVScroll ? ScrollbarShowAlways : ScrollbarShowSmart ); + } + break; + } + + case BASEPROPERTY_GRID_SHOWROWHEADER: + { + bool rowHeader = true; + if( aValue >>= rowHeader ) + { + m_xTableModel->setRowHeaders(rowHeader); + } + break; + } + + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + m_xTableModel->setRowBackgroundColors( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_LINE_COLOR: + m_xTableModel->setLineColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + m_xTableModel->setHeaderBackgroundColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + m_xTableModel->setHeaderTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + m_xTableModel->setActiveSelectionBackColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + m_xTableModel->setInactiveSelectionBackColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + m_xTableModel->setActiveSelectionTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + m_xTableModel->setInactiveSelectionTextColor( aValue ); + pTable->Invalidate(); + break; + + + case BASEPROPERTY_TEXTCOLOR: + m_xTableModel->setTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_TEXTLINECOLOR: + m_xTableModel->setTextLineColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_VERTICALALIGN: + { + VerticalAlignment eAlign( VerticalAlignment_TOP ); + if ( aValue >>= eAlign ) + m_xTableModel->setVerticalAlign( eAlign ); + break; + } + + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + { + bool colHeader = true; + if( aValue >>= colHeader ) + { + m_xTableModel->setColumnHeaders(colHeader); + } + break; + } + case BASEPROPERTY_GRID_DATAMODEL: + { + Reference< XGridDataModel > const xDataModel( aValue, UNO_QUERY ); + if ( !xDataModel.is() ) + throw GridInvalidDataException("Invalid data model.", *this ); + + m_xTableModel->setDataModel( xDataModel ); + impl_checkTableModelInit(); + } + break; + + case BASEPROPERTY_GRID_COLUMNMODEL: + { + // obtain new col model + Reference< XGridColumnModel > const xColumnModel( aValue, UNO_QUERY ); + if ( !xColumnModel.is() ) + throw GridInvalidModelException("Invalid column model.", *this ); + + // remove all old columns + m_xTableModel->removeAllColumns(); + + // announce to the TableModel + m_xTableModel->setColumnModel( xColumnModel ); + impl_checkTableModelInit(); + + // add new columns + impl_updateColumnsFromModel_nothrow(); + break; + } + default: + VCLXWindow::setProperty( PropertyName, aValue ); + break; + } +} + + +void SVTXGridControl::impl_checkTableModelInit() +{ + if ( !(!m_bTableModelInitCompleted && m_xTableModel->hasColumnModel() && m_xTableModel->hasDataModel()) ) + return; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + if ( !pTable ) + return; + + pTable->SetModel( PTableModel( m_xTableModel ) ); + + m_bTableModelInitCompleted = true; + + // ensure default columns exist, if they have not previously been added + Reference< XGridDataModel > const xDataModel( m_xTableModel->getDataModel(), css::uno::UNO_SET_THROW ); + Reference< XGridColumnModel > const xColumnModel( m_xTableModel->getColumnModel(), css::uno::UNO_SET_THROW ); + + sal_Int32 const nDataColumnCount = xDataModel->getColumnCount(); + if ( ( nDataColumnCount > 0 ) && ( xColumnModel->getColumnCount() == 0 ) ) + xColumnModel->setDefaultColumns( nDataColumnCount ); + // this will trigger notifications, which in turn will let us update our m_xTableModel +} + +namespace +{ + void lcl_convertColor( ::std::optional< ::Color > const & i_color, Any & o_colorValue ) + { + if ( !i_color ) + o_colorValue.clear(); + else + o_colorValue <<= sal_Int32(*i_color); + } +} + +Any SVTXGridControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getProperty: no control (anymore)!", Any() ); + + Any aPropertyValue; + + const sal_uInt16 nPropId = GetPropertyId( PropertyName ); + switch(nPropId) + { + case BASEPROPERTY_GRID_SELECTIONMODE: + { + SelectionType eSelectionType; + + SelectionMode eSelMode = pTable->getSelEngine()->GetSelectionMode(); + switch( eSelMode ) + { + case SelectionMode::Single: eSelectionType = SelectionType_SINGLE; break; + case SelectionMode::Range: eSelectionType = SelectionType_RANGE; break; + case SelectionMode::Multiple:eSelectionType = SelectionType_MULTI; break; + default: eSelectionType = SelectionType_NONE; break; + } + aPropertyValue <<= eSelectionType; + break; + } + + case BASEPROPERTY_GRID_SHOWROWHEADER: + aPropertyValue <<= m_xTableModel->hasRowHeaders(); + break; + + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + aPropertyValue <<= m_xTableModel->hasColumnHeaders(); + break; + + case BASEPROPERTY_GRID_DATAMODEL: + aPropertyValue <<= m_xTableModel->getDataModel(); + break; + + case BASEPROPERTY_GRID_COLUMNMODEL: + aPropertyValue <<= m_xTableModel->getColumnModel(); + break; + + case BASEPROPERTY_HSCROLL: + { + bool const bHasScrollbar = ( m_xTableModel->getHorizontalScrollbarVisibility() != ScrollbarShowNever ); + aPropertyValue <<= bHasScrollbar; + break; + } + + case BASEPROPERTY_VSCROLL: + { + bool const bHasScrollbar = ( m_xTableModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ); + aPropertyValue <<= bHasScrollbar; + break; + } + + case BASEPROPERTY_USE_GRID_LINES: + { + GridTableRenderer* pGridRenderer = dynamic_cast< GridTableRenderer* >( + m_xTableModel->getRenderer().get() ); + if ( !pGridRenderer ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::getProperty(UseGridLines): invalid renderer!" ); + break; + } + + aPropertyValue <<= pGridRenderer->useGridLines(); + } + break; + + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + { + ::std::optional< ::std::vector< ::Color > > aColors( m_xTableModel->getRowBackgroundColors() ); + if ( !aColors ) + aPropertyValue.clear(); + else + { + Sequence< css::util::Color > aAPIColors( aColors->size() ); + std::transform(aColors->begin(), aColors->end(), aAPIColors.getArray(), + [](const auto& color) { return sal_Int32(color); }); + aPropertyValue <<= aAPIColors; + } + } + break; + + case BASEPROPERTY_GRID_LINE_COLOR: + lcl_convertColor( m_xTableModel->getLineColor(), aPropertyValue ); + break; + + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + lcl_convertColor( m_xTableModel->getHeaderBackgroundColor(), aPropertyValue ); + break; + + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getHeaderTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + lcl_convertColor( m_xTableModel->getActiveSelectionBackColor(), aPropertyValue ); + break; + + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + lcl_convertColor( m_xTableModel->getInactiveSelectionBackColor(), aPropertyValue ); + break; + + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getActiveSelectionTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getInactiveSelectionTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_TEXTCOLOR: + lcl_convertColor( m_xTableModel->getTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_TEXTLINECOLOR: + lcl_convertColor( m_xTableModel->getTextLineColor(), aPropertyValue ); + break; + + default: + aPropertyValue = VCLXWindow::getProperty( PropertyName ); + break; + } + + return aPropertyValue; +} + + +void SAL_CALL SVTXGridControl::rowsInserted( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + m_xTableModel->notifyRowsInserted( i_event ); +} + + +void SAL_CALL + SVTXGridControl::rowsRemoved( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + m_xTableModel->notifyRowsRemoved( i_event ); +} + + +void SAL_CALL SVTXGridControl::dataChanged( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + + m_xTableModel->notifyDataChanged( i_event ); + + // if the data model is sortable, a dataChanged event is also fired in case the sort order changed. + // So, just in case, invalidate the column header area, too. + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::dataChanged: no control (anymore)!" ); + pTable->getTableControlInterface().invalidate( TableArea::ColumnHeaders ); +} + + +void SAL_CALL SVTXGridControl::rowHeadingChanged( const GridDataEvent& ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::rowHeadingChanged: no control (anymore)!" ); + + // TODO: we could do better than this - invalidate the header area only + pTable->getTableControlInterface().invalidate( TableArea::RowHeaders ); +} + + +void SAL_CALL SVTXGridControl::elementInserted( const ContainerEvent& i_event ) +{ + SolarMutexGuard aGuard; + + Reference< XGridColumn > const xGridColumn( i_event.Element, UNO_QUERY_THROW ); + + sal_Int32 nIndex( m_xTableModel->getColumnCount() ); + OSL_VERIFY( i_event.Accessor >>= nIndex ); + m_xTableModel->insertColumn( nIndex, xGridColumn ); +} + + +void SAL_CALL SVTXGridControl::elementRemoved( const ContainerEvent& i_event ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nIndex( -1 ); + OSL_VERIFY( i_event.Accessor >>= nIndex ); + m_xTableModel->removeColumn( nIndex ); +} + + +void SAL_CALL SVTXGridControl::elementReplaced( const ContainerEvent& ) +{ + OSL_ENSURE( false, "SVTXGridControl::elementReplaced: not implemented!" ); + // at the moment, the XGridColumnModel API does not allow replacing columns + // TODO: replace the respective column in our table model +} + + +void SAL_CALL SVTXGridControl::disposing( const EventObject& Source ) +{ + VCLXWindow::disposing( Source ); +} + + +void SAL_CALL SVTXGridControl::selectRow( ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::selectRow: no control (anymore)!" ); + + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->SelectRow( i_rowIndex, true ); +} + + +void SAL_CALL SVTXGridControl::selectAllRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::selectAllRows: no control (anymore)!" ); + + pTable->SelectAllRows( true ); +} + + +void SAL_CALL SVTXGridControl::deselectRow( ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::deselectRow: no control (anymore)!" ); + + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->SelectRow( i_rowIndex, false ); +} + + +void SAL_CALL SVTXGridControl::deselectAllRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::deselectAllRows: no control (anymore)!" ); + + pTable->SelectAllRows( false ); +} + + +Sequence< ::sal_Int32 > SAL_CALL SVTXGridControl::getSelectedRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getSelectedRows: no control (anymore)!", Sequence< sal_Int32 >() ); + + sal_Int32 selectionCount = pTable->GetSelectedRowCount(); + Sequence< sal_Int32 > selectedRows( selectionCount ); + auto selectedRowsRange = asNonConstRange(selectedRows); + for ( sal_Int32 i=0; i<selectionCount; ++i ) + selectedRowsRange[i] = pTable->GetSelectedRowIndex(i); + return selectedRows; +} + + +sal_Bool SAL_CALL SVTXGridControl::hasSelectedRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::hasSelectedRows: no control (anymore)!", true ); + + return pTable->GetSelectedRowCount() > 0; +} + + +sal_Bool SAL_CALL SVTXGridControl::isRowSelected( ::sal_Int32 index ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::isRowSelected: no control (anymore)!", false ); + + return pTable->IsRowSelected( index ); +} + + +void SVTXGridControl::dispose() +{ + EventObject aObj; + aObj.Source = getXWeak(); + m_aSelectionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + + +void SVTXGridControl::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + SolarMutexGuard aGuard; + + Reference< XWindow > xKeepAlive( this ); + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::ProcessWindowEvent: no control (anymore)!" ); + + bool handled = false; + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::TableRowSelect: + { + if ( m_aSelectionListeners.getLength() ) + ImplCallItemListeners(); + handled = true; + } + break; + + case VclEventId::ControlGetFocus: + { + // TODO: this doesn't belong here. It belongs into the TableControl/_Impl, so A11Y also + // works when the control is used outside the UNO context + if ( pTable->GetRowCount()>0 ) + { + pTable->commitCellEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, + Any(), + Any() + ); + } + else + { + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + } + } + break; + + case VclEventId::ControlLoseFocus: + { + // TODO: this doesn't belong here. It belongs into the TableControl/_Impl, so A11Y also + // works when the control is used outside the UNO context + if ( pTable->GetRowCount()>0 ) + { + pTable->commitCellEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any(), + Any( AccessibleStateType::FOCUSED ) + ); + } + else + { + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any(), + Any( AccessibleStateType::FOCUSED ) + ); + } + } + break; + + default: break; + } + + if ( !handled ) + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); +} + + +void SVTXGridControl::setEnable( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + m_xTableModel->setEnabled( bEnable ); + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->Enable( bEnable ); + pWindow->EnableInput( bEnable ); + pWindow->Invalidate(); + } +} + + +void SVTXGridControl::ImplCallItemListeners() +{ + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::ImplCallItemListeners: no control (anymore)!" ); + + if ( m_aSelectionListeners.getLength() ) + { + GridSelectionEvent aEvent; + aEvent.Source = getXWeak(); + + sal_Int32 const nSelectedRowCount( pTable->GetSelectedRowCount() ); + aEvent.SelectedRowIndexes.realloc( nSelectedRowCount ); + auto pSelectedRowIndexes = aEvent.SelectedRowIndexes.getArray(); + for ( sal_Int32 i=0; i<nSelectedRowCount; ++i ) + pSelectedRowIndexes[i] = pTable->GetSelectedRowIndex( i ); + m_aSelectionListeners.selectionChanged( aEvent ); + } +} + + +void SVTXGridControl::impl_updateColumnsFromModel_nothrow() +{ + Reference< XGridColumnModel > const xColumnModel( m_xTableModel->getColumnModel() ); + ENSURE_OR_RETURN_VOID( xColumnModel.is(), "no model!" ); + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "no table!" ); + + try + { + const Sequence< Reference< XGridColumn > > columns = xColumnModel->getColumns(); + for ( auto const & colRef : columns ) + { + if ( !colRef.is() ) + { + SAL_WARN( "svtools.uno", "illegal column!" ); + continue; + } + + m_xTableModel->appendColumn( colRef ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/cellvalueconversion.cxx b/toolkit/source/controls/table/cellvalueconversion.cxx new file mode 100644 index 0000000000..e07ef35494 --- /dev/null +++ b/toolkit/source/controls/table/cellvalueconversion.cxx @@ -0,0 +1,376 @@ +/* -*- 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 "cellvalueconversion.hxx" + +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <sal/log.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/long.hxx> +#include <unotools/syslocale.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <comphelper/processfactory.hxx> + +#include <limits> +#include <memory> + +namespace svt +{ +using namespace ::com::sun::star::uno; +using ::com::sun::star::util::XNumberFormatter; +using ::com::sun::star::util::NumberFormatter; +using ::com::sun::star::util::XNumberFormatsSupplier; +using ::com::sun::star::util::NumberFormatsSupplier; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::util::DateTime; +using ::com::sun::star::util::XNumberFormatTypes; + +namespace NumberFormat = ::com::sun::star::util::NumberFormat; + +//= helper + +namespace +{ +double lcl_convertDateToDays(sal_uInt16 const i_day, sal_uInt16 const i_month, + sal_Int16 const i_year) +{ + tools::Long const nNullDateDays = ::Date::DateToDays(1, 1, 1900); + tools::Long const nValueDateDays = ::Date::DateToDays(i_day, i_month, i_year); + + return nValueDateDays - nNullDateDays; +} + +double lcl_convertTimeToDays(tools::Long const i_hours, tools::Long const i_minutes, + tools::Long const i_seconds, tools::Long const i_100thSeconds) +{ + return tools::Time(i_hours, i_minutes, i_seconds, i_100thSeconds).GetTimeInDays(); +} +} + +//= StandardFormatNormalizer + +StandardFormatNormalizer::StandardFormatNormalizer(Reference<XNumberFormatter> const& i_formatter, + ::sal_Int32 const i_numberFormatType) + : m_nFormatKey(0) +{ + try + { + ENSURE_OR_THROW(i_formatter.is(), "StandardFormatNormalizer: no formatter!"); + Reference<XNumberFormatsSupplier> const xSupplier(i_formatter->getNumberFormatsSupplier(), + UNO_SET_THROW); + Reference<XNumberFormatTypes> const xTypes(xSupplier->getNumberFormats(), UNO_QUERY_THROW); + m_nFormatKey = xTypes->getStandardFormat(i_numberFormatType, + SvtSysLocale().GetLanguageTag().getLocale()); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } +} + +//= DoubleNormalization + +namespace +{ +class DoubleNormalization : public StandardFormatNormalizer +{ +public: + explicit DoubleNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::NUMBER) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + OSL_VERIFY(i_value >>= returnValue); + return returnValue; + } +}; + +//= IntegerNormalization + +class IntegerNormalization : public StandardFormatNormalizer +{ +public: + explicit IntegerNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::NUMBER) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + sal_Int64 value(0); + OSL_VERIFY(i_value >>= value); + return value; + } +}; + +//= BooleanNormalization + +class BooleanNormalization : public StandardFormatNormalizer +{ +public: + explicit BooleanNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::LOGICAL) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + bool value(false); + OSL_VERIFY(i_value >>= value); + return value ? 1 : 0; + } +}; + +//= DateTimeNormalization + +class DateTimeNormalization : public StandardFormatNormalizer +{ +public: + explicit DateTimeNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::DATETIME) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + + // extract actual UNO value + DateTime aDateTimeValue; + ENSURE_OR_RETURN(i_value >>= aDateTimeValue, "allowed for DateTime values only", + returnValue); + + // date part + returnValue + = lcl_convertDateToDays(aDateTimeValue.Day, aDateTimeValue.Month, aDateTimeValue.Year); + + // time part + returnValue += lcl_convertTimeToDays(aDateTimeValue.Hours, aDateTimeValue.Minutes, + aDateTimeValue.Seconds, aDateTimeValue.NanoSeconds); + + // done + return returnValue; + } +}; + +//= DateNormalization + +class DateNormalization : public StandardFormatNormalizer +{ +public: + explicit DateNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::DATE) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + + // extract + css::util::Date aDateValue; + ENSURE_OR_RETURN(i_value >>= aDateValue, "allowed for Date values only", returnValue); + + // convert + returnValue = lcl_convertDateToDays(aDateValue.Day, aDateValue.Month, aDateValue.Year); + + // done + return returnValue; + } +}; + +//= TimeNormalization + +class TimeNormalization : public StandardFormatNormalizer +{ +public: + explicit TimeNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::TIME) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + + // extract + css::util::Time aTimeValue; + ENSURE_OR_RETURN(i_value >>= aTimeValue, "allowed for tools::Time values only", + returnValue); + + // convert + returnValue += lcl_convertTimeToDays(aTimeValue.Hours, aTimeValue.Minutes, + aTimeValue.Seconds, aTimeValue.NanoSeconds); + + // done + return returnValue; + } +}; +} + +//= operations + +bool CellValueConversion::ensureNumberFormatter() +{ + if (bAttemptedFormatterCreation) + return xNumberFormatter.is(); + bAttemptedFormatterCreation = true; + + try + { + Reference<XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + // a number formatter + Reference<XNumberFormatter> const xFormatter(NumberFormatter::create(xContext), + UNO_QUERY_THROW); + + // a supplier of number formats + Locale aLocale = SvtSysLocale().GetLanguageTag().getLocale(); + + Reference<XNumberFormatsSupplier> const xSupplier + = NumberFormatsSupplier::createWithLocale(xContext, aLocale); + + // ensure a NullDate we will assume later on + css::util::Date const aNullDate(1, 1, 1900); + Reference<XPropertySet> const xFormatSettings(xSupplier->getNumberFormatSettings(), + UNO_SET_THROW); + xFormatSettings->setPropertyValue("NullDate", Any(aNullDate)); + + // knit + xFormatter->attachNumberFormatsSupplier(xSupplier); + + // done + xNumberFormatter = xFormatter; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + + return xNumberFormatter.is(); +} + +bool CellValueConversion::getValueNormalizer(Type const& i_valueType, + std::shared_ptr<StandardFormatNormalizer>& o_formatter) +{ + auto pos = aNormalizers.find(i_valueType.getTypeName()); + if (pos == aNormalizers.end()) + { + // never encountered this type before + o_formatter.reset(); + + OUString const sTypeName(i_valueType.getTypeName()); + TypeClass const eTypeClass = i_valueType.getTypeClass(); + + if (sTypeName == ::cppu::UnoType<DateTime>::get().getTypeName()) + { + o_formatter = std::make_shared<DateTimeNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<css::util::Date>::get().getTypeName()) + { + o_formatter = std::make_shared<DateNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<css::util::Time>::get().getTypeName()) + { + o_formatter = std::make_shared<TimeNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<sal_Bool>::get().getTypeName()) + { + o_formatter = std::make_shared<BooleanNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<double>::get().getTypeName() + || sTypeName == ::cppu::UnoType<float>::get().getTypeName()) + { + o_formatter = std::make_shared<DoubleNormalization>(xNumberFormatter); + } + else if ((eTypeClass == TypeClass_BYTE) || (eTypeClass == TypeClass_SHORT) + || (eTypeClass == TypeClass_UNSIGNED_SHORT) || (eTypeClass == TypeClass_LONG) + || (eTypeClass == TypeClass_UNSIGNED_LONG) || (eTypeClass == TypeClass_HYPER)) + { + o_formatter = std::make_shared<IntegerNormalization>(xNumberFormatter); + } + else + { + SAL_WARN("svtools.table", "unsupported type '" << sTypeName << "'!"); + } + aNormalizers[sTypeName] = o_formatter; + } + else + o_formatter = pos->second; + + return bool(o_formatter); +} + +//= CellValueConversion + +CellValueConversion::CellValueConversion() + : xNumberFormatter() + , bAttemptedFormatterCreation(false) + , aNormalizers() +{ +} + +CellValueConversion::~CellValueConversion() {} + +OUString CellValueConversion::convertToString(const Any& i_value) +{ + OUString sStringValue; + if (!i_value.hasValue()) + return sStringValue; + + if (!(i_value >>= sStringValue)) + { + if (ensureNumberFormatter()) + { + std::shared_ptr<StandardFormatNormalizer> pNormalizer; + if (getValueNormalizer(i_value.getValueType(), pNormalizer)) + { + try + { + double const formatterCompliantValue = pNormalizer->convertToDouble(i_value); + sal_Int32 const formatKey = pNormalizer->getFormatKey(); + sStringValue = xNumberFormatter->convertNumberToString(formatKey, + formatterCompliantValue); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + } + } + } + + return sStringValue; +} + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/cellvalueconversion.hxx b/toolkit/source/controls/table/cellvalueconversion.hxx new file mode 100644 index 0000000000..2e05707e5b --- /dev/null +++ b/toolkit/source/controls/table/cellvalueconversion.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <unordered_map> +#include <memory> + +namespace svt +{ +class StandardFormatNormalizer +{ +public: + /** converts the given <code>Any</code> into a <code>double</code> value to be fed into a number formatter + */ + virtual double convertToDouble(css::uno::Any const& i_value) const = 0; + + /** returns the format key to be used for formatting values + */ + sal_Int32 getFormatKey() const { return m_nFormatKey; } + +protected: + StandardFormatNormalizer(css::uno::Reference<css::util::XNumberFormatter> const& i_formatter, + ::sal_Int32 const i_numberFormatType); + + virtual ~StandardFormatNormalizer() {} + +private: + ::sal_Int32 m_nFormatKey; +}; + +class CellValueConversion +{ +public: + CellValueConversion(); + ~CellValueConversion(); + + OUString convertToString(const css::uno::Any& i_cellValue); + +private: + bool ensureNumberFormatter(); + bool getValueNormalizer(css::uno::Type const& i_valueType, + std::shared_ptr<StandardFormatNormalizer>& o_formatter); + + typedef std::unordered_map<OUString, std::shared_ptr<StandardFormatNormalizer>> NormalizerCache; + + css::uno::Reference<css::util::XNumberFormatter> xNumberFormatter; + bool bAttemptedFormatterCreation; + NormalizerCache aNormalizers; +}; + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/defaultinputhandler.cxx b/toolkit/source/controls/table/defaultinputhandler.cxx new file mode 100644 index 0000000000..171458ef7a --- /dev/null +++ b/toolkit/source/controls/table/defaultinputhandler.cxx @@ -0,0 +1,180 @@ +/* -*- 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 <controls/table/defaultinputhandler.hxx> +#include <controls/table/tablecontrolinterface.hxx> + +#include <vcl/event.hxx> +#include <osl/diagnose.h> + +namespace svt::table +{ + + + //= DefaultInputHandler + + + DefaultInputHandler::DefaultInputHandler() + { + aMouseFunctions.push_back( new ColumnResize ); + aMouseFunctions.push_back( new RowSelection ); + aMouseFunctions.push_back( new ColumnSortHandler ); + } + + + DefaultInputHandler::~DefaultInputHandler() + { + } + + + bool DefaultInputHandler::delegateMouseEvent( ITableControl& i_control, const MouseEvent& i_event, + FunctionResult ( MouseFunction::*i_handlerMethod )( ITableControl&, const MouseEvent& ) ) + { + if ( pActiveFunction.is() ) + { + bool furtherHandler = false; + switch ( (pActiveFunction.get()->*i_handlerMethod)( i_control, i_event ) ) + { + case ActivateFunction: + OSL_ENSURE( false, "lcl_delegateMouseEvent: unexpected - function already *is* active!" ); + break; + case ContinueFunction: + break; + case DeactivateFunction: + pActiveFunction.clear(); + break; + case SkipFunction: + furtherHandler = true; + break; + } + if ( !furtherHandler ) + // handled the event + return true; + } + + // ask all other handlers + bool handled = false; + for (auto const& mouseFunction : aMouseFunctions) + { + if (handled) + break; + if (mouseFunction == pActiveFunction) + // we already invoked this function + continue; + + switch ( (mouseFunction.get()->*i_handlerMethod)( i_control, i_event ) ) + { + case ActivateFunction: + pActiveFunction = mouseFunction; + handled = true; + break; + case ContinueFunction: + case DeactivateFunction: + OSL_ENSURE( false, "lcl_delegateMouseEvent: unexpected: inactive handler cannot be continued or deactivated!" ); + break; + case SkipFunction: + handled = false; + break; + } + } + return handled; + } + + + bool DefaultInputHandler::MouseMove( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return delegateMouseEvent( i_tableControl, i_event, &MouseFunction::handleMouseMove ); + } + + + bool DefaultInputHandler::MouseButtonDown( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return delegateMouseEvent( i_tableControl, i_event, &MouseFunction::handleMouseDown ); + } + + + bool DefaultInputHandler::MouseButtonUp( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return delegateMouseEvent( i_tableControl, i_event, &MouseFunction::handleMouseUp ); + } + + + bool DefaultInputHandler::KeyInput( ITableControl& _rControl, const KeyEvent& rKEvt ) + { + bool bHandled = false; + + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nKeyCode = rKeyCode.GetCode(); + + struct ActionMapEntry + { + sal_uInt16 nKeyCode; + sal_uInt16 nKeyModifier; + TableControlAction eAction; + } + static const aKnownActions[] = { + { KEY_DOWN, 0, cursorDown }, + { KEY_UP, 0, cursorUp }, + { KEY_LEFT, 0, cursorLeft }, + { KEY_RIGHT, 0, cursorRight }, + { KEY_HOME, 0, cursorToLineStart }, + { KEY_END, 0, cursorToLineEnd }, + { KEY_PAGEUP, 0, cursorPageUp }, + { KEY_PAGEDOWN, 0, cursorPageDown }, + { KEY_PAGEUP, KEY_MOD1, cursorToFirstLine }, + { KEY_PAGEDOWN, KEY_MOD1, cursorToLastLine }, + { KEY_HOME, KEY_MOD1, cursorTopLeft }, + { KEY_END, KEY_MOD1, cursorBottomRight }, + { KEY_SPACE, KEY_MOD1, cursorSelectRow }, + { KEY_UP, KEY_SHIFT, cursorSelectRowUp }, + { KEY_DOWN, KEY_SHIFT, cursorSelectRowDown }, + { KEY_END, KEY_SHIFT, cursorSelectRowAreaBottom }, + { KEY_HOME, KEY_SHIFT, cursorSelectRowAreaTop } + }; + for (const ActionMapEntry& rAction : aKnownActions) + { + if ( ( rAction.nKeyCode == nKeyCode ) && ( rAction.nKeyModifier == rKeyCode.GetModifier() ) ) + { + bHandled = _rControl.dispatchAction( rAction.eAction ); + break; + } + } + + return bHandled; + } + + + bool DefaultInputHandler::GetFocus( ITableControl& _rControl ) + { + _rControl.showCursor(); + return false; // continue processing + } + + + bool DefaultInputHandler::LoseFocus( ITableControl& _rControl ) + { + _rControl.hideCursor(); + return false; // continue processing + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/gridtablerenderer.cxx b/toolkit/source/controls/table/gridtablerenderer.cxx new file mode 100644 index 0000000000..aa49f4afdd --- /dev/null +++ b/toolkit/source/controls/table/gridtablerenderer.cxx @@ -0,0 +1,597 @@ +/* -*- 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 "cellvalueconversion.hxx" +#include <controls/table/gridtablerenderer.hxx> +#include <controls/table/tablesort.hxx> + +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/window.hxx> +#include <vcl/image.hxx> +#include <vcl/virdev.hxx> +#include <vcl/decoview.hxx> +#include <vcl/settings.hxx> + + +namespace svt::table +{ + using ::css::uno::Any; + using ::css::uno::Reference; + using ::css::uno::UNO_QUERY; + using ::css::uno::XInterface; + using ::css::uno::TypeClass_INTERFACE; + using ::css::graphic::XGraphic; + using ::css::style::HorizontalAlignment; + using ::css::style::HorizontalAlignment_CENTER; + using ::css::style::HorizontalAlignment_RIGHT; + using ::css::style::VerticalAlignment; + using ::css::style::VerticalAlignment_MIDDLE; + using ::css::style::VerticalAlignment_BOTTOM; + + + //= CachedSortIndicator + + namespace { + + class CachedSortIndicator + { + public: + CachedSortIndicator() + : m_lastHeaderHeight( 0 ) + , m_lastArrowColor( COL_TRANSPARENT ) + { + } + + BitmapEx const & getBitmapFor(vcl::RenderContext const & i_device, tools::Long const i_headerHeight, + StyleSettings const & i_style, bool const i_sortAscending); + + private: + tools::Long m_lastHeaderHeight; + Color m_lastArrowColor; + BitmapEx m_sortAscending; + BitmapEx m_sortDescending; + }; + + } + + BitmapEx const & CachedSortIndicator::getBitmapFor(vcl::RenderContext const& i_device, tools::Long const i_headerHeight, + StyleSettings const & i_style, bool const i_sortAscending ) + { + BitmapEx& rBitmap(i_sortAscending ? m_sortAscending : m_sortDescending); + if (rBitmap.IsEmpty() || (i_headerHeight != m_lastHeaderHeight) || (i_style.GetActiveColor() != m_lastArrowColor)) + { + tools::Long const nSortIndicatorWidth = 2 * i_headerHeight / 3; + tools::Long const nSortIndicatorHeight = 2 * nSortIndicatorWidth / 3; + + Point const aBitmapPos( 0, 0 ); + Size const aBitmapSize( nSortIndicatorWidth, nSortIndicatorHeight ); + ScopedVclPtrInstance< VirtualDevice > aDevice(i_device, DeviceFormat::WITH_ALPHA); + aDevice->SetOutputSizePixel( aBitmapSize ); + + DecorationView aDecoView(aDevice.get()); + aDecoView.DrawSymbol(tools::Rectangle(aBitmapPos, aBitmapSize), + i_sortAscending ? SymbolType::SPIN_UP : SymbolType::SPIN_DOWN, + i_style.GetActiveColor()); + + rBitmap = aDevice->GetBitmapEx(aBitmapPos, aBitmapSize); + m_lastHeaderHeight = i_headerHeight; + m_lastArrowColor = i_style.GetActiveColor(); + } + return rBitmap; + } + + + //= GridTableRenderer_Impl + + struct GridTableRenderer_Impl + { + ITableModel& rModel; + RowPos nCurrentRow; + bool bUseGridLines; + CachedSortIndicator aSortIndicator; + CellValueConversion aStringConverter; + + explicit GridTableRenderer_Impl( ITableModel& _rModel ) + : rModel( _rModel ) + , nCurrentRow( ROW_INVALID ) + , bUseGridLines( true ) + , aSortIndicator( ) + , aStringConverter() + { + } + }; + + + //= helper + + namespace + { + tools::Rectangle lcl_getContentArea( GridTableRenderer_Impl const & i_impl, tools::Rectangle const & i_cellArea ) + { + tools::Rectangle aContentArea( i_cellArea ); + if ( i_impl.bUseGridLines ) + { + aContentArea.AdjustRight( -1 ); + aContentArea.AdjustBottom( -1 ); + } + return aContentArea; + } + tools::Rectangle lcl_getTextRenderingArea( tools::Rectangle const & i_contentArea ) + { + tools::Rectangle aTextArea( i_contentArea ); + aTextArea.AdjustLeft(2 ); aTextArea.AdjustRight( -2 ); + aTextArea.AdjustTop( 1 ); aTextArea.AdjustBottom( -1 ); + return aTextArea; + } + + DrawTextFlags lcl_getAlignmentTextDrawFlags( GridTableRenderer_Impl const & i_impl, ColPos const i_columnPos ) + { + DrawTextFlags nVertFlag = DrawTextFlags::Top; + VerticalAlignment const eVertAlign = i_impl.rModel.getVerticalAlign(); + switch ( eVertAlign ) + { + case VerticalAlignment_MIDDLE: nVertFlag = DrawTextFlags::VCenter; break; + case VerticalAlignment_BOTTOM: nVertFlag = DrawTextFlags::Bottom; break; + default: + break; + } + + DrawTextFlags nHorzFlag = DrawTextFlags::Left; + HorizontalAlignment const eHorzAlign = i_impl.rModel.getColumnCount() > 0 + ? i_impl.rModel.getColumnModel( i_columnPos )->getHorizontalAlign() + : HorizontalAlignment_CENTER; + switch ( eHorzAlign ) + { + case HorizontalAlignment_CENTER: nHorzFlag = DrawTextFlags::Center; break; + case HorizontalAlignment_RIGHT: nHorzFlag = DrawTextFlags::Right; break; + default: + break; + } + + return nVertFlag | nHorzFlag; + } + + } + + + //= GridTableRenderer + + + GridTableRenderer::GridTableRenderer( ITableModel& _rModel ) + :m_pImpl( new GridTableRenderer_Impl( _rModel ) ) + { + } + + + GridTableRenderer::~GridTableRenderer() + { + } + + + bool GridTableRenderer::useGridLines() const + { + return m_pImpl->bUseGridLines; + } + + + void GridTableRenderer::useGridLines( bool const i_use ) + { + m_pImpl->bUseGridLines = i_use; + } + + + namespace + { + Color lcl_getEffectiveColor(std::optional<Color> const& i_modelColor, + StyleSettings const& i_styleSettings, + Color const& (StyleSettings::*i_getDefaultColor) () const) + { + if (!!i_modelColor) + return *i_modelColor; + return (i_styleSettings.*i_getDefaultColor)(); + } + } + + + void GridTableRenderer::PaintHeaderArea(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rArea, + bool _bIsColHeaderArea, bool _bIsRowHeaderArea, const StyleSettings& _rStyle) + { + OSL_PRECOND(_bIsColHeaderArea || _bIsRowHeaderArea, "GridTableRenderer::PaintHeaderArea: invalid area flags!"); + + rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + + Color const background = lcl_getEffectiveColor(m_pImpl->rModel.getHeaderBackgroundColor(), + _rStyle, &StyleSettings::GetDialogColor); + rRenderContext.SetFillColor(background); + + rRenderContext.SetLineColor(); + rRenderContext.DrawRect(_rArea); + + // delimiter lines at bottom/right + std::optional<Color> aLineColor(m_pImpl->rModel.getLineColor()); + Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + rRenderContext.SetLineColor(lineColor); + rRenderContext.DrawLine(_rArea.BottomLeft(), _rArea.BottomRight()); + rRenderContext.DrawLine(_rArea.BottomRight(), _rArea.TopRight()); + + rRenderContext.Pop(); + } + + + void GridTableRenderer::PaintColumnHeader( + ColPos _nCol, + vcl::RenderContext& rRenderContext, + const tools::Rectangle& _rArea, const StyleSettings& _rStyle) + { + rRenderContext.Push(vcl::PushFlags::LINECOLOR); + + OUString sHeaderText; + PColumnModel const pColumn = m_pImpl->rModel.getColumnModel( _nCol ); + DBG_ASSERT( pColumn, "GridTableRenderer::PaintColumnHeader: invalid column model object!" ); + if ( pColumn ) + sHeaderText = pColumn->getName(); + + Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getTextColor(), _rStyle, &StyleSettings::GetFieldTextColor ); + rRenderContext.SetTextColor(textColor); + + tools::Rectangle const aTextRect( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, _rArea ) ) ); + DrawTextFlags nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, _nCol ) | DrawTextFlags::Clip; + if (!m_pImpl->rModel.isEnabled()) + nDrawTextFlags |= DrawTextFlags::Disable; + rRenderContext.DrawText( aTextRect, sHeaderText, nDrawTextFlags ); + + std::optional<Color> const aLineColor( m_pImpl->rModel.getLineColor() ); + Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + rRenderContext.SetLineColor( lineColor ); + rRenderContext.DrawLine( _rArea.BottomRight(), _rArea.TopRight()); + rRenderContext.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + + // draw sort indicator if the model data is sorted by the given column + ITableDataSort const * pSortAdapter = m_pImpl->rModel.getSortAdapter(); + ColumnSort aCurrentSortOrder; + if ( pSortAdapter != nullptr ) + aCurrentSortOrder = pSortAdapter->getCurrentSortOrder(); + if ( aCurrentSortOrder.nColumnPos == _nCol ) + { + tools::Long const nHeaderHeight( _rArea.GetHeight() ); + BitmapEx const aIndicatorBitmap = m_pImpl->aSortIndicator.getBitmapFor(rRenderContext, nHeaderHeight, _rStyle, + aCurrentSortOrder.eSortDirection == ColumnSortAscending); + Size const aBitmapSize( aIndicatorBitmap.GetSizePixel() ); + tools::Long const nSortIndicatorPaddingX = 2; + tools::Long const nSortIndicatorPaddingY = ( nHeaderHeight - aBitmapSize.Height() ) / 2; + + if ( nDrawTextFlags & DrawTextFlags::Right ) + { + // text is right aligned => draw the sort indicator at the left hand side + rRenderContext.DrawBitmapEx(Point(_rArea.Left() + nSortIndicatorPaddingX, _rArea.Top() + nSortIndicatorPaddingY), + aIndicatorBitmap); + } + else + { + // text is left-aligned or centered => draw the sort indicator at the right hand side + rRenderContext.DrawBitmapEx(Point(_rArea.Right() - nSortIndicatorPaddingX - aBitmapSize.Width(), nSortIndicatorPaddingY), + aIndicatorBitmap); + } + } + + rRenderContext.Pop(); + } + + + void GridTableRenderer::PrepareRow(RowPos _nRow, bool i_hasControlFocus, bool _bSelected, vcl::RenderContext& rRenderContext, + const tools::Rectangle& _rRowArea, const StyleSettings& _rStyle) + { + // remember the row for subsequent calls to the other ->ITableRenderer methods + m_pImpl->nCurrentRow = _nRow; + + rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + + Color backgroundColor = _rStyle.GetFieldColor(); + + Color const activeSelectionBackColor = lcl_getEffectiveColor(m_pImpl->rModel.getActiveSelectionBackColor(), + _rStyle, &StyleSettings::GetHighlightColor); + if (_bSelected) + { + // selected rows use the background color from the style + backgroundColor = i_hasControlFocus + ? activeSelectionBackColor + : lcl_getEffectiveColor(m_pImpl->rModel.getInactiveSelectionBackColor(), _rStyle, &StyleSettings::GetDeactiveColor); + } + else + { + std::optional< std::vector<Color> > aRowColors = m_pImpl->rModel.getRowBackgroundColors(); + if (!aRowColors) + { + // use alternating default colors + Color const fieldColor = _rStyle.GetFieldColor(); + if (_rStyle.GetHighContrastMode() || ((m_pImpl->nCurrentRow % 2) == 0)) + { + backgroundColor = fieldColor; + } + else + { + Color hilightColor = activeSelectionBackColor; + hilightColor.SetRed( 9 * ( fieldColor.GetRed() - hilightColor.GetRed() ) / 10 + hilightColor.GetRed() ); + hilightColor.SetGreen( 9 * ( fieldColor.GetGreen() - hilightColor.GetGreen() ) / 10 + hilightColor.GetGreen() ); + hilightColor.SetBlue( 9 * ( fieldColor.GetBlue() - hilightColor.GetBlue() ) / 10 + hilightColor.GetBlue() ); + backgroundColor = hilightColor; + } + } + else + { + if (aRowColors->empty()) + { + // all colors have the same background color + backgroundColor = _rStyle.GetFieldColor(); + } + else + { + backgroundColor = aRowColors->at(m_pImpl->nCurrentRow % aRowColors->size()); + } + } + } + + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(backgroundColor); + rRenderContext.DrawRect(_rRowArea); + + rRenderContext.Pop(); + } + + + void GridTableRenderer::PaintRowHeader(vcl::RenderContext& rRenderContext, + const tools::Rectangle& _rArea, const StyleSettings& _rStyle) + { + rRenderContext.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::TEXTCOLOR ); + + std::optional<Color> const aLineColor( m_pImpl->rModel.getLineColor() ); + Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + rRenderContext.SetLineColor(lineColor); + rRenderContext.DrawLine(_rArea.BottomLeft(), _rArea.BottomRight()); + + Any const rowHeading( m_pImpl->rModel.getRowHeading( m_pImpl->nCurrentRow ) ); + OUString const rowTitle( m_pImpl->aStringConverter.convertToString( rowHeading ) ); + if (!rowTitle.isEmpty()) + { + Color const textColor = lcl_getEffectiveColor(m_pImpl->rModel.getHeaderTextColor(), + _rStyle, &StyleSettings::GetFieldTextColor); + rRenderContext.SetTextColor(textColor); + + tools::Rectangle const aTextRect(lcl_getTextRenderingArea(lcl_getContentArea(*m_pImpl, _rArea))); + DrawTextFlags nDrawTextFlags = lcl_getAlignmentTextDrawFlags(*m_pImpl, 0) | DrawTextFlags::Clip; + if (!m_pImpl->rModel.isEnabled()) + nDrawTextFlags |= DrawTextFlags::Disable; + // TODO: is using the horizontal alignment of the 0'th column a good idea here? This is pretty ... arbitrary .. + rRenderContext.DrawText(aTextRect, rowTitle, nDrawTextFlags); + } + + rRenderContext.Pop(); + } + + + struct GridTableRenderer::CellRenderContext + { + OutputDevice& rDevice; + tools::Rectangle const aContentArea; + StyleSettings const & rStyle; + ColPos const nColumn; + bool const bSelected; + bool const bHasControlFocus; + + CellRenderContext( OutputDevice& i_device, tools::Rectangle const & i_contentArea, + StyleSettings const & i_style, ColPos const i_column, bool const i_selected, bool const i_hasControlFocus ) + :rDevice( i_device ) + ,aContentArea( i_contentArea ) + ,rStyle( i_style ) + ,nColumn( i_column ) + ,bSelected( i_selected ) + ,bHasControlFocus( i_hasControlFocus ) + { + } + }; + + + void GridTableRenderer::PaintCell(ColPos const i_column, bool _bSelected, bool i_hasControlFocus, + vcl::RenderContext& rRenderContext, const tools::Rectangle& _rArea, const StyleSettings& _rStyle) + { + rRenderContext.Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); + + tools::Rectangle const aContentArea(lcl_getContentArea(*m_pImpl, _rArea)); + CellRenderContext const aCellRenderContext(rRenderContext, aContentArea, _rStyle, i_column, _bSelected, i_hasControlFocus); + impl_paintCellContent(aCellRenderContext); + + if ( m_pImpl->bUseGridLines ) + { + ::std::optional< ::Color > aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + + if ( _bSelected && !aLineColor ) + { + // if no line color is specified by the model, use the usual selection color for lines in selected cells + lineColor = i_hasControlFocus + ? lcl_getEffectiveColor( m_pImpl->rModel.getActiveSelectionBackColor(), _rStyle, &StyleSettings::GetHighlightColor ) + : lcl_getEffectiveColor( m_pImpl->rModel.getInactiveSelectionBackColor(), _rStyle, &StyleSettings::GetDeactiveColor ); + } + + rRenderContext.SetLineColor( lineColor ); + rRenderContext.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + rRenderContext.DrawLine( _rArea.BottomRight(), _rArea.TopRight() ); + } + + rRenderContext.Pop(); + } + + + void GridTableRenderer::impl_paintCellImage( CellRenderContext const & i_context, Image const & i_image ) + { + Point imagePos( i_context.aContentArea.Left(), i_context.aContentArea.Top() ); + Size imageSize = i_image.GetSizePixel(); + if ( i_context.aContentArea.GetWidth() > imageSize.Width() ) + { + const HorizontalAlignment eHorzAlign = m_pImpl->rModel.getColumnModel( i_context.nColumn )->getHorizontalAlign(); + switch ( eHorzAlign ) + { + case HorizontalAlignment_CENTER: + imagePos.AdjustX(( i_context.aContentArea.GetWidth() - imageSize.Width() ) / 2 ); + break; + case HorizontalAlignment_RIGHT: + imagePos.setX( i_context.aContentArea.Right() - imageSize.Width() ); + break; + default: + break; + } + + } + else + imageSize.setWidth( i_context.aContentArea.GetWidth() ); + + if ( i_context.aContentArea.GetHeight() > imageSize.Height() ) + { + const VerticalAlignment eVertAlign = m_pImpl->rModel.getVerticalAlign(); + switch ( eVertAlign ) + { + case VerticalAlignment_MIDDLE: + imagePos.AdjustY(( i_context.aContentArea.GetHeight() - imageSize.Height() ) / 2 ); + break; + case VerticalAlignment_BOTTOM: + imagePos.setY( i_context.aContentArea.Bottom() - imageSize.Height() ); + break; + default: + break; + } + } + else + imageSize.setHeight( i_context.aContentArea.GetHeight() - 1 ); + DrawImageFlags const nStyle = m_pImpl->rModel.isEnabled() ? DrawImageFlags::NONE : DrawImageFlags::Disable; + i_context.rDevice.DrawImage( imagePos, imageSize, i_image, nStyle ); + } + + + void GridTableRenderer::impl_paintCellContent( CellRenderContext const & i_context ) + { + Any aCellContent; + m_pImpl->rModel.getCellContent( i_context.nColumn, m_pImpl->nCurrentRow, aCellContent ); + + if ( aCellContent.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > const xContentInterface( aCellContent, UNO_QUERY ); + if ( !xContentInterface.is() ) + // allowed. kind of. + return; + + Reference< XGraphic > const xGraphic( aCellContent, UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xGraphic.is(), "GridTableRenderer::impl_paintCellContent: only XGraphic interfaces (or NULL) are supported for painting." ); + + const Image aImage( xGraphic ); + impl_paintCellImage( i_context, aImage ); + return; + } + + const OUString sText( m_pImpl->aStringConverter.convertToString( aCellContent ) ); + impl_paintCellText( i_context, sText ); + } + + + void GridTableRenderer::impl_paintCellText( CellRenderContext const & i_context, OUString const & i_text ) + { + if ( i_context.bSelected ) + { + ::Color const textColor = i_context.bHasControlFocus + ? lcl_getEffectiveColor( m_pImpl->rModel.getActiveSelectionTextColor(), i_context.rStyle, &StyleSettings::GetHighlightTextColor ) + : lcl_getEffectiveColor( m_pImpl->rModel.getInactiveSelectionTextColor(), i_context.rStyle, &StyleSettings::GetDeactiveTextColor ); + i_context.rDevice.SetTextColor( textColor ); + } + else + { + ::Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getTextColor(), i_context.rStyle, &StyleSettings::GetFieldTextColor ); + i_context.rDevice.SetTextColor( textColor ); + } + + tools::Rectangle const textRect( lcl_getTextRenderingArea( i_context.aContentArea ) ); + DrawTextFlags nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, i_context.nColumn ) | DrawTextFlags::Clip; + if ( !m_pImpl->rModel.isEnabled() ) + nDrawTextFlags |= DrawTextFlags::Disable; + i_context.rDevice.DrawText( textRect, i_text, nDrawTextFlags ); + } + + + void GridTableRenderer::ShowCellCursor( vcl::Window& _rView, const tools::Rectangle& _rCursorRect) + { + _rView.ShowFocus( _rCursorRect ); + } + + + void GridTableRenderer::HideCellCursor( vcl::Window& _rView ) + { + _rView.HideFocus(); + } + + + bool GridTableRenderer::FitsIntoCell( Any const & i_cellContent, + OutputDevice& i_targetDevice, tools::Rectangle const & i_targetArea ) const + { + if ( !i_cellContent.hasValue() ) + return true; + + if ( i_cellContent.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > const xContentInterface( i_cellContent, UNO_QUERY ); + if ( !xContentInterface.is() ) + return true; + + Reference< XGraphic > const xGraphic( i_cellContent, UNO_QUERY ); + if ( xGraphic.is() ) + // for the moment, assume it fits. We can always scale it down during painting ... + return true; + + OSL_ENSURE( false, "GridTableRenderer::FitsIntoCell: only XGraphic interfaces (or NULL) are supported for painting." ); + return true; + } + + OUString const sText( m_pImpl->aStringConverter.convertToString( i_cellContent ) ); + if ( sText.isEmpty() ) + return true; + + tools::Rectangle const aTargetArea( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, i_targetArea ) ) ); + + tools::Long const nTextHeight = i_targetDevice.GetTextHeight(); + if ( nTextHeight > aTargetArea.GetHeight() ) + return false; + + tools::Long const nTextWidth = i_targetDevice.GetTextWidth( sText ); + return nTextWidth <= aTargetArea.GetWidth(); + } + + + bool GridTableRenderer::GetFormattedCellString( Any const & i_cellValue, OUString & o_cellString ) const + { + o_cellString = m_pImpl->aStringConverter.convertToString( i_cellValue ); + + return true; + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/mousefunction.cxx b/toolkit/source/controls/table/mousefunction.cxx new file mode 100644 index 0000000000..d0a784fdf1 --- /dev/null +++ b/toolkit/source/controls/table/mousefunction.cxx @@ -0,0 +1,273 @@ +/* -*- 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 <controls/table/mousefunction.hxx> +#include <controls/table/tablecontrolinterface.hxx> +#include <controls/table/tablesort.hxx> + +#include <comphelper/diagnose_ex.hxx> +#include <vcl/ptrstyle.hxx> + +namespace svt::table +{ + + + //= ColumnResize + + + FunctionResult ColumnResize::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + Point const aPoint = i_event.GetPosPixel(); + + if ( m_nResizingColumn == COL_INVALID ) + { + // if we hit a column divider, change the mouse pointer accordingly + PointerStyle aNewPointer( PointerStyle::Arrow ); + TableCell const tableCell = i_tableControl.hitTest( aPoint ); + if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.eArea == ColumnDivider ) ) + { + aNewPointer = PointerStyle::HSplit; + } + i_tableControl.setPointer( aNewPointer ); + + return SkipFunction; // TODO: is this correct? + } + + ::Size const tableSize = i_tableControl.getTableSizePixel(); + + // set proper pointer + PointerStyle aNewPointer( PointerStyle::Arrow ); + ColumnMetrics const & columnMetrics( i_tableControl.getColumnMetrics( m_nResizingColumn ) ); + if ( ( aPoint.X() > tableSize.Width() ) + || ( aPoint.X() < columnMetrics.nStartPixel ) + ) + { + aNewPointer = PointerStyle::NotAllowed; + } + else + { + aNewPointer = PointerStyle::HSplit; + } + i_tableControl.setPointer( aNewPointer ); + + // show tracking line + i_tableControl.hideTracking(); + i_tableControl.showTracking( + tools::Rectangle( + Point( aPoint.X(), 0 ), + Size( 1, tableSize.Height() ) + ), + ShowTrackFlags::Split | ShowTrackFlags::TrackWindow + ); + + return ContinueFunction; + } + + + FunctionResult ColumnResize::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nResizingColumn != COL_INVALID ) + { + OSL_ENSURE( false, "ColumnResize::handleMouseDown: suspicious: MouseButtonDown while still tracking?" ); + return ContinueFunction; + } + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( tableCell.nRow == ROW_COL_HEADERS ) + { + if ( ( tableCell.nColumn != COL_INVALID ) + && ( tableCell.eArea == ColumnDivider ) + ) + { + m_nResizingColumn = tableCell.nColumn; + i_tableControl.captureMouse(); + return ActivateFunction; + } + } + + return SkipFunction; + } + + + FunctionResult ColumnResize::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nResizingColumn == COL_INVALID ) + return SkipFunction; + + Point const aPoint = i_event.GetPosPixel(); + + i_tableControl.hideTracking(); + PColumnModel const pColumn = i_tableControl.getModel()->getColumnModel( m_nResizingColumn ); + tools::Long const maxWidthLogical = pColumn->getMaxWidth(); + tools::Long const minWidthLogical = pColumn->getMinWidth(); + + // new position of mouse + tools::Long const requestedEnd = aPoint.X(); + + // old position of right border + tools::Long const oldEnd = i_tableControl.getColumnMetrics( m_nResizingColumn ).nEndPixel; + + // position of left border if cursor in the to-be-resized column + tools::Long const columnStart = i_tableControl.getColumnMetrics( m_nResizingColumn ).nStartPixel; + tools::Long const requestedWidth = requestedEnd - columnStart; + // TODO: this is not correct, strictly: It assumes that the mouse was pressed exactly on the "end" pos, + // but for a while now, we have relaxed this, and allow clicking a few pixels aside, too + + if ( requestedEnd >= columnStart ) + { + tools::Long requestedWidthLogical = i_tableControl.pixelWidthToAppFont( requestedWidth ); + // respect column width limits + if ( oldEnd > requestedEnd ) + { + // column has become smaller, check against minimum width + if ( ( minWidthLogical != 0 ) && ( requestedWidthLogical < minWidthLogical ) ) + requestedWidthLogical = minWidthLogical; + } + else if ( oldEnd < requestedEnd ) + { + // column has become larger, check against max width + if ( ( maxWidthLogical != 0 ) && ( requestedWidthLogical >= maxWidthLogical ) ) + requestedWidthLogical = maxWidthLogical; + } + pColumn->setWidth( requestedWidthLogical ); + i_tableControl.invalidate( TableArea::All ); + } + + i_tableControl.setPointer( PointerStyle::Arrow ); + i_tableControl.releaseMouse(); + + m_nResizingColumn = COL_INVALID; + return DeactivateFunction; + } + + + //= RowSelection + + + FunctionResult RowSelection::handleMouseMove( ITableControl&, MouseEvent const & ) + { + return SkipFunction; + } + + + FunctionResult RowSelection::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + bool handled = false; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( tableCell.nRow >= 0 ) + { + if ( i_tableControl.getSelEngine()->GetSelectionMode() == SelectionMode::NONE ) + { + i_tableControl.activateCell( tableCell.nColumn, tableCell.nRow ); + handled = true; + } + else + { + handled = i_tableControl.getSelEngine()->SelMouseButtonDown( i_event ); + } + } + + if ( handled ) + m_bActive = true; + return handled ? ActivateFunction : SkipFunction; + } + + + FunctionResult RowSelection::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + TableCell const tableCell = i_tableControl.hitTest( i_event.GetPosPixel() ); + if ( tableCell.nRow >= 0 ) + { + if ( i_tableControl.getSelEngine()->GetSelectionMode() != SelectionMode::NONE ) + { + i_tableControl.getSelEngine()->SelMouseButtonUp( i_event ); + } + } + if ( m_bActive ) + { + m_bActive = false; + return DeactivateFunction; + } + return SkipFunction; + } + + + //= ColumnSortHandler + + + FunctionResult ColumnSortHandler::handleMouseMove( ITableControl&, MouseEvent const & ) + { + return SkipFunction; + } + + + FunctionResult ColumnSortHandler::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nActiveColumn != COL_INVALID ) + { + OSL_ENSURE( false, "ColumnSortHandler::handleMouseDown: called while already active - suspicious!" ); + return ContinueFunction; + } + + if ( i_tableControl.getModel()->getSortAdapter() == nullptr ) + // no sorting support at the model + return SkipFunction; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( ( tableCell.nRow != ROW_COL_HEADERS ) || ( tableCell.nColumn < 0 ) ) + return SkipFunction; + + // TODO: ensure the column header is rendered in some special way, indicating its current state + + m_nActiveColumn = tableCell.nColumn; + return ActivateFunction; + } + + + FunctionResult ColumnSortHandler::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nActiveColumn == COL_INVALID ) + return SkipFunction; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.nColumn == m_nActiveColumn ) ) + { + ITableDataSort* pSort = i_tableControl.getModel()->getSortAdapter(); + ENSURE_OR_RETURN( pSort != nullptr, "ColumnSortHandler::handleMouseUp: somebody is mocking with us!", DeactivateFunction ); + // in handleMousButtonDown, the model claimed to have sort support ... + + ColumnSortDirection eSortDirection = ColumnSortAscending; + ColumnSort const aCurrentSort = pSort->getCurrentSortOrder(); + if ( aCurrentSort.nColumnPos == m_nActiveColumn ) + // invert existing sort order + eSortDirection = ( aCurrentSort.eSortDirection == ColumnSortAscending ) ? ColumnSortDescending : ColumnSortAscending; + + pSort->sortByColumn( m_nActiveColumn, eSortDirection ); + } + + m_nActiveColumn = COL_INVALID; + return DeactivateFunction; + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablecontrol.cxx b/toolkit/source/controls/table/tablecontrol.cxx new file mode 100644 index 0000000000..42b314569e --- /dev/null +++ b/toolkit/source/controls/table/tablecontrol.cxx @@ -0,0 +1,642 @@ +/* -*- 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 <controls/table/tablecontrol.hxx> + +#include "tablecontrol_impl.hxx" +#include "tabledatawindow.hxx" + +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/settings.hxx> +#include <vcl/vclevent.hxx> + +using namespace ::com::sun::star::uno; +using ::com::sun::star::accessibility::XAccessible; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::lang; + +namespace svt::table +{ + + + namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId; + + + //= TableControl + + + TableControl::TableControl( vcl::Window* _pParent, WinBits _nStyle ) + :Control( _pParent, _nStyle ) + ,m_pImpl( std::make_shared<TableControl_Impl>( *this ) ) + { + TableDataWindow& rDataWindow = m_pImpl->getDataWindow(); + rDataWindow.SetSelectHdl( LINK( this, TableControl, ImplSelectHdl ) ); + + // by default, use the background as determined by the style settings + const Color aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() ); + SetBackground( Wallpaper( aWindowColor ) ); + GetOutDev()->SetFillColor( aWindowColor ); + + SetCompoundControl( true ); + } + + + TableControl::~TableControl() + { + disposeOnce(); + } + + void TableControl::dispose() + { + CallEventListeners( VclEventId::ObjectDying ); + + m_pImpl->setModel( PTableModel() ); + m_pImpl->disposeAccessible(); + m_pImpl.reset(); + Control::dispose(); + } + + + void TableControl::GetFocus() + { + if ( !m_pImpl || !m_pImpl->getInputHandler()->GetFocus( *m_pImpl ) ) + Control::GetFocus(); + } + + + void TableControl::LoseFocus() + { + if ( !m_pImpl || !m_pImpl->getInputHandler()->LoseFocus( *m_pImpl ) ) + Control::LoseFocus(); + } + + + void TableControl::KeyInput( const KeyEvent& rKEvt ) + { + if ( !m_pImpl->getInputHandler()->KeyInput( *m_pImpl, rKEvt ) ) + Control::KeyInput( rKEvt ); + else + { + if ( m_pImpl->isAccessibleAlive() ) + { + m_pImpl->commitCellEvent( AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + // Huh? What the heck? Why do we unconditionally notify a STATE_CHANGE/FOCUSED after each and every + // (handled) key stroke? + + m_pImpl->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, + Any(), + Any() + ); + // ditto: Why do we notify this unconditionally? We should find the right place to notify the + // ACTIVE_DESCENDANT_CHANGED event. + // Also, we should check if STATE_CHANGED/FOCUSED is really necessary: finally, the children are + // transient, aren't they? + } + } + } + + + void TableControl::StateChanged( StateChangedType i_nStateChange ) + { + Control::StateChanged( i_nStateChange ); + + // forward certain settings to the data window + switch ( i_nStateChange ) + { + case StateChangedType::ControlFocus: + m_pImpl->invalidateSelectedRows(); + break; + + case StateChangedType::ControlBackground: + if ( IsControlBackground() ) + getDataWindow().SetControlBackground( GetControlBackground() ); + else + getDataWindow().SetControlBackground(); + break; + + case StateChangedType::ControlForeground: + if ( IsControlForeground() ) + getDataWindow().SetControlForeground( GetControlForeground() ); + else + getDataWindow().SetControlForeground(); + break; + + case StateChangedType::ControlFont: + if ( IsControlFont() ) + getDataWindow().SetControlFont( GetControlFont() ); + else + getDataWindow().SetControlFont(); + break; + default:; + } + } + + + void TableControl::Resize() + { + Control::Resize(); + m_pImpl->onResize(); + } + + + void TableControl::SetModel( const PTableModel& _pModel ) + { + m_pImpl->setModel( _pModel ); + } + + + PTableModel TableControl::GetModel() const + { + return m_pImpl->getModel(); + } + + + sal_Int32 TableControl::GetCurrentRow() const + { + return m_pImpl->getCurrentRow(); + } + + + sal_Int32 TableControl::GetCurrentColumn() const + { + return m_pImpl->getCurrentColumn(); + } + + + void TableControl::GoTo( ColPos _nColumn, RowPos _nRow ) + { + m_pImpl->goTo( _nColumn, _nRow ); + } + + + void TableControl::GoToCell(sal_Int32 _nColPos, sal_Int32 _nRowPos) + { + m_pImpl->goTo( _nColPos, _nRowPos ); + } + + + sal_Int32 TableControl::GetSelectedRowCount() const + { + return sal_Int32( m_pImpl->getSelectedRowCount() ); + } + + + sal_Int32 TableControl::GetSelectedRowIndex( sal_Int32 const i_selectionIndex ) const + { + return m_pImpl->getSelectedRowIndex( i_selectionIndex ); + } + + + bool TableControl::IsRowSelected( sal_Int32 const i_rowIndex ) const + { + return m_pImpl->isRowSelected( i_rowIndex ); + } + + + void TableControl::SelectRow( sal_Int32 const i_rowIndex, bool const i_select ) + { + ENSURE_OR_RETURN_VOID( ( i_rowIndex >= 0 ) && ( i_rowIndex < m_pImpl->getModel()->getRowCount() ), + "TableControl::SelectRow: invalid row index!" ); + + if ( i_select ) + { + if ( !m_pImpl->markRowAsSelected( i_rowIndex ) ) + // nothing to do + return; + } + else + { + m_pImpl->markRowAsDeselected( i_rowIndex ); + } + + m_pImpl->invalidateRowRange( i_rowIndex, i_rowIndex ); + Select(); + } + + + void TableControl::SelectAllRows( bool const i_select ) + { + if ( i_select ) + { + if ( !m_pImpl->markAllRowsAsSelected() ) + // nothing to do + return; + } + else + { + if ( !m_pImpl->markAllRowsAsDeselected() ) + // nothing to do + return; + } + + + Invalidate(); + // TODO: can't we do better than this, and invalidate only the rows which changed? + Select(); + } + + + ITableControl& TableControl::getTableControlInterface() + { + return *m_pImpl; + } + + + SelectionEngine* TableControl::getSelEngine() + { + return m_pImpl->getSelEngine(); + } + + + vcl::Window& TableControl::getDataWindow() + { + return m_pImpl->getDataWindow(); + } + + + Reference< XAccessible > TableControl::CreateAccessible() + { + vcl::Window* pParent = GetAccessibleParentWindow(); + ENSURE_OR_RETURN( pParent, "TableControl::CreateAccessible - parent not found", nullptr ); + + return m_pImpl->getAccessible( *pParent ); + } + + + Reference<XAccessible> TableControl::CreateAccessibleControl( sal_Int32 ) + { + SAL_WARN( "svtools", "TableControl::CreateAccessibleControl: to be overwritten!" ); + return nullptr; + } + + + OUString TableControl::GetAccessibleObjectName( vcl::table::AccessibleTableControlObjType eObjType, sal_Int32 _nRow, sal_Int32 _nCol) const + { + OUString aRetText; + //Window* pWin; + switch( eObjType ) + { + case vcl::table::AccessibleTableControlObjType::GRIDCONTROL: + aRetText = "Grid control"; + break; + case vcl::table::AccessibleTableControlObjType::TABLE: + aRetText = "Grid control"; + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR: + aRetText = "RowHeaderBar"; + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR: + aRetText = "ColumnHeaderBar"; + break; + case vcl::table::AccessibleTableControlObjType::TABLECELL: + //the name of the cell consists of column name and row name if defined + //if the name is equal to cell content, it'll be read twice + if(GetModel()->hasColumnHeaders()) + { + aRetText = GetColumnName(_nCol) + " , "; + } + if(GetModel()->hasRowHeaders()) + { + aRetText += GetRowName(_nRow) + " , "; + } + //aRetText = GetAccessibleCellText(_nRow, _nCol); + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL: + aRetText = GetRowName(_nRow); + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL: + aRetText = GetColumnName(_nCol); + break; + default: + OSL_FAIL("GridControl::GetAccessibleName: invalid enum!"); + } + return aRetText; + } + + + OUString TableControl::GetAccessibleObjectDescription( vcl::table::AccessibleTableControlObjType eObjType ) const + { + OUString aRetText; + switch( eObjType ) + { + case vcl::table::AccessibleTableControlObjType::GRIDCONTROL: + aRetText = "Grid control description"; + break; + case vcl::table::AccessibleTableControlObjType::TABLE: + aRetText = "TABLE description"; + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR: + aRetText = "ROWHEADERBAR description"; + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR: + aRetText = "COLUMNHEADERBAR description"; + break; + case vcl::table::AccessibleTableControlObjType::TABLECELL: + // the description of the cell consists of column name and row name if defined + // if the name is equal to cell content, it'll be read twice + if ( GetModel()->hasColumnHeaders() ) + { + aRetText = GetColumnName( GetCurrentColumn() ) + " , "; + } + if ( GetModel()->hasRowHeaders() ) + { + aRetText += GetRowName( GetCurrentRow() ); + } + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL: + aRetText = "ROWHEADERCELL description"; + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL: + aRetText = "COLUMNHEADERCELL description"; + break; + } + return aRetText; + } + + + OUString TableControl::GetRowName( sal_Int32 _nIndex) const + { + OUString sRowName; + GetModel()->getRowHeading( _nIndex ) >>= sRowName; + return sRowName; + } + + + OUString TableControl::GetColumnName( sal_Int32 _nIndex) const + { + return GetModel()->getColumnModel(_nIndex)->getName(); + } + + + OUString TableControl::GetAccessibleCellText( sal_Int32 _nRowPos, sal_Int32 _nColPos) const + { + return m_pImpl->getCellContentAsString( _nRowPos, _nColPos ); + } + + + void TableControl::FillAccessibleStateSet( + sal_Int64& rStateSet, + vcl::table::AccessibleTableControlObjType eObjType ) const + { + switch( eObjType ) + { + case vcl::table::AccessibleTableControlObjType::GRIDCONTROL: + case vcl::table::AccessibleTableControlObjType::TABLE: + + rStateSet |= AccessibleStateType::FOCUSABLE; + + if ( m_pImpl->getSelEngine()->GetSelectionMode() == SelectionMode::Multiple ) + rStateSet |= AccessibleStateType::MULTI_SELECTABLE; + + if ( HasChildPathFocus() ) + rStateSet |= AccessibleStateType::FOCUSED; + + if ( IsActive() ) + rStateSet |= AccessibleStateType::ACTIVE; + + if ( m_pImpl->getDataWindow().IsEnabled() ) + { + rStateSet |= AccessibleStateType::ENABLED; + rStateSet |= AccessibleStateType::SENSITIVE; + } + + if ( IsReallyVisible() ) + rStateSet |= AccessibleStateType::VISIBLE; + + if ( eObjType == vcl::table::AccessibleTableControlObjType::TABLE ) + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + + case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR: + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR: + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + + case vcl::table::AccessibleTableControlObjType::TABLECELL: + { + rStateSet |= AccessibleStateType::FOCUSABLE; + if ( HasChildPathFocus() ) + rStateSet |= AccessibleStateType::FOCUSED; + rStateSet |= AccessibleStateType::ACTIVE; + rStateSet |= AccessibleStateType::TRANSIENT; + rStateSet |= AccessibleStateType::SELECTABLE; + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::SHOWING; + if ( IsRowSelected( GetCurrentRow() ) ) + // Hmm? Wouldn't we expect the affected row to be a parameter to this function? + rStateSet |= AccessibleStateType::SELECTED; + } + break; + + case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL: + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::TRANSIENT; + break; + + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL: + rStateSet |= AccessibleStateType::VISIBLE; + break; + } + } + + void TableControl::commitCellEventIfAccessibleAlive( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( m_pImpl->isAccessibleAlive() ) + m_pImpl->commitCellEvent( i_eventID, i_newValue, i_oldValue ); + } + + void TableControl::commitTableEventIfAccessibleAlive( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( m_pImpl->isAccessibleAlive() ) + m_pImpl->commitTableEvent( i_eventID, i_newValue, i_oldValue ); + } + + AbsoluteScreenPixelRectangle TableControl::GetWindowExtentsAbsolute() const + { + return Control::GetWindowExtentsAbsolute(); + } + + tools::Rectangle TableControl::GetWindowExtentsRelative(const vcl::Window& rRelativeWindow) const + { + return Control::GetWindowExtentsRelative( rRelativeWindow ); + } + + void TableControl::GrabFocus() + { + Control::GrabFocus(); + } + + Reference< XAccessible > TableControl::GetAccessible() + { + return Control::GetAccessible(); + } + + vcl::Window* TableControl::GetAccessibleParentWindow() const + { + return Control::GetAccessibleParentWindow(); + } + + vcl::Window* TableControl::GetWindowInstance() + { + return this; + } + + + bool TableControl::HasRowHeader() + { + return GetModel()->hasRowHeaders(); + } + + + bool TableControl::HasColHeader() + { + return GetModel()->hasColumnHeaders(); + } + + + sal_Int32 TableControl::GetAccessibleControlCount() const + { + // TC_TABLE is always defined, no matter whether empty or not + sal_Int32 count = 1; + if ( GetModel()->hasRowHeaders() ) + ++count; + if ( GetModel()->hasColumnHeaders() ) + ++count; + return count; + } + + + bool TableControl::ConvertPointToControlIndex( sal_Int32& _rnIndex, const Point& _rPoint ) + { + sal_Int32 nRow = m_pImpl->getRowAtPoint( _rPoint ); + sal_Int32 nCol = m_pImpl->getColAtPoint( _rPoint ); + _rnIndex = nRow * GetColumnCount() + nCol; + return nRow >= 0; + } + + + sal_Int32 TableControl::GetRowCount() const + { + return GetModel()->getRowCount(); + } + + + sal_Int32 TableControl::GetColumnCount() const + { + return GetModel()->getColumnCount(); + } + + + bool TableControl::ConvertPointToCellAddress( sal_Int32& _rnRow, sal_Int32& _rnColPos, const Point& _rPoint ) + { + _rnRow = m_pImpl->getRowAtPoint( _rPoint ); + _rnColPos = m_pImpl->getColAtPoint( _rPoint ); + return _rnRow >= 0; + } + + + void TableControl::FillAccessibleStateSetForCell( sal_Int64& _rStateSet, sal_Int32 _nRow, sal_uInt16 ) const + { + if ( IsRowSelected( _nRow ) ) + _rStateSet |= AccessibleStateType::SELECTED; + if ( HasChildPathFocus() ) + _rStateSet |= AccessibleStateType::FOCUSED; + else // only transient when column is not focused + _rStateSet |= AccessibleStateType::TRANSIENT; + + _rStateSet |= AccessibleStateType::VISIBLE; + _rStateSet |= AccessibleStateType::SHOWING; + _rStateSet |= AccessibleStateType::ENABLED; + _rStateSet |= AccessibleStateType::SENSITIVE; + _rStateSet |= AccessibleStateType::ACTIVE; + } + + + tools::Rectangle TableControl::GetFieldCharacterBounds(sal_Int32,sal_Int32,sal_Int32 nIndex) + { + return GetCharacterBounds(nIndex); + } + + + sal_Int32 TableControl::GetFieldIndexAtPoint(sal_Int32,sal_Int32,const Point& _rPoint) + { + return GetIndexForPoint(_rPoint); + } + + + tools::Rectangle TableControl::calcHeaderRect(bool _bIsColumnBar ) + { + return m_pImpl->calcHeaderRect( !_bIsColumnBar ); + } + + + tools::Rectangle TableControl::calcHeaderCellRect( bool _bIsColumnBar, sal_Int32 nPos ) + { + return m_pImpl->calcHeaderCellRect( _bIsColumnBar, nPos ); + } + + + tools::Rectangle TableControl::calcTableRect() + { + return m_pImpl->calcTableRect(); + } + + + tools::Rectangle TableControl::calcCellRect( sal_Int32 _nRowPos, sal_Int32 _nColPos ) + { + return m_pImpl->calcCellRect( _nRowPos, _nColPos ); + } + + + IMPL_LINK_NOARG(TableControl, ImplSelectHdl, LinkParamNone*, void) + { + Select(); + } + + + void TableControl::Select() + { + ImplCallEventListenersAndHandler( VclEventId::TableRowSelect, nullptr ); + + if ( m_pImpl->isAccessibleAlive() ) + { + m_pImpl->commitAccessibleEvent( AccessibleEventId::SELECTION_CHANGED ); + + m_pImpl->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), Any() ); + // TODO: why do we notify this when the *selection* changed? Shouldn't we find a better place for this, + // actually, when the active descendant, i.e. the current cell, *really* changed? + } + } + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablecontrol_impl.cxx b/toolkit/source/controls/table/tablecontrol_impl.cxx new file mode 100644 index 0000000000..99c7b815ca --- /dev/null +++ b/toolkit/source/controls/table/tablecontrol_impl.cxx @@ -0,0 +1,2551 @@ +/* -*- 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 <controls/table/tablecontrol.hxx> +#include <controls/table/defaultinputhandler.hxx> +#include <controls/table/tablemodel.hxx> + +#include "tabledatawindow.hxx" +#include "tablecontrol_impl.hxx" +#include "tablegeometry.hxx" + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> + +#include <comphelper/flagguard.hxx> +#include <vcl/accessiblefactory.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/seleng.hxx> +#include <vcl/settings.hxx> +#include <vcl/image.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> + +#include <cstdlib> +#include <numeric> + +#define MIN_COLUMN_WIDTH_PIXEL 4 + + +namespace svt::table +{ + + + using ::com::sun::star::accessibility::AccessibleTableModelChange; + using ::com::sun::star::uno::Any; + using ::com::sun::star::accessibility::XAccessible; + using ::com::sun::star::uno::Reference; + + namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId; + namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType; + + + //= SuppressCursor + + namespace { + + class SuppressCursor + { + private: + ITableControl& m_rTable; + + public: + explicit SuppressCursor( ITableControl& _rTable ) + :m_rTable( _rTable ) + { + m_rTable.hideCursor(); + } + ~SuppressCursor() + { + m_rTable.showCursor(); + } + }; + + + //= EmptyTableModel + + /** default implementation of an ->ITableModel, used as fallback when no + real model is present + + Instances of this class are static in any way, and provide the least + necessary default functionality for a table model. + */ + class EmptyTableModel : public ITableModel + { + public: + EmptyTableModel() + { + } + + // ITableModel overridables + virtual TableSize getColumnCount() const override + { + return 0; + } + virtual TableSize getRowCount() const override + { + return 0; + } + virtual bool hasColumnHeaders() const override + { + return false; + } + virtual bool hasRowHeaders() const override + { + return false; + } + virtual PColumnModel getColumnModel( ColPos ) override + { + OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" ); + return PColumnModel(); + } + virtual PTableRenderer getRenderer() const override + { + return PTableRenderer(); + } + virtual PTableInputHandler getInputHandler() const override + { + return PTableInputHandler(); + } + virtual TableMetrics getRowHeight() const override + { + return 5 * 100; + } + virtual TableMetrics getColumnHeaderHeight() const override + { + return 0; + } + virtual TableMetrics getRowHeaderWidth() const override + { + return 0; + } + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const override + { + return ScrollbarShowNever; + } + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const override + { + return ScrollbarShowNever; + } + virtual void addTableModelListener( const PTableModelListener& ) override {} + virtual void removeTableModelListener( const PTableModelListener& ) override {} + virtual ::std::optional< ::Color > getLineColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getHeaderBackgroundColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getHeaderTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getActiveSelectionBackColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getActiveSelectionTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getTextLineColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::std::vector< ::Color > > getRowBackgroundColors() const override + { + return ::std::optional< ::std::vector< ::Color > >(); + } + virtual css::style::VerticalAlignment getVerticalAlign() const override + { + return css::style::VerticalAlignment(0); + } + virtual ITableDataSort* getSortAdapter() override + { + return nullptr; + } + virtual bool isEnabled() const override + { + return true; + } + virtual void getCellContent( ColPos const, RowPos const, css::uno::Any& o_cellContent ) override + { + o_cellContent.clear(); + } + virtual void getCellToolTip( ColPos const, RowPos const, css::uno::Any& ) override + { + } + virtual Any getRowHeading( RowPos const ) const override + { + return Any(); + } + }; + + } + + TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl ) + :m_rAntiImpl ( _rAntiImpl ) + ,m_pModel ( std::make_shared<EmptyTableModel>() ) + ,m_pInputHandler ( ) + ,m_nRowHeightPixel ( 15 ) + ,m_nColHeaderHeightPixel( 0 ) + ,m_nRowHeaderWidthPixel ( 0 ) + ,m_nColumnCount ( 0 ) + ,m_nRowCount ( 0 ) + ,m_nCurColumn ( COL_INVALID ) + ,m_nCurRow ( ROW_INVALID ) + ,m_nLeftColumn ( 0 ) + ,m_nTopRow ( 0 ) + ,m_nCursorHidden ( 1 ) + ,m_pDataWindow ( VclPtr<TableDataWindow>::Create( *this ) ) + ,m_pVScroll ( nullptr ) + ,m_pHScroll ( nullptr ) + ,m_pScrollCorner ( nullptr ) + ,m_aSelectedRows ( ) + ,m_pTableFunctionSet ( new TableFunctionSet( this ) ) + ,m_nAnchor ( -1 ) + ,m_bUpdatingColWidths ( false ) + { + m_pSelEngine.reset( new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet.get() ) ); + m_pSelEngine->SetSelectionMode(SelectionMode::Single); + m_pDataWindow->SetPosPixel( Point( 0, 0 ) ); + m_pDataWindow->Show(); + } + + TableControl_Impl::~TableControl_Impl() + { + m_pVScroll.disposeAndClear(); + m_pHScroll.disposeAndClear(); + m_pScrollCorner.disposeAndClear(); + m_pDataWindow.disposeAndClear(); + m_pTableFunctionSet.reset(); + m_pSelEngine.reset(); + } + + void TableControl_Impl::setModel( const PTableModel& _pModel ) + { + SuppressCursor aHideCursor( *this ); + + if ( m_pModel ) + m_pModel->removeTableModelListener( shared_from_this() ); + + m_pModel = _pModel; + if ( !m_pModel) + m_pModel = std::make_shared<EmptyTableModel>(); + + m_pModel->addTableModelListener( shared_from_this() ); + + m_nCurRow = ROW_INVALID; + m_nCurColumn = COL_INVALID; + + // recalc some model-dependent cached info + impl_ni_updateCachedModelValues(); + impl_ni_relayout(); + + // completely invalidate + m_rAntiImpl.Invalidate(); + + // reset cursor to (0,0) + if ( m_nRowCount ) m_nCurRow = 0; + if ( m_nColumnCount ) m_nCurColumn = 0; + } + + + namespace + { + bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset ) + { + bool didChanges = false; + for (auto & selectionIndex : io_selectionIndexes) + { + if ( selectionIndex < i_firstAffectedRowIndex ) + continue; + selectionIndex += i_offset; + didChanges = true; + } + return didChanges; + } + } + + + void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last ) + { + OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" ); + + TableSize const insertedRows = i_last - i_first + 1; + + // adjust selection, if necessary + bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows ); + + // adjust our cached row count + m_nRowCount = m_pModel->getRowCount(); + + // if the rows have been inserted before the current row, adjust this + if ( i_first <= m_nCurRow ) + goTo( m_nCurColumn, m_nCurRow + insertedRows ); + + // relayout, since the scrollbar need might have changed + impl_ni_relayout(); + + // notify A1YY events + if ( impl_isAccessibleAlive() ) + { + impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( AccessibleTableModelChangeType::ROWS_INSERTED, i_first, i_last, -1, -1 ) ) + ); + } + + // schedule repaint + invalidateRowRange( i_first, ROW_INVALID ); + + // call selection handlers, if necessary + if ( selectionChanged ) + m_rAntiImpl.Select(); + } + + + void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last ) + { + sal_Int32 firstRemovedRow = i_first; + sal_Int32 lastRemovedRow = i_last; + + // adjust selection, if necessary + bool selectionChanged = false; + if ( i_first == -1 ) + { + selectionChanged = markAllRowsAsDeselected(); + + firstRemovedRow = 0; + lastRemovedRow = m_nRowCount - 1; + } + else + { + ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" ); + + for ( sal_Int32 row = i_first; row <= i_last; ++row ) + { + if ( markRowAsDeselected( row ) ) + selectionChanged = true; + } + + if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) ) + selectionChanged = true; + } + + // adjust cached row count + m_nRowCount = m_pModel->getRowCount(); + + // adjust the current row, if it is larger than the row count now + if ( m_nCurRow >= m_nRowCount ) + { + if ( m_nRowCount > 0 ) + goTo( m_nCurColumn, m_nRowCount - 1 ); + else + { + m_nCurRow = ROW_INVALID; + m_nTopRow = 0; + } + } + else if ( m_nRowCount == 0 ) + { + m_nTopRow = 0; + } + + + // relayout, since the scrollbar need might have changed + impl_ni_relayout(); + + // notify A11Y events + if ( impl_isAccessibleAlive() ) + { + commitTableEvent( + AccessibleEventId::TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( + AccessibleTableModelChangeType::ROWS_REMOVED, + firstRemovedRow, + lastRemovedRow, + -1, + -1 + ) ), + Any() + ); + } + + // schedule a repaint + invalidateRowRange( firstRemovedRow, ROW_INVALID ); + + // call selection handlers, if necessary + if ( selectionChanged ) + m_rAntiImpl.Select(); + } + + + void TableControl_Impl::columnInserted() + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_relayout(); + + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::columnRemoved() + { + m_nColumnCount = m_pModel->getColumnCount(); + + // adjust the current column, if it is larger than the column count now + if ( m_nCurColumn >= m_nColumnCount ) + { + if ( m_nColumnCount > 0 ) + goTo( m_nCurColumn - 1, m_nCurRow ); + else + m_nCurColumn = COL_INVALID; + } + + impl_ni_relayout(); + + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::allColumnsRemoved() + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_relayout(); + + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::cellsUpdated( RowPos const i_firstRow, RowPos const i_lastRow ) + { + invalidateRowRange( i_firstRow, i_lastRow ); + } + + + void TableControl_Impl::tableMetricsChanged() + { + impl_ni_updateCachedTableMetrics(); + impl_ni_relayout(); + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::impl_invalidateColumn( ColPos const i_column ) + { + tools::Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() ); + + const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column ); + if ( aColumn.isValid() ) + m_rAntiImpl.Invalidate( aColumn.getRect() ); + } + + + void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) + { + ColumnAttributeGroup nGroup( i_attributeGroup ); + if ( nGroup & ColumnAttributeGroup::APPEARANCE ) + { + impl_invalidateColumn( i_column ); + nGroup &= ~ColumnAttributeGroup::APPEARANCE; + } + + if ( nGroup & ColumnAttributeGroup::WIDTH ) + { + if ( !m_bUpdatingColWidths ) + { + impl_ni_relayout( i_column ); + invalidate( TableArea::All ); + } + + nGroup &= ~ColumnAttributeGroup::WIDTH; + } + + OSL_ENSURE( ( nGroup == ColumnAttributeGroup::NONE ) || ( i_attributeGroup == ColumnAttributeGroup::ALL ), + "TableControl_Impl::columnChanged: don't know how to handle this change!" ); + } + + + tools::Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const + { + tools::Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) ); + + // determine the right-most border of the last column which is + // at least partially visible + aArea.SetRight( m_nRowHeaderWidthPixel ); + if ( !m_aColumnWidths.empty() ) + { + // the number of pixels which are scrolled out of the left hand + // side of the window + const tools::Long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd(); + + ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin(); + do + { + aArea.SetRight(loop->getEnd() - nScrolledOutLeft); + ++loop; + } + while ( ( loop != m_aColumnWidths.rend() ) + && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() ) + ); + } + // so far, aArea.Right() denotes the first pixel *after* the cell area + aArea.AdjustRight( -1 ); + + // determine the last row which is at least partially visible + aArea.SetBottom( + m_nColHeaderHeightPixel + + impl_getVisibleRows( true ) * m_nRowHeightPixel + - 1 ); + + return aArea; + } + + + tools::Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const + { + tools::Rectangle aArea( impl_getAllVisibleCellsArea() ); + aArea.SetLeft( m_nRowHeaderWidthPixel ); + aArea.SetTop( m_nColHeaderHeightPixel ); + return aArea; + } + + + void TableControl_Impl::impl_ni_updateCachedTableMetrics() + { + m_nRowHeightPixel = m_rAntiImpl.LogicToPixel(Size(0, m_pModel->getRowHeight()), MapMode(MapUnit::MapAppFont)).Height(); + + m_nColHeaderHeightPixel = 0; + if ( m_pModel->hasColumnHeaders() ) + m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel(Size(0, m_pModel->getColumnHeaderHeight()), MapMode(MapUnit::MapAppFont)).Height(); + + m_nRowHeaderWidthPixel = 0; + if ( m_pModel->hasRowHeaders() ) + m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel(Size(m_pModel->getRowHeaderWidth(), 0), MapMode(MapUnit::MapAppFont)).Width(); + } + + + void TableControl_Impl::impl_ni_updateCachedModelValues() + { + m_pInputHandler = m_pModel->getInputHandler(); + if ( !m_pInputHandler ) + m_pInputHandler = std::make_shared<DefaultInputHandler>(); + + m_nColumnCount = m_pModel->getColumnCount(); + if ( m_nLeftColumn >= m_nColumnCount ) + m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0; + + m_nRowCount = m_pModel->getRowCount(); + if ( m_nTopRow >= m_nRowCount ) + m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0; + + impl_ni_updateCachedTableMetrics(); + } + + + namespace + { + + /// determines whether a scrollbar is needed for the given values + bool lcl_determineScrollbarNeed( tools::Long const i_position, ScrollbarVisibility const i_visibility, + tools::Long const i_availableSpace, tools::Long const i_neededSpace ) + { + if ( i_visibility == ScrollbarShowNever ) + return false; + if ( i_visibility == ScrollbarShowAlways ) + return true; + if ( i_position > 0 ) + return true; + if ( i_availableSpace >= i_neededSpace ) + return false; + return true; + } + + + void lcl_setButtonRepeat( vcl::Window& _rWindow ) + { + AllSettings aSettings = _rWindow.GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + aMouseSettings.SetButtonRepeat( 0 ); + aSettings.SetMouseSettings( aMouseSettings ); + + _rWindow.SetSettings( aSettings, true ); + } + + + bool lcl_updateScrollbar( vcl::Window& _rParent, VclPtr<ScrollBar>& _rpBar, + bool const i_needBar, tools::Long _nVisibleUnits, + tools::Long _nPosition, tools::Long _nRange, + bool _bHorizontal, const Link<ScrollBar*,void>& _rScrollHandler ) + { + // do we currently have the scrollbar? + bool bHaveBar = _rpBar != nullptr; + + // do we need to correct the scrollbar visibility? + if ( bHaveBar && !i_needBar ) + { + if ( _rpBar->IsTracking() ) + _rpBar->EndTracking(); + _rpBar.disposeAndClear(); + } + else if ( !bHaveBar && i_needBar ) + { + _rpBar = VclPtr<ScrollBar>::Create( + + &_rParent, + WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL ) + ); + _rpBar->SetScrollHdl( _rScrollHandler ); + // get some speed into the scrolling... + lcl_setButtonRepeat( *_rpBar ); + } + + if ( _rpBar ) + { + _rpBar->SetRange( Range( 0, _nRange ) ); + _rpBar->SetVisibleSize( _nVisibleUnits ); + _rpBar->SetPageSize( _nVisibleUnits ); + _rpBar->SetLineSize( 1 ); + _rpBar->SetThumbPos( _nPosition ); + _rpBar->Show(); + } + + return ( bHaveBar != i_needBar ); + } + + + /** returns the number of rows fitting into the given range, + for the given row height. Partially fitting rows are counted, too, if the + respective parameter says so. + */ + TableSize lcl_getRowsFittingInto( tools::Long _nOverallHeight, tools::Long _nRowHeightPixel, bool _bAcceptPartialRow ) + { + return _bAcceptPartialRow + ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel + : _nOverallHeight / _nRowHeightPixel; + } + + + /** returns the number of columns fitting into the given area, + with the first visible column as given. Partially fitting columns are counted, too, + if the respective parameter says so. + */ + TableSize lcl_getColumnsVisibleWithin( const tools::Rectangle& _rArea, ColPos _nFirstVisibleColumn, + const TableControl_Impl& _rControl, bool _bAcceptPartialRow ) + { + TableSize visibleColumns = 0; + TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn ); + while ( aColumn.isValid() ) + { + if ( !_bAcceptPartialRow ) + if ( aColumn.getRect().Right() > _rArea.Right() ) + // this column is only partially visible, and this is not allowed + break; + + aColumn.moveRight(); + ++visibleColumns; + } + return visibleColumns; + } + + } + + + tools::Long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding, + bool const i_assumeVerticalScrollbar, ::std::vector< tools::Long >& o_newColWidthsPixel ) const + { + // the available horizontal space + tools::Long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width(); + ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel ); + if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) ) + { + gridWidthPixel -= m_nRowHeaderWidthPixel; + } + + if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) ) + { + tools::Long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + gridWidthPixel -= nScrollbarMetrics; + } + + // no need to do anything without columns + TableSize const colCount = m_pModel->getColumnCount(); + if ( colCount == 0 ) + return gridWidthPixel; + + // collect some meta data for our columns: + // - their current (pixel) metrics + tools::Long accumulatedCurrentWidth = 0; + ::std::vector< tools::Long > currentColWidths; + currentColWidths.reserve( colCount ); + typedef ::std::vector< ::std::pair< tools::Long, long > > ColumnLimits; + ColumnLimits effectiveColumnLimits; + effectiveColumnLimits.reserve( colCount ); + tools::Long accumulatedMinWidth = 0; + tools::Long accumulatedMaxWidth = 0; + // - their relative flexibility + ::std::vector< ::sal_Int32 > columnFlexibilities; + columnFlexibilities.reserve( colCount ); + tools::Long flexibilityDenominator = 0; + size_t flexibleColumnCount = 0; + for ( ColPos col = 0; col < colCount; ++col ) + { + PColumnModel const pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + + // current width + tools::Long const currentWidth = appFontWidthToPixel( pColumn->getWidth() ); + currentColWidths.push_back( currentWidth ); + + // accumulated width + accumulatedCurrentWidth += currentWidth; + + // flexibility + ::sal_Int32 flexibility = pColumn->getFlexibility(); + OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." ); + if ( ( flexibility < 0 ) // normalization + || ( !pColumn->isResizable() ) // column not resizable => no auto-resize + || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respect this + ) + flexibility = 0; + + // min/max width + tools::Long effectiveMin = currentWidth, effectiveMax = currentWidth; + // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then + if ( flexibility > 0 ) + { + tools::Long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() ); + if ( minWidth > 0 ) + effectiveMin = minWidth; + else + effectiveMin = MIN_COLUMN_WIDTH_PIXEL; + + tools::Long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() ); + OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" ); + if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) ) + effectiveMax = maxWidth; + else + effectiveMax = gridWidthPixel; // TODO: any better guess here? + + if ( effectiveMin == effectiveMax ) + // if the min and the max are identical, this implies no flexibility at all + flexibility = 0; + } + + columnFlexibilities.push_back( flexibility ); + flexibilityDenominator += flexibility; + if ( flexibility > 0 ) + ++flexibleColumnCount; + + effectiveColumnLimits.emplace_back( effectiveMin, effectiveMax ); + accumulatedMinWidth += effectiveMin; + accumulatedMaxWidth += effectiveMax; + } + + o_newColWidthsPixel = currentColWidths; + if ( flexibilityDenominator == 0 ) + { + // no column is flexible => don't adjust anything + } + else if ( gridWidthPixel > accumulatedCurrentWidth ) + { // we have space to give away ... + tools::Long distributePixel = gridWidthPixel - accumulatedCurrentWidth; + if ( gridWidthPixel > accumulatedMaxWidth ) + { + // ... but the column's maximal widths are still less than we have + // => set them all to max + for ( svt::table::TableSize i = 0; i < colCount; ++i ) + { + o_newColWidthsPixel[i] = effectiveColumnLimits[i].second; + } + } + else + { + bool startOver = false; + do + { + startOver = false; + // distribute the remaining space amongst all columns with a positive flexibility + for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) + { + tools::Long const columnFlexibility = columnFlexibilities[i]; + if ( columnFlexibility == 0 ) + continue; + + tools::Long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator; + + if ( newColWidth > effectiveColumnLimits[i].second ) + { // that was too much, we hit the col's maximum + // set the new width to exactly this maximum + newColWidth = effectiveColumnLimits[i].second; + // adjust the flexibility denominator ... + flexibilityDenominator -= columnFlexibility; + columnFlexibilities[i] = 0; + --flexibleColumnCount; + // ... and the remaining width ... + tools::Long const difference = newColWidth - currentColWidths[i]; + distributePixel -= difference; + // ... this way, we ensure that the width not taken up by this column is consumed by the other + // flexible ones (if there are some) + + // and start over with the first column, since there might be earlier columns which need + // to be recalculated now + startOver = true; + } + + o_newColWidthsPixel[i] = newColWidth; + } + } + while ( startOver ); + + // are there pixels left (might be caused by rounding errors)? + distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ); + while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) ) + { + // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible + // columns which did not yet reach their maximum. + for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i ) + { + if ( columnFlexibilities[i] == 0 ) + continue; + + OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second, + "TableControl_Impl::impl_ni_calculateColumnWidths: inconsistency!" ); + if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first ) + { + columnFlexibilities[i] = 0; + --flexibleColumnCount; + continue; + } + + ++o_newColWidthsPixel[i]; + --distributePixel; + } + } + } + } + else if ( gridWidthPixel < accumulatedCurrentWidth ) + { // we need to take away some space from the columns which allow it ... + tools::Long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel; + if ( gridWidthPixel < accumulatedMinWidth ) + { + // ... but the column's minimal widths are still more than we have + // => set them all to min + for ( svt::table::TableSize i = 0; i < colCount; ++i ) + { + o_newColWidthsPixel[i] = effectiveColumnLimits[i].first; + } + } + else + { + bool startOver = false; + do + { + startOver = false; + // take away the space we need from the columns with a positive flexibility + for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) + { + tools::Long const columnFlexibility = columnFlexibilities[i]; + if ( columnFlexibility == 0 ) + continue; + + tools::Long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator; + + if ( newColWidth < effectiveColumnLimits[i].first ) + { // that was too much, we hit the col's minimum + // set the new width to exactly this minimum + newColWidth = effectiveColumnLimits[i].first; + // adjust the flexibility denominator ... + flexibilityDenominator -= columnFlexibility; + columnFlexibilities[i] = 0; + --flexibleColumnCount; + // ... and the remaining width ... + tools::Long const difference = currentColWidths[i] - newColWidth; + takeAwayPixel -= difference; + + // and start over with the first column, since there might be earlier columns which need + // to be recalculated now + startOver = true; + } + + o_newColWidthsPixel[i] = newColWidth; + } + } + while ( startOver ); + + // are there pixels left (might be caused by rounding errors)? + takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel; + while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) ) + { + // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible + // columns which did not yet reach their minimum. + for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i ) + { + if ( columnFlexibilities[i] == 0 ) + continue; + + OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first, + "TableControl_Impl::impl_ni_calculateColumnWidths: inconsistency!" ); + if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first ) + { + columnFlexibilities[i] = 0; + --flexibleColumnCount; + continue; + } + + --o_newColWidthsPixel[i]; + --takeAwayPixel; + } + } + } + } + + return gridWidthPixel; + } + + + void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding ) + { + ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" ); + + m_aColumnWidths.resize( 0 ); + if ( !m_pModel ) + return; + + ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true ); + SuppressCursor aHideCursor( *this ); + + // layouting steps: + + // 1. adjust column widths, leaving space for a vertical scrollbar + // 2. determine need for a vertical scrollbar + // - V-YES: all fine, result from 1. is still valid + // - V-NO: result from 1. is still under consideration + + // 3. determine need for a horizontal scrollbar + // - H-NO: all fine, result from 2. is still valid + // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO + // - V-YES: all fine, result from 1. is still valid + // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it + + ::std::vector< tools::Long > newWidthsPixel; + tools::Long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel ); + + // the width/height of a scrollbar, needed several times below + tools::Long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + + // determine the playground for the data cells (excluding headers) + // TODO: what if the control is smaller than needed for the headers/scrollbars? + tools::Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() ); + aDataCellPlayground.SetLeft( m_nRowHeaderWidthPixel ); + aDataCellPlayground.SetTop( m_nColHeaderHeightPixel ); + + OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ), + "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" ); + tools::Long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 ); + + ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility(); + ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility(); + + // do we need a vertical scrollbar? + bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed( + m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); + bool bFirstRoundVScrollNeed = false; + if ( bNeedVerticalScrollbar ) + { + aDataCellPlayground.AdjustRight( -nScrollbarMetrics ); + bFirstRoundVScrollNeed = true; + } + + // do we need a horizontal scrollbar? + bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed( + m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth ); + if ( bNeedHorizontalScrollbar ) + { + aDataCellPlayground.AdjustBottom( -nScrollbarMetrics ); + + // now that we just found that we need a horizontal scrollbar, + // the need for a vertical one may have changed, since the horizontal + // SB might just occupy enough space so that not all rows do fit + // anymore + if ( !bFirstRoundVScrollNeed ) + { + bNeedVerticalScrollbar = lcl_determineScrollbarNeed( + m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); + if ( bNeedVerticalScrollbar ) + { + aDataCellPlayground.AdjustRight( -nScrollbarMetrics ); + } + } + } + + // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now, + // we know that this is not the case, re-calculate the column widths. + if ( !bNeedVerticalScrollbar ) + gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel ); + + // update the column objects with the new widths we finally calculated + TableSize const colCount = m_pModel->getColumnCount(); + m_aColumnWidths.reserve( colCount ); + tools::Long accumulatedWidthPixel = m_nRowHeaderWidthPixel; + bool anyColumnWidthChanged = false; + for ( ColPos col = 0; col < colCount; ++col ) + { + const tools::Long columnStart = accumulatedWidthPixel; + const tools::Long columnEnd = columnStart + newWidthsPixel[col]; + m_aColumnWidths.emplace_back( columnStart, columnEnd ); + accumulatedWidthPixel = columnEnd; + + // and don't forget to forward this to the column models + PColumnModel const pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + + tools::Long const oldColumnWidthAppFont = pColumn->getWidth(); + tools::Long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] ); + pColumn->setWidth( newColumnWidthAppFont ); + + anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont ); + } + + // if the column widths changed, ensure everything is repainted + if ( anyColumnWidthChanged ) + invalidate( TableArea::All ); + + // if the column resizing happened to leave some space at the right, but there are columns + // scrolled out to the left, scroll them in + while ( ( m_nLeftColumn > 0 ) + && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel ) + ) + { + --m_nLeftColumn; + } + + // now adjust the column metrics, since they currently ignore the horizontal scroll position + if ( m_nLeftColumn > 0 ) + { + const tools::Long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart(); + for (auto & columnWidth : m_aColumnWidths) + { + columnWidth.move( offsetPixel ); + } + } + + // show or hide the scrollbars as needed, and position the data window + impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar ); + } + + + void TableControl_Impl::impl_ni_positionChildWindows( tools::Rectangle const & i_dataCellPlayground, + bool const i_verticalScrollbar, bool const i_horizontalScrollbar ) + { + tools::Long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + + // create or destroy the vertical scrollbar, as needed + lcl_updateScrollbar( + m_rAntiImpl, + m_pVScroll, + i_verticalScrollbar, + lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel, false ), + // visible units + m_nTopRow, // current position + m_nRowCount, // range + false, // vertical + LINK( this, TableControl_Impl, OnScroll ) // scroll handler + ); + + // position it + if ( m_pVScroll ) + { + tools::Rectangle aScrollbarArea( + Point( i_dataCellPlayground.Right() + 1, 0 ), + Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 ) + ); + m_pVScroll->SetPosSizePixel( + aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); + } + + // create or destroy the horizontal scrollbar, as needed + lcl_updateScrollbar( + m_rAntiImpl, + m_pHScroll, + i_horizontalScrollbar, + lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ), + // visible units + m_nLeftColumn, // current position + m_nColumnCount, // range + true, // horizontal + LINK( this, TableControl_Impl, OnScroll ) // scroll handler + ); + + // position it + if ( m_pHScroll ) + { + TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ); + TableMetrics const nRange = m_nColumnCount; + if( m_nLeftColumn + nVisibleUnits == nRange - 1 ) + { + if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() ) + { + m_pHScroll->SetVisibleSize( nVisibleUnits -1 ); + m_pHScroll->SetPageSize( nVisibleUnits - 1 ); + } + } + tools::Rectangle aScrollbarArea( + Point( 0, i_dataCellPlayground.Bottom() + 1 ), + Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics ) + ); + m_pHScroll->SetPosSizePixel( + aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); + } + + // the corner window connecting the two scrollbars in the lower right corner + bool bHaveScrollCorner = nullptr != m_pScrollCorner; + bool bNeedScrollCorner = ( nullptr != m_pHScroll ) && ( nullptr != m_pVScroll ); + if ( bHaveScrollCorner && !bNeedScrollCorner ) + { + m_pScrollCorner.disposeAndClear(); + } + else if ( !bHaveScrollCorner && bNeedScrollCorner ) + { + m_pScrollCorner = VclPtr<ScrollBarBox>::Create( &m_rAntiImpl ); + m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) ); + m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); + m_pScrollCorner->Show(); + } + else if(bHaveScrollCorner && bNeedScrollCorner) + { + m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); + m_pScrollCorner->Show(); + } + + // resize the data window + m_pDataWindow->SetSizePixel( Size( + i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel, + i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel + ) ); + } + + + void TableControl_Impl::onResize() + { + impl_ni_relayout(); + checkCursorPosition(); + } + + + void TableControl_Impl::doPaintContent(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rUpdateRect) + { + if (!getModel()) + return; + PTableRenderer pRenderer = getModel()->getRenderer(); + DBG_ASSERT(!!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!"); + if (!pRenderer) + return; + + // our current style settings, to be passed to the renderer + const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings(); + m_nRowCount = m_pModel->getRowCount(); + // the area occupied by all (at least partially) visible cells, including + // headers + tools::Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() ); + + // draw the header column area + if (m_pModel->hasColumnHeaders()) + { + TableRowGeometry const aHeaderRow(*this, tools::Rectangle(Point(0, 0), aAllCellsWithHeaders.BottomRight()), ROW_COL_HEADERS); + tools::Rectangle const aColRect(aHeaderRow.getRect()); + pRenderer->PaintHeaderArea(rRenderContext, aColRect, true, false, rStyle); + // Note that strictly, aHeaderRow.getRect() also contains the intersection between column + // and row header area. However, below we go to paint this intersection, again, + // so this hopefully doesn't hurt if we already paint it here. + + for (TableCellGeometry aCell(aHeaderRow, m_nLeftColumn); aCell.isValid(); aCell.moveRight()) + { + if (_rUpdateRect.GetIntersection(aCell.getRect()).IsEmpty()) + continue; + + pRenderer->PaintColumnHeader(aCell.getColumn(), rRenderContext, aCell.getRect(), rStyle); + } + } + // the area occupied by the row header, if any + tools::Rectangle aRowHeaderArea; + if (m_pModel->hasRowHeaders()) + { + aRowHeaderArea = aAllCellsWithHeaders; + aRowHeaderArea.SetRight( m_nRowHeaderWidthPixel - 1 ); + + TableSize const nVisibleRows = impl_getVisibleRows(true); + TableSize nActualRows = nVisibleRows; + if (m_nTopRow + nActualRows > m_nRowCount) + nActualRows = m_nRowCount - m_nTopRow; + aRowHeaderArea.SetBottom( m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1 ); + + pRenderer->PaintHeaderArea(rRenderContext, aRowHeaderArea, false, true, rStyle); + // Note that strictly, aRowHeaderArea also contains the intersection between column + // and row header area. However, below we go to paint this intersection, again, + // so this hopefully doesn't hurt if we already paint it here. + + if (m_pModel->hasColumnHeaders()) + { + TableCellGeometry const aIntersection(*this, tools::Rectangle(Point(0, 0), aAllCellsWithHeaders.BottomRight()), + COL_ROW_HEADERS, ROW_COL_HEADERS); + tools::Rectangle const aInters(aIntersection.getRect()); + pRenderer->PaintHeaderArea(rRenderContext, aInters, true, true, rStyle); + } + } + + // draw the table content row by row + TableSize colCount = getModel()->getColumnCount(); + + // paint all rows + tools::Rectangle const aAllDataCellsArea(impl_getAllVisibleDataCellArea()); + for (TableRowGeometry aRowIterator(*this, aAllCellsWithHeaders, getTopRow()); aRowIterator.isValid(); aRowIterator.moveDown()) + { + if (_rUpdateRect.GetIntersection(aRowIterator.getRect() ).IsEmpty()) + continue; + + bool const isControlFocused = m_rAntiImpl.HasControlFocus(); + bool const isSelectedRow = isRowSelected(aRowIterator.getRow()); + + tools::Rectangle const aRect = aRowIterator.getRect().GetIntersection(aAllDataCellsArea); + + // give the renderer a chance to prepare the row + pRenderer->PrepareRow(aRowIterator.getRow(), isControlFocused, isSelectedRow, rRenderContext, aRect, rStyle); + + // paint the row header + if (m_pModel->hasRowHeaders()) + { + const tools::Rectangle aCurrentRowHeader(aRowHeaderArea.GetIntersection(aRowIterator.getRect())); + pRenderer->PaintRowHeader(rRenderContext, aCurrentRowHeader, rStyle); + } + + if (!colCount) + continue; + + // paint all cells in this row + for (TableCellGeometry aCell(aRowIterator, m_nLeftColumn); aCell.isValid(); aCell.moveRight()) + { + pRenderer->PaintCell(aCell.getColumn(), isSelectedRow, isControlFocused, + rRenderContext, aCell.getRect(), rStyle); + } + } + } + + void TableControl_Impl::hideCursor() + { + if ( ++m_nCursorHidden == 1 ) + impl_ni_doSwitchCursor( false ); + } + + + void TableControl_Impl::showCursor() + { + DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" ); + if ( --m_nCursorHidden == 0 ) + impl_ni_doSwitchCursor( true ); + } + + + bool TableControl_Impl::dispatchAction( TableControlAction _eAction ) + { + bool bSuccess = false; + bool selectionChanged = false; + + switch ( _eAction ) + { + case cursorDown: + if ( m_pSelEngine->GetSelectionMode() == SelectionMode::Single ) + { + //if other rows already selected, deselect them + if(!m_aSelectedRows.empty()) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + } + if ( m_nCurRow < m_nRowCount-1 ) + { + ++m_nCurRow; + m_aSelectedRows.push_back(m_nCurRow); + } + else + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + ensureVisible(m_nCurColumn,m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + else + { + if ( m_nCurRow < m_nRowCount - 1 ) + bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 ); + } + break; + + case cursorUp: + if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + { + if(!m_aSelectedRows.empty()) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + } + if(m_nCurRow>0) + { + --m_nCurRow; + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + ensureVisible(m_nCurColumn,m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + else + { + if ( m_nCurRow > 0 ) + bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 ); + } + break; + case cursorLeft: + if ( m_nCurColumn > 0 ) + bSuccess = goTo( m_nCurColumn - 1, m_nCurRow ); + else + if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) ) + bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 ); + break; + + case cursorRight: + if ( m_nCurColumn < m_nColumnCount - 1 ) + bSuccess = goTo( m_nCurColumn + 1, m_nCurRow ); + else + if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) ) + bSuccess = goTo( 0, m_nCurRow + 1 ); + break; + + case cursorToLineStart: + bSuccess = goTo( 0, m_nCurRow ); + break; + + case cursorToLineEnd: + bSuccess = goTo( m_nColumnCount - 1, m_nCurRow ); + break; + + case cursorToFirstLine: + bSuccess = goTo( m_nCurColumn, 0 ); + break; + + case cursorToLastLine: + bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 ); + break; + + case cursorPageUp: + { + RowPos nNewRow = ::std::max( RowPos(0), m_nCurRow - impl_getVisibleRows( false ) ); + bSuccess = goTo( m_nCurColumn, nNewRow ); + } + break; + + case cursorPageDown: + { + RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) ); + bSuccess = goTo( m_nCurColumn, nNewRow ); + } + break; + + case cursorTopLeft: + bSuccess = goTo( 0, 0 ); + break; + + case cursorBottomRight: + bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 ); + break; + + case cursorSelectRow: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + return false; + //pos is the position of the current row in the vector of selected rows, if current row is selected + int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + //if current row is selected, it should be deselected, when ALT+SPACE are pressed + if(pos>-1) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+pos); + if(m_aSelectedRows.empty() && m_nAnchor != -1) + m_nAnchor = -1; + } + //else select the row->put it in the vector + else + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + selectionChanged = true; + bSuccess = true; + } + break; + case cursorSelectRowUp: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + return false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + { + //if there are other selected rows, deselect them + return false; + } + else + { + //there are other selected rows + if(!m_aSelectedRows.empty()) + { + //the anchor wasn't set -> a region is not selected, that's why clear all selection + //and select the current row + if(m_nAnchor==-1) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + else + { + //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected + int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1); + if(prevRow>-1) + { + //if m_nCurRow isn't the upper one, can move up, otherwise not + if(m_nCurRow>0) + m_nCurRow--; + else + return true; + //if nextRow already selected, deselect it, otherwise select it + if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); + invalidateRow( m_nCurRow + 1 ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + else + { + if(m_nCurRow>0) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow--; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); + } + } + } + } + else + { + //if nothing is selected and the current row isn't the upper one + //select the current and one row above + //otherwise select only the upper row + if(m_nCurRow>0) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow--; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + m_pSelEngine->SetAnchor(true); + m_nAnchor = m_nCurRow; + ensureVisible(m_nCurColumn, m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + } + break; + case cursorSelectRowDown: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + { + bSuccess = false; + } + else + { + if(!m_aSelectedRows.empty()) + { + //the anchor wasn't set -> a region is not selected, that's why clear all selection + //and select the current row + if(m_nAnchor==-1) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + else + { + //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected + int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1); + if(prevRow>-1) + { + //if m_nCurRow isn't the last one, can move down, otherwise not + if(m_nCurRow<m_nRowCount-1) + m_nCurRow++; + else + return true; + //if next row already selected, deselect it, otherwise select it + if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); + invalidateRow( m_nCurRow - 1 ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + else + { + if(m_nCurRow<m_nRowCount-1) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow++; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); + } + } + } + } + else + { + //there wasn't any selection, select current and row beneath, otherwise only row beneath + if(m_nCurRow<m_nRowCount-1) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow++; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + m_pSelEngine->SetAnchor(true); + m_nAnchor = m_nCurRow; + ensureVisible(m_nCurColumn, m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + } + break; + + case cursorSelectRowAreaTop: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + bSuccess = false; + else + { + //select the region between the current and the upper row + RowPos iter = m_nCurRow; + invalidateSelectedRegion( m_nCurRow, 0 ); + //put the rows in vector + while(iter>=0) + { + if ( !isRowSelected( iter ) ) + m_aSelectedRows.push_back(iter); + --iter; + } + m_nCurRow = 0; + m_nAnchor = m_nCurRow; + m_pSelEngine->SetAnchor(true); + ensureVisible(m_nCurColumn, 0); + selectionChanged = true; + bSuccess = true; + } + } + break; + + case cursorSelectRowAreaBottom: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + return false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + return false; + //select the region between the current and the last row + RowPos iter = m_nCurRow; + invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 ); + //put the rows in the vector + while(iter<=m_nRowCount) + { + if ( !isRowSelected( iter ) ) + m_aSelectedRows.push_back(iter); + ++iter; + } + m_nCurRow = m_nRowCount-1; + m_nAnchor = m_nCurRow; + m_pSelEngine->SetAnchor(true); + ensureVisible(m_nCurColumn, m_nRowCount-1); + selectionChanged = true; + bSuccess = true; + } + break; + default: + OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" ); + break; + } + + if ( bSuccess && selectionChanged ) + { + m_rAntiImpl.Select(); + } + + return bSuccess; + } + + + void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow ) + { + PTableRenderer pRenderer = m_pModel ? m_pModel->getRenderer() : PTableRenderer(); + if ( pRenderer ) + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect ); + if ( _bShow ) + pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect ); + else + pRenderer->HideCellCursor( *m_pDataWindow ); + } + } + + + void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, tools::Rectangle& _rCellRect ) const + { + if ( !m_pModel + || ( COL_INVALID == _nColumn ) + || ( ROW_INVALID == _nRow ) + ) + { + _rCellRect.SetEmpty(); + return; + } + + TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow ); + _rCellRect = aCell.getRect(); + } + + + RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const + { + return impl_getRowForAbscissa( rPoint.Y() ); + } + + + ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const + { + return impl_getColumnForOrdinate( rPoint.X() ); + } + + + TableCell TableControl_Impl::hitTest( Point const & i_point ) const + { + TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) ); + if ( aCell.nColumn > COL_ROW_HEADERS ) + { + PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn ); + MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] ); + if ( ( rColInfo.getEnd() - 3 <= i_point.X() ) + && ( rColInfo.getEnd() >= i_point.X() ) + && pColumn->isResizable() + ) + { + aCell.eArea = ColumnDivider; + } + } + return aCell; + } + + + ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const + { + ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ), + "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() ); + return m_aColumnWidths[ i_column ]; + } + + + PTableModel TableControl_Impl::getModel() const + { + return m_pModel; + } + + + ColPos TableControl_Impl::getCurrentColumn() const + { + return m_nCurColumn; + } + + + RowPos TableControl_Impl::getCurrentRow() const + { + return m_nCurRow; + } + + + ::Size TableControl_Impl::getTableSizePixel() const + { + return m_pDataWindow->GetOutputSizePixel(); + } + + + void TableControl_Impl::setPointer( PointerStyle i_pointer ) + { + m_pDataWindow->SetPointer( i_pointer ); + } + + + void TableControl_Impl::captureMouse() + { + m_pDataWindow->CaptureMouse(); + } + + + void TableControl_Impl::releaseMouse() + { + m_pDataWindow->ReleaseMouse(); + } + + + void TableControl_Impl::invalidate( TableArea const i_what ) + { + switch ( i_what ) + { + case TableArea::ColumnHeaders: + m_pDataWindow->Invalidate( calcHeaderRect( true ) ); + break; + + case TableArea::RowHeaders: + m_pDataWindow->Invalidate( calcHeaderRect( false ) ); + break; + + case TableArea::All: + m_pDataWindow->Invalidate(); + m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent ); + break; + } + } + + + tools::Long TableControl_Impl::pixelWidthToAppFont( tools::Long const i_pixels ) const + { + return m_pDataWindow->PixelToLogic(Size(i_pixels, 0), MapMode(MapUnit::MapAppFont)).Width(); + } + + + tools::Long TableControl_Impl::appFontWidthToPixel( tools::Long const i_appFontUnits ) const + { + return m_pDataWindow->LogicToPixel(Size(i_appFontUnits, 0), MapMode(MapUnit::MapAppFont)).Width(); + } + + + void TableControl_Impl::hideTracking() + { + m_pDataWindow->HideTracking(); + } + + + void TableControl_Impl::showTracking( tools::Rectangle const & i_location, ShowTrackFlags const i_flags ) + { + m_pDataWindow->ShowTracking( i_location, i_flags ); + } + + + void TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row ) + { + goTo( i_col, i_row ); + } + + + void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ) + { + // get the visible area of the table control and set the Left and right border of the region to be repainted + tools::Rectangle const aAllCells( impl_getAllVisibleCellsArea() ); + + tools::Rectangle aInvalidateRect; + aInvalidateRect.SetLeft( aAllCells.Left() ); + aInvalidateRect.SetRight( aAllCells.Right() ); + // if only one row is selected + if ( _nPrevRow == _nCurRow ) + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + aInvalidateRect.SetTop( aCellRect.Top() ); + aInvalidateRect.SetBottom( aCellRect.Bottom() ); + } + //if the region is above the current row + else if(_nPrevRow < _nCurRow ) + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); + aInvalidateRect.SetTop( aCellRect.Top() ); + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + aInvalidateRect.SetBottom( aCellRect.Bottom() ); + } + //if the region is beneath the current row + else + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + aInvalidateRect.SetTop( aCellRect.Top() ); + impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); + aInvalidateRect.SetBottom( aCellRect.Bottom() ); + } + + invalidateRect(aInvalidateRect); + } + + void TableControl_Impl::invalidateRect(const tools::Rectangle &rInvalidateRect) + { + m_pDataWindow->Invalidate( rInvalidateRect, + m_pDataWindow->GetControlBackground().IsTransparent() ? InvalidateFlags::Transparent : InvalidateFlags::NONE ); + } + + + void TableControl_Impl::invalidateSelectedRows() + { + for (auto const& selectedRow : m_aSelectedRows) + { + invalidateRow(selectedRow); + } + } + + + void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ) + { + RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow; + RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1; + RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow; + + tools::Rectangle aInvalidateRect; + + tools::Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() ); + TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true ); + while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) ) + { + aInvalidateRect.Union( aRow.getRect() ); + aRow.moveDown(); + } + + if ( i_lastRow == ROW_INVALID ) + aInvalidateRect.SetBottom( m_pDataWindow->GetOutputSizePixel().Height() ); + + invalidateRect(aInvalidateRect); + } + + + void TableControl_Impl::checkCursorPosition() + { + + TableSize nVisibleRows = impl_getVisibleRows(true); + TableSize nVisibleCols = impl_getVisibleColumns(true); + if ( ( m_nTopRow + nVisibleRows > m_nRowCount ) + && ( m_nRowCount >= nVisibleRows ) + ) + { + --m_nTopRow; + } + else + { + m_nTopRow = 0; + } + + if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount ) + && ( m_nColumnCount >= nVisibleCols ) + ) + { + --m_nLeftColumn; + } + else + { + m_nLeftColumn = 0; + } + + m_pDataWindow->Invalidate(); + } + + + TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const + { + DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" ); + + return lcl_getRowsFittingInto( + m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel, + m_nRowHeightPixel, + _bAcceptPartialRow + ); + } + + + TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const + { + DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" ); + + return lcl_getColumnsVisibleWithin( + tools::Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ), + m_nLeftColumn, + *this, + _bAcceptPartialCol + ); + } + + + bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow ) + { + // TODO: give veto listeners a chance + + if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount ) + || ( _nRow < 0 ) || ( _nRow >= m_nRowCount ) + ) + { + OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" ); + return false; + } + + SuppressCursor aHideCursor( *this ); + m_nCurColumn = _nColumn; + m_nCurRow = _nRow; + + // ensure that the new cell is visible + ensureVisible( m_nCurColumn, m_nCurRow ); + return true; + } + + + void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow ) + { + DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount ) + && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ), + "TableControl_Impl::ensureVisible: invalid coordinates!" ); + + SuppressCursor aHideCursor( *this ); + + if ( _nColumn < m_nLeftColumn ) + impl_scrollColumns( _nColumn - m_nLeftColumn ); + else + { + TableSize nVisibleColumns = impl_getVisibleColumns( false/*bAcceptPartialVisibility*/ ); + if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 ) + { + impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) ); + // TODO: since not all columns have the same width, this might in theory result + // in the column still not being visible. + } + } + + if ( _nRow < m_nTopRow ) + impl_scrollRows( _nRow - m_nTopRow ); + else + { + TableSize nVisibleRows = impl_getVisibleRows( false/*_bAcceptPartialVisibility*/ ); + if ( _nRow > m_nTopRow + nVisibleRows - 1 ) + impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) ); + } + } + + + OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col ) + { + Any aCellValue; + m_pModel->getCellContent( i_col, i_row, aCellValue ); + + OUString sCellStringContent; + m_pModel->getRenderer()->GetFormattedCellString( aCellValue, sCellStringContent ); + + return sCellStringContent; + } + + + TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta ) + { + // compute new top row + RowPos nNewTopRow = + ::std::max( + ::std::min( static_cast<RowPos>( m_nTopRow + _nRowDelta ), static_cast<RowPos>( m_nRowCount - 1 ) ), + RowPos(0) + ); + + RowPos nOldTopRow = m_nTopRow; + m_nTopRow = nNewTopRow; + + // if updates are enabled currently, scroll the viewport + if ( m_nTopRow != nOldTopRow ) + { + SuppressCursor aHideCursor( *this ); + // TODO: call an onStartScroll at our listener (or better an own onStartScroll, + // which hides the cursor and then calls the listener) + // Same for onEndScroll + + // scroll the view port, if possible + tools::Long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow ); + + tools::Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() ); + + if ( m_pDataWindow->GetBackground().IsScrollable() + && std::abs( nPixelDelta ) < aDataArea.GetHeight() + ) + { + m_pDataWindow->Scroll( 0, static_cast<tools::Long>(-nPixelDelta), aDataArea, ScrollFlags::Clip | ScrollFlags::Update | ScrollFlags::Children); + } + else + { + m_pDataWindow->Invalidate( InvalidateFlags::Update ); + m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent ); + } + + // update the position at the vertical scrollbar + if ( m_pVScroll != nullptr ) + m_pVScroll->SetThumbPos( m_nTopRow ); + } + + // The scroll bar availability might change when we scrolled. + // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10. + // Now let + // - the user scroll to row number 6, so the last 5 rows are visible + // - somebody remove the last 4 rows + // - the user scroll to row number 5 being the top row, so the last two rows are visible + // - somebody remove row number 6 + // - the user scroll to row number 1 + // => in this case, the need for the scrollbar vanishes immediately. + if ( m_nTopRow == 0 ) + m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); + + return static_cast<TableSize>( m_nTopRow - nOldTopRow ); + } + + + TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta ) + { + return impl_ni_ScrollRows( i_rowDelta ); + } + + + TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta ) + { + // compute new left column + const ColPos nNewLeftColumn = + ::std::max( + ::std::min( static_cast<ColPos>( m_nLeftColumn + _nColumnDelta ), static_cast<ColPos>( m_nColumnCount - 1 ) ), + ColPos(0) + ); + + const ColPos nOldLeftColumn = m_nLeftColumn; + m_nLeftColumn = nNewLeftColumn; + + // if updates are enabled currently, scroll the viewport + if ( m_nLeftColumn != nOldLeftColumn ) + { + SuppressCursor aHideCursor( *this ); + // TODO: call an onStartScroll at our listener (or better an own onStartScroll, + // which hides the cursor and then calls the listener) + // Same for onEndScroll + + // scroll the view port, if possible + const tools::Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() ); + + tools::Long nPixelDelta = + m_aColumnWidths[ nOldLeftColumn ].getStart() + - m_aColumnWidths[ m_nLeftColumn ].getStart(); + + // update our column positions + // Do this *before* scrolling, as ScrollFlags::Update will trigger a paint, which already needs the correct + // information in m_aColumnWidths + for (auto & columnWidth : m_aColumnWidths) + { + columnWidth.move(nPixelDelta); + } + + // scroll the window content (if supported and possible), or invalidate the complete window + if ( m_pDataWindow->GetBackground().IsScrollable() + && std::abs( nPixelDelta ) < aDataArea.GetWidth() + ) + { + m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, ScrollFlags::Clip | ScrollFlags::Update ); + } + else + { + m_pDataWindow->Invalidate( InvalidateFlags::Update ); + m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent ); + } + + // update the position at the horizontal scrollbar + if ( m_pHScroll != nullptr ) + m_pHScroll->SetThumbPos( m_nLeftColumn ); + } + + // The scroll bar availability might change when we scrolled. This is because we do not hide + // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will + // be auto-hidden when it's scrolled back to pos 0. + if ( m_nLeftColumn == 0 ) + m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); + + return static_cast<TableSize>( m_nLeftColumn - nOldLeftColumn ); + } + + + TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta ) + { + return impl_ni_ScrollColumns( i_columnDelta ); + } + + + SelectionEngine* TableControl_Impl::getSelEngine() + { + return m_pSelEngine.get(); + } + + bool TableControl_Impl::isRowSelected( RowPos i_row ) const + { + return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end(); + } + + + RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const + { + if ( i_selectionIndex < m_aSelectedRows.size() ) + return m_aSelectedRows[ i_selectionIndex ]; + return ROW_INVALID; + } + + + int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current) + { + std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current); + if ( it != selectedRows.end() ) + { + return it - selectedRows.begin(); + } + return -1; + } + + + ColPos TableControl_Impl::impl_getColumnForOrdinate( tools::Long const i_ordinate ) const + { + if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) ) + return COL_INVALID; + + if ( i_ordinate < m_nRowHeaderWidthPixel ) + return COL_ROW_HEADERS; + + ColumnPositions::const_iterator lowerBound = ::std::lower_bound( + m_aColumnWidths.begin(), + m_aColumnWidths.end(), + MutableColumnMetrics(i_ordinate+1, i_ordinate+1), + ColumnInfoPositionLess() + ); + if ( lowerBound == m_aColumnWidths.end() ) + { + // point is *behind* the start of the last column ... + if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() ) + // ... but still before its end + return m_nColumnCount - 1; + return COL_INVALID; + } + return lowerBound - m_aColumnWidths.begin(); + } + + + RowPos TableControl_Impl::impl_getRowForAbscissa( tools::Long const i_abscissa ) const + { + if ( i_abscissa < 0 ) + return ROW_INVALID; + + if ( i_abscissa < m_nColHeaderHeightPixel ) + return ROW_COL_HEADERS; + + tools::Long const abscissa = i_abscissa - m_nColHeaderHeightPixel; + tools::Long const row = m_nTopRow + abscissa / m_nRowHeightPixel; + return row < m_pModel->getRowCount() ? row : ROW_INVALID; + } + + + bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex ) + { + ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex ); + if ( selPos == m_aSelectedRows.end() ) + return false; + + m_aSelectedRows.erase( selPos ); + return true; + } + + + bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex ) + { + if ( isRowSelected( i_rowIndex ) ) + return false; + + SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); + switch ( eSelMode ) + { + case SelectionMode::Single: + if ( !m_aSelectedRows.empty() ) + { + OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" ); + m_aSelectedRows[0] = i_rowIndex; + break; + } + [[fallthrough]]; + + case SelectionMode::Multiple: + m_aSelectedRows.push_back( i_rowIndex ); + break; + + default: + OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" ); + return false; + } + + return true; + } + + + bool TableControl_Impl::markAllRowsAsDeselected() + { + if ( m_aSelectedRows.empty() ) + return false; + + m_aSelectedRows.clear(); + return true; + } + + + bool TableControl_Impl::markAllRowsAsSelected() + { + SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); + ENSURE_OR_RETURN_FALSE( eSelMode == SelectionMode::Multiple, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" ); + + if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) ) + { + #if OSL_DEBUG_LEVEL > 0 + for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row ) + { + OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" ); + } + #endif + // already all rows marked as selected + return false; + } + + m_aSelectedRows.clear(); + for ( RowPos i=0; i < m_pModel->getRowCount(); ++i ) + m_aSelectedRows.push_back(i); + + return true; + } + + + void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID ) + { + impl_commitAccessibleEvent( i_eventID, Any() ); + } + + + void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue ); + } + + + void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue ); + } + + + tools::Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader) + { + tools::Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() ); + Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() ); + if ( bColHeader ) + return tools::Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) ); + else + return tools::Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) ); + } + + + tools::Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ) + { + tools::Rectangle const aHeaderRect = calcHeaderRect( bColHeader ); + TableCellGeometry const aGeometry( + *this, aHeaderRect, + bColHeader ? nPos : COL_ROW_HEADERS, + bColHeader ? ROW_COL_HEADERS : nPos + ); + return aGeometry.getRect(); + } + + + tools::Rectangle TableControl_Impl::calcTableRect() const + { + return impl_getAllVisibleDataCellArea(); + } + + + tools::Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) const + { + tools::Rectangle aCellRect; + impl_getCellRect( nRow, nCol, aCellRect ); + return aCellRect; + } + + + IMPL_LINK_NOARG( TableControl_Impl, OnUpdateScrollbars, void*, void ) + { + // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of + // doing a complete re-layout? + impl_ni_relayout(); + } + + + IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar, void ) + { + DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ), + "TableControl_Impl::OnScroll: where did this come from?" ); + + if ( _pScrollbar == m_pVScroll ) + impl_ni_ScrollRows( _pScrollbar->GetDelta() ); + else + impl_ni_ScrollColumns( _pScrollbar->GetDelta() ); + } + + + rtl::Reference<vcl::table::IAccessibleTableControl> TableControl_Impl::getAccessible( vcl::Window& i_parentWindow ) + { + if (m_pAccessibleTable) + return m_pAccessibleTable; + + DBG_TESTSOLARMUTEX(); + if ( m_pAccessibleTable == nullptr ) + { + Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible(); + if ( xAccParent.is() ) + { + m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl( + xAccParent, m_rAntiImpl + ); + } + } + + return m_pAccessibleTable; + } + + + void TableControl_Impl::disposeAccessible() + { + if ( m_pAccessibleTable ) + m_pAccessibleTable->DisposeAccessImpl(); + m_pAccessibleTable = nullptr; + } + + + bool TableControl_Impl::impl_isAccessibleAlive() const + { + return m_pAccessibleTable && m_pAccessibleTable->isAlive(); + } + + + void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue ) + { + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitEvent( i_eventID, i_newValue ); + } + + + //= TableFunctionSet + + + TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl) + :m_pTableControl( _pTableControl) + ,m_nCurrentRow( ROW_INVALID ) + { + } + + TableFunctionSet::~TableFunctionSet() + { + } + + void TableFunctionSet::BeginDrag() + { + } + + void TableFunctionSet::CreateAnchor() + { + m_pTableControl->setAnchor( m_pTableControl->getCurRow() ); + } + + + void TableFunctionSet::DestroyAnchor() + { + m_pTableControl->setAnchor( ROW_INVALID ); + } + + + void TableFunctionSet::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor) + { + // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click + RowPos newRow = m_pTableControl->getRowAtPoint( rPoint ); + if ( newRow == ROW_COL_HEADERS ) + newRow = m_pTableControl->getTopRow(); + + ColPos newCol = m_pTableControl->getColAtPoint( rPoint ); + if ( newCol == COL_ROW_HEADERS ) + newCol = m_pTableControl->getLeftColumn(); + + if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) ) + return; + + if ( bDontSelectAtCursor ) + { + if ( m_pTableControl->getSelectedRowCount() > 1 ) + m_pTableControl->getSelEngine()->AddAlways(true); + } + else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() ) + { + //selected region lies above the last selection + if( m_pTableControl->getCurRow() >= newRow) + { + //put selected rows in vector + while ( m_pTableControl->getAnchor() >= newRow ) + { + m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); + m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); + } + m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); + } + //selected region lies beneath the last selected row + else + { + while ( m_pTableControl->getAnchor() <= newRow ) + { + m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); + m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); + } + m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); + } + m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow ); + } + //no region selected + else + { + if ( !m_pTableControl->hasRowSelection() ) + m_pTableControl->markRowAsSelected( newRow ); + else + { + if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SelectionMode::Single ) + { + DeselectAll(); + m_pTableControl->markRowAsSelected( newRow ); + } + else + { + m_pTableControl->markRowAsSelected( newRow ); + } + } + if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SelectionMode::Single ) + m_pTableControl->getSelEngine()->AddAlways(true); + + m_pTableControl->invalidateRow( newRow ); + } + m_pTableControl->goTo( newCol, newRow ); + } + + bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint ) + { + m_pTableControl->getSelEngine()->AddAlways(false); + if ( !m_pTableControl->hasRowSelection() ) + return false; + else + { + RowPos curRow = m_pTableControl->getRowAtPoint( rPoint ); + m_pTableControl->setAnchor( ROW_INVALID ); + bool selected = m_pTableControl->isRowSelected( curRow ); + m_nCurrentRow = curRow; + return selected; + } + } + + void TableFunctionSet::DeselectAtPoint( const Point& ) + { + m_pTableControl->invalidateRow( m_nCurrentRow ); + m_pTableControl->markRowAsDeselected( m_nCurrentRow ); + } + + + void TableFunctionSet::DeselectAll() + { + if ( m_pTableControl->hasRowSelection() ) + { + for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i ) + { + RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i); + m_pTableControl->invalidateRow( rowIndex ); + } + + m_pTableControl->markAllRowsAsDeselected(); + } + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablecontrol_impl.hxx b/toolkit/source/controls/table/tablecontrol_impl.hxx new file mode 100644 index 0000000000..a9498958e7 --- /dev/null +++ b/toolkit/source/controls/table/tablecontrol_impl.hxx @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <controls/table/tablemodel.hxx> +#include <controls/table/tablecontrolinterface.hxx> + +#include <vcl/svtaccessiblefactory.hxx> +#include <vcl/accessibletable.hxx> + +#include <vcl/seleng.hxx> + +#include <vector> + +class ScrollBar; +class ScrollBarBox; + +namespace svt::table +{ + struct MutableColumnMetrics : public ColumnMetrics + { + MutableColumnMetrics() + :ColumnMetrics() + { + } + + MutableColumnMetrics( tools::Long const i_startPixel, tools::Long const i_endPixel ) + :ColumnMetrics( i_startPixel, i_endPixel ) + { + } + + tools::Long getStart() const { return nStartPixel; } + tools::Long getEnd() const { return nEndPixel; } + + void move( tools::Long const i_offset ) { nStartPixel += i_offset; nEndPixel += i_offset; } + + tools::Long getWidth() const { return nEndPixel - nStartPixel; } + }; + + struct ColumnInfoPositionLess + { + bool operator()( MutableColumnMetrics const& i_lhs, MutableColumnMetrics const& i_rhs ) + { + return i_lhs.getEnd() < i_rhs.getStart(); + } + }; + + typedef ::std::vector< MutableColumnMetrics > ColumnPositions; + + class TableControl; + class TableDataWindow; + class TableFunctionSet; + + + //= TableControl_Impl + + class TableControl_Impl :public ITableControl + ,public ITableModelListener + { + friend class TableGeometry; + friend class TableRowGeometry; + friend class TableColumnGeometry; + friend class SuspendInvariants; + + private: + /// the control whose impl-instance we implement + TableControl& m_rAntiImpl; + /// the model of the table control + PTableModel m_pModel; + /// the input handler to use, usually the input handler as provided by ->m_pModel + PTableInputHandler m_pInputHandler; + /// info about the widths of our columns + ColumnPositions m_aColumnWidths; + + /// the height of a single row in the table, measured in pixels + tools::Long m_nRowHeightPixel; + /// the height of the column header row in the table, measured in pixels + tools::Long m_nColHeaderHeightPixel; + /// the width of the row header column in the table, measured in pixels + tools::Long m_nRowHeaderWidthPixel; + + /// the number of columns in the table control. Cached model value. + TableSize m_nColumnCount; + + /// the number of rows in the table control. Cached model value. + TableSize m_nRowCount; + + ColPos m_nCurColumn; + RowPos m_nCurRow; + ColPos m_nLeftColumn; + RowPos m_nTopRow; + + sal_Int32 m_nCursorHidden; + + /** the window to contain all data content, including header bars + + The window's upper left corner is at position (0,0), relative to the + table control, which is the direct parent of the data window. + */ + VclPtr<TableDataWindow> m_pDataWindow; + /// the vertical scrollbar, if any + VclPtr<ScrollBar> m_pVScroll; + /// the horizontal scrollbar, if any + VclPtr<ScrollBar> m_pHScroll; + VclPtr<ScrollBarBox> m_pScrollCorner; + //selection engine - for determining selection range, e.g. single, multiple + std::unique_ptr<SelectionEngine> m_pSelEngine; + //vector which contains the selected rows + std::vector<RowPos> m_aSelectedRows; + //part of selection engine + std::unique_ptr<TableFunctionSet> m_pTableFunctionSet; + //part of selection engine + RowPos m_nAnchor; + bool m_bUpdatingColWidths; + + vcl::AccessibleFactoryAccess m_aFactoryAccess; + rtl::Reference<vcl::table::IAccessibleTableControl> m_pAccessibleTable; + + public: + void setModel( const PTableModel& _pModel ); + + const PTableInputHandler& getInputHandler() const { return m_pInputHandler; } + + RowPos getCurRow() const { return m_nCurRow; } + + RowPos getAnchor() const { return m_nAnchor; } + void setAnchor( RowPos const i_anchor ) { m_nAnchor = i_anchor; } + + RowPos getTopRow() const { return m_nTopRow; } + ColPos getLeftColumn() const { return m_nLeftColumn; } + + const TableControl& getAntiImpl() const { return m_rAntiImpl; } + TableControl& getAntiImpl() { return m_rAntiImpl; } + + public: + explicit TableControl_Impl( TableControl& _rAntiImpl ); + virtual ~TableControl_Impl() override; + + /** to be called when the anti-impl instance has been resized + */ + void onResize(); + + /** paints the table control content which intersects with the given rectangle + */ + void doPaintContent(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rUpdateRect); + + /** moves the cursor to the cell with the given coordinates + + To ease the caller's code, the coordinates must not necessarily denote a + valid position. If they don't, <FALSE/> will be returned. + */ + bool goTo( ColPos _nColumn, RowPos _nRow ); + + /** ensures that the given coordinate is visible + @param _nColumn + the column position which should be visible. Must be non-negative, and smaller + than the column count. + @param _nRow + the row position which should be visibleMust be non-negative, and smaller + than the row count. + */ + void ensureVisible( ColPos _nColumn, RowPos _nRow ); + + /** retrieves the content of the given cell, converted to a string + */ + OUString getCellContentAsString( RowPos const i_row, ColPos const i_col ); + + /** returns the position of the current row in the selection vector */ + static int getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current); + + void invalidateRect(const tools::Rectangle &rInvalidateRect); + + /** ??? */ + void invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ); + + /** invalidates the part of the data window which is covered by the given rows + @param i_firstRow + the index of the first row to include in the invalidation + @param i_lastRow + the index of the last row to include in the invalidation, or ROW_INVALID if the invalidation + should happen down to the bottom of the data window. + */ + void invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ); + + /** invalidates the part of the data window which is covered by the given row + */ + void invalidateRow( RowPos const i_row ) { invalidateRowRange( i_row, i_row ); } + + /** invalidates all selected rows + */ + void invalidateSelectedRows(); + + void checkCursorPosition(); + + bool hasRowSelection() const { return !m_aSelectedRows.empty(); } + size_t getSelectedRowCount() const { return m_aSelectedRows.size(); } + RowPos getSelectedRowIndex( size_t const i_selectionIndex ) const; + + /** removes the given row index from m_aSelectedRows + + @return + <TRUE/> if and only if the row was previously marked as selected + */ + bool markRowAsDeselected( RowPos const i_rowIndex ); + + /** marks the given row as selected, by putting it into m_aSelectedRows + @return + <TRUE/> if and only if the row was previously <em>not</em> marked as selected + */ + bool markRowAsSelected( RowPos const i_rowIndex ); + + /** marks all rows as deselected + @return + <TRUE/> if and only if the selection actually changed by this operation + */ + bool markAllRowsAsDeselected(); + + /** marks all rows as selected + @return + <FALSE/> if and only if all rows were selected already. + */ + bool markAllRowsAsSelected(); + + void commitAccessibleEvent( sal_Int16 const i_eventID ); + void commitCellEvent( sal_Int16 const i_eventID, const css::uno::Any& i_newValue, const css::uno::Any& i_oldValue ); + void commitTableEvent( sal_Int16 const i_eventID, const css::uno::Any& i_newValue, const css::uno::Any& i_oldValue ); + + // ITableControl + virtual void hideCursor() override; + virtual void showCursor() override; + virtual bool dispatchAction( TableControlAction _eAction ) override; + virtual SelectionEngine* getSelEngine() override; + virtual PTableModel getModel() const override; + virtual ColPos getCurrentColumn() const override; + virtual RowPos getCurrentRow() const override; + virtual void activateCell( ColPos const i_col, RowPos const i_row ) override; + virtual ::Size getTableSizePixel() const override; + virtual void setPointer( PointerStyle i_pointer ) override; + virtual void captureMouse() override; + virtual void releaseMouse() override; + virtual void invalidate( TableArea const i_what ) override; + virtual tools::Long pixelWidthToAppFont( tools::Long const i_pixels ) const override; + virtual void hideTracking() override; + virtual void showTracking( tools::Rectangle const & i_location, ShowTrackFlags const i_flags ) override; + RowPos getRowAtPoint( const Point& rPoint ) const; + ColPos getColAtPoint( const Point& rPoint ) const; + virtual TableCell hitTest( const Point& rPoint ) const override; + virtual ColumnMetrics getColumnMetrics( ColPos const i_column ) const override; + virtual bool isRowSelected( RowPos i_row ) const override; + + + tools::Long appFontWidthToPixel( tools::Long const i_appFontUnits ) const; + + TableDataWindow& getDataWindow() { return *m_pDataWindow; } + const TableDataWindow& getDataWindow() const { return *m_pDataWindow; } + ScrollBar* getHorzScrollbar() { return m_pHScroll; } + ScrollBar* getVertScrollbar() { return m_pVScroll; } + + tools::Rectangle calcHeaderRect( bool bColHeader ); + tools::Rectangle calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ); + tools::Rectangle calcTableRect() const; + tools::Rectangle calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) const; + + // A11Y + rtl::Reference<vcl::table::IAccessibleTableControl> + getAccessible( vcl::Window& i_parentWindow ); + void disposeAccessible(); + + bool isAccessibleAlive() const { return impl_isAccessibleAlive(); } + + // ITableModelListener + virtual void rowsInserted( RowPos first, RowPos last ) override; + virtual void rowsRemoved( RowPos first, RowPos last ) override; + virtual void columnInserted() override; + virtual void columnRemoved() override; + virtual void allColumnsRemoved() override; + virtual void cellsUpdated( RowPos const i_firstRow, RowPos const i_lastRow ) override; + virtual void columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) override; + virtual void tableMetricsChanged() override; + + private: + bool impl_isAccessibleAlive() const; + void impl_commitAccessibleEvent( + sal_Int16 const i_eventID, + css::uno::Any const & i_newValue + ); + + /** toggles the cursor visibility + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + */ + void impl_ni_doSwitchCursor( bool _bOn ); + + /** returns the number of visible rows. + + @param _bAcceptPartialRow + specifies whether a possible only partially visible last row is + counted, too. + */ + TableSize impl_getVisibleRows( bool _bAcceptPartialRow ) const; + + /** returns the number of visible columns + + The value may change with different horizontal scroll positions, as + different columns have different widths. For instance, if your control is + 100 pixels wide, and has three columns of width 50, 50, 100, respectively, + then this method will return either "2" or "1", depending on which column + is the first visible one. + + @param _bAcceptPartialRow + specifies whether a possible only partially visible last row is + counted, too. + */ + TableSize impl_getVisibleColumns( bool _bAcceptPartialCol ) const; + + /** determines the rectangle occupied by the given cell + */ + void impl_getCellRect( ColPos _nColumn, RowPos _nRow, tools::Rectangle& _rCellRect ) const; + + /** updates all cached model values + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + */ + void impl_ni_updateCachedModelValues(); + + /** updates the cached table metrics (row height etc.) + */ + void impl_ni_updateCachedTableMetrics(); + + /** does a relayout of the table control + + Column widths, and consequently the availability of the vertical and horizontal scrollbar, are updated + with a call to this method. + + @param i_assumeInflexibleColumnsUpToIncluding + the index of a column up to which all columns should be considered as inflexible, or + <code>COL_INVALID</code>. + */ + void impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding = COL_INVALID ); + + /** calculates the new width of our columns, taking into account their min and max widths, and their relative + flexibility. + + @param i_assumeInflexibleColumnsUpToIncluding + the index of a column up to which all columns should be considered as inflexible, or + <code>COL_INVALID</code>. + + @param i_assumeVerticalScrollbar + controls whether or not we should assume the presence of a vertical scrollbar. If <true/>, and + if the model has a VerticalScrollbarVisibility != ScrollbarShowNever, the method will leave + space for a vertical scrollbar. + + @return + the overall width of the grid, which is available for columns + */ + tools::Long impl_ni_calculateColumnWidths( + ColPos const i_assumeInflexibleColumnsUpToIncluding, + bool const i_assumeVerticalScrollbar, + ::std::vector< tools::Long >& o_newColWidthsPixel + ) const; + + /** positions all child windows, e.g. the both scrollbars, the corner window, and the data window + */ + void impl_ni_positionChildWindows( + tools::Rectangle const & i_dataCellPlayground, + bool const i_verticalScrollbar, + bool const i_horizontalScrollbar + ); + + /** scrolls the view by the given number of rows + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + + @return + the number of rows by which the viewport was scrolled. This may differ + from the given numbers to scroll in case the begin or the end of the + row range were reached. + */ + TableSize impl_ni_ScrollRows( TableSize _nRowDelta ); + + /** equivalent to impl_ni_ScrollRows, but checks the instances invariants beforehand (in a non-product build only) + */ + TableSize impl_scrollRows( TableSize const i_rowDelta ); + + /** scrolls the view by the given number of columns + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + + @return + the number of columns by which the viewport was scrolled. This may differ + from the given numbers to scroll in case the begin or the end of the + column range were reached. + */ + TableSize impl_ni_ScrollColumns( TableSize _nColumnDelta ); + + /** equivalent to impl_ni_ScrollColumns, but checks the instances invariants beforehand (in a non-product build only) + */ + TableSize impl_scrollColumns( TableSize const i_columnDelta ); + + /** retrieves the area occupied by the totality of (at least partially) visible cells + + The returned area includes row and column headers. Also, it takes into + account the fact that there might be less columns than would normally + find room in the control. + + As a result of respecting the partial visibility of rows and columns, + the returned area might be larger than the data window's output size. + */ + tools::Rectangle impl_getAllVisibleCellsArea() const; + + /** retrieves the area occupied by all (at least partially) visible data cells. + + Effectively, the returned area is the same as returned by ->impl_getAllVisibleCellsArea, + minus the row and column header areas. + */ + tools::Rectangle impl_getAllVisibleDataCellArea() const; + + /** retrieves the column which covers the given ordinate + */ + ColPos impl_getColumnForOrdinate( tools::Long const i_ordinate ) const; + + /** retrieves the row which covers the given abscissa + */ + RowPos impl_getRowForAbscissa( tools::Long const i_abscissa ) const; + + /// invalidates the window area occupied by the given column + void impl_invalidateColumn( ColPos const i_column ); + + DECL_LINK( OnScroll, ScrollBar*, void ); + DECL_LINK( OnUpdateScrollbars, void*, void ); + }; + + //see seleng.hxx, seleng.cxx, FunctionSet overridables, part of selection engine + class TableFunctionSet : public FunctionSet + { + private: + TableControl_Impl* m_pTableControl; + RowPos m_nCurrentRow; + + public: + explicit TableFunctionSet(TableControl_Impl* _pTableControl); + virtual ~TableFunctionSet() override; + + virtual void BeginDrag() override; + virtual void CreateAnchor() override; + virtual void DestroyAnchor() override; + virtual void SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor = false) override; + virtual bool IsSelectionAtPoint( const Point& rPoint ) override; + virtual void DeselectAtPoint( const Point& rPoint ) override; + virtual void DeselectAll() override; + }; + + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tabledatawindow.cxx b/toolkit/source/controls/table/tabledatawindow.cxx new file mode 100644 index 0000000000..fc49ee08d6 --- /dev/null +++ b/toolkit/source/controls/table/tabledatawindow.cxx @@ -0,0 +1,201 @@ +/* -*- 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 <controls/table/tablecontrol.hxx> + +#include "tabledatawindow.hxx" +#include "tablecontrol_impl.hxx" +#include "tablegeometry.hxx" + +#include <vcl/help.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> + +namespace svt::table +{ + using css::uno::Any; + + TableDataWindow::TableDataWindow( TableControl_Impl& _rTableControl ) + :Window( &_rTableControl.getAntiImpl() ) + ,m_rTableControl( _rTableControl ) + { + // by default, use the background as determined by the style settings + const Color aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() ); + SetBackground( Wallpaper( aWindowColor ) ); + GetOutDev()->SetFillColor( aWindowColor ); + } + + TableDataWindow::~TableDataWindow() + { + disposeOnce(); + } + + void TableDataWindow::dispose() + { + impl_hideTipWindow(); + Window::dispose(); + } + + void TableDataWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rUpdateRect ) + { + m_rTableControl.doPaintContent(rRenderContext, rUpdateRect); + } + + void TableDataWindow::RequestHelp( const HelpEvent& rHEvt ) + { + HelpEventMode const nHelpMode = rHEvt.GetMode(); + if ( IsMouseCaptured() + || !( nHelpMode & HelpEventMode::QUICK ) + ) + { + Window::RequestHelp( rHEvt ); + return; + } + + OUString sHelpText; + QuickHelpFlags nHelpStyle = QuickHelpFlags::NONE; + + Point const aMousePos( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) ); + RowPos const hitRow = m_rTableControl.getRowAtPoint( aMousePos ); + ColPos const hitCol = m_rTableControl.getColAtPoint( aMousePos ); + + PTableModel const pTableModel( m_rTableControl.getModel() ); + if ( ( hitCol >= 0 ) && ( hitCol < pTableModel->getColumnCount() ) ) + { + if ( hitRow == ROW_COL_HEADERS ) + { + sHelpText = pTableModel->getColumnModel( hitCol )->getHelpText(); + } + else if ( ( hitRow >= 0 ) && ( hitRow < pTableModel->getRowCount() ) ) + { + Any aCellToolTip; + pTableModel->getCellToolTip( hitCol, hitRow, aCellToolTip ); + if ( !aCellToolTip.hasValue() ) + { + // use the cell content + pTableModel->getCellContent( hitCol, hitRow, aCellToolTip ); + + // use the cell content as tool tip only if it doesn't fit into the cell. + tools::Rectangle const aWindowRect( Point( 0, 0 ), GetOutputSizePixel() ); + TableCellGeometry const aCell( m_rTableControl, aWindowRect, hitCol, hitRow ); + tools::Rectangle const aCellRect( aCell.getRect() ); + + PTableRenderer const pRenderer = pTableModel->getRenderer(); + if ( pRenderer->FitsIntoCell( aCellToolTip, *GetOutDev(), aCellRect ) ) + aCellToolTip.clear(); + } + + pTableModel->getRenderer()->GetFormattedCellString( aCellToolTip, sHelpText ); + + if ( sHelpText.indexOf( '\n' ) >= 0 ) + nHelpStyle = QuickHelpFlags::TipStyleBalloon; + } + } + + if ( !sHelpText.isEmpty() ) + { + // hide the standard (singleton) help window, so we do not have two help windows open at the same time + Help::HideBalloonAndQuickHelp(); + + tools::Rectangle const aControlScreenRect( + OutputToScreenPixel( Point( 0, 0 ) ), + GetOutputSizePixel() + ); + + Help::ShowQuickHelp(this, aControlScreenRect, sHelpText, nHelpStyle); + } + else + { + impl_hideTipWindow(); + Window::RequestHelp( rHEvt ); + } + } + + void TableDataWindow::impl_hideTipWindow() + { + Help::HideBalloonAndQuickHelp(); + } + + void TableDataWindow::MouseMove( const MouseEvent& rMEvt ) + { + if ( rMEvt.IsLeaveWindow() ) + impl_hideTipWindow(); + + if ( !m_rTableControl.getInputHandler()->MouseMove( m_rTableControl, rMEvt ) ) + { + Window::MouseMove( rMEvt ); + } + } + + void TableDataWindow::MouseButtonDown( const MouseEvent& rMEvt ) + { + impl_hideTipWindow(); + + Point const aPoint = rMEvt.GetPosPixel(); + RowPos const hitRow = m_rTableControl.getRowAtPoint( aPoint ); + bool const wasRowSelected = m_rTableControl.isRowSelected( hitRow ); + size_t const nPrevSelRowCount = m_rTableControl.getSelectedRowCount(); + + if ( !m_rTableControl.getInputHandler()->MouseButtonDown( m_rTableControl, rMEvt ) ) + { + Window::MouseButtonDown( rMEvt ); + return; + } + + bool const isRowSelected = m_rTableControl.isRowSelected( hitRow ); + size_t const nCurSelRowCount = m_rTableControl.getSelectedRowCount(); + if ( isRowSelected != wasRowSelected || nCurSelRowCount != nPrevSelRowCount ) + { + m_aSelectHdl.Call( nullptr ); + } + } + + + void TableDataWindow::MouseButtonUp( const MouseEvent& rMEvt ) + { + if ( !m_rTableControl.getInputHandler()->MouseButtonUp( m_rTableControl, rMEvt ) ) + Window::MouseButtonUp( rMEvt ); + + m_rTableControl.getAntiImpl().GrabFocus(); + } + + + bool TableDataWindow::EventNotify(NotifyEvent& rNEvt ) + { + bool bDone = false; + if ( rNEvt.GetType() == NotifyEventType::COMMAND ) + { + const CommandEvent& rCEvt = *rNEvt.GetCommandEvent(); + if ( rCEvt.GetCommand() == CommandEventId::Wheel ) + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) ) + { + bDone = HandleScrollCommand( rCEvt, m_rTableControl.getHorzScrollbar(), m_rTableControl.getVertScrollbar() ); + } + } + } + return bDone || Window::EventNotify( rNEvt ); + } + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tabledatawindow.hxx b/toolkit/source/controls/table/tabledatawindow.hxx new file mode 100644 index 0000000000..e42a054939 --- /dev/null +++ b/toolkit/source/controls/table/tabledatawindow.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <vcl/window.hxx> + + +namespace svt::table +{ + class TableControl_Impl; + class TableFunctionSet; + + /** the window containing the content area (including headers) of + a table control + */ + class TableDataWindow : public vcl::Window + { + friend class TableFunctionSet; + private: + TableControl_Impl& m_rTableControl; + Link<LinkParamNone*,void> m_aSelectHdl; + + public: + explicit TableDataWindow( TableControl_Impl& _rTableControl ); + virtual ~TableDataWindow() override; + virtual void dispose() override; + + void SetSelectHdl(const Link<LinkParamNone*,void>& rLink) + { + m_aSelectHdl = rLink; + } + + // Window overridables + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void MouseMove( const MouseEvent& rMEvt) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt) override; + virtual bool EventNotify(NotifyEvent& rNEvt) override; + virtual void RequestHelp( const HelpEvent& rHEvt ) override; + + private: + static void impl_hideTipWindow(); + }; + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablegeometry.cxx b/toolkit/source/controls/table/tablegeometry.cxx new file mode 100644 index 0000000000..5b18826c50 --- /dev/null +++ b/toolkit/source/controls/table/tablegeometry.cxx @@ -0,0 +1,154 @@ +/* -*- 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 "tablegeometry.hxx" +#include "tablecontrol_impl.hxx" + + +namespace svt::table +{ + + + //= TableRowGeometry + + + TableRowGeometry::TableRowGeometry( TableControl_Impl const & _rControl, tools::Rectangle const & _rBoundaries, + RowPos const _nRow, bool const i_allowVirtualRows ) + :TableGeometry( _rControl, _rBoundaries ) + ,m_nRowPos( _nRow ) + ,m_bAllowVirtualRows( i_allowVirtualRows ) + { + if ( m_nRowPos == ROW_COL_HEADERS ) + { + m_aRect.SetTop( 0 ); + m_aRect.SetBottom( m_rControl.m_nColHeaderHeightPixel - 1 ); + } + else + { + impl_initRect(); + } + } + + + void TableRowGeometry::impl_initRect() + { + if ( ( m_nRowPos >= m_rControl.m_nTopRow ) && impl_isValidRow( m_nRowPos ) ) + { + m_aRect.SetTop( m_rControl.m_nColHeaderHeightPixel + ( m_nRowPos - m_rControl.m_nTopRow ) * m_rControl.m_nRowHeightPixel ); + m_aRect.SetBottom( m_aRect.Top() + m_rControl.m_nRowHeightPixel - 1 ); + } + else + m_aRect.SetEmpty(); + } + + + bool TableRowGeometry::impl_isValidRow( RowPos const i_row ) const + { + return m_bAllowVirtualRows || ( i_row < m_rControl.m_pModel->getRowCount() ); + } + + + bool TableRowGeometry::moveDown() + { + if ( m_nRowPos == ROW_COL_HEADERS ) + { + m_nRowPos = m_rControl.m_nTopRow; + impl_initRect(); + } + else + { + if ( impl_isValidRow( ++m_nRowPos ) ) + m_aRect.Move( 0, m_rControl.m_nRowHeightPixel ); + else + m_aRect.SetEmpty(); + } + return isValid(); + } + + + //= TableColumnGeometry + + + TableColumnGeometry::TableColumnGeometry( TableControl_Impl const & _rControl, tools::Rectangle const & _rBoundaries, + ColPos const _nCol ) + :TableGeometry( _rControl, _rBoundaries ) + ,m_nColPos( _nCol ) + { + if ( m_nColPos == COL_ROW_HEADERS ) + { + m_aRect.SetLeft( 0 ); + m_aRect.SetRight( m_rControl.m_nRowHeaderWidthPixel - 1 ); + } + else + { + impl_initRect(); + } + } + + + void TableColumnGeometry::impl_initRect() + { + ColPos nLeftColumn = m_rControl.m_nLeftColumn; + if ( ( m_nColPos >= nLeftColumn ) && impl_isValidColumn( m_nColPos ) ) + { + m_aRect.SetLeft( m_rControl.m_nRowHeaderWidthPixel ); + // TODO: take into account any possibly frozen columns + + for ( ColPos col = nLeftColumn; col < m_nColPos; ++col ) + m_aRect.AdjustLeft(m_rControl.m_aColumnWidths[ col ].getWidth() ); + m_aRect.SetRight( m_aRect.Left() + m_rControl.m_aColumnWidths[ m_nColPos ].getWidth() - 1 ); + } + else + m_aRect.SetEmpty(); + } + + + bool TableColumnGeometry::impl_isValidColumn( ColPos const i_column ) const + { + return i_column < ColPos( m_rControl.m_aColumnWidths.size() ); + } + + + bool TableColumnGeometry::moveRight() + { + if ( m_nColPos == COL_ROW_HEADERS ) + { + m_nColPos = m_rControl.m_nLeftColumn; + impl_initRect(); + } + else + { + if ( impl_isValidColumn( ++m_nColPos ) ) + { + m_aRect.SetLeft( m_aRect.Right() + 1 ); + m_aRect.AdjustRight(m_rControl.m_aColumnWidths[ m_nColPos ].getWidth() ); + } + else + m_aRect.SetEmpty(); + } + + return isValid(); + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablegeometry.hxx b/toolkit/source/controls/table/tablegeometry.hxx new file mode 100644 index 0000000000..dde156ffd2 --- /dev/null +++ b/toolkit/source/controls/table/tablegeometry.hxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/table/tabletypes.hxx> +#include <tools/gen.hxx> + +namespace svt::table +{ + + + class TableControl_Impl; + + + //= TableGeometry + + class TableGeometry + { + protected: + const TableControl_Impl& m_rControl; + const tools::Rectangle& m_rBoundaries; + tools::Rectangle m_aRect; + + protected: + TableGeometry( + const TableControl_Impl& _rControl, + const tools::Rectangle& _rBoundaries + ) + :m_rControl( _rControl ) + ,m_rBoundaries( _rBoundaries ) + ,m_aRect( _rBoundaries ) + { + } + + public: + // attribute access + const TableControl_Impl& getControl() const { return m_rControl; } + + // status + const tools::Rectangle& getRect() const { return m_aRect; } + bool isValid() const { return !m_aRect.GetIntersection( m_rBoundaries ).IsEmpty(); } + }; + + + //= TableRowGeometry + + class TableRowGeometry final : public TableGeometry + { + public: + TableRowGeometry( + TableControl_Impl const & _rControl, + tools::Rectangle const & _rBoundaries, + RowPos const _nRow, + bool const i_allowVirtualRows = false + // allow rows >= getRowCount()? + ); + + // status + RowPos getRow() const { return m_nRowPos; } + // operations + bool moveDown(); + + private: + void impl_initRect(); + bool impl_isValidRow( RowPos const i_row ) const; + + RowPos m_nRowPos; + bool m_bAllowVirtualRows; + }; + + + //= TableColumnGeometry + + class TableColumnGeometry final : public TableGeometry + { + public: + TableColumnGeometry( + TableControl_Impl const & _rControl, + tools::Rectangle const & _rBoundaries, + ColPos const _nCol + ); + + // status + ColPos getCol() const { return m_nColPos; } + // operations + bool moveRight(); + + private: + void impl_initRect(); + bool impl_isValidColumn( ColPos const i_column ) const; + + ColPos m_nColPos; + }; + + + //= TableCellGeometry + + /** a helper representing geometry information of a cell + */ + class TableCellGeometry + { + private: + TableRowGeometry m_aRow; + TableColumnGeometry m_aCol; + + public: + TableCellGeometry( + TableControl_Impl const & _rControl, + tools::Rectangle const & _rBoundaries, + ColPos const _nCol, + RowPos const _nRow + ) + :m_aRow( _rControl, _rBoundaries, _nRow, false/*allowVirtualCells*/ ) + ,m_aCol( _rControl, _rBoundaries, _nCol ) + { + } + + TableCellGeometry( + const TableRowGeometry& _rRow, + ColPos _nCol + ) + :m_aRow( _rRow ) + ,m_aCol( _rRow.getControl(), _rRow.getRect(), _nCol ) + { + } + + tools::Rectangle getRect() const { return m_aRow.getRect().GetIntersection( m_aCol.getRect() ); } + ColPos getColumn() const { return m_aCol.getCol(); } + bool isValid() const { return !getRect().IsEmpty(); } + + bool moveRight() {return m_aCol.moveRight(); } + }; + + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tabpagecontainer.cxx b/toolkit/source/controls/tabpagecontainer.cxx new file mode 100644 index 0000000000..367b5c4f22 --- /dev/null +++ b/toolkit/source/controls/tabpagecontainer.cxx @@ -0,0 +1,351 @@ +/* -*- 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 <controls/geometrycontrolmodel.hxx> +#include <controls/tabpagecontainer.hxx> +#include <controls/tabpagemodel.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <o3tl/safeint.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; +using ::com::sun::star::awt::tab::XTabPageModel; + +constexpr OUStringLiteral WRONG_TYPE_EXCEPTION = u"Type must be css::awt::tab::XTabPageModel!"; + + +UnoControlTabPageContainerModel::UnoControlTabPageContainerModel( const Reference< XComponentContext >& i_factory ) + :UnoControlTabPageContainerModel_Base( i_factory ) + ,maContainerListeners( *this ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); +} + +OUString UnoControlTabPageContainerModel::getServiceName() +{ + return "com.sun.star.awt.tab.UnoControlTabPageContainerModel"; +} + +uno::Any UnoControlTabPageContainerModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch(nPropId) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString("com.sun.star.awt.tab.UnoControlTabPageContainer") ); + case BASEPROPERTY_BORDER: + return uno::Any(sal_Int16(0)); // No Border + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } +} + +::cppu::IPropertyArrayHelper& UnoControlTabPageContainerModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} +Reference< css::beans::XPropertySetInfo > UnoControlTabPageContainerModel::getPropertySetInfo( ) +{ + static Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +namespace +{ + Reference< XTabPageModel > lcl_createTabPageModel( Reference<XComponentContext> const & i_context, + Sequence< Any > const & i_initArguments, Reference< XPropertySet > const & i_parentModel ) + { + try + { + Reference< XPropertySetInfo > const xPSI( i_parentModel->getPropertySetInfo() ); + bool const isGeometryControlModel = xPSI.is() && xPSI->hasPropertyByName("PositionX"); + + Reference< XInterface > xInstance; + if ( isGeometryControlModel ) + xInstance = *( new OGeometryControlModel< UnoControlTabPageModel >( i_context ) ); + else + xInstance = *( new UnoControlTabPageModel( i_context ) ); + + Reference< XTabPageModel > const xTabPageModel( xInstance, UNO_QUERY_THROW ); + Reference< XInitialization > const xInit( xTabPageModel, UNO_QUERY_THROW ); + xInit->initialize( i_initArguments ); + + return xTabPageModel; + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + return nullptr; + } +} + +Reference< XTabPageModel > SAL_CALL UnoControlTabPageContainerModel::createTabPage( ::sal_Int16 i_tabPageID ) +{ + Sequence< Any > aInitArgs{ Any(i_tabPageID) }; + return lcl_createTabPageModel( m_xContext, aInitArgs, this ); +} + +Reference< XTabPageModel > SAL_CALL UnoControlTabPageContainerModel::loadTabPage( ::sal_Int16 i_tabPageID, const OUString& i_resourceURL ) +{ + Sequence< Any > aInitArgs{ Any(i_tabPageID), Any(i_resourceURL) }; + return lcl_createTabPageModel( m_xContext, aInitArgs, this ); +} + +void SAL_CALL UnoControlTabPageContainerModel::insertByIndex( ::sal_Int32 nIndex, const css::uno::Any& aElement) +{ + SolarMutexGuard aSolarGuard; + uno::Reference < XTabPageModel > xTabPageModel; + if(!(aElement >>= xTabPageModel)) + throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, getXWeak(), 2 ); + + if ( sal_Int32( m_aTabPageVector.size()) ==nIndex ) + m_aTabPageVector.push_back( xTabPageModel ); + else if ( sal_Int32( m_aTabPageVector.size()) > nIndex ) + { + std::vector< uno::Reference< XTabPageModel > >::iterator aIter = m_aTabPageVector.begin(); + aIter += nIndex; + m_aTabPageVector.insert( aIter, xTabPageModel ); + } + else + throw IndexOutOfBoundsException( OUString(), getXWeak() ); + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= OUString::number(nIndex); + maContainerListeners.elementInserted( aEvent ); + +} + +void SAL_CALL UnoControlTabPageContainerModel::removeByIndex( ::sal_Int32 /*Index*/ ) +{ +} +// XIndexReplace +void SAL_CALL UnoControlTabPageContainerModel::replaceByIndex( ::sal_Int32 /*Index*/, const uno::Any& /*Element*/ ) +{ +} + +// XIndexAccess +::sal_Int32 SAL_CALL UnoControlTabPageContainerModel::getCount( ) +{ + std::unique_lock aGuard( m_aMutex ); + return sal_Int32( m_aTabPageVector.size()); +} + +uno::Any SAL_CALL UnoControlTabPageContainerModel::getByIndex( ::sal_Int32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( nIndex < 0 || o3tl::make_unsigned(nIndex) > m_aTabPageVector.size() ) + throw lang::IndexOutOfBoundsException(); + return uno::Any(m_aTabPageVector[nIndex]); +} + +// XElementAccess +uno::Type SAL_CALL UnoControlTabPageContainerModel::getElementType( ) +{ + return cppu::UnoType<css::awt::XControlModel>::get(); +} + +sal_Bool SAL_CALL UnoControlTabPageContainerModel::hasElements( ) +{ + std::unique_lock aGuard( m_aMutex ); + return !m_aTabPageVector.empty(); +} +// XContainer +void UnoControlTabPageContainerModel::addContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void UnoControlTabPageContainerModel::removeContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + + + +UnoControlTabPageContainer::UnoControlTabPageContainer( const uno::Reference< uno::XComponentContext >& rxContext ) + :UnoControlTabPageContainer_Base(rxContext) + ,m_aTabPageListeners( *this ) +{ +} + +OUString UnoControlTabPageContainer::GetComponentServiceName() const +{ + return "TabPageContainer"; +} + +void SAL_CALL UnoControlTabPageContainer::dispose( ) +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + m_aTabPageListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoControlTabPageContainer::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + if ( m_aTabPageListeners.getLength() ) + xTPContainer->addTabPageContainerListener(&m_aTabPageListeners); +} + + +// XTabPageContainer + +::sal_Int16 SAL_CALL UnoControlTabPageContainer::getActiveTabPageID() +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getActiveTabPageID(); +} +void SAL_CALL UnoControlTabPageContainer::setActiveTabPageID( ::sal_Int16 _activetabpageid ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + xTPContainer->setActiveTabPageID(_activetabpageid); +} +::sal_Int16 SAL_CALL UnoControlTabPageContainer::getTabPageCount( ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getTabPageCount(); +} +sal_Bool SAL_CALL UnoControlTabPageContainer::isTabPageActive( ::sal_Int16 tabPageIndex ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->isTabPageActive(tabPageIndex); +} +Reference< css::awt::tab::XTabPage > SAL_CALL UnoControlTabPageContainer::getTabPage( ::sal_Int16 tabPageIndex ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getTabPage(tabPageIndex); +} +Reference< css::awt::tab::XTabPage > SAL_CALL UnoControlTabPageContainer::getTabPageByID( ::sal_Int16 tabPageID ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getTabPageByID(tabPageID); +} +void SAL_CALL UnoControlTabPageContainer::addTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + m_aTabPageListeners.addInterface( listener ); + if( getPeer().is() && m_aTabPageListeners.getLength() == 1 ) + { + uno::Reference < awt::tab::XTabPageContainer > xTabPageContainer( getPeer(), uno::UNO_QUERY ); + xTabPageContainer->addTabPageContainerListener( &m_aTabPageListeners ); + } +} +void SAL_CALL UnoControlTabPageContainer::removeTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + if( getPeer().is() && m_aTabPageListeners.getLength() == 1 ) + { + uno::Reference < awt::tab::XTabPageContainer > xTabPageContainer( getPeer(), uno::UNO_QUERY ); + xTabPageContainer->removeTabPageContainerListener( &m_aTabPageListeners ); + } + m_aTabPageListeners.removeInterface( listener ); +} + +void UnoControlTabPageContainer::propertiesChange(const::css::uno::Sequence<PropertyChangeEvent> &aEvent) +{ + UnoControlTabPageContainer_Base::propertiesChange(aEvent); + + SolarMutexGuard aSolarGuard; + Reference< XPropertiesChangeListener > xPropertiesChangeListener( getPeer(), UNO_QUERY_THROW ); + return xPropertiesChangeListener->propertiesChange(aEvent); +} + +void UnoControlTabPageContainer::updateFromModel() +{ + UnoControlTabPageContainer_Base::updateFromModel(); + if (!getPeer().is()) + throw RuntimeException("No peer for tabpage container!"); + Reference< XContainerListener > xContainerListener( getPeer(), UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xContainerListener.is(), "UnoListBoxControl::updateFromModel: a peer which is no ItemListListener?!" ); + + ContainerEvent aEvent; + aEvent.Source = getModel(); + const Sequence< Reference< XControl > > aControls = getControls(); + + for ( const Reference< XControl >& rCtrl : aControls ) + { + aEvent.Element <<= rCtrl; + xContainerListener->elementInserted( aEvent ); + } +} +void SAL_CALL UnoControlTabPageContainer::addControl( const OUString& Name, const Reference< css::awt::XControl >& Control ) +{ + SolarMutexGuard aSolarGuard; + ControlContainerBase::addControl(Name,Control); + if (!getPeer().is()) + throw RuntimeException("No peer for tabpage container!"); + Reference< XContainerListener > xContainerListener( getPeer(), UNO_QUERY ); + ContainerEvent aEvent; + aEvent.Source = getModel(); + aEvent.Element <<= Control; + xContainerListener->elementInserted( aEvent ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPageContainerModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPageContainerModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPageContainer_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPageContainer(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tabpagemodel.cxx b/toolkit/source/controls/tabpagemodel.cxx new file mode 100644 index 0000000000..7185f82419 --- /dev/null +++ b/toolkit/source/controls/tabpagemodel.cxx @@ -0,0 +1,295 @@ +/* -*- 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 <controls/tabpagemodel.hxx> + +#include <vcl/svapp.hxx> +#include <helper/property.hxx> +#include <com/sun/star/awt/UnoControlDialogModelProvider.hpp> +#include <com/sun/star/awt/tab/XTabPage.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <tools/debug.hxx> +#include <vcl/outdev.hxx> + +#include <controls/controlmodelcontainerbase.hxx> +#include <controls/unocontrolcontainer.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; + +UnoControlTabPageModel::UnoControlTabPageModel( Reference< XComponentContext > const & i_factory ) + :ControlModelContainerBase( i_factory ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_TITLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_SCROLLWIDTH ); + ImplRegisterProperty( BASEPROPERTY_SCROLLHEIGHT ); + ImplRegisterProperty( BASEPROPERTY_SCROLLTOP ); + ImplRegisterProperty( BASEPROPERTY_SCROLLLEFT ); + ImplRegisterProperty( BASEPROPERTY_IMAGEURL ); +} + +OUString SAL_CALL UnoControlTabPageModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlTabPageModel"; +} + +css::uno::Sequence< OUString > SAL_CALL UnoControlTabPageModel::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > aNames = ControlModelContainerBase::getSupportedServiceNames( ); + aNames.realloc( aNames.getLength() + 1 ); + aNames.getArray()[ aNames.getLength() - 1 ] = "com.sun.star.awt.tab.UnoControlTabPageModel"; + return aNames; +} + +OUString UnoControlTabPageModel::getServiceName( ) +{ + return "com.sun.star.awt.tab.UnoControlTabPageModel"; +} + +Any UnoControlTabPageModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString("com.sun.star.awt.tab.UnoControlTabPage"); + break; + case BASEPROPERTY_USERFORMCONTAINEES: + { + // We do not have here any usercontainers (yet?), but let's return empty container back + // so normal properties could be set without triggering UnknownPropertyException + aAny <<= uno::Reference< XNameContainer >(); + break; + } + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& UnoControlTabPageModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlTabPageModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +////----- XInitialization ------------------------------------------------------------------- +void SAL_CALL UnoControlTabPageModel::initialize (const Sequence<Any>& rArguments) +{ + sal_Int16 nPageId = -1; + if ( rArguments.getLength() == 1 ) + { + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + } + else if ( rArguments.getLength() == 2 ) + { + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + OUString sURL; + if ( !( rArguments[ 1 ] >>= sURL )) + throw lang::IllegalArgumentException(); + Reference<container::XNameContainer > xDialogModel = awt::UnoControlDialogModelProvider::create( m_xContext, sURL ); + if ( xDialogModel.is() ) + { + const Sequence< OUString> aNames = xDialogModel->getElementNames(); + for(const OUString& rName : aNames) + { + try + { + Any aElement(xDialogModel->getByName(rName)); + xDialogModel->removeByName(rName); + insertByName(rName,aElement); + } + catch(const Exception&) {} + } + Reference<XPropertySet> xDialogProp(xDialogModel,UNO_QUERY); + if ( xDialogProp.is() ) + { + static constexpr OUString s_sResourceResolver = u"ResourceResolver"_ustr; + setPropertyValue(s_sResourceResolver,xDialogProp->getPropertyValue(s_sResourceResolver)); + setPropertyValue(GetPropertyName(BASEPROPERTY_TITLE),xDialogProp->getPropertyValue(GetPropertyName(BASEPROPERTY_TITLE))); + setPropertyValue(GetPropertyName(BASEPROPERTY_HELPTEXT),xDialogProp->getPropertyValue(GetPropertyName(BASEPROPERTY_HELPTEXT))); + setPropertyValue(GetPropertyName(BASEPROPERTY_HELPURL),xDialogProp->getPropertyValue(GetPropertyName(BASEPROPERTY_HELPURL))); + } + } + } + else + m_nTabPageId = -1; +} + + +UnoControlTabPage::UnoControlTabPage( const uno::Reference< uno::XComponentContext >& rxContext ) + :UnoControlTabPage_Base(rxContext) + ,m_bWindowListener(false) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} +UnoControlTabPage::~UnoControlTabPage() +{ +} + +OUString UnoControlTabPage::GetComponentServiceName() const +{ + return "TabPageModel"; +} + +OUString SAL_CALL UnoControlTabPage::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlTabPage"; +} + +sal_Bool SAL_CALL UnoControlTabPage::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL UnoControlTabPage::getSupportedServiceNames() +{ + return { "com.sun.star.awt.tab.UnoControlTabPage" }; +} + +void SAL_CALL UnoControlTabPage::disposing( const lang::EventObject& Source ) +{ + ControlContainerBase::disposing( Source ); +} + +void UnoControlTabPage::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aSolarGuard; + ImplUpdateResourceResolver(); + + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); + + Reference < tab::XTabPage > xTabPage( getPeer(), UNO_QUERY ); + if ( xTabPage.is() ) + { + if ( !m_bWindowListener ) + { + Reference< XWindowListener > xWL(this); + addWindowListener( xWL ); + m_bWindowListener = true; + } + } +} + +static ::Size ImplMapPixelToAppFont( OutputDevice const * pOutDev, const ::Size& aSize ) +{ + ::Size aTmp = pOutDev->PixelToLogic(aSize, MapMode(MapUnit::MapAppFont)); + return aTmp; +} +// css::awt::XWindowListener +void SAL_CALL UnoControlTabPage::windowResized( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbSizeModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aAppFontSize( e.Width, e.Height ); + + Reference< XControl > xDialogControl( *this, UNO_QUERY_THROW ); + Reference< XDevice > xDialogDevice( xDialogControl->getPeer(), UNO_QUERY ); + OSL_ENSURE( xDialogDevice.is(), "UnoDialogControl::windowResized: no peer, but a windowResized event?" ); + if ( xDialogDevice.is() ) + { + DeviceInfo aDeviceInfo( xDialogDevice->getInfo() ); + aAppFontSize.AdjustWidth( -(aDeviceInfo.LeftInset + aDeviceInfo.RightInset) ); + aAppFontSize.AdjustHeight( -(aDeviceInfo.TopInset + aDeviceInfo.BottomInset) ); + } + + aAppFontSize = ImplMapPixelToAppFont( pOutDev, aAppFontSize ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbSizeModified = true; + // Properties in a sequence must be sorted! + Sequence< OUString > aProps{ "Height", "Width" }; + Sequence< Any > aValues{ Any(aAppFontSize.Height()), Any(aAppFontSize.Width()) }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbSizeModified = false; + +} + +void SAL_CALL UnoControlTabPage::windowMoved( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbPosModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aTmp( e.X, e.Y ); + aTmp = ImplMapPixelToAppFont( pOutDev, aTmp ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbPosModified = true; + Sequence< OUString > aProps{ "PositionX", "PositionY" }; + Sequence< Any > aValues{ Any(aTmp.Width()), Any(aTmp.Height()) }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbPosModified = false; + +} + +void SAL_CALL UnoControlTabPage::windowShown( const css::lang::EventObject& ) {} + +void SAL_CALL UnoControlTabPage::windowHidden( const css::lang::EventObject& ) {} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPageModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPageModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPage_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPage(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tkscrollbar.cxx b/toolkit/source/controls/tkscrollbar.cxx new file mode 100644 index 0000000000..ff2c8cd623 --- /dev/null +++ b/toolkit/source/controls/tkscrollbar.cxx @@ -0,0 +1,324 @@ +/* -*- 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 <controls/tkscrollbar.hxx> +#include <helper/property.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <toolkit/awt/vclxwindows.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +namespace toolkit +{ + + + using namespace ::com::sun::star; + + + //= UnoControlScrollBarModel + + + UnoControlScrollBarModel::UnoControlScrollBarModel( const uno::Reference< uno::XComponentContext >& i_factory ) + :UnoControlModel( i_factory ) + { + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXScrollBar>(); + } + + + OUString UnoControlScrollBarModel::getServiceName( ) + { + return "stardiv.vcl.controlmodel.ScrollBar"; + } + + OUString UnoControlScrollBarModel::getImplementationName() + { + return "stardiv.Toolkit.UnoControlScrollBarModel"; + } + + css::uno::Sequence<OUString> + UnoControlScrollBarModel::getSupportedServiceNames() + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlScrollBarModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.ScrollBar"; + return s; + } + + uno::Any UnoControlScrollBarModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + switch ( nPropId ) + { + case BASEPROPERTY_LIVE_SCROLL: + return uno::Any( false ); + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.ScrollBar" ) ); + + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } + } + + + ::cppu::IPropertyArrayHelper& UnoControlScrollBarModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + uno::Reference< beans::XPropertySetInfo > UnoControlScrollBarModel::getPropertySetInfo( ) + { + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + //= UnoControlScrollBarModel + + UnoScrollBarControl::UnoScrollBarControl() + :maAdjustmentListeners( *this ) + { + } + + OUString UnoScrollBarControl::GetComponentServiceName() const + { + return "ScrollBar"; + } + + // css::uno::XInterface + uno::Any UnoScrollBarControl::queryAggregation( const uno::Type & rType ) + { + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XAdjustmentListener* >(this), + static_cast< awt::XScrollBar* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); + } + + IMPL_IMPLEMENTATION_ID( UnoScrollBarControl ) + + // css::lang::XTypeProvider + css::uno::Sequence< css::uno::Type > UnoScrollBarControl::getTypes() + { + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XAdjustmentListener>::get(), + cppu::UnoType<awt::XScrollBar>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); + } + + void UnoScrollBarControl::dispose() + { + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maAdjustmentListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); + } + + void UnoScrollBarControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) + { + UnoControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + xScrollBar->addAdjustmentListener( this ); + } + + // css::awt::XAdjustmentListener + void UnoScrollBarControl::adjustmentValueChanged( const css::awt::AdjustmentEvent& rEvent ) + { + switch ( rEvent.Type ) + { + case css::awt::AdjustmentType_ADJUST_LINE: + case css::awt::AdjustmentType_ADJUST_PAGE: + case css::awt::AdjustmentType_ADJUST_ABS: + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + + if ( xScrollBar.is() ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE ), uno::Any(xScrollBar->getValue()), false ); + } + } + break; + default: + { + OSL_FAIL( "UnoScrollBarControl::adjustmentValueChanged - unknown Type" ); + + } + } + + if ( maAdjustmentListeners.getLength() ) + maAdjustmentListeners.adjustmentValueChanged( rEvent ); + } + + // css::awt::XScrollBar + void UnoScrollBarControl::addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) + { + maAdjustmentListeners.addInterface( l ); + } + + void UnoScrollBarControl::removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) + { + maAdjustmentListeners.removeInterface( l ); + } + + void UnoScrollBarControl::setValue( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE ), uno::Any( n ), true ); + } + + void UnoScrollBarControl::setValues( sal_Int32 nValue, sal_Int32 nVisible, sal_Int32 nMax ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE ), uno::Any(nValue), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VISIBLESIZE ), uno::Any(nVisible), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE_MAX ), uno::Any(nMax), true ); + } + + sal_Int32 UnoScrollBarControl::getValue() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getValue(); + } + return n; + } + + void UnoScrollBarControl::setMaximum( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE_MAX ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getMaximum() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getMaximum(); + } + return n; + } + + void UnoScrollBarControl::setLineIncrement( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LINEINCREMENT ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getLineIncrement() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getLineIncrement(); + } + return n; + } + + void UnoScrollBarControl::setBlockIncrement( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_BLOCKINCREMENT ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getBlockIncrement() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getBlockIncrement(); + } + return n; + } + + void UnoScrollBarControl::setVisibleSize( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VISIBLESIZE ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getVisibleSize() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getVisibleSize(); + } + return n; + } + + void UnoScrollBarControl::setOrientation( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ORIENTATION ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getOrientation() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getOrientation(); + } + return n; + } + + OUString UnoScrollBarControl::getImplementationName() + { + return "stardiv.Toolkit.UnoScrollBarControl"; + } + + css::uno::Sequence<OUString> UnoScrollBarControl::getSupportedServiceNames() + { + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlScrollBar"; + ps[s.getLength() - 1] = "stardiv.vcl.control.ScrollBar"; + return s; + } + +} // namespace toolkit + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlScrollBarModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoControlScrollBarModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoScrollBarControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoScrollBarControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tkspinbutton.cxx b/toolkit/source/controls/tkspinbutton.cxx new file mode 100644 index 0000000000..59dad491c5 --- /dev/null +++ b/toolkit/source/controls/tkspinbutton.cxx @@ -0,0 +1,422 @@ +/* -*- 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/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/XSpinValue.hpp> +#include <com/sun/star/awt/XAdjustmentListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase2.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <helper/property.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; + +namespace { + +class UnoSpinButtonModel : public UnoControlModel +{ +protected: + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +public: + explicit UnoSpinButtonModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoSpinButtonModel(const UnoSpinButtonModel & rOther) : UnoControlModel(rOther) {} + + rtl::Reference<UnoControlModel> Clone() const override { return new UnoSpinButtonModel( *this ); } + + // XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + + +//= UnoSpinButtonControl + + +typedef ::cppu::ImplHelper2 < css::awt::XAdjustmentListener + , css::awt::XSpinValue + > UnoSpinButtonControl_Base; + +class UnoSpinButtonControl :public UnoControlBase + ,public UnoSpinButtonControl_Base +{ +private: + AdjustmentListenerMultiplexer maAdjustmentListeners; + +public: + UnoSpinButtonControl(); + OUString GetComponentServiceName() const override; + + DECLARE_UNO3_AGG_DEFAULTS( UnoSpinButtonControl, UnoControlBase ) + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + void SAL_CALL disposing( const css::lang::EventObject& Source ) override { UnoControlBase::disposing( Source ); } + void SAL_CALL dispose( ) override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XAdjustmentListener + void SAL_CALL adjustmentValueChanged( const css::awt::AdjustmentEvent& rEvent ) override; + + // XSpinValue + virtual void SAL_CALL addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& listener ) override; + virtual void SAL_CALL removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& listener ) override; + virtual void SAL_CALL setValue( sal_Int32 value ) override; + virtual void SAL_CALL setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) override; + virtual sal_Int32 SAL_CALL getValue( ) override; + virtual void SAL_CALL setMinimum( sal_Int32 minValue ) override; + virtual void SAL_CALL setMaximum( sal_Int32 maxValue ) override; + virtual sal_Int32 SAL_CALL getMinimum( ) override; + virtual sal_Int32 SAL_CALL getMaximum( ) override; + virtual void SAL_CALL setSpinIncrement( sal_Int32 spinIncrement ) override; + virtual sal_Int32 SAL_CALL getSpinIncrement( ) override; + virtual void SAL_CALL setOrientation( sal_Int32 orientation ) override; + virtual sal_Int32 SAL_CALL getOrientation( ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + + + //= UnoSpinButtonModel + + + UnoSpinButtonModel::UnoSpinButtonModel( 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_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_ORIENTATION ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_REPEAT ); + ImplRegisterProperty( BASEPROPERTY_REPEAT_DELAY ); + ImplRegisterProperty( BASEPROPERTY_SYMBOL_COLOR ); + ImplRegisterProperty( BASEPROPERTY_SPINVALUE ); + ImplRegisterProperty( BASEPROPERTY_SPINVALUE_MIN ); + ImplRegisterProperty( BASEPROPERTY_SPINVALUE_MAX ); + ImplRegisterProperty( BASEPROPERTY_SPININCREMENT ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + } + + + OUString UnoSpinButtonModel::getServiceName( ) + { + return "com.sun.star.awt.UnoControlSpinButtonModel"; + } + + + Any UnoSpinButtonModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return Any( OUString("com.sun.star.awt.UnoControlSpinButton") ); + + case BASEPROPERTY_BORDER: + return Any( sal_Int16(0) ); + + case BASEPROPERTY_REPEAT: + return Any( true ); + + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } + } + + + ::cppu::IPropertyArrayHelper& UnoSpinButtonModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + Reference< XPropertySetInfo > UnoSpinButtonModel::getPropertySetInfo( ) + { + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + OUString SAL_CALL UnoSpinButtonModel::getImplementationName( ) + { + return "stardiv.Toolkit.UnoSpinButtonModel"; + } + + + Sequence< OUString > SAL_CALL UnoSpinButtonModel::getSupportedServiceNames() + { + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlSpinButtonModel" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); + } + + + //= UnoSpinButtonControl + + + UnoSpinButtonControl::UnoSpinButtonControl() + :maAdjustmentListeners( *this ) + { + } + + + OUString UnoSpinButtonControl::GetComponentServiceName() const + { + return "SpinButton"; + } + + + Any UnoSpinButtonControl::queryAggregation( const Type & rType ) + { + Any aRet = UnoControlBase::queryAggregation( rType ); + if ( !aRet.hasValue() ) + aRet = UnoSpinButtonControl_Base::queryInterface( rType ); + return aRet; + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoSpinButtonControl, UnoControlBase, UnoSpinButtonControl_Base ) + + + void UnoSpinButtonControl::dispose() + { + ::osl::ClearableMutexGuard aGuard( GetMutex() ); + if ( maAdjustmentListeners.getLength() ) + { + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + xSpinnable->removeAdjustmentListener( this ); + + EventObject aDisposeEvent; + aDisposeEvent.Source = *this; + + aGuard.clear(); + maAdjustmentListeners.disposeAndClear( aDisposeEvent ); + } + + UnoControl::dispose(); + } + + + OUString SAL_CALL UnoSpinButtonControl::getImplementationName( ) + { + return "stardiv.Toolkit.UnoSpinButtonControl"; + } + + + Sequence< OUString > SAL_CALL UnoSpinButtonControl::getSupportedServiceNames() + { + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlSpinButton" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); + } + + + void UnoSpinButtonControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) + { + UnoControl::createPeer( rxToolkit, rParentPeer ); + + Reference < XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + xSpinnable->addAdjustmentListener( this ); + } + + + void UnoSpinButtonControl::adjustmentValueChanged( const AdjustmentEvent& rEvent ) + { + switch ( rEvent.Type ) + { + case AdjustmentType_ADJUST_LINE: + case AdjustmentType_ADJUST_PAGE: + case AdjustmentType_ADJUST_ABS: + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE ), Any( rEvent.Value ), false ); + break; + default: + OSL_FAIL( "UnoSpinButtonControl::adjustmentValueChanged - unknown Type" ); + } + + if ( maAdjustmentListeners.getLength() ) + { + AdjustmentEvent aEvent( rEvent ); + aEvent.Source = *this; + maAdjustmentListeners.adjustmentValueChanged( aEvent ); + } + } + + + void UnoSpinButtonControl::addAdjustmentListener( const Reference< XAdjustmentListener > & listener ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + maAdjustmentListeners.addInterface( listener ); + } + + + void UnoSpinButtonControl::removeAdjustmentListener( const Reference< XAdjustmentListener > & listener ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + maAdjustmentListeners.removeInterface( listener ); + } + + + void SAL_CALL UnoSpinButtonControl::setValue( sal_Int32 value ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE ), Any( value ), true ); + } + + + void SAL_CALL UnoSpinButtonControl::setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MIN ), Any( minValue ), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MAX ), Any( maxValue ), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE ), Any( currentValue ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getValue( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nValue = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nValue = xSpinnable->getValue(); + + return nValue; + } + + + void SAL_CALL UnoSpinButtonControl::setMinimum( sal_Int32 minValue ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MIN ), Any( minValue ), true ); + } + + + void SAL_CALL UnoSpinButtonControl::setMaximum( sal_Int32 maxValue ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MAX ), Any( maxValue ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getMinimum( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nMin = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nMin = xSpinnable->getMinimum(); + + return nMin; + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getMaximum( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nMax = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nMax = xSpinnable->getMaximum(); + + return nMax; + } + + + void SAL_CALL UnoSpinButtonControl::setSpinIncrement( sal_Int32 spinIncrement ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPININCREMENT ), Any( spinIncrement ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getSpinIncrement( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nIncrement = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nIncrement = xSpinnable->getSpinIncrement(); + + return nIncrement; + } + + + void SAL_CALL UnoSpinButtonControl::setOrientation( sal_Int32 orientation ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ORIENTATION ), Any( orientation ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getOrientation( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nOrientation = ScrollBarOrientation::HORIZONTAL; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nOrientation = xSpinnable->getOrientation(); + + return nOrientation; + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoSpinButtonModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoSpinButtonModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoSpinButtonControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoSpinButtonControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treecontrol.cxx b/toolkit/source/controls/tree/treecontrol.cxx new file mode 100644 index 0000000000..c696220001 --- /dev/null +++ b/toolkit/source/controls/tree/treecontrol.cxx @@ -0,0 +1,524 @@ +/* -*- 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 <helper/property.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/implbase1.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::AggImplInheritanceHelper1< 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 = getXWeak(); + 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 ) +{ + std::unique_lock g(m_aMutex); + ::comphelper::OInterfaceIteratorHelper4 aIt(g, maListeners); + g.unlock(); + 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() ) + { + std::unique_lock g2(m_aMutex); + aIt.remove(g2); + } + } + catch( const RuntimeException& ) + { + DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEditing ) + } + } +} + +void SAL_CALL TreeEditListenerMultiplexer::nodeEdited( const Reference< XTreeNode >& Node, const OUString& NewText ) +{ + std::unique_lock g(m_aMutex); + ::comphelper::OInterfaceIteratorHelper4 aIt(g, maListeners); + g.unlock(); + 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() ) + { + std::unique_lock g2(m_aMutex); + aIt.remove(g2); + } + } + 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 0000000000..357afddb0f --- /dev/null +++ b/toolkit/source/controls/tree/treecontrol.hxx @@ -0,0 +1,69 @@ +/* -*- 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& getInfoHelper() override; + +public: + explicit UnoTreeModel(const css::uno::Reference<css::uno::XComponentContext>& i_factory); + UnoTreeModel(const UnoTreeModel& rOther) + : UnoControlModel(rOther) + { + } + + 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 0000000000..c05650f972 --- /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 <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( getXWeak() ); + EventObject aEvent( xSource ); + maSelectionListeners.selectionChanged( aEvent ); +} + + +void TreeControlPeer::onRequestChildNodes( const Reference< XTreeNode >& xNode ) +{ + try + { + Reference< XInterface > xSource( getXWeak() ); + TreeExpansionEvent aEvent( xSource, xNode ); + maTreeExpansionListeners.requestChildNodes( aEvent ); + } + catch( Exception& ) + { + } +} + + +bool TreeControlPeer::onExpanding( const Reference< XTreeNode >& xNode, bool bExpanding ) +{ + try + { + Reference< XInterface > xSource( getXWeak() ); + 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( getXWeak() ); + 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 0000000000..4471697fb6 --- /dev/null +++ b/toolkit/source/controls/tree/treedatamodel.cxx @@ -0,0 +1,535 @@ +/* -*- 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/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ref.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <mutex> +#include <utility> + +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::WeakImplHelper< XMutableTreeDataModel, XServiceInfo > +{ +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: + void broadcastImpl( std::unique_lock<std::mutex>& rGuard, broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ); + + std::mutex m_aMutex; + comphelper::OInterfaceContainerHelper4<XTreeDataModelListener> maTreeDataModelListeners; + comphelper::OInterfaceContainerHelper4<XEventListener> maEventListeners; + bool mbDisposed; + rtl::Reference< MutableTreeNode > mxRootNode; +}; + +class MutableTreeNode: public ::cppu::WeakImplHelper< XMutableTreeNode, XServiceInfo > +{ + friend class MutableTreeDataModel; + +public: + MutableTreeNode( rtl::Reference< MutableTreeDataModel > xModel, Any aValue, 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 ) +{ + std::unique_lock aGuard(m_aMutex); + broadcastImpl(aGuard, eType, xParentNode, rNode); +} + +void MutableTreeDataModel::broadcastImpl( std::unique_lock<std::mutex>& rGuard, broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ) +{ + if( !maTreeDataModelListeners.getLength(rGuard) ) + return; + + Reference< XInterface > xSource( getXWeak() ); + const Sequence< Reference< XTreeNode > > aNodes { rNode }; + TreeDataModelEvent aEvent( xSource, aNodes, xParentNode ); + + comphelper::OInterfaceIteratorHelper4 aListIter(rGuard, maTreeDataModelListeners); + rGuard.unlock(); + while(aListIter.hasMoreElements()) + { + XTreeDataModelListener* pListener = aListIter.next().get(); + 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(); + + std::unique_lock aGuard( m_aMutex ); + if( xNode.get() == mxRootNode.get() ) + return; + + if( mxRootNode.is() ) + mxRootNode->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; + broadcastImpl( aGuard, structure_changed, xParentNode, mxRootNode ); +} + +Reference< XTreeNode > SAL_CALL MutableTreeDataModel::getRoot( ) +{ + std::unique_lock aGuard( m_aMutex ); + return mxRootNode; +} + +void SAL_CALL MutableTreeDataModel::addTreeDataModelListener( const Reference< XTreeDataModelListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maTreeDataModelListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL MutableTreeDataModel::removeTreeDataModelListener( const Reference< XTreeDataModelListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maTreeDataModelListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL MutableTreeDataModel::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mbDisposed ) + { + mbDisposed = true; + css::lang::EventObject aEvent; + aEvent.Source.set( getXWeak() ); + maTreeDataModelListeners.disposeAndClear( aGuard, aEvent ); + maEventListeners.disposeAndClear( aGuard, aEvent ); + } +} + +void SAL_CALL MutableTreeDataModel::addEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maEventListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL MutableTreeDataModel::removeEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maEventListeners.removeInterface( aGuard, 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( rtl::Reference< MutableTreeDataModel > xModel, Any aValue, bool bChildrenOnDemand ) +: maDisplayValue(std::move( aValue )) +, mbHasChildrenOnDemand( bChildrenOnDemand ) +, mpParent( nullptr ) +, mxModel(std::move( 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: */ diff --git a/toolkit/source/controls/unocontrol.cxx b/toolkit/source/controls/unocontrol.cxx new file mode 100644 index 0000000000..0880455581 --- /dev/null +++ b/toolkit/source/controls/unocontrol.cxx @@ -0,0 +1,1588 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <toolkit/controls/unocontrol.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/mutex.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <helper/property.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <controls/accessiblecontrolcontext.hxx> + +#include <algorithm> +#include <map> +#include <string_view> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; + +using ::com::sun::star::accessibility::XAccessibleContext; +using ::com::sun::star::accessibility::XAccessible; + +namespace { + +struct LanguageDependentProp +{ + const char* pPropName; + sal_Int32 nPropNameLength; +}; + +} + +const LanguageDependentProp aLanguageDependentProp[] = +{ + { "Text", 4 }, + { "Label", 5 }, + { "Title", 5 }, + { "HelpText", 8 }, + { "CurrencySymbol", 14 }, + { "StringItemList", 14 }, + { nullptr, 0 } +}; + +static Sequence< OUString> lcl_ImplGetPropertyNames( const Reference< XMultiPropertySet > & rxModel ) +{ + Sequence< OUString> aNames; + Reference< XPropertySetInfo > xPSInf = rxModel->getPropertySetInfo(); + DBG_ASSERT( xPSInf.is(), "UpdateFromModel: No PropertySetInfo!" ); + if ( xPSInf.is() ) + { + const Sequence< Property> aProps = xPSInf->getProperties(); + sal_Int32 nLen = aProps.getLength(); + aNames = Sequence< OUString>( nLen ); + std::transform(aProps.begin(), aProps.end(), aNames.getArray(), + [](const Property& rProp) -> OUString { return rProp.Name; }); + } + return aNames; +} + +namespace { + +class VclListenerLock +{ +private: + VCLXWindow* m_pLockWindow; + +public: + explicit VclListenerLock( VCLXWindow* _pLockWindow ) + : m_pLockWindow( _pLockWindow ) + { + if ( m_pLockWindow ) + m_pLockWindow->suspendVclEventListening( ); + } + ~VclListenerLock() + { + if ( m_pLockWindow ) + m_pLockWindow->resumeVclEventListening( ); + } + VclListenerLock(const VclListenerLock&) = delete; + VclListenerLock& operator=(const VclListenerLock&) = delete; +}; + +} + +typedef ::std::map< OUString, sal_Int32 > MapString2Int; +struct UnoControl_Data +{ + MapString2Int aSuspendedPropertyNotifications; + /// true if and only if our model has a property ResourceResolver + bool bLocalizationSupport; + + UnoControl_Data() + :bLocalizationSupport( false ) + { + } +}; + +UnoControl::UnoControl() : + maDisposeListeners( *this ) + , maWindowListeners( *this ) + , maFocusListeners( *this ) + , maKeyListeners( *this ) + , maMouseListeners( *this ) + , maMouseMotionListeners( *this ) + , maPaintListeners( *this ) + , maModeChangeListeners( GetMutex() ) + , mpData( new UnoControl_Data ) +{ + mbDisposePeer = true; + mbRefreshingPeer = false; + mbCreatingPeer = false; + mbCreatingCompatiblePeer = false; + mbDesignMode = false; +} + +UnoControl::~UnoControl() +{ +} + +OUString UnoControl::GetComponentServiceName() const +{ + return OUString(); +} + +Reference< XVclWindowPeer > UnoControl::ImplGetCompatiblePeer() +{ + DBG_ASSERT( !mbCreatingCompatiblePeer, "ImplGetCompatiblePeer - recursive?" ); + + mbCreatingCompatiblePeer = true; + + Reference< XVclWindowPeer > xCompatiblePeer = getVclWindowPeer(); + + if ( !xCompatiblePeer.is() ) + { + // Create the pair as invisible + bool bVis = maComponentInfos.bVisible; + if( bVis ) + maComponentInfos.bVisible = false; + + Reference< XVclWindowPeer > xCurrentPeer = getVclWindowPeer(); + setPeer( nullptr ); + + // queryInterface ourself, to allow aggregation + Reference< XControl > xMe; + OWeakAggObject::queryInterface( cppu::UnoType<decltype(xMe)>::get() ) >>= xMe; + + vcl::Window* pParentWindow( nullptr ); + { + SolarMutexGuard aGuard; + auto pDefaultDevice = Application::GetDefaultDevice(); + if (pDefaultDevice) + pParentWindow = pDefaultDevice->GetOwnerWindow(); + ENSURE_OR_THROW( pParentWindow != nullptr, "could obtain a default parent window!" ); + } + try + { + xMe->createPeer( nullptr, pParentWindow->GetComponentInterface() ); + } + catch( const Exception& ) + { + mbCreatingCompatiblePeer = false; + throw; + } + xCompatiblePeer = getVclWindowPeer(); + setPeer( xCurrentPeer ); + + if ( xCompatiblePeer.is() && mxGraphics.is() ) + { + Reference< XView > xPeerView( xCompatiblePeer, UNO_QUERY ); + if ( xPeerView.is() ) + xPeerView->setGraphics( mxGraphics ); + } + + if( bVis ) + maComponentInfos.bVisible = true; + } + + mbCreatingCompatiblePeer = false; + + return xCompatiblePeer; +} + +bool UnoControl::ImplCheckLocalize( OUString& _rPossiblyLocalizable ) +{ + if ( !mpData->bLocalizationSupport + || ( _rPossiblyLocalizable.isEmpty() ) + || ( _rPossiblyLocalizable[0] != '&' ) + // TODO: make this reasonable. At the moment, everything which by accident starts with a & is considered + // localizable, which is probably wrong. + ) + return false; + + try + { + Reference< XPropertySet > xPropSet( mxModel, UNO_QUERY_THROW ); + Reference< resource::XStringResourceResolver > xStringResourceResolver( + xPropSet->getPropertyValue("ResourceResolver"), + UNO_QUERY + ); + if ( xStringResourceResolver.is() ) + { + OUString aLocalizationKey( _rPossiblyLocalizable.copy( 1 ) ); + _rPossiblyLocalizable = xStringResourceResolver->resolveString( aLocalizationKey ); + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + return false; +} + +void UnoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) +{ + // since a change made in propertiesChange, we can't be sure that this is called with a valid getPeer(), + // this assumption may be false in some (seldom) multi-threading scenarios (cause propertiesChange + // releases our mutex before calling here in) + // That's why this additional check + + if ( !mxVclWindowPeer.is() ) + return; + + Any aConvertedValue( rVal ); + + if ( mpData->bLocalizationSupport ) + { + // We now support a mapping for language dependent properties. This is the + // central method to implement it. + if( rPropName == "Text" || + rPropName == "Label" || + rPropName == "Title" || + rPropName == "HelpText" || + rPropName == "CurrencySymbol" || + rPropName == "StringItemList" ) + { + OUString aValue; + uno::Sequence< OUString > aSeqValue; + if ( aConvertedValue >>= aValue ) + { + if ( ImplCheckLocalize( aValue ) ) + aConvertedValue <<= aValue; + } + else if ( aConvertedValue >>= aSeqValue ) + { + for ( auto& rValue : asNonConstRange(aSeqValue) ) + ImplCheckLocalize( rValue ); + aConvertedValue <<= aSeqValue; + } + } + } + + mxVclWindowPeer->setProperty( rPropName, aConvertedValue ); +} + +void UnoControl::PrepareWindowDescriptor( WindowDescriptor& ) +{ +} + +Reference< XWindow > UnoControl::getParentPeer() const +{ + Reference< XWindow > xPeer; + if( mxContext.is() ) + { + Reference< XControl > xContComp( mxContext, UNO_QUERY ); + if ( xContComp.is() ) + { + Reference< XWindowPeer > xP = xContComp->getPeer(); + if ( xP.is() ) + xPeer.set( xP, UNO_QUERY ); + } + } + return xPeer; +} + +void UnoControl::updateFromModel() +{ + // Read default properties and hand over to peer + if( getPeer().is() ) + { + Reference< XMultiPropertySet > xPropSet( mxModel, UNO_QUERY ); + if( xPropSet.is() ) + { + Sequence< OUString> aNames = lcl_ImplGetPropertyNames( xPropSet ); + xPropSet->firePropertiesChangeEvent( aNames, this ); + } + } +} + + +// XTypeProvider +IMPL_IMPLEMENTATION_ID( UnoControl ) + +void +UnoControl::DisposeAccessibleContext(Reference<XComponent> const& xContextComp) +{ + if (xContextComp.is()) + { + try + { + xContextComp->removeEventListener( this ); + xContextComp->dispose(); + } + catch( const Exception& ) + { + OSL_FAIL( "UnoControl::disposeAccessibleContext: could not dispose my AccessibleContext!" ); + } + } +} + +void UnoControl::dispose( ) +{ + Reference< XVclWindowPeer > xPeer; + Reference<XComponent> xAccessibleComp; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if( mbDisposePeer ) + { + xPeer = mxVclWindowPeer; + } + setPeer( nullptr ); + xAccessibleComp.set(maAccessibleContext, UNO_QUERY); + maAccessibleContext.clear(); + } + if( xPeer.is() ) + { + xPeer->dispose(); + } + + // dispose our AccessibleContext - without Mutex locked + DisposeAccessibleContext(xAccessibleComp); + + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< XAggregation* >( this ); + + maDisposeListeners.disposeAndClear( aDisposeEvent ); + maWindowListeners.disposeAndClear( aDisposeEvent ); + maFocusListeners.disposeAndClear( aDisposeEvent ); + maKeyListeners.disposeAndClear( aDisposeEvent ); + maMouseListeners.disposeAndClear( aDisposeEvent ); + maMouseMotionListeners.disposeAndClear( aDisposeEvent ); + maPaintListeners.disposeAndClear( aDisposeEvent ); + maModeChangeListeners.disposeAndClear( aDisposeEvent ); + + // release Model again + setModel( Reference< XControlModel > () ); + setContext( Reference< XInterface > () ); +} + +void UnoControl::addEventListener( const Reference< XEventListener >& rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + maDisposeListeners.addInterface( rxListener ); +} + +void UnoControl::removeEventListener( const Reference< XEventListener >& rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + maDisposeListeners.removeInterface( rxListener ); +} + +bool UnoControl::requiresNewPeer( const OUString& /* _rPropertyName */ ) const +{ + return false; +} + +// XPropertiesChangeListener +void UnoControl::propertiesChange( const Sequence< PropertyChangeEvent >& rEvents ) +{ + Sequence< PropertyChangeEvent > aEvents( rEvents ); + { + ::osl::MutexGuard aGuard( GetMutex() ); + + if ( !mpData->aSuspendedPropertyNotifications.empty() ) + { + // strip the property which we are currently updating (somewhere up the stack) + PropertyChangeEvent* pEvents = aEvents.getArray(); + PropertyChangeEvent* pEventsEnd = pEvents + aEvents.getLength(); + for ( ; pEvents < pEventsEnd; ) + if ( mpData->aSuspendedPropertyNotifications.find( pEvents->PropertyName ) != mpData->aSuspendedPropertyNotifications.end() ) + { + std::copy(pEvents + 1, pEventsEnd, pEvents); + --pEventsEnd; + } + else + ++pEvents; + aEvents.realloc( pEventsEnd - aEvents.getConstArray() ); + + if ( !aEvents.hasElements() ) + return; + } + } + + ImplModelPropertiesChanged( aEvents ); +} + +void UnoControl::ImplLockPropertyChangeNotification( const OUString& rPropertyName, bool bLock ) +{ + MapString2Int::iterator pos = mpData->aSuspendedPropertyNotifications.find( rPropertyName ); + if ( bLock ) + { + if ( pos == mpData->aSuspendedPropertyNotifications.end() ) + pos = mpData->aSuspendedPropertyNotifications.emplace( rPropertyName, 0 ).first; + ++pos->second; + } + else + { + OSL_ENSURE( pos != mpData->aSuspendedPropertyNotifications.end(), "UnoControl::ImplLockPropertyChangeNotification: property not locked!" ); + if ( pos != mpData->aSuspendedPropertyNotifications.end() ) + { + OSL_ENSURE( pos->second > 0, "UnoControl::ImplLockPropertyChangeNotification: invalid suspension counter!" ); + if ( 0 == --pos->second ) + mpData->aSuspendedPropertyNotifications.erase( pos ); + } + } +} + +void UnoControl::ImplLockPropertyChangeNotifications( const Sequence< OUString >& rPropertyNames, bool bLock ) +{ + for ( auto const & propertyName : rPropertyNames ) + ImplLockPropertyChangeNotification( propertyName, bLock ); +} + +void UnoControl::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + ::osl::ClearableGuard< ::osl::Mutex > aGuard( GetMutex() ); + + if( !getPeer().is() ) + return; + + std::vector< PropertyValue > aPeerPropertiesToSet; + sal_Int32 nIndependentPos = 0; + bool bResourceResolverSet( false ); + // position where to insert the independent properties into aPeerPropertiesToSet, + // dependent ones are inserted at the end of the vector + + bool bNeedNewPeer = false; + // some properties require a re-creation of the peer, 'cause they can't be changed on the fly + + Reference< XControlModel > xOwnModel = getModel(); + // our own model for comparison + Reference< XPropertySet > xPS( xOwnModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI = xPS->getPropertySetInfo(); + OSL_ENSURE( xPSI.is(), "UnoControl::ImplModelPropertiesChanged: should have property set meta data!" ); + + sal_Int32 nLen = rEvents.getLength(); + aPeerPropertiesToSet.reserve(nLen); + + for( const PropertyChangeEvent& rEvent : rEvents ) + { + Reference< XControlModel > xModel( rEvent.Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == xOwnModel.get(); + if ( !bOwnModel ) + continue; + + // Detect changes on our resource resolver which invalidates + // automatically some language dependent properties. + if ( rEvent.PropertyName == "ResourceResolver" ) + { + Reference< resource::XStringResourceResolver > xStrResolver; + if ( rEvent.NewValue >>= xStrResolver ) + bResourceResolverSet = xStrResolver.is(); + } + + sal_uInt16 nPType = GetPropertyId( rEvent.PropertyName ); + if ( mbDesignMode && mbDisposePeer && !mbRefreshingPeer && !mbCreatingPeer ) + { + // if we're in design mode, then some properties can change which + // require creating a *new* peer (since these properties cannot + // be switched at existing peers) + if ( nPType ) + bNeedNewPeer = ( nPType == BASEPROPERTY_BORDER ) + || ( nPType == BASEPROPERTY_MULTILINE ) + || ( nPType == BASEPROPERTY_DROPDOWN ) + || ( nPType == BASEPROPERTY_HSCROLL ) + || ( nPType == BASEPROPERTY_VSCROLL ) + || ( nPType == BASEPROPERTY_AUTOHSCROLL ) + || ( nPType == BASEPROPERTY_AUTOVSCROLL ) + || ( nPType == BASEPROPERTY_ORIENTATION ) + || ( nPType == BASEPROPERTY_SPIN ) + || ( nPType == BASEPROPERTY_ALIGN ) + || ( nPType == BASEPROPERTY_PAINTTRANSPARENT ); + else + bNeedNewPeer = requiresNewPeer( rEvent.PropertyName ); + + if ( bNeedNewPeer ) + break; + } + + if ( nPType && ( nLen > 1 ) && DoesDependOnOthers( nPType ) ) + { + // Add properties with dependencies on other properties last + // since they're dependent on properties added later (such as + // VALUE dependency on VALUEMIN/MAX) + aPeerPropertiesToSet.emplace_back(rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE); + } + else + { + if ( bResourceResolverSet ) + { + // The resource resolver property change should be one of the first ones. + // All language dependent properties are dependent on this property. + // As BASEPROPERTY_NATIVE_WIDGET_LOOK is not dependent on resource + // resolver. We don't need to handle a special order for these two props. + aPeerPropertiesToSet.insert( + aPeerPropertiesToSet.begin(), + PropertyValue( rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE ) ); + ++nIndependentPos; + } + else if ( nPType == BASEPROPERTY_NATIVE_WIDGET_LOOK ) + { + // since *a lot* of other properties might be overruled by this one, we need + // a special handling: + // NativeWidgetLook needs to be set first: If it is set to ON, all other + // properties describing the look (e.g. BackgroundColor) are ignored, anyway. + // If it is switched OFF, then we need to do it first because else it will + // overrule other look-related properties, and re-initialize them from system + // defaults. + aPeerPropertiesToSet.insert( + aPeerPropertiesToSet.begin(), + PropertyValue( rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE ) ); + ++nIndependentPos; + } + else + { + aPeerPropertiesToSet.insert(aPeerPropertiesToSet.begin() + nIndependentPos, + PropertyValue(rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE)); + ++nIndependentPos; + } + } + } + + Reference< XWindow > xParent = getParentPeer(); + Reference< XControl > xThis(this); + // call createPeer via an interface got from queryInterface, so the aggregating class can intercept it + + DBG_ASSERT( !bNeedNewPeer || xParent.is(), "Need new peer, but don't have a parent!" ); + + // Check if we have to update language dependent properties + if ( !bNeedNewPeer && bResourceResolverSet ) + { + // Add language dependent properties into the peer property set. + // Our resource resolver has been changed and we must be sure + // that language dependent props use the new resolver. + const LanguageDependentProp* pLangDepProp = aLanguageDependentProp; + while ( pLangDepProp->pPropName != nullptr ) + { + bool bMustBeInserted( true ); + for (const PropertyValue & i : aPeerPropertiesToSet) + { + if ( i.Name.equalsAsciiL( + pLangDepProp->pPropName, pLangDepProp->nPropNameLength )) + { + bMustBeInserted = false; + break; + } + } + + if ( bMustBeInserted ) + { + // Add language dependent props at the end + OUString aPropName( OUString::createFromAscii( pLangDepProp->pPropName )); + if ( xPSI.is() && xPSI->hasPropertyByName( aPropName ) ) + { + aPeerPropertiesToSet.emplace_back( aPropName, 0, xPS->getPropertyValue( aPropName ), PropertyState_DIRECT_VALUE ); + } + } + + ++pLangDepProp; + } + } + aGuard.clear(); + + // clear the guard before creating a new peer - as usual, our peer implementations use the SolarMutex + + if (bNeedNewPeer && xParent.is()) + { + SolarMutexGuard aVclGuard; + // and now this is the final withdrawal: + // I have no other idea than locking the SolarMutex here... + // I really hate the fact that VCL is not threadsafe... + + // Doesn't work for Container! + getPeer()->dispose(); + mxVclWindowPeer.clear(); + mbRefreshingPeer = true; + Reference< XWindowPeer > xP( xParent, UNO_QUERY ); + xThis->createPeer( Reference< XToolkit > (), xP ); + mbRefreshingPeer = false; + aPeerPropertiesToSet.clear(); + } + + // lock the multiplexing of VCL events to our UNO listeners + // this is for compatibility reasons: in OOo 1.0.x, changes which were done at the + // model did not cause the listeners of the controls/peers to be called + // Since the implementations for the listeners changed a lot towards 1.1, this + // would not be the case anymore, if we would not do this listener-lock below + // #i14703# + VCLXWindow* pPeer; + { + SolarMutexGuard g; + VclPtr<vcl::Window> pVclPeer = VCLUnoHelper::GetWindow( getPeer() ); + pPeer = pVclPeer ? pVclPeer->GetWindowPeer() : nullptr; + } + VclListenerLock aNoVclEventMultiplexing( pPeer ); + + // setting peer properties may result in an attempt to acquire the solar mutex, 'cause the peers + // usually don't have an own mutex but use the SolarMutex instead. + // To prevent deadlocks resulting from this, we do this without our own mutex locked + for (const auto& rProp : aPeerPropertiesToSet) + { + ImplSetPeerProperty( rProp.Name, rProp.Value ); + } + +} + +void UnoControl::disposing( const EventObject& rEvt ) +{ + ::osl::ClearableMutexGuard aGuard( GetMutex() ); + // do not compare differing types in case of multiple inheritance + + if ( maAccessibleContext.get() == rEvt.Source ) + { + // just in case the context is disposed, but not released - ensure that we do not re-use it in the future + maAccessibleContext.clear(); + } + else if( mxModel.get() == Reference< XControlModel >(rEvt.Source,UNO_QUERY).get() ) + { + // #62337# if the model dies, it does not make sense for us to live ... + Reference< XControl > xThis = this; + + aGuard.clear(); + xThis->dispose(); + + DBG_ASSERT( !mxModel.is(), "UnoControl::disposing: invalid dispose behaviour!" ); + mxModel.clear(); + } +} + + +void SAL_CALL UnoControl::setOutputSize( const awt::Size& aSize ) +{ + Reference< XWindow2 > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + + if ( xPeerWindow.is() ) + xPeerWindow->setOutputSize( aSize ); +} + +namespace +{ + template < typename RETVALTYPE, typename DEFAULTTYPE > + RETVALTYPE lcl_askPeer( const uno::Reference< awt::XWindowPeer >& _rxPeer, RETVALTYPE (SAL_CALL XWindow2::*_pMethod)(), DEFAULTTYPE _aDefault ) + { + RETVALTYPE aReturn( _aDefault ); + + Reference< XWindow2 > xPeerWindow( _rxPeer, UNO_QUERY ); + if ( xPeerWindow.is() ) + aReturn = (xPeerWindow.get()->*_pMethod)(); + + return aReturn; + } +} + +awt::Size SAL_CALL UnoControl::getOutputSize( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::getOutputSize, awt::Size() ); +} + +sal_Bool SAL_CALL UnoControl::isVisible( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::isVisible, maComponentInfos.bVisible ); +} + +sal_Bool SAL_CALL UnoControl::isActive( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::isActive, false ); +} + +sal_Bool SAL_CALL UnoControl::isEnabled( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::isEnabled, maComponentInfos.bEnable ); +} + +sal_Bool SAL_CALL UnoControl::hasFocus( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::hasFocus, false ); +} + +// XWindow +void UnoControl::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + if ( Flags & awt::PosSize::X ) + maComponentInfos.nX = X; + if ( Flags & awt::PosSize::Y ) + maComponentInfos.nY = Y; + if ( Flags & awt::PosSize::WIDTH ) + maComponentInfos.nWidth = Width; + if ( Flags & awt::PosSize::HEIGHT ) + maComponentInfos.nHeight = Height; + maComponentInfos.nFlags |= Flags; + + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + + if( xWindow.is() ) + xWindow->setPosSize( X, Y, Width, Height, Flags ); +} + +awt::Rectangle UnoControl::getPosSize( ) +{ + awt::Rectangle aRect( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight); + Reference< XWindow > xWindow; + + { + ::osl::MutexGuard aGuard( GetMutex() ); + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + + if( xWindow.is() ) + aRect = xWindow->getPosSize(); + return aRect; +} + +void UnoControl::setVisible( sal_Bool bVisible ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + // Visible status is handled by View + maComponentInfos.bVisible = bVisible; + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xWindow.is() ) + xWindow->setVisible( bVisible ); +} + +void UnoControl::setEnable( sal_Bool bEnable ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + // Enable status is handled by View + maComponentInfos.bEnable = bEnable; + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xWindow.is() ) + xWindow->setEnable( bEnable ); +} + +void UnoControl::setFocus( ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xWindow.is() ) + xWindow->setFocus(); +} + +void UnoControl::addWindowListener( const Reference< XWindowListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maWindowListeners.addInterface( rxListener ); + if ( maWindowListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addWindowListener( &maWindowListeners ); +} + +void UnoControl::removeWindowListener( const Reference< XWindowListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maWindowListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maWindowListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeWindowListener( &maWindowListeners ); +} + +void UnoControl::addFocusListener( const Reference< XFocusListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maFocusListeners.addInterface( rxListener ); + if ( maFocusListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addFocusListener( &maFocusListeners ); +} + +void UnoControl::removeFocusListener( const Reference< XFocusListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maFocusListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maFocusListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeFocusListener( &maFocusListeners ); +} + +void UnoControl::addKeyListener( const Reference< XKeyListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maKeyListeners.addInterface( rxListener ); + if ( maKeyListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addKeyListener( &maKeyListeners); +} + +void UnoControl::removeKeyListener( const Reference< XKeyListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maKeyListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maKeyListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeKeyListener( &maKeyListeners); +} + +void UnoControl::addMouseListener( const Reference< XMouseListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maMouseListeners.addInterface( rxListener ); + if ( maMouseListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addMouseListener( &maMouseListeners); +} + +void UnoControl::removeMouseListener( const Reference< XMouseListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maMouseListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maMouseListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeMouseListener( &maMouseListeners ); +} + +void UnoControl::addMouseMotionListener( const Reference< XMouseMotionListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maMouseMotionListeners.addInterface( rxListener ); + if ( maMouseMotionListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addMouseMotionListener( &maMouseMotionListeners); +} + +void UnoControl::removeMouseMotionListener( const Reference< XMouseMotionListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maMouseMotionListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maMouseMotionListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeMouseMotionListener( &maMouseMotionListeners ); +} + +void UnoControl::addPaintListener( const Reference< XPaintListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maPaintListeners.addInterface( rxListener ); + if ( maPaintListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addPaintListener( &maPaintListeners); +} + +void UnoControl::removePaintListener( const Reference< XPaintListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maPaintListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maPaintListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removePaintListener( &maPaintListeners ); +} + +// XView +sal_Bool UnoControl::setGraphics( const Reference< XGraphics >& rDevice ) +{ + Reference< XView > xView; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + mxGraphics = rDevice; + xView.set(getPeer(), css::uno::UNO_QUERY); + } + return !xView.is() || xView->setGraphics( rDevice ); +} + +Reference< XGraphics > UnoControl::getGraphics( ) +{ + return mxGraphics; +} + +awt::Size UnoControl::getSize( ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + return awt::Size( maComponentInfos.nWidth, maComponentInfos.nHeight ); +} + +void UnoControl::draw( sal_Int32 x, sal_Int32 y ) +{ + Reference< XWindowPeer > xDrawPeer; + Reference< XView > xDrawPeerView; + + bool bDisposeDrawPeer( false ); + { + ::osl::MutexGuard aGuard( GetMutex() ); + + xDrawPeer = ImplGetCompatiblePeer(); + bDisposeDrawPeer = xDrawPeer.is() && ( xDrawPeer != getPeer() ); + + xDrawPeerView.set( xDrawPeer, UNO_QUERY ); + DBG_ASSERT( xDrawPeerView.is(), "UnoControl::draw: no peer!" ); + } + + if ( xDrawPeerView.is() ) + { + Reference< XVclWindowPeer > xWindowPeer; + xWindowPeer.set( xDrawPeer, UNO_QUERY ); + if ( xWindowPeer.is() ) + xWindowPeer->setDesignMode( mbDesignMode ); + xDrawPeerView->draw( x, y ); + } + + if ( bDisposeDrawPeer ) + xDrawPeer->dispose(); +} + +void UnoControl::setZoom( float fZoomX, float fZoomY ) +{ + Reference< XView > xView; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + maComponentInfos.nZoomX = fZoomX; + maComponentInfos.nZoomY = fZoomY; + + xView.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xView.is() ) + xView->setZoom( fZoomX, fZoomY ); +} + +// XControl +void UnoControl::setContext( const Reference< XInterface >& rxContext ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + mxContext = rxContext; +} + +Reference< XInterface > UnoControl::getContext( ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + return mxContext; +} + +void UnoControl::peerCreated() +{ + Reference< XWindow > xWindow( getPeer(), UNO_QUERY ); + if ( !xWindow.is() ) + return; + + if ( maWindowListeners.getLength() ) + xWindow->addWindowListener( &maWindowListeners ); + + if ( maFocusListeners.getLength() ) + xWindow->addFocusListener( &maFocusListeners ); + + if ( maKeyListeners.getLength() ) + xWindow->addKeyListener( &maKeyListeners ); + + if ( maMouseListeners.getLength() ) + xWindow->addMouseListener( &maMouseListeners ); + + if ( maMouseMotionListeners.getLength() ) + xWindow->addMouseMotionListener( &maMouseMotionListeners ); + + if ( maPaintListeners.getLength() ) + xWindow->addPaintListener( &maPaintListeners ); +} + +void UnoControl::createPeer( const Reference< XToolkit >& rxToolkit, const Reference< XWindowPeer >& rParentPeer ) +{ + ::osl::ClearableMutexGuard aGuard( GetMutex() ); + if ( !mxModel.is() ) + { + throw RuntimeException("createPeer: no model!", getXWeak()); + } + + if( getPeer().is() ) + return; + + mbCreatingPeer = true; + + WindowClass eType; + Reference< XToolkit > xToolkit = rxToolkit; + if( rParentPeer.is() && mxContext.is() ) + { + // no TopWindow + if ( !xToolkit.is() ) + xToolkit = rParentPeer->getToolkit(); + Any aAny = OWeakAggObject::queryInterface( cppu::UnoType<XControlContainer>::get()); + Reference< XControlContainer > xC; + aAny >>= xC; + if( xC.is() ) + // It's a container + eType = WindowClass_CONTAINER; + else + eType = WindowClass_SIMPLE; + } + else + { // This is only correct for Top Window + if( rParentPeer.is() ) + { + if ( !xToolkit.is() ) + xToolkit = rParentPeer->getToolkit(); + eType = WindowClass_CONTAINER; + } + else + { + if ( !xToolkit.is() ) + xToolkit = VCLUnoHelper::CreateToolkit(); + eType = WindowClass_TOP; + } + } + WindowDescriptor aDescr; + aDescr.Type = eType; + aDescr.WindowServiceName = GetComponentServiceName(); + aDescr.Parent = rParentPeer; + aDescr.Bounds = getPosSize(); + aDescr.WindowAttributes = 0; + + // Border + Reference< XPropertySet > xPSet( mxModel, UNO_QUERY ); + Reference< XPropertySetInfo > xInfo = xPSet->getPropertySetInfo(); + + Any aVal; + OUString aPropName = GetPropertyName( BASEPROPERTY_BORDER ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + sal_Int16 n = sal_Int16(); + if ( aVal >>= n ) + { + if ( n ) + aDescr.WindowAttributes |= WindowAttribute::BORDER; + else + aDescr.WindowAttributes |= VclWindowPeerAttribute::NOBORDER; + } + } + + // DESKTOP_AS_PARENT + if ( aDescr.Type == WindowClass_TOP ) + { + aPropName = GetPropertyName( BASEPROPERTY_DESKTOP_AS_PARENT ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.ParentIndex = -1; + } + } + // Moveable + aPropName = GetPropertyName( BASEPROPERTY_MOVEABLE ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= WindowAttribute::MOVEABLE; + } + + // Sizeable + aPropName = GetPropertyName( BASEPROPERTY_SIZEABLE ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= WindowAttribute::SIZEABLE; + } + + // Closeable + aPropName = GetPropertyName( BASEPROPERTY_CLOSEABLE ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= WindowAttribute::CLOSEABLE; + } + + // Dropdown + aPropName = GetPropertyName( BASEPROPERTY_DROPDOWN ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::DROPDOWN; + } + + // Spin + aPropName = GetPropertyName( BASEPROPERTY_SPIN ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::SPIN; + } + + // HScroll + aPropName = GetPropertyName( BASEPROPERTY_HSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::HSCROLL; + } + + // VScroll + aPropName = GetPropertyName( BASEPROPERTY_VSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::VSCROLL; + } + + // AutoHScroll + aPropName = GetPropertyName( BASEPROPERTY_AUTOHSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::AUTOHSCROLL; + } + + // AutoVScroll + aPropName = GetPropertyName( BASEPROPERTY_AUTOVSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::AUTOVSCROLL; + } + + //added for issue79712 + //NoLabel + aPropName = GetPropertyName( BASEPROPERTY_NOLABEL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>=b ) && b ) + aDescr.WindowAttributes |= VclWindowPeerAttribute::NOLABEL; + } + //issue79712 ends + + // Align + aPropName = GetPropertyName( BASEPROPERTY_ALIGN ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + sal_Int16 n = sal_Int16(); + if ( aVal >>= n ) + { + if ( n == PROPERTY_ALIGN_LEFT ) + aDescr.WindowAttributes |= VclWindowPeerAttribute::LEFT; + else if ( n == PROPERTY_ALIGN_CENTER ) + aDescr.WindowAttributes |= VclWindowPeerAttribute::CENTER; + else + aDescr.WindowAttributes |= VclWindowPeerAttribute::RIGHT; + } + } + + // Allow derivates to manipulate attributes + PrepareWindowDescriptor(aDescr); + + // create the peer + Reference<XWindowPeer> xTemp = xToolkit->createWindow( aDescr ); + mxVclWindowPeer.set(xTemp, UNO_QUERY); + assert(mxVclWindowPeer); + + // release the mutex guard (and work with copies of our members) + // this is necessary as our peer may lock the SolarMutex (actually, all currently known peers do), so calling + // into the peer with our own mutex locked may cause deadlocks + // (We _really_ need peers which do not use the SolarMutex. It's really pissing me off that from time to + // time deadlocks pop up because the low-level components like our peers use a mutex which usually + // is locked at the top of the stack (it protects the global message looping). This is always dangerous, and + // can not always be solved by tampering with other mutexes. + // Unfortunately, the VCL used in the peers is not threadsafe, and by definition needs a locked SolarMutex.) + // 82300 - 12/21/00 - FS + UnoControlComponentInfos aComponentInfos(maComponentInfos); + bool bDesignMode(mbDesignMode); + + Reference< XGraphics > xGraphics( mxGraphics ); + Reference< XView > xView ( getPeer(), UNO_QUERY_THROW ); + Reference< XWindow > xWindow ( getPeer(), UNO_QUERY_THROW ); + + aGuard.clear(); + + // tdf#150886 if false use the same settings for widgets regardless of theme + // for consistency of document across platforms and in pdf/print output + // note: tdf#155029 do this before updateFromModel + if (xInfo->hasPropertyByName("StandardTheme")) + { + aVal = xPSet->getPropertyValue("StandardTheme"); + bool bUseStandardTheme = false; + aVal >>= bUseStandardTheme; + if (bUseStandardTheme) + { + VclPtr<vcl::Window> pVclPeer = VCLUnoHelper::GetWindow(getPeer()); + AllSettings aAllSettings = pVclPeer->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + aStyleSettings.SetStandardStyles(); + aAllSettings.SetStyleSettings(aStyleSettings); + pVclPeer->SetSettings(aAllSettings); + } + } + + // the updateFromModel is done without a locked mutex, too. + // The reason is that the only thing this method does is firing property changes, and this in general has + // to be done without locked mutexes (as every notification to external listeners). + // 82300 - 12/21/00 - FS + updateFromModel(); + + xView->setZoom( aComponentInfos.nZoomX, aComponentInfos.nZoomY ); + + setPosSize( aComponentInfos.nX, aComponentInfos.nY, aComponentInfos.nWidth, aComponentInfos.nHeight, aComponentInfos.nFlags ); + + if( aComponentInfos.bVisible && !bDesignMode ) + // Show only after setting the data + xWindow->setVisible( aComponentInfos.bVisible ); + + if( !aComponentInfos.bEnable ) + xWindow->setEnable( aComponentInfos.bEnable ); + + xView->setGraphics( xGraphics ); + + peerCreated(); + + mbCreatingPeer = false; + +} + +Reference< XWindowPeer > UnoControl::getPeer() +{ + ::osl::MutexGuard aGuard( GetMutex() ); + return mxVclWindowPeer; +} + +Reference< XVclWindowPeer > UnoControl::getVclWindowPeer() +{ + ::osl::MutexGuard aGuard( GetMutex() ); + return mxVclWindowPeer; +} + +sal_Bool UnoControl::setModel( const Reference< XControlModel >& rxModel ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + Reference< XMultiPropertySet > xPropSet( mxModel, UNO_QUERY ); + + // query for the XPropertiesChangeListener - our delegator is allowed to overwrite this interface + Reference< XPropertiesChangeListener > xListener; + queryInterface( cppu::UnoType<decltype(xListener)>::get() ) >>= xListener; + + if( xPropSet.is() ) + xPropSet->removePropertiesChangeListener( xListener ); + + mpData->bLocalizationSupport = false; + mxModel = rxModel; + + if( mxModel.is() ) + { + try + { + xPropSet.set( mxModel, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPSI( xPropSet->getPropertySetInfo(), UNO_SET_THROW ); + + Sequence< OUString> aNames = lcl_ImplGetPropertyNames( xPropSet ); + xPropSet->addPropertiesChangeListener( aNames, xListener ); + + mpData->bLocalizationSupport = xPSI->hasPropertyByName("ResourceResolver"); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + mxModel.clear(); + } + } + + return mxModel.is(); +} + +Reference< XControlModel > UnoControl::getModel( ) +{ + return mxModel; +} + +Reference< XView > UnoControl::getView( ) +{ + return static_cast< XView* >( this ); +} + +void UnoControl::setDesignMode( sal_Bool bOn ) +{ + ModeChangeEvent aModeChangeEvent; + + Reference< XWindow > xWindow; + Reference<XComponent> xAccessibleComp; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( bool(bOn) == mbDesignMode ) + return; + + // remember this + mbDesignMode = bOn; + xWindow.set(getPeer(), css::uno::UNO_QUERY); + + xAccessibleComp.set(maAccessibleContext, UNO_QUERY); + maAccessibleContext.clear(); + + aModeChangeEvent.Source = *this; + aModeChangeEvent.NewMode = mbDesignMode ? std::u16string_view(u"design") : std::u16string_view(u"alive" ); + } + + // dispose current AccessibleContext, if we have one - without Mutex lock + // (changing the design mode implies having a new implementation for this context, + // so the old one must be declared DEFUNC) + DisposeAccessibleContext(xAccessibleComp); + + // adjust the visibility of our window + if ( xWindow.is() ) + xWindow->setVisible( !bOn ); + + // and notify our mode listeners + maModeChangeListeners.notifyEach( &XModeChangeListener::modeChanged, aModeChangeEvent ); +} + +sal_Bool UnoControl::isDesignMode( ) +{ + return mbDesignMode; +} + +sal_Bool UnoControl::isTransparent( ) +{ + return false; +} + +// XServiceInfo +OUString UnoControl::getImplementationName( ) +{ + OSL_FAIL( "This method should be overridden!" ); + return OUString(); +} + +sal_Bool UnoControl::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > UnoControl::getSupportedServiceNames( ) +{ + return { "com.sun.star.awt.UnoControl" }; +} + + +Reference< XAccessibleContext > SAL_CALL UnoControl::getAccessibleContext( ) +{ + // creation of the context will certainly require the SolarMutex ... + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( GetMutex() ); + + Reference< XAccessibleContext > xCurrentContext( maAccessibleContext.get(), UNO_QUERY ); + if ( !xCurrentContext.is() ) + { + if ( !mbDesignMode ) + { // in alive mode, use the AccessibleContext of the peer + Reference< XAccessible > xPeerAcc( getPeer(), UNO_QUERY ); + if ( xPeerAcc.is() ) + xCurrentContext = xPeerAcc->getAccessibleContext( ); + } + else + // in design mode, use a fallback + xCurrentContext = ::toolkit::OAccessibleControlContext::create( this ); + + DBG_ASSERT( xCurrentContext.is(), "UnoControl::getAccessibleContext: invalid context (invalid peer?)!" ); + maAccessibleContext = xCurrentContext; + + // get notified when the context is disposed + Reference< XComponent > xContextComp( xCurrentContext, UNO_QUERY ); + if ( xContextComp.is() ) + xContextComp->addEventListener( this ); + // In an ideal world, this is not necessary - there the object would be released as soon as it has been + // disposed, and thus our weak reference would be empty, too. + // But 'til this ideal world comes (means 'til we do never have any refcount/lifetime bugs anymore), we + // need to listen for disposal and reset our weak reference then. + } + + return xCurrentContext; +} + +void SAL_CALL UnoControl::addModeChangeListener( const Reference< XModeChangeListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + maModeChangeListeners.addInterface( _rxListener ); +} + +void SAL_CALL UnoControl::removeModeChangeListener( const Reference< XModeChangeListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + maModeChangeListeners.removeInterface( _rxListener ); +} + +void SAL_CALL UnoControl::addModeChangeApproveListener( const Reference< XModeChangeApproveListener >& ) +{ + throw NoSupportException( ); +} + +void SAL_CALL UnoControl::removeModeChangeApproveListener( const Reference< XModeChangeApproveListener >& ) +{ + throw NoSupportException( ); +} + + +awt::Point SAL_CALL UnoControl::convertPointToLogic( const awt::Point& i_Point, ::sal_Int16 i_TargetUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertPointToLogic( i_Point, i_TargetUnit ); + return awt::Point( ); +} + + +awt::Point SAL_CALL UnoControl::convertPointToPixel( const awt::Point& i_Point, ::sal_Int16 i_SourceUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertPointToPixel( i_Point, i_SourceUnit ); + return awt::Point( ); +} + + +awt::Size SAL_CALL UnoControl::convertSizeToLogic( const awt::Size& i_Size, ::sal_Int16 i_TargetUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertSizeToLogic( i_Size, i_TargetUnit ); + return awt::Size( ); +} + + +awt::Size SAL_CALL UnoControl::convertSizeToPixel( const awt::Size& i_Size, ::sal_Int16 i_SourceUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertSizeToPixel( i_Size, i_SourceUnit ); + return awt::Size( ); +} + + +uno::Reference< awt::XStyleSettings > SAL_CALL UnoControl::getStyleSettings() +{ + Reference< awt::XStyleSettingsSupplier > xPeerSupplier; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerSupplier.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerSupplier.is() ) + return xPeerSupplier->getStyleSettings(); + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolbase.cxx b/toolkit/source/controls/unocontrolbase.cxx new file mode 100644 index 0000000000..134e7b181b --- /dev/null +++ b/toolkit/source/controls/unocontrolbase.cxx @@ -0,0 +1,253 @@ +/* -*- 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/XLayoutConstrains.hpp> +#include <com/sun/star/awt/XTextLayoutConstrains.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> + +#include <toolkit/controls/unocontrolbase.hxx> +#include <helper/property.hxx> + +#include <tools/debug.hxx> + +using namespace com::sun::star; + + + + +bool UnoControlBase::ImplHasProperty( sal_uInt16 nPropId ) +{ + const OUString& aPropName( GetPropertyName( nPropId ) ); + return ImplHasProperty( aPropName ); +} + +bool UnoControlBase::ImplHasProperty( const OUString& aPropertyName ) +{ + css::uno::Reference< css::beans::XPropertySet > xPSet( mxModel, css::uno::UNO_QUERY ); + if ( !xPSet.is() ) + return false; + css::uno::Reference< css::beans::XPropertySetInfo > xInfo = xPSet->getPropertySetInfo(); + if ( !xInfo.is() ) + return false; + + return xInfo->hasPropertyByName( aPropertyName ); +} + +void UnoControlBase::ImplSetPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues, bool bUpdateThis ) +{ + css::uno::Reference< css::beans::XMultiPropertySet > xMPS( mxModel, css::uno::UNO_QUERY ); + if ( !mxModel.is() ) + return; + + DBG_ASSERT( xMPS.is(), "UnoControlBase::ImplSetPropertyValues: no multi property set interface!" ); + if ( !xMPS.is() ) + return; + + if ( !bUpdateThis ) + ImplLockPropertyChangeNotifications( aPropertyNames, true ); + + try + { + xMPS->setPropertyValues( aPropertyNames, aValues ); + } + catch( const css::uno::Exception& ) + { + if ( !bUpdateThis ) + ImplLockPropertyChangeNotifications( aPropertyNames, false ); + } + if ( !bUpdateThis ) + ImplLockPropertyChangeNotifications( aPropertyNames, false ); +} + +void UnoControlBase::ImplSetPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue, bool bUpdateThis ) +{ + // Model might be logged off already but an event still fires + if ( !mxModel.is() ) + return; + + css::uno::Reference< css::beans::XPropertySet > xPSet( mxModel, css::uno::UNO_QUERY ); + if ( !bUpdateThis ) + ImplLockPropertyChangeNotification( aPropertyName, true ); + + try + { + xPSet->setPropertyValue( aPropertyName, aValue ); + } + catch( const css::uno::Exception& ) + { + if ( !bUpdateThis ) + ImplLockPropertyChangeNotification( aPropertyName, false ); + throw; + } + if ( !bUpdateThis ) + ImplLockPropertyChangeNotification( aPropertyName, false ); +} + +css::uno::Any UnoControlBase::ImplGetPropertyValue( const OUString& aPropertyName ) const +{ + css::uno::Reference< css::beans::XPropertySet > xPSet( mxModel, css::uno::UNO_QUERY ); + if ( xPSet.is() ) + return xPSet->getPropertyValue( aPropertyName ); + else + return css::uno::Any(); +} + +template <typename T> T UnoControlBase::ImplGetPropertyValuePOD( sal_uInt16 nProp ) +{ + T t(0); + if ( mxModel.is() ) + { + css::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) ); + aVal >>= t; + } + return t; +} + +template <typename T> T UnoControlBase::ImplGetPropertyValueClass( sal_uInt16 nProp ) +{ + T t; + if ( mxModel.is() ) + { + css::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) ); + aVal >>= t; + } + return t; +} + +bool UnoControlBase::ImplGetPropertyValue_BOOL( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<bool>(nProp); +} + +sal_Int16 UnoControlBase::ImplGetPropertyValue_INT16( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<sal_Int16>(nProp); +} + +sal_Int32 UnoControlBase::ImplGetPropertyValue_INT32( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<sal_Int32>(nProp); +} + +double UnoControlBase::ImplGetPropertyValue_DOUBLE( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<double>(nProp); +} + +OUString UnoControlBase::ImplGetPropertyValue_UString( sal_uInt16 nProp ) +{ + return ImplGetPropertyValueClass<OUString>(nProp); +} + +util::Date UnoControlBase::ImplGetPropertyValue_Date( sal_uInt16 nProp ) +{ + return ImplGetPropertyValueClass<util::Date>(nProp); +} + +util::Time UnoControlBase::ImplGetPropertyValue_Time( sal_uInt16 nProp ) +{ + return ImplGetPropertyValueClass<util::Time>(nProp); +} + +css::awt::Size UnoControlBase::Impl_getMinimumSize() +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->getMinimumSize(); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +css::awt::Size UnoControlBase::Impl_getPreferredSize() +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->getPreferredSize(); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +css::awt::Size UnoControlBase::Impl_calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->calcAdjustedSize( rNewSize ); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +css::awt::Size UnoControlBase::Impl_getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XTextLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->getMinimumSize( nCols, nLines ); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +void UnoControlBase::Impl_getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XTextLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + xL->getColumnsAndLines( nCols, nLines ); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolcontainer.cxx b/toolkit/source/controls/unocontrolcontainer.cxx new file mode 100644 index 0000000000..1610f36629 --- /dev/null +++ b/toolkit/source/controls/unocontrolcontainer.cxx @@ -0,0 +1,823 @@ +/* -*- 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/XVclContainerPeer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <controls/unocontrolcontainer.hxx> +#include <comphelper/sequence.hxx> + +#include <tools/debug.hxx> + +#include <limits> +#include <map> +#include <memory> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <utility> + +using namespace ::com::sun::star; + + + +namespace { + +struct UnoControlHolder +{ + uno::Reference< awt::XControl > mxControl; + OUString msName; + +public: + UnoControlHolder( OUString aName, uno::Reference< awt::XControl > xControl ) + : mxControl(std::move( xControl )), + msName(std::move( aName )) + { + } + + const OUString& getName() const { return msName; } + const uno::Reference< awt::XControl >& getControl() const { return mxControl; } +}; + +} + +class UnoControlHolderList +{ +public: + typedef sal_Int32 ControlIdentifier; +private: + typedef std::shared_ptr< UnoControlHolder > ControlInfo; + typedef ::std::map< ControlIdentifier, ControlInfo > ControlMap; + +private: + ControlMap maControls; + +public: + UnoControlHolderList(); + + /** adds a control with the given name to the list + @param _rxControl + the control to add. Must not be <NULL/> + @param _pBName + the name of the control, or <NULL/> if an automatic name should be generated + @return + the identifier of the newly added control + */ + ControlIdentifier addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ); + + /** determines whether or not the list is empty + */ + bool empty() const { return maControls.empty(); } + + /** retrieves all controls currently in the list + */ + void getControls( uno::Sequence< uno::Reference< awt::XControl > >& _out_rControls ) const; + + /** retrieves all identifiers of all controls currently in the list + */ + void getIdentifiers( uno::Sequence< sal_Int32 >& _out_rIdentifiers ) const; + + /** returns the first control which is registered under the given name + */ + uno::Reference< awt::XControl > + getControlForName( const OUString& _rName ) const; + + /** returns the identifier which a control is registered for, or -1 if the control + isn't registered + */ + ControlIdentifier + getControlIdentifier( const uno::Reference< awt::XControl >& _rxControl ); + + /** retrieves the control for a given id + @param _nIdentifier + the identifier for the control + @param _out_rxControl + takes the XControl upon successful return + @return + <TRUE/> if and only if a control with the given id is part of the list + */ + bool getControlForIdentifier( ControlIdentifier _nIdentifier, uno::Reference< awt::XControl >& _out_rxControl ) const; + + /** removes a control from the list, given by id + @param _nId + The identifier of the control to remove. + */ + void removeControlById( ControlIdentifier _nId ); + + /** replaces a control from the list with another one + @param _nId + The identifier of the control to replace + @param _rxNewControl + the new control to put into the list + */ + void replaceControlById( ControlIdentifier _nId, const uno::Reference< awt::XControl >& _rxNewControl ); + +private: + /** adds a control + @param _rxControl + the control to add to the container + @param _pName + pointer to the name of the control. Might be <NULL/>, in this case, a name is generated. + @return + the identifier of the newly inserted control + */ + ControlIdentifier impl_addControl( + const uno::Reference< awt::XControl >& _rxControl, + const OUString* _pName + ); + + /** finds a free identifier + @throw uno::RuntimeException + if no free identifier can be found + */ + ControlIdentifier impl_getFreeIdentifier_throw(); + + /** finds a free name + @throw uno::RuntimeException + if no free name can be found + */ + OUString impl_getFreeName_throw(); +}; + + +UnoControlHolderList::UnoControlHolderList() +{ +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ) +{ + return impl_addControl( _rxControl, _pName ); +} + + +void UnoControlHolderList::getControls( uno::Sequence< uno::Reference< awt::XControl > >& _out_rControls ) const +{ + _out_rControls.realloc( maControls.size() ); + uno::Reference< awt::XControl >* pControls = _out_rControls.getArray(); + for (const auto& rEntry : maControls) + { + *pControls = rEntry.second->getControl(); + ++pControls; + } +} + + +void UnoControlHolderList::getIdentifiers( uno::Sequence< sal_Int32 >& _out_rIdentifiers ) const +{ + _out_rIdentifiers.realloc( maControls.size() ); + sal_Int32* pIdentifiers = _out_rIdentifiers.getArray(); + for (const auto& rEntry : maControls) + { + *pIdentifiers = rEntry.first; + ++pIdentifiers; + } +} + + +uno::Reference< awt::XControl > UnoControlHolderList::getControlForName( const OUString& _rName ) const +{ + auto loop = std::find_if(maControls.begin(), maControls.end(), + [&_rName](const ControlMap::value_type& rEntry) { return rEntry.second->getName() == _rName; }); + if (loop != maControls.end()) + return loop->second->getControl(); + return uno::Reference< awt::XControl >(); +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::getControlIdentifier( const uno::Reference< awt::XControl >& _rxControl ) +{ + auto loop = std::find_if(maControls.begin(), maControls.end(), + [&_rxControl](const ControlMap::value_type& rEntry) { return rEntry.second->getControl().get() == _rxControl.get(); }); + if (loop != maControls.end()) + return loop->first; + return -1; +} + + +bool UnoControlHolderList::getControlForIdentifier( UnoControlHolderList::ControlIdentifier _nIdentifier, uno::Reference< awt::XControl >& _out_rxControl ) const +{ + ControlMap::const_iterator pos = maControls.find( _nIdentifier ); + if ( pos == maControls.end() ) + return false; + _out_rxControl = pos->second->getControl(); + return true; +} + + +void UnoControlHolderList::removeControlById( UnoControlHolderList::ControlIdentifier _nId ) +{ + ControlMap::iterator pos = maControls.find( _nId ); + DBG_ASSERT( pos != maControls.end(), "UnoControlHolderList::removeControlById: invalid id!" ); + if ( pos == maControls.end() ) + return; + + maControls.erase( pos ); +} + + +void UnoControlHolderList::replaceControlById( ControlIdentifier _nId, const uno::Reference< awt::XControl >& _rxNewControl ) +{ + DBG_ASSERT( _rxNewControl.is(), "UnoControlHolderList::replaceControlById: invalid new control!" ); + + ControlMap::iterator pos = maControls.find( _nId ); + DBG_ASSERT( pos != maControls.end(), "UnoControlHolderList::replaceControlById: invalid id!" ); + if ( pos == maControls.end() ) + return; + + pos->second = std::make_shared<UnoControlHolder>( pos->second->getName(), _rxNewControl ); +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::impl_addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ) +{ + DBG_ASSERT( _rxControl.is(), "UnoControlHolderList::impl_addControl: invalid control!" ); + + OUString sName = _pName ? *_pName : impl_getFreeName_throw(); + sal_Int32 nId = impl_getFreeIdentifier_throw(); + + maControls[ nId ] = std::make_shared<UnoControlHolder>( sName, _rxControl ); + return nId; +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::impl_getFreeIdentifier_throw() +{ + for ( ControlIdentifier candidateId = 0; candidateId < ::std::numeric_limits< ControlIdentifier >::max(); ++candidateId ) + { + ControlMap::const_iterator existent = maControls.find( candidateId ); + if ( existent == maControls.end() ) + return candidateId; + } + throw uno::RuntimeException("out of identifiers" ); +} + + +OUString UnoControlHolderList::impl_getFreeName_throw() +{ + for ( ControlIdentifier candidateId = 0; candidateId < ::std::numeric_limits< ControlIdentifier >::max(); ++candidateId ) + { + OUString candidateName( "control_" + OUString::number( candidateId ) ); + if ( std::none_of(maControls.begin(), maControls.end(), + [&candidateName](const ControlMap::value_type& rEntry) { return rEntry.second->getName() == candidateName; }) ) + return candidateName; + } + throw uno::RuntimeException("out of identifiers" ); +} + +// Function to set the controls' visibility according +// to the dialog's "Step" property + +static void implUpdateVisibility +( + sal_Int32 nDialogStep, + const uno::Reference< awt::XControlContainer >& xControlContainer +) +{ + const uno::Sequence< uno::Reference< awt::XControl > > + aCtrls = xControlContainer->getControls(); + bool bCompleteVisible = (nDialogStep == 0); + for( const uno::Reference< awt::XControl >& xControl : aCtrls ) + { + bool bVisible = bCompleteVisible; + if( !bVisible ) + { + uno::Reference< awt::XControlModel > xModel( xControl->getModel() ); + uno::Reference< beans::XPropertySet > xPSet + ( xModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > + xInfo = xPSet->getPropertySetInfo(); + OUString aPropName( "Step" ); + sal_Int32 nControlStep = 0; + if ( xInfo->hasPropertyByName( aPropName ) ) + { + uno::Any aVal = xPSet->getPropertyValue( aPropName ); + aVal >>= nControlStep; + } + bVisible = (nControlStep == 0) || (nControlStep == nDialogStep); + } + + uno::Reference< awt::XWindow> xWindow + ( xControl, uno::UNO_QUERY ); + if( xWindow.is() ) + xWindow->setVisible( bVisible ); + } +} + + + +typedef ::cppu::WeakImplHelper< beans::XPropertyChangeListener > PropertyChangeListenerHelper; + +namespace { + +class DialogStepChangedListener: public PropertyChangeListenerHelper +{ +private: + uno::Reference< awt::XControlContainer > mxControlContainer; + +public: + explicit DialogStepChangedListener( uno::Reference< awt::XControlContainer > xControlContainer ) + : mxControlContainer(std::move( xControlContainer )) {} + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const beans::PropertyChangeEvent& evt ) override; + +}; + +} + +void SAL_CALL DialogStepChangedListener::disposing( const lang::EventObject& /*_rSource*/) +{ + mxControlContainer.clear(); +} + +void SAL_CALL DialogStepChangedListener::propertyChange( const beans::PropertyChangeEvent& evt ) +{ + // evt.PropertyName HAS to be "Step" because we only use the listener for that + sal_Int32 nDialogStep = 0; + evt.NewValue >>= nDialogStep; + implUpdateVisibility( nDialogStep, mxControlContainer ); +} + + + +UnoControlContainer::UnoControlContainer() + :maCListeners( *this ) +{ + mpControls.reset(new UnoControlHolderList); +} + +UnoControlContainer::UnoControlContainer(const uno::Reference< awt::XVclWindowPeer >& xP ) + :maCListeners( *this ) +{ + setPeer( xP ); + mbDisposePeer = false; + mpControls.reset(new UnoControlHolderList); +} + +UnoControlContainer::~UnoControlContainer() +{ +} + +void UnoControlContainer::ImplActivateTabControllers() +{ + for ( auto& rTabController : asNonConstRange(maTabControllers) ) + { + rTabController->setContainer( this ); + rTabController->activateTabOrder(); + } +} + +// lang::XComponent +void UnoControlContainer::dispose( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + lang::EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< uno::XAggregation* >( this ); + + // Notify listeners about disposal of this Container (This is much faster if they + // listen on the controls and the container). + maDisposeListeners.disposeAndClear( aDisposeEvent ); + maCListeners.disposeAndClear( aDisposeEvent ); + + + const uno::Sequence< uno::Reference< awt::XControl > > aCtrls = getControls(); + + for( uno::Reference< awt::XControl > const & control : aCtrls ) + { + removingControl( control ); + // Delete control + control->dispose(); + } + + + // Delete all structures + mpControls.reset(new UnoControlHolderList); + + UnoControlBase::dispose(); +} + +// lang::XEventListener +void UnoControlContainer::disposing( const lang::EventObject& _rEvt ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl( _rEvt.Source, uno::UNO_QUERY ); + if ( xControl.is() ) + removeControl( xControl ); + + UnoControlBase::disposing( _rEvt ); +} + +// container::XContainer +void UnoControlContainer::addContainerListener( const uno::Reference< container::XContainerListener >& rxListener ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maCListeners.addInterface( rxListener ); +} + +void UnoControlContainer::removeContainerListener( const uno::Reference< container::XContainerListener >& rxListener ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maCListeners.removeInterface( rxListener ); +} + + +::sal_Int32 SAL_CALL UnoControlContainer::insert( const uno::Any& _rElement ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl; + if ( !( _rElement >>= xControl ) || !xControl.is() ) + throw lang::IllegalArgumentException( + "Elements must support the XControl interface.", + *this, + 1 + ); + + return impl_addControl( xControl ); +} + +void SAL_CALL UnoControlContainer::removeByIdentifier( ::sal_Int32 _nIdentifier ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl; + if ( !mpControls->getControlForIdentifier( _nIdentifier, xControl ) ) + throw container::NoSuchElementException( + "There is no element with the given identifier.", + *this + ); + + impl_removeControl( _nIdentifier, xControl ); +} + +void SAL_CALL UnoControlContainer::replaceByIdentifer( ::sal_Int32 _nIdentifier, const uno::Any& _rElement ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xExistentControl; + if ( !mpControls->getControlForIdentifier( _nIdentifier, xExistentControl ) ) + throw container::NoSuchElementException( + "There is no element with the given identifier.", + *this + ); + + uno::Reference< awt::XControl > xNewControl; + if ( !( _rElement >>= xNewControl ) ) + throw lang::IllegalArgumentException( + "Elements must support the XControl interface.", + *this, + 1 + ); + + removingControl( xExistentControl ); + + mpControls->replaceControlById( _nIdentifier, xNewControl ); + + addingControl( xNewControl ); + + impl_createControlPeerIfNecessary( xNewControl ); + + if ( maCListeners.getLength() ) + { + container::ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= _nIdentifier; + aEvent.Element <<= xNewControl; + aEvent.ReplacedElement <<= xExistentControl; + maCListeners.elementReplaced( aEvent ); + } +} + +uno::Any SAL_CALL UnoControlContainer::getByIdentifier( ::sal_Int32 _nIdentifier ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl; + if ( !mpControls->getControlForIdentifier( _nIdentifier, xControl ) ) + throw container::NoSuchElementException(); + return uno::Any( xControl ); +} + +uno::Sequence< ::sal_Int32 > SAL_CALL UnoControlContainer::getIdentifiers( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Sequence< ::sal_Int32 > aIdentifiers; + mpControls->getIdentifiers( aIdentifiers ); + return aIdentifiers; +} + +// container::XElementAccess +uno::Type SAL_CALL UnoControlContainer::getElementType( ) +{ + return cppu::UnoType<awt::XControlModel>::get(); +} + +sal_Bool SAL_CALL UnoControlContainer::hasElements( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + return !mpControls->empty(); +} + +// awt::XControlContainer +void UnoControlContainer::setStatusText( const OUString& rStatusText ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // Descend the parent hierarchy + uno::Reference< awt::XControlContainer > xContainer( mxContext, uno::UNO_QUERY ); + if( xContainer.is() ) + xContainer->setStatusText( rStatusText ); +} + +uno::Sequence< uno::Reference< awt::XControl > > UnoControlContainer::getControls( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + uno::Sequence< uno::Reference< awt::XControl > > aControls; + mpControls->getControls( aControls ); + return aControls; +} + +uno::Reference< awt::XControl > UnoControlContainer::getControl( const OUString& rName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + return mpControls->getControlForName( rName ); +} + +void UnoControlContainer::addingControl( const uno::Reference< awt::XControl >& _rxControl ) +{ + if ( _rxControl.is() ) + { + uno::Reference< uno::XInterface > xThis; + OWeakAggObject::queryInterface( cppu::UnoType<uno::XInterface>::get() ) >>= xThis; + + _rxControl->setContext( xThis ); + _rxControl->addEventListener( this ); + } +} + +void UnoControlContainer::impl_createControlPeerIfNecessary( const uno::Reference< awt::XControl >& _rxControl ) +{ + OSL_PRECOND( _rxControl.is(), "UnoControlContainer::impl_createControlPeerIfNecessary: invalid control, this will crash!" ); + + // if the container already has a peer, then also create a peer for the control + uno::Reference< awt::XWindowPeer > xMyPeer( getPeer() ); + + if( xMyPeer.is() ) + { + _rxControl->createPeer( nullptr, xMyPeer ); + ImplActivateTabControllers(); + } + +} + +sal_Int32 UnoControlContainer::impl_addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + UnoControlHolderList::ControlIdentifier id = mpControls->addControl( _rxControl, _pName ); + + addingControl( _rxControl ); + + impl_createControlPeerIfNecessary( _rxControl ); + + if ( maCListeners.getLength() ) + { + container::ContainerEvent aEvent; + aEvent.Source = *this; + if (_pName) + aEvent.Accessor <<= *_pName; + else + aEvent.Accessor <<= static_cast<sal_Int32>(id); + aEvent.Element <<= _rxControl; + maCListeners.elementInserted( aEvent ); + } + + return id; +} + +void UnoControlContainer::addControl( const OUString& rName, const uno::Reference< awt::XControl >& rControl ) +{ + if ( rControl.is() ) + impl_addControl( rControl, &rName ); +} + +void UnoControlContainer::removingControl( const uno::Reference< awt::XControl >& _rxControl ) +{ + if ( _rxControl.is() ) + { + _rxControl->removeEventListener( this ); + _rxControl->setContext( nullptr ); + } +} + +void UnoControlContainer::impl_removeControl( sal_Int32 _nId, const uno::Reference< awt::XControl >& _rxControl ) +{ +#ifdef DBG_UTIL + { + uno::Reference< awt::XControl > xControl; + bool bHas = mpControls->getControlForIdentifier( _nId, xControl ); + DBG_ASSERT( bHas && xControl == _rxControl, "UnoControlContainer::impl_removeControl: inconsistency in the parameters!" ); + } +#endif + removingControl( _rxControl ); + + mpControls->removeControlById( _nId ); + + if ( maCListeners.getLength() ) + { + container::ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= _nId; + aEvent.Element <<= _rxControl; + maCListeners.elementRemoved( aEvent ); + } +} + +void UnoControlContainer::removeControl( const uno::Reference< awt::XControl >& _rxControl ) +{ + if ( _rxControl.is() ) + { + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + UnoControlHolderList::ControlIdentifier id = mpControls->getControlIdentifier( _rxControl ); + if ( id != -1 ) + impl_removeControl( id, _rxControl ); + } +} + + +// awt::XUnoControlContainer +void UnoControlContainer::setTabControllers( const uno::Sequence< uno::Reference< awt::XTabController > >& TabControllers ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maTabControllers = TabControllers; +} + +uno::Sequence< uno::Reference< awt::XTabController > > UnoControlContainer::getTabControllers( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return maTabControllers; +} + +void UnoControlContainer::addTabController( const uno::Reference< awt::XTabController >& TabController ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + sal_uInt32 nCount = maTabControllers.getLength(); + maTabControllers.realloc( nCount + 1 ); + maTabControllers.getArray()[ nCount ] = TabController; +} + +void UnoControlContainer::removeTabController( const uno::Reference< awt::XTabController >& TabController ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + auto pTabController = std::find_if(std::cbegin(maTabControllers), std::cend(maTabControllers), + [&TabController](const uno::Reference< awt::XTabController >& rTabController) { + return rTabController.get() == TabController.get(); }); + if (pTabController != std::cend(maTabControllers)) + { + auto n = static_cast<sal_Int32>(std::distance(std::cbegin(maTabControllers), pTabController)); + ::comphelper::removeElementAt( maTabControllers, n ); + } +} + +// awt::XControl +void UnoControlContainer::createPeer( const uno::Reference< awt::XToolkit >& rxToolkit, const uno::Reference< awt::XWindowPeer >& rParent ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + if( getPeer().is() ) + return; + + bool bVis = maComponentInfos.bVisible; + if( bVis ) + UnoControl::setVisible( false ); + + // Create a new peer + UnoControl::createPeer( rxToolkit, rParent ); + + // Create all children's peers + if ( !mbCreatingCompatiblePeer ) + { + // Evaluate "Step" property + uno::Reference< awt::XControlModel > xModel( getModel() ); + uno::Reference< beans::XPropertySet > xPSet + ( xModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > + xInfo = xPSet->getPropertySetInfo(); + OUString aPropName( "Step" ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + css::uno::Any aVal = xPSet->getPropertyValue( aPropName ); + sal_Int32 nDialogStep = 0; + aVal >>= nDialogStep; + uno::Reference< awt::XControlContainer > xContainer = + static_cast< awt::XControlContainer* >(this); + implUpdateVisibility( nDialogStep, xContainer ); + + uno::Reference< beans::XPropertyChangeListener > xListener = + new DialogStepChangedListener(xContainer); + xPSet->addPropertyChangeListener( aPropName, xListener ); + } + + uno::Sequence< uno::Reference< awt::XControl > > aCtrls = getControls(); + for( auto& rCtrl : asNonConstRange(aCtrls) ) + rCtrl->createPeer( rxToolkit, getPeer() ); + + uno::Reference< awt::XVclContainerPeer > xC( getPeer(), uno::UNO_QUERY ); + if ( xC.is() ) + xC->enableDialogControl( true ); + ImplActivateTabControllers(); + } + + if( bVis && !isDesignMode() ) + UnoControl::setVisible( true ); +} + + +// awt::XWindow +void UnoControlContainer::setVisible( sal_Bool bVisible ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + UnoControl::setVisible( bVisible ); + if( !mxContext.is() && bVisible ) + // This is a Topwindow, thus show it automatically + createPeer( uno::Reference< awt::XToolkit > (), uno::Reference< awt::XWindowPeer > () ); +} + +OUString UnoControlContainer::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlContainer"; +} + +css::uno::Sequence<OUString> UnoControlContainer::getSupportedServiceNames() +{ + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlContainer"; + ps[s.getLength() - 1] = "stardiv.vcl.control.ControlContainer"; + return s; +} + +void UnoControlContainer::PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) +{ + // HACK due to the fact that we can't really use VSCROLL & HSCROLL + // for Dialog ( css::awt::VclWindowPeerAttribute::VSCROLL + // has the same value as + // css::awt::WindowAttribute::NODECORATION ) + // For convenience in the PropBrowse using HSCROLL and VSCROLL ensures + // the Correct text. We exchange them here and the control knows + // about this hack ( it sucks badly I know ) + if ( rDesc.WindowAttributes & css::awt::VclWindowPeerAttribute::VSCROLL ) + { + rDesc.WindowAttributes &= ~css::awt::VclWindowPeerAttribute::VSCROLL; + rDesc.WindowAttributes |= css::awt::VclWindowPeerAttribute::AUTOVSCROLL; + } + if ( rDesc.WindowAttributes & css::awt::VclWindowPeerAttribute::HSCROLL ) + { + rDesc.WindowAttributes &= ~css::awt::VclWindowPeerAttribute::HSCROLL; + rDesc.WindowAttributes |= css::awt::VclWindowPeerAttribute::AUTOHSCROLL; + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlContainer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlContainer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolcontainermodel.cxx b/toolkit/source/controls/unocontrolcontainermodel.cxx new file mode 100644 index 0000000000..6bfaf4448c --- /dev/null +++ b/toolkit/source/controls/unocontrolcontainermodel.cxx @@ -0,0 +1,93 @@ +/* -*- 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/uno/XComponentContext.hpp> +#include <controls/unocontrolcontainermodel.hxx> +#include <helper/property.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + + +UnoControlContainerModel::UnoControlContainerModel( 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_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); +} + +OUString UnoControlContainerModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ControlContainer"; +} + +OUString UnoControlContainerModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlContainerModel"; +} + +css::uno::Sequence<OUString> +UnoControlContainerModel::getSupportedServiceNames() +{ + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlContainerModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.ControlContainer"; + return s; +} + +css::uno::Any UnoControlContainerModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + css::uno::Any aDefault; + if ( nPropId == BASEPROPERTY_BORDER ) + aDefault <<= sal_Int16(0); + else + aDefault = UnoControlModel::ImplGetDefaultValue( nPropId ); + return aDefault; +} + + +css::uno::Reference< css::beans::XPropertySetInfo > UnoControlContainerModel::getPropertySetInfo( ) +{ + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& UnoControlContainerModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlContainerModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlContainerModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolmodel.cxx b/toolkit/source/controls/unocontrolmodel.cxx new file mode 100644 index 0000000000..0868fc2f6a --- /dev/null +++ b/toolkit/source/controls/unocontrolmodel.cxx @@ -0,0 +1,1357 @@ +/* -*- 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/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/FontWidth.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/MouseWheelBehavior.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/io/XMarkableStream.hpp> +#include <com/sun/star/i18n/Currency2.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> +#include <tools/long.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/emptyfontdescriptor.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/configmgr.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/unohelp.hxx> + +#include <memory> +#include <o3tl/sorted_vector.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::i18n; +using ::com::sun::star::awt::FontDescriptor; + + +#define UNOCONTROL_STREAMVERSION short(2) + +static void lcl_ImplMergeFontProperty( FontDescriptor& rFD, sal_uInt16 nPropId, const Any& rValue ) +{ + // some props are defined with other types than the matching FontDescriptor members have + // (e.g. FontWidth, FontSlant) + // 78474 - 09/19/2000 - FS + float nExtractFloat = 0; + sal_Int16 nExtractShort = 0; + + switch ( nPropId ) + { + case BASEPROPERTY_FONTDESCRIPTORPART_NAME: rValue >>= rFD.Name; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME: rValue >>= rFD.StyleName; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_FAMILY: rValue >>= rFD.Family; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARSET: rValue >>= rFD.CharSet; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT: rValue >>= nExtractFloat; rFD.Height = static_cast<sal_Int16>(nExtractFloat); + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT: rValue >>= rFD.Weight; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_SLANT: if ( rValue >>= nExtractShort ) + rFD.Slant = static_cast<css::awt::FontSlant>(nExtractShort); + else + rValue >>= rFD.Slant; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE: rValue >>= rFD.Underline; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT: rValue >>= rFD.Strikeout; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WIDTH: rValue >>= rFD.Width; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_PITCH: rValue >>= rFD.Pitch; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH: rValue >>= rFD.CharacterWidth; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION: rValue >>= rFD.Orientation; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_KERNING: rValue >>= rFD.Kerning; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE: rValue >>= rFD.WordLineMode; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_TYPE: rValue >>= rFD.Type; + break; + default: OSL_FAIL( "FontProperty?!" ); + } +} + + + +UnoControlModel::UnoControlModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel_Base() + ,maDisposeListeners( *this ) + ,m_xContext( rxContext ) +{ + // Insert properties from Model into table, + // only existing properties are valid, even if they're VOID +} + +UnoControlModel::UnoControlModel( const UnoControlModel& rModel ) + : UnoControlModel_Base(), OPropertySetHelper() + , maData( rModel.maData ) + , maDisposeListeners( *this ) + , m_xContext( rModel.m_xContext ) +{ +} + +css::uno::Sequence<sal_Int32> UnoControlModel::ImplGetPropertyIds() const +{ + sal_uInt32 nIDs = maData.size(); + css::uno::Sequence<sal_Int32> aIDs( nIDs ); + sal_Int32* pIDs = aIDs.getArray(); + sal_uInt32 n = 0; + for ( const auto& rData : maData ) + pIDs[n++] = rData.first; + return aIDs; +} + +bool UnoControlModel::ImplHasProperty( sal_uInt16 nPropId ) const +{ + if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + nPropId = BASEPROPERTY_FONTDESCRIPTOR; + + return maData.find( nPropId ) != maData.end(); +} + +css::uno::Any UnoControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + css::uno::Any aDefault; + + if ( + (nPropId == BASEPROPERTY_FONTDESCRIPTOR) || + ( + (nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START) && + (nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END) + ) + ) + { + EmptyFontDescriptor aFD; + switch ( nPropId ) + { + case BASEPROPERTY_FONTDESCRIPTOR: aDefault <<= aFD; break; + case BASEPROPERTY_FONTDESCRIPTORPART_NAME: aDefault <<= aFD.Name; break; + case BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME: aDefault <<= aFD.StyleName; break; + case BASEPROPERTY_FONTDESCRIPTORPART_FAMILY: aDefault <<= aFD.Family; break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARSET: aDefault <<= aFD.CharSet; break; + case BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT: aDefault <<= static_cast<float>(aFD.Height); break; + case BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT: aDefault <<= aFD.Weight; break; + case BASEPROPERTY_FONTDESCRIPTORPART_SLANT: aDefault <<= static_cast<sal_Int16>(aFD.Slant); break; + case BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE: aDefault <<= aFD.Underline; break; + case BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT: aDefault <<= aFD.Strikeout; break; + case BASEPROPERTY_FONTDESCRIPTORPART_WIDTH: aDefault <<= aFD.Width; break; + case BASEPROPERTY_FONTDESCRIPTORPART_PITCH: aDefault <<= aFD.Pitch; break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH: aDefault <<= aFD.CharacterWidth; break; + case BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION: aDefault <<= aFD.Orientation; break; + case BASEPROPERTY_FONTDESCRIPTORPART_KERNING: aDefault <<= aFD.Kerning; break; + case BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE: aDefault <<= aFD.WordLineMode; break; + case BASEPROPERTY_FONTDESCRIPTORPART_TYPE: aDefault <<= aFD.Type; break; + default: OSL_FAIL( "FontProperty?!" ); + } + } + else + { + switch ( nPropId ) + { + case BASEPROPERTY_GRAPHIC: + aDefault <<= Reference< graphic::XGraphic >(); + break; + + case BASEPROPERTY_REFERENCE_DEVICE: + aDefault <<= Reference< awt::XDevice >(); + break; + + case BASEPROPERTY_ITEM_SEPARATOR_POS: + case BASEPROPERTY_VERTICALALIGN: + case BASEPROPERTY_BORDERCOLOR: + case BASEPROPERTY_SYMBOL_COLOR: + case BASEPROPERTY_TABSTOP: + case BASEPROPERTY_TEXTCOLOR: + case BASEPROPERTY_TEXTLINECOLOR: + case BASEPROPERTY_DATE: + case BASEPROPERTY_DATESHOWCENTURY: + case BASEPROPERTY_TIME: + case BASEPROPERTY_VALUE_DOUBLE: + case BASEPROPERTY_PROGRESSVALUE: + case BASEPROPERTY_SCROLLVALUE: + case BASEPROPERTY_VISIBLESIZE: + case BASEPROPERTY_BACKGROUNDCOLOR: + case BASEPROPERTY_FILLCOLOR: + case BASEPROPERTY_HIGHLIGHT_COLOR: + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: break; // Void + + case BASEPROPERTY_FONTRELIEF: + case BASEPROPERTY_FONTEMPHASISMARK: + case BASEPROPERTY_MAXTEXTLEN: + case BASEPROPERTY_STATE: + case BASEPROPERTY_EXTDATEFORMAT: + case BASEPROPERTY_EXTTIMEFORMAT: + case BASEPROPERTY_ECHOCHAR: aDefault <<= sal_Int16(0); break; + case BASEPROPERTY_BORDER: aDefault <<= sal_Int16(1); break; + case BASEPROPERTY_DECIMALACCURACY: aDefault <<= sal_Int16(2); break; + case BASEPROPERTY_LINECOUNT: aDefault <<= sal_Int16(5); break; + case BASEPROPERTY_ALIGN: aDefault <<= sal_Int16(PROPERTY_ALIGN_LEFT); break; + case BASEPROPERTY_IMAGEALIGN: aDefault <<= sal_Int16(1) /*ImageAlign::Top*/; break; + case BASEPROPERTY_IMAGEPOSITION: aDefault <<= sal_Int16(12) /*ImagePosition::Centered*/; break; + case BASEPROPERTY_PUSHBUTTONTYPE: aDefault <<= sal_Int16(0) /*PushButtonType::STANDARD*/; break; + case BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR:aDefault <<= sal_Int16(awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY); break; + + case BASEPROPERTY_DATEMAX: aDefault <<= util::Date( 31, 12, 2200 ); break; + case BASEPROPERTY_DATEMIN: aDefault <<= util::Date( 1, 1, 1900 ); break; + case BASEPROPERTY_TIMEMAX: aDefault <<= util::Time(0, 0, 59, 23, false); break; + case BASEPROPERTY_TIMEMIN: aDefault <<= util::Time(); break; + case BASEPROPERTY_VALUEMAX_DOUBLE: aDefault <<= double(1000000); break; + case BASEPROPERTY_VALUEMIN_DOUBLE: aDefault <<= double(-1000000); break; + case BASEPROPERTY_VALUESTEP_DOUBLE: aDefault <<= double(1); break; + case BASEPROPERTY_PROGRESSVALUE_MAX: aDefault <<= sal_Int32(100); break; + case BASEPROPERTY_PROGRESSVALUE_MIN: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SCROLLVALUE_MAX: aDefault <<= sal_Int32(100); break; + case BASEPROPERTY_SCROLLVALUE_MIN: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_LINEINCREMENT: aDefault <<= sal_Int32(1); break; + case BASEPROPERTY_BLOCKINCREMENT: aDefault <<= sal_Int32(10); break; + case BASEPROPERTY_ORIENTATION: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SPINVALUE: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SPININCREMENT: aDefault <<= sal_Int32(1); break; + case BASEPROPERTY_SPINVALUE_MIN: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SPINVALUE_MAX: aDefault <<= sal_Int32(100); break; + case BASEPROPERTY_REPEAT_DELAY: aDefault <<= sal_Int32(50); break; // 50 milliseconds + case BASEPROPERTY_DEFAULTCONTROL: aDefault <<= const_cast<UnoControlModel*>(this)->getServiceName(); break; + + case BASEPROPERTY_AUTOHSCROLL: + case BASEPROPERTY_AUTOVSCROLL: + case BASEPROPERTY_MOVEABLE: + case BASEPROPERTY_CLOSEABLE: + case BASEPROPERTY_SIZEABLE: + case BASEPROPERTY_HSCROLL: + case BASEPROPERTY_DEFAULTBUTTON: + case BASEPROPERTY_MULTILINE: + case BASEPROPERTY_MULTISELECTION: + case BASEPROPERTY_TRISTATE: + case BASEPROPERTY_DROPDOWN: + case BASEPROPERTY_SPIN: + case BASEPROPERTY_READONLY: + case BASEPROPERTY_VSCROLL: + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + case BASEPROPERTY_STRICTFORMAT: + case BASEPROPERTY_REPEAT: + case BASEPROPERTY_PAINTTRANSPARENT: + case BASEPROPERTY_DESKTOP_AS_PARENT: + case BASEPROPERTY_HARDLINEBREAKS: + case BASEPROPERTY_NOLABEL: aDefault <<= false; break; + + case BASEPROPERTY_MULTISELECTION_SIMPLEMODE: + case BASEPROPERTY_HIDEINACTIVESELECTION: + case BASEPROPERTY_ENFORCE_FORMAT: + case BASEPROPERTY_AUTOCOMPLETE: + case BASEPROPERTY_SCALEIMAGE: + case BASEPROPERTY_ENABLED: + case BASEPROPERTY_PRINTABLE: + case BASEPROPERTY_ENABLEVISIBLE: + case BASEPROPERTY_DECORATION: aDefault <<= true; break; + + case BASEPROPERTY_GROUPNAME: + case BASEPROPERTY_HELPTEXT: + case BASEPROPERTY_HELPURL: + case BASEPROPERTY_IMAGEURL: + case BASEPROPERTY_DIALOGSOURCEURL: + case BASEPROPERTY_EDITMASK: + case BASEPROPERTY_LITERALMASK: + case BASEPROPERTY_LABEL: + case BASEPROPERTY_TITLE: + case BASEPROPERTY_TEXT: aDefault <<= OUString(); break; + + case BASEPROPERTY_WRITING_MODE: + case BASEPROPERTY_CONTEXT_WRITING_MODE: + aDefault <<= text::WritingMode2::CONTEXT; + break; + + case BASEPROPERTY_STRINGITEMLIST: + { + css::uno::Sequence< OUString> aStringSeq; + aDefault <<= aStringSeq; + + } + break; + case BASEPROPERTY_TYPEDITEMLIST: + { + css::uno::Sequence< css::uno::Any > aAnySeq; + aDefault <<= aAnySeq; + + } + break; + case BASEPROPERTY_SELECTEDITEMS: + { + css::uno::Sequence<sal_Int16> aINT16Seq; + aDefault <<= aINT16Seq; + } + break; + case BASEPROPERTY_CURRENCYSYMBOL: + { + OUString sDefaultCurrency( + utl::ConfigManager::getDefaultCurrency() ); + + // extract the bank symbol + sal_Int32 nSepPos = sDefaultCurrency.indexOf( '-' ); + OUString sBankSymbol; + if ( nSepPos >= 0 ) + { + sBankSymbol = sDefaultCurrency.copy( 0, nSepPos ); + sDefaultCurrency = sDefaultCurrency.copy( nSepPos + 1 ); + } + + // the remaining is the locale + LocaleDataWrapper aLocaleInfo( m_xContext, LanguageTag(sDefaultCurrency) ); + if ( sBankSymbol.isEmpty() ) + sBankSymbol = aLocaleInfo.getCurrBankSymbol(); + + // look for the currency entry (for this language) which has the given bank symbol + const Sequence< Currency2 > aAllCurrencies = aLocaleInfo.getAllCurrencies(); + + OUString sCurrencySymbol = aLocaleInfo.getCurrSymbol(); + if ( sBankSymbol.isEmpty() ) + { + DBG_ASSERT( aAllCurrencies.hasElements(), "UnoControlModel::ImplGetDefaultValue: no currencies at all!" ); + if ( aAllCurrencies.hasElements() ) + { + sBankSymbol = aAllCurrencies[0].BankSymbol; + sCurrencySymbol = aAllCurrencies[0].Symbol; + } + } + + if ( !sBankSymbol.isEmpty() ) + { + bool bLegacy = false; + bool bFound = false; + for ( const Currency2& rCurrency : aAllCurrencies ) + if ( rCurrency.BankSymbol == sBankSymbol ) + { + sCurrencySymbol = rCurrency.Symbol; + if ( rCurrency.LegacyOnly ) + bLegacy = true; + else + { + bFound = true; + break; + } + } + DBG_ASSERT( bLegacy || bFound, "UnoControlModel::ImplGetDefaultValue: did not find the given bank symbol!" ); + } + + aDefault <<= sCurrencySymbol; + } + break; + + default: OSL_FAIL( "ImplGetDefaultValue - unknown Property" ); + } + } + + return aDefault; +} + +void UnoControlModel::ImplRegisterProperty( sal_uInt16 nPropId, const css::uno::Any& rDefault ) +{ + maData[ nPropId ] = rDefault; +} + +void UnoControlModel::ImplRegisterProperty( sal_uInt16 nPropId ) +{ + ImplRegisterProperty( nPropId, ImplGetDefaultValue( nPropId ) ); + + if ( nPropId == BASEPROPERTY_FONTDESCRIPTOR ) + { + // some properties are not included in the FontDescriptor, but every time + // when we have a FontDescriptor we want to have these properties too. + // => Easier to register the here, instead everywhere where I register the FontDescriptor... + + ImplRegisterProperty( BASEPROPERTY_TEXTCOLOR ); + ImplRegisterProperty( BASEPROPERTY_TEXTLINECOLOR ); + ImplRegisterProperty( BASEPROPERTY_FONTRELIEF ); + ImplRegisterProperty( BASEPROPERTY_FONTEMPHASISMARK ); + } +} + +void UnoControlModel::ImplRegisterProperties( const std::vector< sal_uInt16 > &rIds ) +{ + for (const auto& rId : rIds) + { + if( !ImplHasProperty( rId ) ) + ImplRegisterProperty( rId, ImplGetDefaultValue( rId ) ); + } +} + +// css::uno::XInterface +css::uno::Any UnoControlModel::queryAggregation( const css::uno::Type & rType ) +{ + Any aRet = UnoControlModel_Base::queryAggregation( rType ); + if ( !aRet.hasValue() ) + aRet = ::comphelper::OPropertySetHelper::queryInterface( rType ); + return aRet; +} + +// XInterface +IMPLEMENT_FORWARD_REFCOUNT( UnoControlModel, UnoControlModel_Base ) + +// css::lang::XTypeProvider +IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoControlModel, UnoControlModel_Base, ::comphelper::OPropertySetHelper ) + + +uno::Reference< util::XCloneable > UnoControlModel::createClone() +{ + rtl::Reference<UnoControlModel> pClone = Clone(); + return pClone; +} + +// css::lang::XComponent +void UnoControlModel::dispose( ) +{ + std::unique_lock aGuard( m_aMutex ); + + css::lang::EventObject aEvt; + aEvt.Source = static_cast<css::uno::XAggregation*>(static_cast<cppu::OWeakAggObject*>(this)); + maDisposeListeners.disposeAndClear( aGuard, aEvt ); + + // let the property set helper notify our property listeners + OPropertySetHelper::disposing(aGuard); +} + +void UnoControlModel::addEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + maDisposeListeners.addInterface( rxListener ); +} + +void UnoControlModel::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + maDisposeListeners.removeInterface( rxListener ); +} + + +// css::beans::XPropertyState +css::beans::PropertyState UnoControlModel::getPropertyState( const OUString& PropertyName ) +{ + std::unique_lock aGuard( m_aMutex ); + return getPropertyStateImpl(aGuard, PropertyName); +} + +css::beans::PropertyState UnoControlModel::getPropertyStateImpl( std::unique_lock<std::mutex>& rGuard, const OUString& PropertyName ) +{ + sal_uInt16 nPropId = GetPropertyId( PropertyName ); + + css::uno::Any aValue = getPropertyValueImpl( rGuard, PropertyName ); + css::uno::Any aDefault = ImplGetDefaultValue( nPropId ); + + return CompareProperties( aValue, aDefault ) ? css::beans::PropertyState_DEFAULT_VALUE : css::beans::PropertyState_DIRECT_VALUE; +} + +css::uno::Sequence< css::beans::PropertyState > UnoControlModel::getPropertyStates( const css::uno::Sequence< OUString >& PropertyNames ) +{ + std::unique_lock aGuard( m_aMutex ); + + sal_Int32 nNames = PropertyNames.getLength(); + + css::uno::Sequence< css::beans::PropertyState > aStates( nNames ); + + std::transform(PropertyNames.begin(), PropertyNames.end(), aStates.getArray(), + [this, &aGuard](const OUString& rName) -> css::beans::PropertyState + { return getPropertyStateImpl(aGuard, rName); }); + + return aStates; +} + +void UnoControlModel::setPropertyToDefault( const OUString& PropertyName ) +{ + Any aDefaultValue; + { + std::unique_lock aGuard( m_aMutex ); + aDefaultValue = ImplGetDefaultValue( GetPropertyId( PropertyName ) ); + } + setPropertyValue( PropertyName, aDefaultValue ); +} + +css::uno::Any UnoControlModel::getPropertyDefault( const OUString& rPropertyName ) +{ + std::unique_lock aGuard( m_aMutex ); + + return ImplGetDefaultValue( GetPropertyId( rPropertyName ) ); +} + + +// css::io::XPersistObjec +OUString UnoControlModel::getServiceName( ) +{ + OSL_FAIL( "ServiceName of UnoControlModel ?!" ); + return OUString(); +} + +void UnoControlModel::write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) +{ + std::unique_lock aGuard( m_aMutex ); + + css::uno::Reference< css::io::XMarkableStream > xMark( OutStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no css::io::XMarkableStream!" ); + + OutStream->writeShort( UNOCONTROL_STREAMVERSION ); + + o3tl::sorted_vector<sal_uInt16> aProps; + + for (const auto& rData : maData) + { + if ( ( ( GetPropertyAttribs( rData.first ) & css::beans::PropertyAttribute::TRANSIENT ) == 0 ) + && ( getPropertyStateImpl( aGuard, GetPropertyName( rData.first ) ) != css::beans::PropertyState_DEFAULT_VALUE ) ) + { + aProps.insert( rData.first ); + } + } + + sal_uInt32 nProps = aProps.size(); + + // Save FontProperty always in the old format (due to missing distinction + // between 5.0 and 5.1) + OutStream->writeLong( ( aProps.find( BASEPROPERTY_FONTDESCRIPTOR ) != aProps.end() ) ? ( nProps + 3 ) : nProps ); + for ( const auto& rProp : aProps ) + { + sal_Int32 nPropDataBeginMark = xMark->createMark(); + OutStream->writeLong( 0 ); // DataLen + + const css::uno::Any* pProp = &(maData[rProp]); + OutStream->writeShort( rProp ); + + bool bVoid = pProp->getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + OutStream->writeBoolean( bVoid ); + + if ( !bVoid ) + { + const css::uno::Any& rValue = *pProp; + const css::uno::Type& rType = rValue.getValueType(); + + if ( rType == cppu::UnoType< bool >::get() ) + { + bool b = false; + rValue >>= b; + OutStream->writeBoolean( b ); + } + else if ( rType == ::cppu::UnoType< OUString >::get() ) + { + OUString aUString; + rValue >>= aUString; + OutStream->writeUTF( aUString ); + } + else if ( rType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() ) + { + sal_uInt16 n = 0; + rValue >>= n; + OutStream->writeShort( n ); + } + else if ( rType == cppu::UnoType<sal_Int16>::get() ) + { + sal_Int16 n = 0; + rValue >>= n; + OutStream->writeShort( n ); + } + else if ( rType == cppu::UnoType<sal_uInt32>::get() ) + { + sal_uInt32 n = 0; + rValue >>= n; + OutStream->writeLong( n ); + } + else if ( rType == cppu::UnoType<sal_Int32>::get() ) + { + sal_Int32 n = 0; + rValue >>= n; + OutStream->writeLong( n ); + } + else if ( rType == cppu::UnoType<double>::get() ) + { + double n = 0; + rValue >>= n; + OutStream->writeDouble( n ); + } + else if ( rType == cppu::UnoType< css::awt::FontDescriptor >::get() ) + { + css::awt::FontDescriptor aFD; + rValue >>= aFD; + OutStream->writeUTF( aFD.Name ); + OutStream->writeShort( aFD.Height ); + OutStream->writeShort( aFD.Width ); + OutStream->writeUTF( aFD.StyleName ); + OutStream->writeShort( aFD.Family ); + OutStream->writeShort( aFD.CharSet ); + OutStream->writeShort( aFD.Pitch ); + OutStream->writeDouble( aFD.CharacterWidth ); + OutStream->writeDouble( aFD.Weight ); + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >(aFD.Slant) ); + OutStream->writeShort( aFD.Underline ); + OutStream->writeShort( aFD.Strikeout ); + OutStream->writeDouble( aFD.Orientation ); + OutStream->writeBoolean( aFD.Kerning ); + OutStream->writeBoolean( aFD.WordLineMode ); + OutStream->writeShort( aFD.Type ); + } + else if ( rType == cppu::UnoType<css::util::Date>::get() ) + { + css::util::Date d; + rValue >>= d; + OutStream->writeLong(d.Day + 100 * d.Month + 10000 * d.Year); + // YYYYMMDD + } + else if ( rType == cppu::UnoType<css::util::Time>::get() ) + { + css::util::Time t; + rValue >>= t; + OutStream->writeLong( + t.NanoSeconds / 1000000 + 100 * t.Seconds + + 10000 * t.Minutes + 1000000 * t.Hours); // HHMMSShh + } + else if ( rType == cppu::UnoType< css::uno::Sequence< OUString> >::get() ) + { + css::uno::Sequence< OUString> aSeq; + rValue >>= aSeq; + tools::Long nEntries = aSeq.getLength(); + OutStream->writeLong( nEntries ); + for ( const auto& rVal : std::as_const(aSeq) ) + OutStream->writeUTF( rVal ); + } + else if ( rType == cppu::UnoType< cppu::UnoSequenceType<cppu::UnoUnsignedShortType> >::get() ) + { + css::uno::Sequence<sal_uInt16> aSeq; + rValue >>= aSeq; + tools::Long nEntries = aSeq.getLength(); + OutStream->writeLong( nEntries ); + for ( const auto nVal : std::as_const(aSeq) ) + OutStream->writeShort( nVal ); + } + else if ( rType == cppu::UnoType< css::uno::Sequence<sal_Int16> >::get() ) + { + css::uno::Sequence<sal_Int16> aSeq; + rValue >>= aSeq; + tools::Long nEntries = aSeq.getLength(); + OutStream->writeLong( nEntries ); + for ( const auto nVal : std::as_const(aSeq) ) + OutStream->writeShort( nVal ); + } + else if ( rType.getTypeClass() == TypeClass_ENUM ) + { + sal_Int32 nAsInt = 0; + ::cppu::enum2int( nAsInt, rValue ); + OutStream->writeLong( nAsInt ); + } +#if OSL_DEBUG_LEVEL > 0 + else + { + SAL_WARN( "toolkit", "UnoControlModel::write: don't know how to handle a property of type '" + << rType.getTypeName() + << "'.\n(Currently handling property '" + << GetPropertyName( rProp ) + << "'.)"); + } +#endif + } + + sal_Int32 nPropDataLen = xMark->offsetToMark( nPropDataBeginMark ); + xMark->jumpToMark( nPropDataBeginMark ); + OutStream->writeLong( nPropDataLen ); + xMark->jumpToFurthest(); + xMark->deleteMark(nPropDataBeginMark); + } + + if ( aProps.find( BASEPROPERTY_FONTDESCRIPTOR ) == aProps.end() ) + return; + + const css::uno::Any* pProp = &maData[ BASEPROPERTY_FONTDESCRIPTOR ]; + // Until 5.0 export arrives, write old format... + css::awt::FontDescriptor aFD; + (*pProp) >>= aFD; + + for ( sal_uInt16 n = BASEPROPERTY_FONT_TYPE; n <= BASEPROPERTY_FONT_ATTRIBS; n++ ) + { + sal_Int32 nPropDataBeginMark = xMark->createMark(); + OutStream->writeLong( 0 ); // DataLen + OutStream->writeShort( n ); // PropId + OutStream->writeBoolean( false ); // Void + + if ( n == BASEPROPERTY_FONT_TYPE ) + { + OutStream->writeUTF( aFD.Name ); + OutStream->writeUTF( aFD.StyleName ); + OutStream->writeShort( aFD.Family ); + OutStream->writeShort( aFD.CharSet ); + OutStream->writeShort( aFD.Pitch ); + } + else if ( n == BASEPROPERTY_FONT_SIZE ) + { + OutStream->writeLong( aFD.Width ); + OutStream->writeLong( aFD.Height ); + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >( + vcl::unohelper::ConvertFontWidth(aFD.CharacterWidth)) ); + } + else if ( n == BASEPROPERTY_FONT_ATTRIBS ) + { + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >( + vcl::unohelper::ConvertFontWeight(aFD.Weight)) ); + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >(aFD.Slant) ); + OutStream->writeShort( aFD.Underline ); + OutStream->writeShort( aFD.Strikeout ); + OutStream->writeShort( static_cast<short>(aFD.Orientation * 10) ); + OutStream->writeBoolean( aFD.Kerning ); + OutStream->writeBoolean( aFD.WordLineMode ); + } + else + { + OSL_FAIL( "Property?!" ); + } + + sal_Int32 nPropDataLen = xMark->offsetToMark( nPropDataBeginMark ); + xMark->jumpToMark( nPropDataBeginMark ); + OutStream->writeLong( nPropDataLen ); + xMark->jumpToFurthest(); + xMark->deleteMark(nPropDataBeginMark); + } +} + +void UnoControlModel::read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) +{ + std::unique_lock aGuard( m_aMutex ); + + css::uno::Reference< css::io::XMarkableStream > xMark( InStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "read: no css::io::XMarkableStream!" ); + + short nVersion = InStream->readShort(); + sal_uInt32 nProps = static_cast<sal_uInt32>(InStream->readLong()); + css::uno::Sequence< OUString> aProps( nProps ); + css::uno::Sequence< css::uno::Any> aValues( nProps ); + bool bInvalidEntries = false; + + // Unfortunately, there's no mark for the whole block, thus only properties may be changed. + // No data for the model may be added following the properties + + // Used for import of old parts in css::awt::FontDescriptor + std::unique_ptr<css::awt::FontDescriptor> pFD; + + for ( sal_uInt32 i = 0; i < nProps; i++ ) + { + sal_Int32 nPropDataBeginMark = xMark->createMark(); + sal_Int32 nPropDataLen = InStream->readLong(); + + sal_uInt16 nPropId = static_cast<sal_uInt16>(InStream->readShort()); + + css::uno::Any aValue; + bool bIsVoid = InStream->readBoolean(); + if ( !bIsVoid ) + { + if ( maData.find( nPropId ) != maData.end() ) + { + const css::uno::Type* pType = GetPropertyType( nPropId ); + if ( *pType == cppu::UnoType<bool>::get() ) + { + bool b = InStream->readBoolean(); + aValue <<= b; + } + else if ( *pType == cppu::UnoType<OUString>::get() ) + { + OUString aUTF = InStream->readUTF(); + aValue <<= aUTF; + } + else if ( *pType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() ) + { + sal_uInt16 n = InStream->readShort(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<sal_Int16>::get() ) + { + sal_Int16 n = InStream->readShort(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<sal_uInt32>::get() ) + { + sal_uInt32 n = InStream->readLong(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<sal_Int32>::get() ) + { + sal_Int32 n = InStream->readLong(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<double>::get() ) + { + double n = InStream->readDouble(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType< css::awt::FontDescriptor >::get() ) + { + css::awt::FontDescriptor aFD; + aFD.Name = InStream->readUTF(); + aFD.Height = InStream->readShort(); + aFD.Width = InStream->readShort(); + aFD.StyleName = InStream->readUTF(); + aFD.Family = InStream->readShort(); + aFD.CharSet = InStream->readShort(); + aFD.Pitch = InStream->readShort(); + aFD.CharacterWidth = static_cast<float>(InStream->readDouble()); + aFD.Weight = static_cast<float>(InStream->readDouble()); + aFD.Slant = static_cast<css::awt::FontSlant>(InStream->readShort()); + aFD.Underline = InStream->readShort(); + aFD.Strikeout = InStream->readShort(); + aFD.Orientation = static_cast<float>(InStream->readDouble()); + aFD.Kerning = InStream->readBoolean() != 0; + aFD.WordLineMode = InStream->readBoolean() != 0; + aFD.Type = InStream->readShort(); + aValue <<= aFD; + } + else if ( *pType == cppu::UnoType<css::util::Date>::get() ) + { + sal_Int32 n = InStream->readLong(); // YYYYMMDD + aValue <<= css::util::Date( + n % 100, (n / 100) % 100, n / 10000); + } + else if ( *pType == cppu::UnoType<css::util::Time>::get() ) + { + sal_Int32 n = InStream->readLong(); // HHMMSShh + aValue <<= css::util::Time( + (n % 100) * 1000000, (n / 100) % 100, (n / 10000) % 100, + n / 1000000, false); + } + else if ( *pType == cppu::UnoType< css::uno::Sequence< OUString> >::get() ) + { + tools::Long nEntries = InStream->readLong(); + css::uno::Sequence< OUString> aSeq( nEntries ); + for ( tools::Long n = 0; n < nEntries; n++ ) + aSeq.getArray()[n] = InStream->readUTF(); + aValue <<= aSeq; + + } + else if ( *pType == cppu::UnoType< cppu::UnoSequenceType<cppu::UnoUnsignedShortType> >::get() ) + + { + tools::Long nEntries = InStream->readLong(); + css::uno::Sequence<sal_uInt16> aSeq( nEntries ); + for ( tools::Long n = 0; n < nEntries; n++ ) + aSeq.getArray()[n] = static_cast<sal_uInt16>(InStream->readShort()); + aValue <<= aSeq; + } + else if ( *pType == cppu::UnoType< css::uno::Sequence<sal_Int16> >::get() ) + { + tools::Long nEntries = InStream->readLong(); + css::uno::Sequence<sal_Int16> aSeq( nEntries ); + for ( tools::Long n = 0; n < nEntries; n++ ) + aSeq.getArray()[n] = InStream->readShort(); + aValue <<= aSeq; + } + else if ( pType->getTypeClass() == TypeClass_ENUM ) + { + sal_Int32 nAsInt = InStream->readLong(); + aValue = ::cppu::int2enum( nAsInt, *pType ); + } + else + { + SAL_WARN( "toolkit", "UnoControlModel::read: don't know how to handle a property of type '" + << pType->getTypeName() + << "'.\n(Currently handling property '" + << GetPropertyName( nPropId ) + << "'.)"); + } + } + else + { + // Old trash from 5.0 + if ( nPropId == BASEPROPERTY_FONT_TYPE ) + { + // Redundant information for older versions + // is skipped by MarkableStream + if ( nVersion < 2 ) + { + if ( !pFD ) + { + pFD.reset(new css::awt::FontDescriptor); + auto it = maData.find( BASEPROPERTY_FONTDESCRIPTOR ); + if ( it != maData.end() ) // due to defaults... + it->second >>= *pFD; + } + pFD->Name = InStream->readUTF(); + pFD->StyleName = InStream->readUTF(); + pFD->Family = InStream->readShort(); + pFD->CharSet = InStream->readShort(); + pFD->Pitch = InStream->readShort(); + } + } + else if ( nPropId == BASEPROPERTY_FONT_SIZE ) + { + if ( nVersion < 2 ) + { + if ( !pFD ) + { + pFD.reset(new css::awt::FontDescriptor); + auto it = maData.find(BASEPROPERTY_FONTDESCRIPTOR); + if ( it != maData.end() ) // due to defaults... + it->second >>= *pFD; + } + pFD->Width = static_cast<sal_Int16>(InStream->readLong()); + pFD->Height = static_cast<sal_Int16>(InStream->readLong()); + InStream->readShort(); // ignore css::awt::FontWidth - it was + // misspelled and is no longer needed + pFD->CharacterWidth = css::awt::FontWidth::DONTKNOW; + } + } + else if ( nPropId == BASEPROPERTY_FONT_ATTRIBS ) + { + if ( nVersion < 2 ) + { + if ( !pFD ) + { + pFD.reset(new css::awt::FontDescriptor); + auto it = maData.find(BASEPROPERTY_FONTDESCRIPTOR); + if ( it != maData.end() ) // due to defaults... + it->second >>= *pFD; + } + pFD->Weight = vcl::unohelper::ConvertFontWeight(static_cast<FontWeight>(InStream->readShort())); + pFD->Slant = static_cast<css::awt::FontSlant>(InStream->readShort()); + pFD->Underline = InStream->readShort(); + pFD->Strikeout = InStream->readShort(); + pFD->Orientation = static_cast<float>(static_cast<double>(InStream->readShort())) / 10; + pFD->Kerning = InStream->readBoolean() != 0; + pFD->WordLineMode = InStream->readBoolean() != 0; + } + } + else + { + OSL_FAIL( "read: unknown Property!" ); + } + } + } + else // bVoid + { + if ( nPropId == BASEPROPERTY_FONTDESCRIPTOR ) + { + EmptyFontDescriptor aFD; + aValue <<= aFD; + } + } + + if ( maData.find( nPropId ) != maData.end() ) + { + aProps.getArray()[i] = GetPropertyName( nPropId ); + aValues.getArray()[i] = aValue; + } + else + { + bInvalidEntries = true; + } + + // Skip rest of input if there is more data in stream than this version can handle + xMark->jumpToMark( nPropDataBeginMark ); + InStream->skipBytes( nPropDataLen ); + xMark->deleteMark(nPropDataBeginMark); + } + if ( bInvalidEntries ) + { + for ( sal_Int32 i = 0; i < aProps.getLength(); i++ ) + { + if ( aProps.getConstArray()[i].isEmpty() ) + { + ::comphelper::removeElementAt( aProps, i ); + ::comphelper::removeElementAt( aValues, i ); + i--; + } + } + } + + try + { + setPropertyValuesImpl( aGuard, aProps, aValues ); + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + if ( pFD ) + { + css::uno::Any aValue; + aValue <<= *pFD; + setFastPropertyValueImpl( aGuard, BASEPROPERTY_FONTDESCRIPTOR, aValue ); + } +} + + +// css::lang::XServiceInfo +OUString UnoControlModel::getImplementationName( ) +{ + OSL_FAIL( "This method should be overridden!" ); + return OUString(); + +} + +sal_Bool UnoControlModel::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > UnoControlModel::getSupportedServiceNames( ) +{ + return { "com.sun.star.awt.UnoControlModel" }; +} + +bool UnoControlModel::convertFastPropertyValue( std::unique_lock<std::mutex>& rGuard, Any & rConvertedValue, Any & rOldValue, sal_Int32 nPropId, const Any& rValue ) +{ + bool bVoid = rValue.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + if ( bVoid ) + { + rConvertedValue.clear(); + } + else + { + const css::uno::Type* pDestType = GetPropertyType( static_cast<sal_uInt16>(nPropId) ); + if ( pDestType->getTypeClass() == TypeClass_ANY ) + { + rConvertedValue = rValue; + } + else + { + if ( pDestType->equals( rValue.getValueType() ) ) + { + rConvertedValue = rValue; + } + else + { + bool bConverted = false; + // 13.03.2001 - 84923 - frank.schoenheit@germany.sun.com + + switch (pDestType->getTypeClass()) + { + case TypeClass_DOUBLE: + { + // try as double + double nAsDouble = 0; + bConverted = ( rValue >>= nAsDouble ); + if ( bConverted ) + rConvertedValue <<= nAsDouble; + else + { // try as integer + sal_Int32 nAsInteger = 0; + bConverted = ( rValue >>= nAsInteger ); + if ( bConverted ) + rConvertedValue <<= static_cast<double>(nAsInteger); + } + } + break; + case TypeClass_SHORT: + { + sal_Int16 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_UNSIGNED_SHORT: + { + sal_uInt16 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_LONG: + { + sal_Int32 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_INTERFACE: + { + if ( rValue.getValueType().getTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > xPure( rValue, UNO_QUERY ); + if ( xPure.is() ) + rConvertedValue = xPure->queryInterface( *pDestType ); + else + rConvertedValue.setValue( nullptr, *pDestType ); + bConverted = true; + } + } + break; + case TypeClass_ENUM: + { + sal_Int32 nValue = 0; + bConverted = ( rValue >>= nValue ); + if ( bConverted ) + rConvertedValue = ::cppu::int2enum( nValue, *pDestType ); + } + break; + default: ; // avoid compiler warning + } + + if (!bConverted) + { + throw css::lang::IllegalArgumentException( + "Unable to convert the given value for the property " + + GetPropertyName( static_cast<sal_uInt16>(nPropId) ) + + ".\nExpected type: " + pDestType->getTypeName() + + "\nFound type: " + rValue.getValueType().getTypeName(), + static_cast< css::beans::XPropertySet* >(this), + 1); + } + } + } + } + + // the current value + getFastPropertyValue( rGuard, rOldValue, nPropId ); + return !CompareProperties( rConvertedValue, rOldValue ); +} + +void UnoControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 nPropId, const css::uno::Any& rValue ) +{ + // Missing: the fake solo properties of the FontDescriptor + + ImplPropertyTable::const_iterator it = maData.find( nPropId ); + const css::uno::Any* pProp = it == maData.end() ? nullptr : &(it->second); + ENSURE_OR_RETURN_VOID( pProp, "UnoControlModel::setFastPropertyValue_NoBroadcast: invalid property id!" ); + + DBG_ASSERT( ( rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID ) || ( GetPropertyAttribs( static_cast<sal_uInt16>(nPropId) ) & css::beans::PropertyAttribute::MAYBEVOID ), "Property should not be VOID!" ); + maData[ nPropId ] = rValue; +} + +void UnoControlModel::getFastPropertyValue( std::unique_lock<std::mutex>& /*rGuard*/, css::uno::Any& rValue, sal_Int32 nPropId ) const +{ + ImplPropertyTable::const_iterator it = maData.find( nPropId ); + const css::uno::Any* pProp = it == maData.end() ? nullptr : &(it->second); + + if ( pProp ) + rValue = *pProp; + else if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + { + const auto iter = maData.find( BASEPROPERTY_FONTDESCRIPTOR ); + assert(iter != maData.end()); + pProp = &(iter->second); + css::awt::FontDescriptor aFD; + (*pProp) >>= aFD; + switch ( nPropId ) + { + case BASEPROPERTY_FONTDESCRIPTORPART_NAME: rValue <<= aFD.Name; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME: rValue <<= aFD.StyleName; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_FAMILY: rValue <<= aFD.Family; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARSET: rValue <<= aFD.CharSet; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT: rValue <<= static_cast<float>(aFD.Height); + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT: rValue <<= aFD.Weight; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_SLANT: rValue <<= static_cast<sal_Int16>(aFD.Slant); + break; + case BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE: rValue <<= aFD.Underline; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT: rValue <<= aFD.Strikeout; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WIDTH: rValue <<= aFD.Width; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_PITCH: rValue <<= aFD.Pitch; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH: rValue <<= aFD.CharacterWidth; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION: rValue <<= aFD.Orientation; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_KERNING: rValue <<= aFD.Kerning; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE: rValue <<= aFD.WordLineMode; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_TYPE: rValue <<= aFD.Type; + break; + default: OSL_FAIL( "FontProperty?!" ); + } + } + else + { + OSL_FAIL( "getFastPropertyValue - invalid Property!" ); + } +} + +// css::beans::XFastPropertySet +void UnoControlModel::setFastPropertyValueImpl( std::unique_lock<std::mutex>& rGuard, sal_Int32 nPropId, const css::uno::Any& rValue ) +{ + if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + { + Any aOldSingleValue; + getFastPropertyValue( rGuard, aOldSingleValue, BASEPROPERTY_FONTDESCRIPTORPART_START ); + + css::uno::Any* pProp = &maData[ BASEPROPERTY_FONTDESCRIPTOR ]; + FontDescriptor aOldFontDescriptor; + (*pProp) >>= aOldFontDescriptor; + + FontDescriptor aNewFontDescriptor( aOldFontDescriptor ); + lcl_ImplMergeFontProperty( aNewFontDescriptor, static_cast<sal_uInt16>(nPropId), rValue ); + + Any aNewValue; + aNewValue <<= aNewFontDescriptor; + sal_Int32 nDescriptorId = BASEPROPERTY_FONTDESCRIPTOR; + + // also, we need fire a propertyChange event for the single property, since with + // the above line, only an event for the FontDescriptor property will be fired + Any aNewSingleValue; + getFastPropertyValue( rGuard, aNewSingleValue, BASEPROPERTY_FONTDESCRIPTORPART_START ); + + setFastPropertyValues( rGuard, 1, &nDescriptorId, &aNewValue, 1 ); + fire( rGuard, &nPropId, &aNewSingleValue, &aOldSingleValue, 1, false ); + } + else + setFastPropertyValues( rGuard, 1, &nPropId, &rValue, 1 ); +} + +// css::beans::XMultiPropertySet +css::uno::Reference< css::beans::XPropertySetInfo > UnoControlModel::getPropertySetInfo( ) +{ + OSL_FAIL( "UnoControlModel::getPropertySetInfo() not possible!" ); + return css::uno::Reference< css::beans::XPropertySetInfo >(); +} + +void UnoControlModel::setPropertyValues( const css::uno::Sequence< OUString >& rPropertyNames, const css::uno::Sequence< css::uno::Any >& Values ) +{ + std::unique_lock aGuard( m_aMutex ); + setPropertyValuesImpl(aGuard, rPropertyNames, Values); +} + +void UnoControlModel::setPropertyValuesImpl( std::unique_lock<std::mutex>& rGuard, const css::uno::Sequence< OUString >& rPropertyNames, const css::uno::Sequence< css::uno::Any >& Values ) +{ + sal_Int32 nProps = rPropertyNames.getLength(); + if (nProps != Values.getLength()) + throw css::lang::IllegalArgumentException("lengths do not match", + getXWeak(), -1); + +// sal_Int32* pHandles = new sal_Int32[nProps]; + // don't do this - it leaks in case of an exception + Sequence< sal_Int32 > aHandles( nProps ); + sal_Int32* pHandles = aHandles.getArray(); + + // may need to change the order in the sequence, for this we need a non-const value sequence + uno::Sequence< uno::Any > aValues( Values ); + uno::Any* pValues = aValues.getArray(); + + sal_Int32 nValidHandles = getInfoHelper().fillHandles( pHandles, rPropertyNames ); + + if ( !nValidHandles ) + return; + + // if somebody sets properties which are single aspects of a font descriptor, + // remove them, and build a font descriptor instead + std::unique_ptr< awt::FontDescriptor > pFD; + for ( sal_Int32 n = 0; n < nProps; ++n ) + { + if ( ( pHandles[n] >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( pHandles[n] <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + { + if (!pFD) + { + css::uno::Any* pProp = &maData[ BASEPROPERTY_FONTDESCRIPTOR ]; + pFD.reset( new awt::FontDescriptor ); + (*pProp) >>= *pFD; + } + lcl_ImplMergeFontProperty( *pFD, static_cast<sal_uInt16>(pHandles[n]), pValues[n] ); + pHandles[n] = -1; + nValidHandles--; + } + } + + if ( nValidHandles ) + { + ImplNormalizePropertySequence( nProps, pHandles, pValues, &nValidHandles ); + setFastPropertyValues( rGuard, nProps, pHandles, pValues, nValidHandles ); + } + + // Don't merge FD property into array, as it is sorted + if (pFD) + { + css::uno::Any aValue; + aValue <<= *pFD; + sal_Int32 nHandle = BASEPROPERTY_FONTDESCRIPTOR; + setFastPropertyValues( rGuard, 1, &nHandle, &aValue, 1 ); + } +} + + +void UnoControlModel::ImplNormalizePropertySequence( const sal_Int32, sal_Int32*, + uno::Any*, sal_Int32* ) const +{ + // nothing to do here +} + +void UnoControlModel::ImplEnsureHandleOrder( const sal_Int32 _nCount, sal_Int32* _pHandles, + uno::Any* _pValues, sal_Int32 _nFirstHandle, sal_Int32 _nSecondHandle ) +{ + for ( sal_Int32 i=0; i < _nCount; ++_pHandles, ++_pValues, ++i ) + { + if ( _nSecondHandle == *_pHandles ) + { + sal_Int32* pLaterHandles = _pHandles + 1; + uno::Any* pLaterValues = _pValues + 1; + for ( sal_Int32 j = i + 1; j < _nCount; ++j, ++pLaterHandles, ++pLaterValues ) + { + if ( _nFirstHandle == *pLaterHandles ) + { + // indeed it is -> exchange the both places in the sequences + sal_Int32 nHandle( *_pHandles ); + *_pHandles = *pLaterHandles; + *pLaterHandles = nHandle; + + uno::Any aValue( *_pValues ); + *_pValues = *pLaterValues; + *pLaterValues = aValue; + + break; + // this will leave the inner loop, and continue with the outer loop. + // Note that this means we will encounter the _nSecondHandle handle, again, once we reached + // (in the outer loop) the place where we just put it. + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrols.cxx b/toolkit/source/controls/unocontrols.cxx new file mode 100644 index 0000000000..d9bc55f8cb --- /dev/null +++ b/toolkit/source/controls/unocontrols.cxx @@ -0,0 +1,4757 @@ +/* -*- 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/XTextArea.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/LineEndFormat.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> + +#include <o3tl/safeint.hxx> +#include <controls/formattedcontrol.hxx> +#include <toolkit/controls/unocontrols.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/macros.hxx> + +// for introspection +#include <awt/vclxwindows.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <algorithm> + +#include <helper/imagealign.hxx> +#include <helper/unopropertyarrayhelper.hxx> +#include <utility> + +using namespace css; +using namespace css::awt; +using namespace css::lang; +using namespace css::uno; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::uno::Reference; +using namespace ::toolkit; + +uno::Reference< graphic::XGraphic > +ImageHelper::getGraphicAndGraphicObjectFromURL_nothrow( uno::Reference< graphic::XGraphicObject >& xOutGraphicObj, const OUString& _rURL ) +{ + xOutGraphicObj = nullptr; + return ImageHelper::getGraphicFromURL_nothrow( _rURL ); +} + +css::uno::Reference< css::graphic::XGraphic > +ImageHelper::getGraphicFromURL_nothrow( const OUString& _rURL ) +{ + uno::Reference< graphic::XGraphic > xGraphic; + if ( _rURL.isEmpty() ) + return xGraphic; + + try + { + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference< graphic::XGraphicProvider > xProvider( graphic::GraphicProvider::create(xContext) ); + xGraphic = xProvider->queryGraphic({ comphelper::makePropertyValue("URL", _rURL) }); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + return xGraphic; +} + + +UnoControlEditModel::UnoControlEditModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXEdit>(); +} + +OUString UnoControlEditModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.Edit"; +} + +uno::Any UnoControlEditModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + uno::Any aReturn; + + switch ( nPropId ) + { + case BASEPROPERTY_LINE_END_FORMAT: + aReturn <<= sal_Int16(awt::LineEndFormat::LINE_FEED); // LF + break; + case BASEPROPERTY_DEFAULTCONTROL: + aReturn <<= OUString( "stardiv.vcl.control.Edit" ); + break; + default: + aReturn = UnoControlModel::ImplGetDefaultValue( nPropId ); + break; + } + return aReturn; +} + +::cppu::IPropertyArrayHelper& UnoControlEditModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlEditModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlEditModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlEditModel"; +} + +css::uno::Sequence<OUString> UnoControlEditModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlEditModel", "stardiv.vcl.controlmodel.Edit" }; + return comphelper::concatSequences(UnoControlModel::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlEditModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlEditModel(context)); +} + + + +UnoEditControl::UnoEditControl() + :maTextListeners( *this ) + ,mnMaxTextLen( 0 ) + ,mbSetTextInPeer( false ) + ,mbSetMaxTextLenInPeer( false ) + ,mbHasTextProperty( false ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +uno::Any SAL_CALL UnoEditControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aReturn = UnoControlBase::queryAggregation( rType ); + if ( !aReturn.hasValue() ) + aReturn = UnoEditControl_Base::queryInterface( rType ); + return aReturn; +} + +uno::Any SAL_CALL UnoEditControl::queryInterface( const uno::Type & rType ) +{ + return UnoControlBase::queryInterface( rType ); +} + +void SAL_CALL UnoEditControl::acquire( ) noexcept +{ + UnoControlBase::acquire(); +} + +void SAL_CALL UnoEditControl::release( ) noexcept +{ + UnoControlBase::release(); +} + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoEditControl, UnoControlBase, UnoEditControl_Base ) + +OUString UnoEditControl::GetComponentServiceName() const +{ + // by default, we want a simple edit field + OUString sName( "Edit" ); + + // but maybe we are to display multi-line text? + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_MULTILINE ) ); + bool b = bool(); + if ( ( aVal >>= b ) && b ) + sName = "MultiLineEdit"; + + return sName; +} + +sal_Bool SAL_CALL UnoEditControl::setModel(const uno::Reference< awt::XControlModel >& _rModel) +{ + bool bReturn = UnoControlBase::setModel( _rModel ); + mbHasTextProperty = ImplHasProperty( BASEPROPERTY_TEXT ); + return bReturn; +} + +void UnoEditControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + bool bDone = false; + if ( GetPropertyId( rPropName ) == BASEPROPERTY_TEXT ) + { + // #96986# use setText(), or text listener will not be called. + uno::Reference < awt::XTextComponent > xTextComponent( getPeer(), uno::UNO_QUERY ); + if ( xTextComponent.is() ) + { + OUString sText; + rVal >>= sText; + ImplCheckLocalize( sText ); + xTextComponent->setText( sText ); + bDone = true; + } + } + + if ( !bDone ) + UnoControlBase::ImplSetPeerProperty( rPropName, rVal ); +} + +void UnoEditControl::dispose() +{ + lang::EventObject aEvt( *this ); + maTextListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoEditControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + { + xText->addTextListener( this ); + + if ( mbSetMaxTextLenInPeer ) + xText->setMaxTextLen( mnMaxTextLen ); + if ( mbSetTextInPeer ) + xText->setText( maText ); + } +} + +void UnoEditControl::textChanged(const awt::TextEvent& e) +{ + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + + if ( mbHasTextProperty ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TEXT ), uno::Any(xText->getText()), false ); + } + else + { + maText = xText->getText(); + } + + if ( maTextListeners.getLength() ) + maTextListeners.textChanged( e ); +} + +void UnoEditControl::addTextListener(const uno::Reference< awt::XTextListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XTextListener"); + return; + } + maTextListeners.addInterface( l ); +} + +void UnoEditControl::removeTextListener(const uno::Reference< awt::XTextListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XTextListener"); + return; + } + maTextListeners.removeInterface( l ); +} + +void UnoEditControl::setText( const OUString& aText ) +{ + if ( mbHasTextProperty ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TEXT ), uno::Any(aText), true ); + } + else + { + maText = aText; + mbSetTextInPeer = true; + uno::Reference < awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + xText->setText( maText ); + } + + // Setting the property to the VCLXWindow doesn't call textChanged + if ( maTextListeners.getLength() ) + { + awt::TextEvent aEvent; + aEvent.Source = *this; + maTextListeners.textChanged( aEvent ); + } +} + +namespace +{ + void lcl_normalize( awt::Selection& _rSel ) + { + if ( _rSel.Min > _rSel.Max ) + ::std::swap( _rSel.Min, _rSel.Max ); + } +} + +void UnoEditControl::insertText( const awt::Selection& rSel, const OUString& rNewText ) +{ + // normalize the selection - OUString::replaceAt has a strange behaviour if the min is greater than the max + awt::Selection aSelection( rSel ); + lcl_normalize( aSelection ); + + OUString aOldText = getText(); + if (aSelection.Min < 0 || aOldText.getLength() < aSelection.Max) + { + throw lang::IllegalArgumentException(); + } + + // preserve the selection resp. cursor position + awt::Selection aNewSelection( getSelection() ); +#ifdef ALSO_PRESERVE_COMPLETE_SELECTION + // (not sure - looks uglier ...) + sal_Int32 nDeletedCharacters = ( aSelection.Max - aSelection.Min ) - rNewText.getLength(); + if ( aNewSelection.Min > aSelection.Min ) + aNewSelection.Min -= nDeletedCharacters; + if ( aNewSelection.Max > aSelection.Max ) + aNewSelection.Max -= nDeletedCharacters; +#else + aNewSelection.Max = ::std::min( aNewSelection.Min, aNewSelection.Max ) + rNewText.getLength(); + aNewSelection.Min = aNewSelection.Max; +#endif + + OUString aNewText = aOldText.replaceAt( aSelection.Min, aSelection.Max - aSelection.Min, rNewText ); + setText( aNewText ); + + setSelection( aNewSelection ); +} + +OUString UnoEditControl::getText() +{ + OUString aText = maText; + + if ( mbHasTextProperty ) + aText = ImplGetPropertyValue_UString( BASEPROPERTY_TEXT ); + else + { + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + aText = xText->getText(); + } + + return aText; +} + +OUString UnoEditControl::getSelectedText() +{ + OUString sSelected; + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + sSelected = xText->getSelectedText(); + + return sSelected; +} + +void UnoEditControl::setSelection( const awt::Selection& aSelection ) +{ + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + xText->setSelection( aSelection ); +} + +awt::Selection UnoEditControl::getSelection() +{ + awt::Selection aSel; + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + aSel = xText->getSelection(); + return aSel; +} + +sal_Bool UnoEditControl::isEditable() +{ + return !ImplGetPropertyValue_BOOL( BASEPROPERTY_READONLY ); +} + +void UnoEditControl::setEditable( sal_Bool bEditable ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_READONLY ), uno::Any(!bEditable), true ); +} + +sal_Int16 UnoEditControl::getMaxTextLen() +{ + sal_Int16 nMaxLen = mnMaxTextLen; + + if ( ImplHasProperty( BASEPROPERTY_MAXTEXTLEN ) ) + nMaxLen = ImplGetPropertyValue_INT16( BASEPROPERTY_MAXTEXTLEN ); + + return nMaxLen; +} + +void UnoEditControl::setMaxTextLen( sal_Int16 nLen ) +{ + if ( ImplHasProperty( BASEPROPERTY_MAXTEXTLEN) ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MAXTEXTLEN ), uno::Any(nLen), true ); + } + else + { + mnMaxTextLen = nLen; + mbSetMaxTextLenInPeer = true; + uno::Reference < awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + xText->setMaxTextLen( mnMaxTextLen ); + } +} + +awt::Size UnoEditControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoEditControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoEditControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +awt::Size UnoEditControl::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + return Impl_getMinimumSize( nCols, nLines ); +} + +void UnoEditControl::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + Impl_getColumnsAndLines( nCols, nLines ); +} + +OUString UnoEditControl::getImplementationName( ) +{ + return "stardiv.Toolkit.UnoEditControl"; +} + +uno::Sequence< OUString > UnoEditControl::getSupportedServiceNames() +{ + css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlEdit", "stardiv.vcl.control.Edit" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames( ), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoEditControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoEditControl()); +} + + + +UnoControlFileControlModel::UnoControlFileControlModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_ALIGN ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_READONLY ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); + ImplRegisterProperty( BASEPROPERTY_VERTICALALIGN ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_HIDEINACTIVESELECTION ); +} + +OUString UnoControlFileControlModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.FileControl"; +} + +uno::Any UnoControlFileControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.FileControl" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFileControlModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFileControlModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlFileControlModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlFileControlModel"; +} + +css::uno::Sequence<OUString> +UnoControlFileControlModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFileControlModel", "stardiv.vcl.controlmodel.FileControl" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFileControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFileControlModel(context)); +} + + + +UnoFileControl::UnoFileControl() +{ +} + +OUString UnoFileControl::GetComponentServiceName() const +{ + return "filecontrol"; +} + +OUString UnoFileControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoFileControl"; +} + +css::uno::Sequence<OUString> UnoFileControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFileControl", "stardiv.vcl.control.FileControl" }; + return comphelper::concatSequences( UnoEditControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFileControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFileControl()); +} + + + +uno::Any GraphicControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_GRAPHIC ) + return uno::Any( uno::Reference< graphic::XGraphic >() ); + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +void GraphicControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + // - ImageAlign and ImagePosition need to correspond to each other + // - Graphic and ImageURL need to correspond to each other + try + { + switch ( nHandle ) + { + case BASEPROPERTY_IMAGEURL: + if ( !mbAdjustingGraphic && ImplHasProperty( BASEPROPERTY_GRAPHIC ) ) + { + mbAdjustingGraphic = true; + OUString sImageURL; + OSL_VERIFY( rValue >>= sImageURL ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_GRAPHIC, uno::Any( ImageHelper::getGraphicFromURL_nothrow( sImageURL ) ) ); + mbAdjustingGraphic = false; + } + break; + + case BASEPROPERTY_GRAPHIC: + if ( !mbAdjustingGraphic && ImplHasProperty( BASEPROPERTY_IMAGEURL ) ) + { + mbAdjustingGraphic = true; + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGEURL, uno::Any( OUString() ) ); + mbAdjustingGraphic = false; + } + break; + + case BASEPROPERTY_IMAGEALIGN: + if ( !mbAdjustingImagePosition && ImplHasProperty( BASEPROPERTY_IMAGEPOSITION ) ) + { + mbAdjustingImagePosition = true; + sal_Int16 nUNOValue = 0; + OSL_VERIFY( rValue >>= nUNOValue ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGEPOSITION, uno::Any( getExtendedImagePosition( nUNOValue ) ) ); + mbAdjustingImagePosition = false; + } + break; + case BASEPROPERTY_IMAGEPOSITION: + if ( !mbAdjustingImagePosition && ImplHasProperty( BASEPROPERTY_IMAGEALIGN ) ) + { + mbAdjustingImagePosition = true; + sal_Int16 nUNOValue = 0; + OSL_VERIFY( rValue >>= nUNOValue ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGEALIGN, uno::Any( getCompatibleImageAlign( translateImagePosition( nUNOValue ) ) ) ); + mbAdjustingImagePosition = false; + } + break; + } + } + catch( const css::uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + OSL_FAIL( "GraphicControlModel::setFastPropertyValue_NoBroadcast: caught an exception while aligning the ImagePosition/ImageAlign properties!" ); + mbAdjustingImagePosition = false; + } +} + + + +UnoControlButtonModel::UnoControlButtonModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXButton>(); + + osl_atomic_increment( &m_refCount ); + { + std::unique_lock aGuard(m_aMutex); + setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_IMAGEPOSITION, ImplGetDefaultValue( BASEPROPERTY_IMAGEPOSITION ) ); + // this ensures that our ImagePosition is consistent with our ImageAlign property (since both + // defaults are not per se consistent), since both are coupled in setFastPropertyValue_NoBroadcast + } + osl_atomic_decrement( &m_refCount ); +} + +OUString UnoControlButtonModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.Button"; +} + +uno::Any UnoControlButtonModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.Button" ) ); + case BASEPROPERTY_TOGGLE: + return uno::Any( false ); + case BASEPROPERTY_ALIGN: + return uno::Any( sal_Int16(PROPERTY_ALIGN_CENTER) ); + case BASEPROPERTY_FOCUSONCLICK: + return uno::Any( true ); + } + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlButtonModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlButtonModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlButtonModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlButtonModel"; +} + +css::uno::Sequence<OUString> UnoControlButtonModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlButtonModel", "stardiv.vcl.controlmodel.Button" }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlButtonModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlButtonModel(context)); +} + + + +UnoButtonControl::UnoButtonControl() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ + maComponentInfos.nWidth = 50; + maComponentInfos.nHeight = 14; +} + +OUString UnoButtonControl::GetComponentServiceName() const +{ + OUString aName( "pushbutton" ); + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_PUSHBUTTONTYPE ) ); + sal_Int16 n = sal_Int16(); + if ( ( aVal >>= n ) && n ) + { + // Use PushButtonType later when available... + switch ( n ) + { + case 1 /*PushButtonType::OK*/: aName = "okbutton"; + break; + case 2 /*PushButtonType::CANCEL*/: aName = "cancelbutton"; + break; + case 3 /*PushButtonType::HELP*/: aName = "helpbutton"; + break; + default: + { + OSL_FAIL( "Unknown Button Type!" ); + } + } + } + return aName; +} + +void UnoButtonControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + maItemListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + +void UnoButtonControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( maActionCommand ); + if ( maActionListeners.getLength() ) + xButton->addActionListener( &maActionListeners ); + + uno::Reference< XToggleButton > xPushButton( getPeer(), uno::UNO_QUERY ); + if ( xPushButton.is() ) + xPushButton->addItemListener( this ); +} + +void UnoButtonControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XActionListener"); + return; + } + + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->addActionListener( &maActionListeners ); + } +} + +void UnoButtonControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XActionListener"); + return; + } + + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoButtonControl::addItemListener(const uno::Reference< awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoButtonControl::removeItemListener(const uno::Reference< awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void SAL_CALL UnoButtonControl::disposing( const lang::EventObject& Source ) +{ + UnoControlBase::disposing( Source ); +} + +void SAL_CALL UnoButtonControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + // forward to model + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(static_cast<sal_Int16>(rEvent.Selected)), false ); + + // multiplex + ItemEvent aEvent( rEvent ); + aEvent.Source = *this; + maItemListeners.itemStateChanged( aEvent ); +} + +void UnoButtonControl::setLabel( const OUString& rLabel ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(rLabel), true ); +} + +void UnoButtonControl::setActionCommand( const OUString& rCommand ) +{ + maActionCommand = rCommand; + if ( getPeer().is() ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( rCommand ); + } +} + +awt::Size UnoButtonControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoButtonControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoButtonControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoButtonControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoButtonControl"; +} + +css::uno::Sequence<OUString> UnoButtonControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlButton", "stardiv.vcl.control.Button" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoButtonControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoButtonControl()); +} + + + +UnoControlImageControlModel::UnoControlImageControlModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) + ,mbAdjustingImageScaleMode( false ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXImageControl>(); +} + +OUString UnoControlImageControlModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ImageControl"; +} + +OUString UnoControlImageControlModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlImageControlModel"; +} + +css::uno::Sequence<OUString> +UnoControlImageControlModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { + "com.sun.star.awt.UnoControlImageButtonModel", + "com.sun.star.awt.UnoControlImageControlModel", + "stardiv.vcl.controlmodel.ImageButton", + "stardiv.vcl.controlmodel.ImageControl" + }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals); +} + +uno::Any UnoControlImageControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + return uno::Any( OUString( "stardiv.vcl.control.ImageControl" ) ); + + if ( nPropId == BASEPROPERTY_IMAGE_SCALE_MODE ) + return Any( awt::ImageScaleMode::ANISOTROPIC ); + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlImageControlModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlImageControlModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void UnoControlImageControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 _nHandle, const css::uno::Any& _rValue ) +{ + GraphicControlModel::setFastPropertyValue_NoBroadcast( rGuard, _nHandle, _rValue ); + + // ScaleImage is an older (and less powerful) version of ScaleMode, but keep both in sync as far as possible + try + { + switch ( _nHandle ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + if ( !mbAdjustingImageScaleMode && ImplHasProperty( BASEPROPERTY_SCALEIMAGE ) ) + { + mbAdjustingImageScaleMode = true; + sal_Int16 nScaleMode( awt::ImageScaleMode::ANISOTROPIC ); + OSL_VERIFY( _rValue >>= nScaleMode ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_SCALEIMAGE, uno::Any( nScaleMode != awt::ImageScaleMode::NONE ) ); + mbAdjustingImageScaleMode = false; + } + break; + case BASEPROPERTY_SCALEIMAGE: + if ( !mbAdjustingImageScaleMode && ImplHasProperty( BASEPROPERTY_IMAGE_SCALE_MODE ) ) + { + mbAdjustingImageScaleMode = true; + bool bScale = true; + OSL_VERIFY( _rValue >>= bScale ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGE_SCALE_MODE, uno::Any( bScale ? awt::ImageScaleMode::ANISOTROPIC : awt::ImageScaleMode::NONE ) ); + mbAdjustingImageScaleMode = false; + } + break; + } + } + catch( const Exception& ) + { + mbAdjustingImageScaleMode = false; + throw; + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlImageControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlImageControlModel(context)); +} + + + +UnoImageControlControl::UnoImageControlControl() + :maActionListeners( *this ) +{ + // TODO: Where should I look for defaults? + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 100; +} + +OUString UnoImageControlControl::GetComponentServiceName() const +{ + return "fixedimage"; +} + +void UnoImageControlControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +sal_Bool UnoImageControlControl::isTransparent() +{ + return true; +} + +awt::Size UnoImageControlControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoImageControlControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoImageControlControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoImageControlControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoImageControlControl"; +} + +css::uno::Sequence<OUString> UnoImageControlControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { + "com.sun.star.awt.UnoControlImageButton", + "com.sun.star.awt.UnoControlImageControl", + "stardiv.vcl.control.ImageButton", + "stardiv.vcl.control.ImageControl" + }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoImageControlControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoImageControlControl()); +} + + + +UnoControlRadioButtonModel::UnoControlRadioButtonModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXRadioButton>(); +} + +OUString UnoControlRadioButtonModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.RadioButton"; +} + +uno::Any UnoControlRadioButtonModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.RadioButton" ) ); + + case BASEPROPERTY_VISUALEFFECT: + return uno::Any( sal_Int16(awt::VisualEffect::LOOK3D) ); + } + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlRadioButtonModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlRadioButtonModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlRadioButtonModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlRadioButtonModel"; +} + +css::uno::Sequence<OUString> +UnoControlRadioButtonModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlRadioButtonModel", "stardiv.vcl.controlmodel.RadioButton" }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlRadioButtonModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlRadioButtonModel(context)); +} + + + +UnoRadioButtonControl::UnoRadioButtonControl() + :maItemListeners( *this ) + ,maActionListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoRadioButtonControl::GetComponentServiceName() const +{ + return "radiobutton"; +} + +void UnoRadioButtonControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maItemListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + + +sal_Bool UnoRadioButtonControl::isTransparent() +{ + return true; +} + +void UnoRadioButtonControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XRadioButton > xRadioButton( getPeer(), uno::UNO_QUERY ); + xRadioButton->addItemListener( this ); + + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( maActionCommand ); + if ( maActionListeners.getLength() ) + xButton->addActionListener( &maActionListeners ); + + // as default, set the "AutoToggle" to true + // (it is set to false in VCLXToolkit::ImplCreateWindow because of #87254#, but we want to + // have it enabled by default because of 85071) + uno::Reference< awt::XVclWindowPeer > xVclWindowPeer( getPeer(), uno::UNO_QUERY ); + if ( xVclWindowPeer.is() ) + xVclWindowPeer->setProperty( GetPropertyName( BASEPROPERTY_AUTOTOGGLE ), css::uno::Any(true) ); +} + +void UnoRadioButtonControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoRadioButtonControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void UnoRadioButtonControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->addActionListener( &maActionListeners ); + } +} + +void UnoRadioButtonControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoRadioButtonControl::setLabel( const OUString& rLabel ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(rLabel), true ); +} + +void UnoRadioButtonControl::setActionCommand( const OUString& rCommand ) +{ + maActionCommand = rCommand; + if ( getPeer().is() ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( rCommand ); + } +} + +void UnoRadioButtonControl::setState( sal_Bool bOn ) +{ + sal_Int16 nState = bOn ? 1 : 0; + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(nState), true ); +} + +sal_Bool UnoRadioButtonControl::getState() +{ + sal_Int16 nState = 0; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ) ); + aVal >>= nState; + return nState != 0; +} + +void UnoRadioButtonControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(static_cast<sal_Int16>(rEvent.Selected)), false ); + + // compatibility: + // in OOo 1.0.x, when the user clicked a radio button in a group of buttons, this resulted + // in _one_ itemStateChanged call for exactly the radio button which's state changed from + // "0" to "1". + // Nowadays, since the listener handling changed a lot towards 1.1 (the VCLXWindow reacts on more + // basic events from the VCL-windows, not anymore on the Link-based events like in 1.0.x), this + // isn't the case anymore: For instance, this method here gets called for the radio button + // which is being implicitly _de_selected, too. This is pretty bad for compatibility. + // Thus, we suppress all events with a new state other than "1". This is unlogical, and weird, when looking + // from a pure API perspective, but it's _compatible_ with older product versions, and this is + // all which matters here. + // #i14703# + if ( 1 == rEvent.Selected ) + { + if ( maItemListeners.getLength() ) + maItemListeners.itemStateChanged( rEvent ); + } + // note that speaking strictly, this is wrong: When in 1.0.x, the user would have de-selected + // a radio button _without_ selecting another one, this would have caused a notification. + // With the change done here, this today won't cause a notification anymore. + + // Fortunately, it's not possible for the user to de-select a radio button without selecting another on, + // at least not via the regular UI. It _would_ be possible via the Accessibility API, which + // counts as "user input", too. But in 1.0.x, there was no Accessibility API, so there is nothing + // to be inconsistent with. +} + +awt::Size UnoRadioButtonControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoRadioButtonControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoRadioButtonControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoRadioButtonControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoRadioButtonControl"; +} + +css::uno::Sequence<OUString> UnoRadioButtonControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlRadioButton", "stardiv.vcl.control.RadioButton" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoRadioButtonControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoRadioButtonControl()); +} + + + +UnoControlCheckBoxModel::UnoControlCheckBoxModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXCheckBox>(); +} + +OUString UnoControlCheckBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.CheckBox"; +} + +uno::Any UnoControlCheckBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.CheckBox" ) ); + + case BASEPROPERTY_VISUALEFFECT: + return uno::Any( sal_Int16(awt::VisualEffect::LOOK3D) ); + } + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlCheckBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlCheckBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlCheckBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlCheckBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlCheckBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCheckBoxModel", "stardiv.vcl.controlmodel.CheckBox" }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlCheckBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlCheckBoxModel(context)); +} + + + +UnoCheckBoxControl::UnoCheckBoxControl() + :maItemListeners( *this ), maActionListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoCheckBoxControl::GetComponentServiceName() const +{ + return "checkbox"; +} + +void UnoCheckBoxControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maItemListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + +sal_Bool UnoCheckBoxControl::isTransparent() +{ + return true; +} + +void UnoCheckBoxControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XCheckBox > xCheckBox( getPeer(), uno::UNO_QUERY ); + xCheckBox->addItemListener( this ); + + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( maActionCommand ); + if ( maActionListeners.getLength() ) + xButton->addActionListener( &maActionListeners ); +} + +void UnoCheckBoxControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoCheckBoxControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void UnoCheckBoxControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->addActionListener( &maActionListeners ); + } +} + +void UnoCheckBoxControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoCheckBoxControl::setActionCommand( const OUString& rCommand ) +{ + maActionCommand = rCommand; + if ( getPeer().is() ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( rCommand ); + } +} + + +void UnoCheckBoxControl::setLabel( const OUString& rLabel ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(rLabel), true ); +} + +void UnoCheckBoxControl::setState( sal_Int16 n ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(n), true ); +} + +sal_Int16 UnoCheckBoxControl::getState() +{ + sal_Int16 nState = 0; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ) ); + aVal >>= nState; + return nState; +} + +void UnoCheckBoxControl::enableTriState( sal_Bool b ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TRISTATE ), uno::Any(b), true ); +} + +void UnoCheckBoxControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(static_cast<sal_Int16>(rEvent.Selected)), false ); + + if ( maItemListeners.getLength() ) + maItemListeners.itemStateChanged( rEvent ); +} + +awt::Size UnoCheckBoxControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoCheckBoxControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoCheckBoxControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoCheckBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoCheckBoxControl"; +} + +css::uno::Sequence<OUString> UnoCheckBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCheckBox", "stardiv.vcl.control.CheckBox" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoCheckBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoCheckBoxControl()); +} + + + +UnoControlFixedHyperlinkModel::UnoControlFixedHyperlinkModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXFixedHyperlink>(); +} + +OUString UnoControlFixedHyperlinkModel::getServiceName() +{ + return "com.sun.star.awt.UnoControlFixedHyperlinkModel"; +} + +uno::Any UnoControlFixedHyperlinkModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlFixedHyperlink" ) ); + } + else if ( nPropId == BASEPROPERTY_BORDER ) + { + return uno::Any(sal_Int16(0)); + } + else if ( nPropId == BASEPROPERTY_URL ) + { + return uno::Any( OUString() ); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFixedHyperlinkModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFixedHyperlinkModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFixedHyperlinkModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFixedHyperlinkModel(context)); +} + + + +UnoFixedHyperlinkControl::UnoFixedHyperlinkControl() + :maActionListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoFixedHyperlinkControl::GetComponentServiceName() const +{ + return "fixedhyperlink"; +} + +// uno::XInterface +uno::Any UnoFixedHyperlinkControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XFixedHyperlink* >(this), + static_cast< awt::XLayoutConstrains* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoFixedHyperlinkControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoFixedHyperlinkControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XFixedHyperlink>::get(), + cppu::UnoType<awt::XLayoutConstrains>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); +} + +sal_Bool UnoFixedHyperlinkControl::isTransparent() +{ + return true; +} + +void UnoFixedHyperlinkControl::setText( const OUString& Text ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(Text), true ); +} + +OUString UnoFixedHyperlinkControl::getText() +{ + return ImplGetPropertyValue_UString( BASEPROPERTY_LABEL ); +} + +void UnoFixedHyperlinkControl::setURL( const OUString& URL ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_URL ), uno::Any(URL), true ); +} + +OUString UnoFixedHyperlinkControl::getURL( ) +{ + return ImplGetPropertyValue_UString( BASEPROPERTY_URL ); +} + +void UnoFixedHyperlinkControl::setAlignment( sal_Int16 nAlign ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ), uno::Any(nAlign), true ); +} + +sal_Int16 UnoFixedHyperlinkControl::getAlignment() +{ + sal_Int16 nAlign = 0; + if ( mxModel.is() ) + { + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ) ); + aVal >>= nAlign; + } + return nAlign; +} + +awt::Size UnoFixedHyperlinkControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoFixedHyperlinkControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoFixedHyperlinkControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +void UnoFixedHyperlinkControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + +void UnoFixedHyperlinkControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XFixedHyperlink > xFixedHyperlink( getPeer(), uno::UNO_QUERY ); + if ( maActionListeners.getLength() ) + xFixedHyperlink->addActionListener( &maActionListeners ); +} + +void UnoFixedHyperlinkControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XFixedHyperlink > xFixedHyperlink( getPeer(), uno::UNO_QUERY ); + xFixedHyperlink->addActionListener( &maActionListeners ); + } +} + +void UnoFixedHyperlinkControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XFixedHyperlink > xFixedHyperlink( getPeer(), uno::UNO_QUERY ); + xFixedHyperlink->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFixedHyperlinkControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFixedHyperlinkControl()); +} + + + +UnoControlFixedTextModel::UnoControlFixedTextModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXFixedText>(); +} + +OUString UnoControlFixedTextModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.FixedText"; +} + +uno::Any UnoControlFixedTextModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.FixedText" ) ); + } + else if ( nPropId == BASEPROPERTY_BORDER ) + { + return uno::Any(sal_Int16(0)); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFixedTextModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFixedTextModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlFixedTextModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlFixedTextModel"; +} + +css::uno::Sequence<OUString> +UnoControlFixedTextModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedTextModel", "stardiv.vcl.controlmodel.FixedText" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFixedTextModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFixedTextModel(context)); +} + + + +UnoFixedTextControl::UnoFixedTextControl() +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoFixedTextControl::GetComponentServiceName() const +{ + return "fixedtext"; +} + +// uno::XInterface +uno::Any UnoFixedTextControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XFixedText* >(this), + static_cast< awt::XLayoutConstrains* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoFixedTextControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoFixedTextControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XFixedText>::get(), + cppu::UnoType<awt::XLayoutConstrains>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); +} + +sal_Bool UnoFixedTextControl::isTransparent() +{ + return true; +} + +void UnoFixedTextControl::setText( const OUString& Text ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(Text), true ); +} + +OUString UnoFixedTextControl::getText() +{ + return ImplGetPropertyValue_UString( BASEPROPERTY_LABEL ); +} + +void UnoFixedTextControl::setAlignment( sal_Int16 nAlign ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ), uno::Any(nAlign), true ); +} + +sal_Int16 UnoFixedTextControl::getAlignment() +{ + sal_Int16 nAlign = 0; + if ( mxModel.is() ) + { + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ) ); + aVal >>= nAlign; + } + return nAlign; +} + +awt::Size UnoFixedTextControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoFixedTextControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoFixedTextControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoFixedTextControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoFixedTextControl"; +} + +css::uno::Sequence<OUString> UnoFixedTextControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedText", "stardiv.vcl.control.FixedText" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFixedTextControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFixedTextControl()); +} + + + +UnoControlGroupBoxModel::UnoControlGroupBoxModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_LABEL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); +} + +OUString UnoControlGroupBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.GroupBox"; +} + +uno::Any UnoControlGroupBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any(OUString( "stardiv.vcl.control.GroupBox" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlGroupBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlGroupBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlGroupBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlGroupBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlGroupBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlGroupBoxModel", "stardiv.vcl.controlmodel.GroupBox" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlGroupBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlGroupBoxModel(context)); +} + + + +UnoGroupBoxControl::UnoGroupBoxControl() +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 100; +} + +OUString UnoGroupBoxControl::GetComponentServiceName() const +{ + return "groupbox"; +} + +sal_Bool UnoGroupBoxControl::isTransparent() +{ + return true; +} + +OUString UnoGroupBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoGroupBoxControl"; +} + +css::uno::Sequence<OUString> UnoGroupBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlGroupBox", "stardiv.vcl.control.GroupBox" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoGroupBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoGroupBoxControl()); +} + + +// = UnoControlListBoxModel_Data + +namespace { + +struct ListItem +{ + OUString ItemText; + OUString ItemImageURL; + Any ItemData; + + ListItem() + { + } + + explicit ListItem( OUString i_ItemText ) + :ItemText(std::move( i_ItemText )) + { + } +}; + +} + +typedef beans::Pair< OUString, OUString > UnoListItem; + +namespace { + +struct StripItemData +{ + UnoListItem operator()( const ListItem& i_rItem ) + { + return UnoListItem( i_rItem.ItemText, i_rItem.ItemImageURL ); + } +}; + +} + +struct UnoControlListBoxModel_Data +{ + explicit UnoControlListBoxModel_Data( UnoControlListBoxModel& i_rAntiImpl ) + :m_bSettingLegacyProperty( false ) + ,m_rAntiImpl( i_rAntiImpl ) + { + } + + sal_Int32 getItemCount() const { return sal_Int32( m_aListItems.size() ); } + + const ListItem& getItem( const sal_Int32 i_nIndex ) const + { + if ( ( i_nIndex < 0 ) || ( o3tl::make_unsigned(i_nIndex) >= m_aListItems.size() ) ) + throw IndexOutOfBoundsException( OUString(), m_rAntiImpl ); + return m_aListItems[ i_nIndex ]; + } + + ListItem& getItem( const sal_Int32 i_nIndex ) + { + return const_cast< ListItem& >( static_cast< const UnoControlListBoxModel_Data* >( this )->getItem( i_nIndex ) ); + } + + ListItem& insertItem( const sal_Int32 i_nIndex ) + { + if ( ( i_nIndex < 0 ) || ( o3tl::make_unsigned(i_nIndex) > m_aListItems.size() ) ) + throw IndexOutOfBoundsException( OUString(), m_rAntiImpl ); + return *m_aListItems.insert( m_aListItems.begin() + i_nIndex, ListItem() ); + } + + Sequence< UnoListItem > getAllItems() const + { + Sequence< UnoListItem > aItems( sal_Int32( m_aListItems.size() ) ); + ::std::transform( m_aListItems.begin(), m_aListItems.end(), aItems.getArray(), StripItemData() ); + return aItems; + } + + void copyItems( const UnoControlListBoxModel_Data& i_copySource ) + { + m_aListItems = i_copySource.m_aListItems; + } + + void setAllItems( ::std::vector< ListItem >&& i_rItems ) + { + m_aListItems = std::move(i_rItems); + } + + void removeItem( const sal_Int32 i_nIndex ) + { + if ( ( i_nIndex < 0 ) || ( o3tl::make_unsigned(i_nIndex) >= m_aListItems.size() ) ) + throw IndexOutOfBoundsException( OUString(), m_rAntiImpl ); + m_aListItems.erase( m_aListItems.begin() + i_nIndex ); + } + + void removeAllItems() + { + std::vector<ListItem>().swap(m_aListItems); + } + +public: + bool m_bSettingLegacyProperty; + +private: + UnoControlListBoxModel& m_rAntiImpl; + ::std::vector< ListItem > m_aListItems; +}; + + +// = UnoControlListBoxModel + + +UnoControlListBoxModel::UnoControlListBoxModel( const Reference< XComponentContext >& rxContext, ConstructorMode const i_mode ) + :UnoControlListBoxModel_Base( rxContext ) + ,m_xData( new UnoControlListBoxModel_Data( *this ) ) +{ + if ( i_mode == ConstructDefault ) + { + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXListBox>(); + } +} + +UnoControlListBoxModel::UnoControlListBoxModel( const UnoControlListBoxModel& i_rSource ) + :UnoControlListBoxModel_Base( i_rSource ) + ,m_xData( new UnoControlListBoxModel_Data( *this ) ) +{ + m_xData->copyItems( *i_rSource.m_xData ); +} +UnoControlListBoxModel::~UnoControlListBoxModel() +{ +} + +OUString UnoControlListBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlListBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlListBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlListBoxModel", "stardiv.vcl.controlmodel.ListBox" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +OUString UnoControlListBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ListBox"; +} + + +uno::Any UnoControlListBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.ListBox" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlListBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlListBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +namespace +{ + struct CreateListItem + { + ListItem operator()( const OUString& i_rItemText ) + { + return ListItem( i_rItemText ); + } + }; +} + + +void UnoControlListBoxModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const uno::Any& rValue ) +{ + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + if ( nHandle != BASEPROPERTY_STRINGITEMLIST ) + return; + + // reset selection + uno::Sequence<sal_Int16> aSeq; + setDependentFastPropertyValue( rGuard, BASEPROPERTY_SELECTEDITEMS, uno::Any(aSeq) ); + + if ( m_xData->m_bSettingLegacyProperty ) + return; + + // synchronize the legacy StringItemList property with our list items + Sequence< OUString > aStringItemList; + Any aPropValue; + getFastPropertyValue( rGuard, aPropValue, BASEPROPERTY_STRINGITEMLIST ); + OSL_VERIFY( aPropValue >>= aStringItemList ); + + ::std::vector< ListItem > aItems( aStringItemList.getLength() ); + ::std::transform( + std::cbegin(aStringItemList), + std::cend(aStringItemList), + aItems.begin(), + CreateListItem() + ); + m_xData->setAllItems( std::move(aItems) ); + + // since an XItemListListener does not have a "all items modified" or some such method, + // we simulate this by notifying removal of all items, followed by insertion of all new + // items + lang::EventObject aEvent; + aEvent.Source = *this; + m_aItemListListeners.notifyEach( rGuard, &XItemListListener::itemListChanged, aEvent ); + // TODO: OPropertySetHelper calls into this method with the mutex locked ... + // which is wrong for the above notifications ... +} + + +void UnoControlListBoxModel::ImplNormalizePropertySequence( const sal_Int32 _nCount, sal_Int32* _pHandles, + uno::Any* _pValues, sal_Int32* _pValidHandles ) const +{ + // dependencies we know: + // BASEPROPERTY_STRINGITEMLIST->BASEPROPERTY_SELECTEDITEMS + ImplEnsureHandleOrder( _nCount, _pHandles, _pValues, BASEPROPERTY_STRINGITEMLIST, BASEPROPERTY_SELECTEDITEMS ); + // BASEPROPERTY_STRINGITEMLIST->BASEPROPERTY_TYPEDITEMLIST + ImplEnsureHandleOrder( _nCount, _pHandles, _pValues, BASEPROPERTY_STRINGITEMLIST, BASEPROPERTY_TYPEDITEMLIST ); + + UnoControlModel::ImplNormalizePropertySequence( _nCount, _pHandles, _pValues, _pValidHandles ); +} + + +::sal_Int32 SAL_CALL UnoControlListBoxModel::getItemCount() +{ + std::unique_lock aGuard( m_aMutex ); + return m_xData->getItemCount(); +} + + +void SAL_CALL UnoControlListBoxModel::insertItem( ::sal_Int32 i_nPosition, const OUString& i_rItemText, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->insertItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleInsert( aGuard, i_nPosition, i_rItemText, i_rItemImageURL ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::insertItemText( ::sal_Int32 i_nPosition, const OUString& i_rItemText ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->insertItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + + impl_handleInsert( aGuard, i_nPosition, i_rItemText, ::std::optional< OUString >() ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::insertItemImage( ::sal_Int32 i_nPosition, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->insertItem( i_nPosition ) ); + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleInsert( aGuard, i_nPosition, ::std::optional< OUString >(), i_rItemImageURL ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::removeItem( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + m_xData->removeItem( i_nPosition ); + + impl_handleRemove( i_nPosition, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::removeAllItems( ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + m_xData->removeAllItems(); + + impl_handleRemove( -1, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemText( ::sal_Int32 i_nPosition, const OUString& i_rItemText ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + + impl_handleModify( i_nPosition, i_rItemText, ::std::optional< OUString >(), aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemImage( ::sal_Int32 i_nPosition, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleModify( i_nPosition, ::std::optional< OUString >(), i_rItemImageURL, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemTextAndImage( ::sal_Int32 i_nPosition, const OUString& i_rItemText, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleModify( i_nPosition, i_rItemText, i_rItemImageURL, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemData( ::sal_Int32 i_nPosition, const Any& i_rDataValue ) +{ + std::unique_lock aGuard( m_aMutex ); + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemData = i_rDataValue; +} + + +OUString SAL_CALL UnoControlListBoxModel::getItemText( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return rItem.ItemText; +} + + +OUString SAL_CALL UnoControlListBoxModel::getItemImage( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return rItem.ItemImageURL; +} + + +beans::Pair< OUString, OUString > SAL_CALL UnoControlListBoxModel::getItemTextAndImage( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return beans::Pair< OUString, OUString >( rItem.ItemText, rItem.ItemImageURL ); +} + + +Any SAL_CALL UnoControlListBoxModel::getItemData( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return rItem.ItemData; +} + + +Sequence< beans::Pair< OUString, OUString > > SAL_CALL UnoControlListBoxModel::getAllItems( ) +{ + std::unique_lock aGuard( m_aMutex ); + return m_xData->getAllItems(); +} + + +void SAL_CALL UnoControlListBoxModel::addItemListListener( const uno::Reference< awt::XItemListListener >& i_Listener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( i_Listener.is() ) + m_aItemListListeners.addInterface( aGuard, i_Listener ); +} + + +void SAL_CALL UnoControlListBoxModel::removeItemListListener( const uno::Reference< awt::XItemListListener >& i_Listener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( i_Listener.is() ) + m_aItemListListeners.removeInterface( aGuard, i_Listener ); +} + + +void UnoControlListBoxModel::impl_getStringItemList( std::unique_lock<std::mutex>& rGuard, ::std::vector< OUString >& o_rStringItems ) const +{ + Sequence< OUString > aStringItemList; + Any aPropValue; + getFastPropertyValue( rGuard, aPropValue, BASEPROPERTY_STRINGITEMLIST ); + OSL_VERIFY( aPropValue >>= aStringItemList ); + + comphelper::sequenceToContainer(o_rStringItems, aStringItemList); +} + + +void UnoControlListBoxModel::impl_setStringItemList( std::unique_lock<std::mutex>& rGuard, const ::std::vector< OUString >& i_rStringItems ) +{ + Sequence< OUString > aStringItems( comphelper::containerToSequence(i_rStringItems) ); + m_xData->m_bSettingLegacyProperty = true; + try + { + setFastPropertyValueImpl( rGuard, BASEPROPERTY_STRINGITEMLIST, uno::Any( aStringItems ) ); + } + catch( const Exception& ) + { + m_xData->m_bSettingLegacyProperty = false; + throw; + } + m_xData->m_bSettingLegacyProperty = false; +} + + +void UnoControlListBoxModel::impl_handleInsert( std::unique_lock<std::mutex>& rGuard, + const sal_Int32 i_nItemPosition, + const ::std::optional< OUString >& i_rItemText, + const ::std::optional< OUString >& i_rItemImageURL ) +{ + // SYNCHRONIZED -----> + // sync with legacy StringItemList property + ::std::vector< OUString > aStringItems; + impl_getStringItemList( rGuard, aStringItems ); + OSL_ENSURE( o3tl::make_unsigned( i_nItemPosition ) <= aStringItems.size(), "UnoControlListBoxModel::impl_handleInsert" ); + if ( o3tl::make_unsigned( i_nItemPosition ) <= aStringItems.size() ) + { + const OUString sItemText( !!i_rItemText ? *i_rItemText : OUString() ); + aStringItems.insert( aStringItems.begin() + i_nItemPosition, sItemText ); + } + + impl_setStringItemList( rGuard, aStringItems ); + + // notify ItemListListeners + impl_notifyItemListEvent( rGuard, i_nItemPosition, i_rItemText, i_rItemImageURL, &XItemListListener::listItemInserted ); +} + + +void UnoControlListBoxModel::impl_handleRemove( + const sal_Int32 i_nItemPosition, + std::unique_lock<std::mutex>& i_rClearBeforeNotify ) +{ + // SYNCHRONIZED -----> + const bool bAllItems = ( i_nItemPosition < 0 ); + // sync with legacy StringItemList property + ::std::vector< OUString > aStringItems; + impl_getStringItemList( i_rClearBeforeNotify, aStringItems ); + if ( !bAllItems ) + { + OSL_ENSURE( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size(), "UnoControlListBoxModel::impl_handleRemove" ); + if ( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size() ) + { + aStringItems.erase( aStringItems.begin() + i_nItemPosition ); + } + } + else + { + aStringItems.resize(0); + } + + impl_setStringItemList( i_rClearBeforeNotify, aStringItems ); + + // notify ItemListListeners + if ( bAllItems ) + { + EventObject aEvent( *this ); + m_aItemListListeners.notifyEach( i_rClearBeforeNotify, &XItemListListener::allItemsRemoved, aEvent ); + } + else + { + impl_notifyItemListEvent( i_rClearBeforeNotify, i_nItemPosition, ::std::optional< OUString >(), ::std::optional< OUString >(), + &XItemListListener::listItemRemoved ); + } +} + + +void UnoControlListBoxModel::impl_handleModify( + const sal_Int32 i_nItemPosition, const ::std::optional< OUString >& i_rItemText, + const ::std::optional< OUString >& i_rItemImageURL, + std::unique_lock<std::mutex>& i_rClearBeforeNotify ) +{ + // SYNCHRONIZED -----> + if ( !!i_rItemText ) + { + // sync with legacy StringItemList property + ::std::vector< OUString > aStringItems; + impl_getStringItemList( i_rClearBeforeNotify, aStringItems ); + OSL_ENSURE( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size(), "UnoControlListBoxModel::impl_handleModify" ); + if ( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size() ) + { + aStringItems[ i_nItemPosition] = *i_rItemText; + } + + impl_setStringItemList( i_rClearBeforeNotify, aStringItems ); + } + + // notify ItemListListeners + impl_notifyItemListEvent( i_rClearBeforeNotify, i_nItemPosition, i_rItemText, i_rItemImageURL, &XItemListListener::listItemModified ); +} + + +void UnoControlListBoxModel::impl_notifyItemListEvent( + std::unique_lock<std::mutex>& rGuard, + const sal_Int32 i_nItemPosition, const ::std::optional< OUString >& i_rItemText, + const ::std::optional< OUString >& i_rItemImageURL, + void ( SAL_CALL XItemListListener::*NotificationMethod )( const ItemListEvent& ) ) +{ + ItemListEvent aEvent; + aEvent.Source = *this; + aEvent.ItemPosition = i_nItemPosition; + if ( !!i_rItemText ) + { + aEvent.ItemText.IsPresent = true; + aEvent.ItemText.Value = *i_rItemText; + } + if ( !!i_rItemImageURL ) + { + aEvent.ItemImageURL.IsPresent = true; + aEvent.ItemImageURL.Value = *i_rItemImageURL; + } + + m_aItemListListeners.notifyEach( rGuard, NotificationMethod, aEvent ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlListBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlListBoxModel(context)); +} + + + +UnoListBoxControl::UnoListBoxControl() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoListBoxControl::GetComponentServiceName() const +{ + return "listbox"; +} + +OUString UnoListBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoListBoxControl"; +} + +css::uno::Sequence<OUString> UnoListBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlListBox", "stardiv.vcl.control.ListBox" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals); +} + +void UnoListBoxControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + maItemListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoListBoxControl::ImplUpdateSelectedItemsProperty() +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + DBG_ASSERT( xListBox.is(), "XListBox?" ); + + uno::Sequence<sal_Int16> aSeq = xListBox->getSelectedItemsPos(); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SELECTEDITEMS ), uno::Any(aSeq), false ); + } +} + +void UnoListBoxControl::updateFromModel() +{ + UnoControlBase::updateFromModel(); + + Reference< XItemListListener > xItemListListener( getPeer(), UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xItemListListener.is(), "UnoListBoxControl::updateFromModel: a peer which is no ItemListListener?!" ); + + EventObject aEvent( getModel() ); + xItemListListener->itemListChanged( aEvent ); + + // notify the change of the SelectedItems property, again. While our base class, in updateFromModel, + // already did this, our peer(s) can only legitimately set the selection after they have the string + // item list, which we just notified with the itemListChanged call. + const OUString& sSelectedItemsPropName( GetPropertyName( BASEPROPERTY_SELECTEDITEMS ) ); + ImplSetPeerProperty( sSelectedItemsPropName, ImplGetPropertyValue( sSelectedItemsPropName ) ); +} + +void UnoListBoxControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + if ( rPropName == GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ) + // do not forward this to our peer. We are a XItemListListener at our model, and changes in the string item + // list (which is a legacy property) will, later, arrive as changes in the ItemList. Those latter changes + // will be forwarded to the peer, which will update itself accordingly. + return; + + UnoControl::ImplSetPeerProperty( rPropName, rVal ); +} + +void UnoListBoxControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->addItemListener( this ); + + if ( maActionListeners.getLength() ) + xListBox->addActionListener( &maActionListeners ); +} + +void UnoListBoxControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->addActionListener( &maActionListeners ); + } +} + +void UnoListBoxControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoListBoxControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoListBoxControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void UnoListBoxControl::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + uno::Sequence<OUString> aSeq { aItem }; + addItems( aSeq, nPos ); +} + +void UnoListBoxControl::addItems( const uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nNewItems = static_cast<sal_uInt16>(aItems.getLength()); + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + sal_uInt16 nNewLen = nOldLen + nNewItems; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + if ( ( nPos < 0 ) || ( nPos > nOldLen ) ) + nPos = nOldLen; + + // Items before the Paste-Position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // New Items + it = std::copy(aItems.begin(), aItems.end(), it); + + // Rest of old Items + std::copy(std::next(std::cbegin(aSeq), nPos), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), uno::Any(aNewSeq), true ); +} + +void UnoListBoxControl::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + if ( !(nOldLen && ( nPos < nOldLen )) ) + return; + + if ( nCount > ( nOldLen-nPos ) ) + nCount = nOldLen-nPos; + + sal_uInt16 nNewLen = nOldLen - nCount; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + // Items before the Remove-Position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // Rest of Items + std::copy(std::next(std::cbegin(aSeq), nPos + nCount), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), uno::Any(aNewSeq), true ); +} + +sal_Int16 UnoListBoxControl::getItemCount() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return static_cast<sal_Int16>(aSeq.getLength()); +} + +OUString UnoListBoxControl::getItem( sal_Int16 nPos ) +{ + OUString aItem; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + if ( nPos < aSeq.getLength() ) + aItem = aSeq[nPos]; + return aItem; +} + +uno::Sequence< OUString> UnoListBoxControl::getItems() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return aSeq; +} + +sal_Int16 UnoListBoxControl::getSelectedItemPos() +{ + sal_Int16 n = -1; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + n = xListBox->getSelectedItemPos(); + } + return n; +} + +uno::Sequence<sal_Int16> UnoListBoxControl::getSelectedItemsPos() +{ + uno::Sequence<sal_Int16> aSeq; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + aSeq = xListBox->getSelectedItemsPos(); + } + return aSeq; +} + +OUString UnoListBoxControl::getSelectedItem() +{ + OUString aItem; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + aItem = xListBox->getSelectedItem(); + } + return aItem; +} + +uno::Sequence< OUString> UnoListBoxControl::getSelectedItems() +{ + uno::Sequence< OUString> aSeq; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + aSeq = xListBox->getSelectedItems(); + } + return aSeq; +} + +void UnoListBoxControl::selectItemPos( sal_Int16 nPos, sal_Bool bSelect ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->selectItemPos( nPos, bSelect ); + } + ImplUpdateSelectedItemsProperty(); +} + +void UnoListBoxControl::selectItemsPos( const uno::Sequence<sal_Int16>& aPositions, sal_Bool bSelect ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->selectItemsPos( aPositions, bSelect ); + } + ImplUpdateSelectedItemsProperty(); +} + +void UnoListBoxControl::selectItem( const OUString& aItem, sal_Bool bSelect ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->selectItem( aItem, bSelect ); + } + ImplUpdateSelectedItemsProperty(); +} + +void UnoListBoxControl::makeVisible( sal_Int16 nEntry ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->makeVisible( nEntry ); + } +} + +void UnoListBoxControl::setDropDownLineCount( sal_Int16 nLines ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LINECOUNT ), uno::Any(nLines), true ); +} + +sal_Int16 UnoListBoxControl::getDropDownLineCount() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_LINECOUNT ); +} + +sal_Bool UnoListBoxControl::isMutipleMode() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_MULTISELECTION ); +} + +void UnoListBoxControl::setMultipleMode( sal_Bool bMulti ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTISELECTION ), uno::Any(bMulti), true ); +} + +void UnoListBoxControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + ImplUpdateSelectedItemsProperty(); + if ( maItemListeners.getLength() ) + { + try + { + maItemListeners.itemStateChanged( rEvent ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "UnoListBoxControl::itemStateChanged"); + } + } +} + +awt::Size UnoListBoxControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoListBoxControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoListBoxControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +awt::Size UnoListBoxControl::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + return Impl_getMinimumSize( nCols, nLines ); +} + +void UnoListBoxControl::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + Impl_getColumnsAndLines( nCols, nLines ); +} + +sal_Bool SAL_CALL UnoListBoxControl::setModel( const uno::Reference< awt::XControlModel >& i_rModel ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + const Reference< XItemList > xOldItems( getModel(), UNO_QUERY ); + OSL_ENSURE( xOldItems.is() || !getModel().is(), "UnoListBoxControl::setModel: illegal old model!" ); + const Reference< XItemList > xNewItems( i_rModel, UNO_QUERY ); + OSL_ENSURE( xNewItems.is() || !i_rModel.is(), "UnoListBoxControl::setModel: illegal new model!" ); + + if ( !UnoListBoxControl_Base::setModel( i_rModel ) ) + return false; + + if ( xOldItems.is() ) + xOldItems->removeItemListListener( this ); + if ( xNewItems.is() ) + xNewItems->addItemListListener( this ); + + return true; +} + +void SAL_CALL UnoListBoxControl::listItemInserted( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::listItemInserted: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemInserted( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::listItemRemoved( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::listItemRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemRemoved( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::listItemModified( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::listItemModified: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemModified( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::allItemsRemoved( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::allItemsRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->allItemsRemoved( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::itemListChanged( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::itemListChanged: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->itemListChanged( i_rEvent ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoListBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoListBoxControl()); +} + + + +UnoControlComboBoxModel::UnoControlComboBoxModel( const Reference< XComponentContext >& rxContext ) + :UnoControlListBoxModel( rxContext, ConstructWithoutProperties ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXComboBox>(); +} + +OUString UnoControlComboBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlComboBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlComboBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlComboBoxModel", "stardiv.vcl.controlmodel.ComboBox" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals); +} + +uno::Reference< beans::XPropertySetInfo > UnoControlComboBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& UnoControlComboBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + + +OUString UnoControlComboBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ComboBox"; +} + +void UnoControlComboBoxModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const uno::Any& rValue ) +{ + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + if (nHandle != BASEPROPERTY_STRINGITEMLIST || m_xData->m_bSettingLegacyProperty) + return; + + // synchronize the legacy StringItemList property with our list items + Sequence< OUString > aStringItemList; + Any aPropValue; + getFastPropertyValue( rGuard, aPropValue, BASEPROPERTY_STRINGITEMLIST ); + OSL_VERIFY( aPropValue >>= aStringItemList ); + + ::std::vector< ListItem > aItems( aStringItemList.getLength() ); + ::std::transform( + std::cbegin(aStringItemList), + std::cend(aStringItemList), + aItems.begin(), + CreateListItem() + ); + m_xData->setAllItems( std::move(aItems) ); + + // since an XItemListListener does not have a "all items modified" or some such method, + // we simulate this by notifying removal of all items, followed by insertion of all new + // items + lang::EventObject aEvent; + aEvent.Source = *this; + m_aItemListListeners.notifyEach( rGuard, &XItemListListener::itemListChanged, aEvent ); +} + +uno::Any UnoControlComboBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.ComboBox" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlComboBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlComboBoxModel(context)); +} + + + +UnoComboBoxControl::UnoComboBoxControl() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoComboBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoComboBoxControl"; +} + +css::uno::Sequence<OUString> UnoComboBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlComboBox", "stardiv.vcl.control.ComboBox" }; + return comphelper::concatSequences( UnoEditControl::getSupportedServiceNames(), vals); +} + +OUString UnoComboBoxControl::GetComponentServiceName() const +{ + return "combobox"; +} + +void UnoComboBoxControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + maItemListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} +uno::Any UnoComboBoxControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XComboBox* >(this) ); + if ( !aRet.hasValue() ) + { + aRet = ::cppu::queryInterface( rType, + static_cast< awt::XItemListener* >(this) ); + if ( !aRet.hasValue() ) + { + aRet = ::cppu::queryInterface( rType, + static_cast< awt::XItemListListener* >(this) ); + } + } + return (aRet.hasValue() ? aRet : UnoEditControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoComboBoxControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoComboBoxControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<awt::XComboBox>::get(), + cppu::UnoType<awt::XItemListener>::get(), + cppu::UnoType<awt::XItemListListener>::get(), + UnoEditControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoComboBoxControl::updateFromModel() +{ + UnoEditControl::updateFromModel(); + + Reference< XItemListListener > xItemListListener( getPeer(), UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xItemListListener.is(), "UnoComboBoxControl::updateFromModel: a peer which is no ItemListListener?!" ); + + EventObject aEvent( getModel() ); + xItemListListener->itemListChanged( aEvent ); +} +void UnoComboBoxControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + if ( rPropName == GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ) + // do not forward this to our peer. We are a XItemListListener at our model, and changes in the string item + // list (which is a legacy property) will, later, arrive as changes in the ItemList. Those latter changes + // will be forwarded to the peer, which will update itself accordingly. + return; + + UnoEditControl::ImplSetPeerProperty( rPropName, rVal ); +} +void UnoComboBoxControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoEditControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + if ( maActionListeners.getLength() ) + xComboBox->addActionListener( &maActionListeners ); + if ( maItemListeners.getLength() ) + xComboBox->addItemListener( &maItemListeners ); +} + +void UnoComboBoxControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->addActionListener( &maActionListeners ); + } +} + +void UnoComboBoxControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoComboBoxControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->addItemListener( &maItemListeners ); + } +} + +void UnoComboBoxControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + // This call is prettier than creating a Ref and calling query + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->removeItemListener( &maItemListeners ); + } + maItemListeners.removeInterface( l ); +} +void UnoComboBoxControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + if ( maItemListeners.getLength() ) + { + try + { + maItemListeners.itemStateChanged( rEvent ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "UnoComboBoxControl::itemStateChanged"); + } + } +} +sal_Bool SAL_CALL UnoComboBoxControl::setModel( const uno::Reference< awt::XControlModel >& i_rModel ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + const Reference< XItemList > xOldItems( getModel(), UNO_QUERY ); + OSL_ENSURE( xOldItems.is() || !getModel().is(), "UnoComboBoxControl::setModel: illegal old model!" ); + const Reference< XItemList > xNewItems( i_rModel, UNO_QUERY ); + OSL_ENSURE( xNewItems.is() || !i_rModel.is(), "UnoComboBoxControl::setModel: illegal new model!" ); + + if ( !UnoEditControl::setModel( i_rModel ) ) + return false; + + if ( xOldItems.is() ) + xOldItems->removeItemListListener( this ); + if ( xNewItems.is() ) + xNewItems->addItemListListener( this ); + + return true; +} + +void SAL_CALL UnoComboBoxControl::listItemInserted( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::listItemInserted: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemInserted( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::listItemRemoved( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::listItemRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemRemoved( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::listItemModified( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::listItemModified: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemModified( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::allItemsRemoved( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::allItemsRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->allItemsRemoved( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::itemListChanged( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::itemListChanged: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->itemListChanged( i_rEvent ); +} + +void UnoComboBoxControl::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + uno::Sequence<OUString> aSeq { aItem }; + addItems( aSeq, nPos ); +} + +void UnoComboBoxControl::addItems( const uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nNewItems = static_cast<sal_uInt16>(aItems.getLength()); + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + sal_uInt16 nNewLen = nOldLen + nNewItems; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + if ( ( nPos < 0 ) || ( nPos > nOldLen ) ) + nPos = nOldLen; + + // items before the insert position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // New items + it = std::copy(aItems.begin(), aItems.end(), it); + + // remainder of old items + std::copy(std::next(std::cbegin(aSeq), nPos), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), Any(aNewSeq), true ); +} + +void UnoComboBoxControl::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + if ( !nOldLen || ( nPos >= nOldLen ) ) + return; + + if ( nCount > ( nOldLen-nPos ) ) + nCount = nOldLen-nPos; + + sal_uInt16 nNewLen = nOldLen - nCount; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + // items before the deletion position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // remainder of old items + std::copy(std::next(std::cbegin(aSeq), nPos + nCount), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), uno::Any(aNewSeq), true ); +} + +sal_Int16 UnoComboBoxControl::getItemCount() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return static_cast<sal_Int16>(aSeq.getLength()); +} + +OUString UnoComboBoxControl::getItem( sal_Int16 nPos ) +{ + OUString aItem; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + if ( nPos < aSeq.getLength() ) + aItem = aSeq[nPos]; + return aItem; +} + +uno::Sequence< OUString> UnoComboBoxControl::getItems() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return aSeq; +} + +void UnoComboBoxControl::setDropDownLineCount( sal_Int16 nLines ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LINECOUNT ), uno::Any(nLines), true ); +} + +sal_Int16 UnoComboBoxControl::getDropDownLineCount() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_LINECOUNT ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoComboBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoComboBoxControl()); +} + + +// UnoSpinFieldControl + +UnoSpinFieldControl::UnoSpinFieldControl() + :maSpinListeners( *this ) +{ + mbRepeat = false; +} + +// uno::XInterface +uno::Any UnoSpinFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XSpinField* >(this) ); + return (aRet.hasValue() ? aRet : UnoEditControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoSpinFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoSpinFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XSpinField>::get(), + UnoEditControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoSpinFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoEditControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + xField->enableRepeat( mbRepeat ); + if ( maSpinListeners.getLength() ) + xField->addSpinListener( &maSpinListeners ); +} + + // css::awt::XSpinField +void UnoSpinFieldControl::addSpinListener( const css::uno::Reference< css::awt::XSpinListener >& l ) +{ + maSpinListeners.addInterface( l ); + if( getPeer().is() && maSpinListeners.getLength() == 1 ) + { + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + xField->addSpinListener( &maSpinListeners ); + } +} + +void UnoSpinFieldControl::removeSpinListener( const css::uno::Reference< css::awt::XSpinListener >& l ) +{ + if( getPeer().is() && maSpinListeners.getLength() == 1 ) + { + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + xField->removeSpinListener( &maSpinListeners ); + } + maSpinListeners.removeInterface( l ); +} + +void UnoSpinFieldControl::up() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->up(); +} + +void UnoSpinFieldControl::down() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->down(); +} + +void UnoSpinFieldControl::first() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->first(); +} + +void UnoSpinFieldControl::last() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->last(); +} + +void UnoSpinFieldControl::enableRepeat( sal_Bool bRepeat ) +{ + mbRepeat = bRepeat; + + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->enableRepeat( bRepeat ); +} + + + +UnoControlDateFieldModel::UnoControlDateFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXDateField>(); +} + +OUString UnoControlDateFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.DateField"; +} + +uno::Any UnoControlDateFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.DateField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlDateFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlDateFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlDateFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlDateFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlDateFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlDateFieldModel", "stardiv.vcl.controlmodel.DateField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlDateFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlDateFieldModel(context)); +} + + + +UnoDateFieldControl::UnoDateFieldControl() +{ + mnFirst = util::Date( 1, 1, 1900 ); + mnLast = util::Date( 31, 12, 2200 ); + mbLongFormat = TRISTATE_INDET; +} + +OUString UnoDateFieldControl::GetComponentServiceName() const +{ + return "datefield"; +} + +// uno::XInterface +uno::Any UnoDateFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XDateField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoDateFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoDateFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XDateField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoDateFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); + if ( mbLongFormat != TRISTATE_INDET ) + xField->setLongFormat( mbLongFormat != TRISTATE_FALSE); +} + + +void UnoDateFieldControl::textChanged( const awt::TextEvent& e ) +{ + uno::Reference< awt::XVclWindowPeer > xPeer( getPeer(), uno::UNO_QUERY ); + + // also change the text property (#i25106#) + if ( xPeer.is() ) + { + const OUString& sTextPropertyName = GetPropertyName( BASEPROPERTY_TEXT ); + ImplSetPropertyValue( sTextPropertyName, xPeer->getProperty( sTextPropertyName ), false ); + } + + // re-calc the Date property + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + uno::Any aValue; + if ( xField->isEmpty() ) + { + // the field says it's empty + bool bEnforceFormat = true; + if ( xPeer.is() ) + xPeer->getProperty( GetPropertyName( BASEPROPERTY_ENFORCE_FORMAT ) ) >>= bEnforceFormat; + if ( !bEnforceFormat ) + { + // and it also says that it is currently accepting invalid inputs, without + // forcing it to a valid date + uno::Reference< awt::XTextComponent > xText( xPeer, uno::UNO_QUERY ); + if ( xText.is() && xText->getText().getLength() ) + // and in real, the text of the peer is *not* empty + // -> simulate an invalid date, which is different from "no date" + aValue <<= util::Date(); + } + } + else + aValue <<= xField->getDate(); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATE ), aValue, false ); + + // multiplex the event + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoDateFieldControl::setDate( const util::Date& Date ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATE ), uno::Any(Date), true ); +} + +util::Date UnoDateFieldControl::getDate() +{ + return ImplGetPropertyValue_Date( BASEPROPERTY_DATE ); +} + +void UnoDateFieldControl::setMin( const util::Date& Date ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATEMIN ), uno::Any(Date), true ); +} + +util::Date UnoDateFieldControl::getMin() +{ + return ImplGetPropertyValue_Date( BASEPROPERTY_DATEMIN ); +} + +void UnoDateFieldControl::setMax( const util::Date& Date ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATEMAX ), uno::Any(Date), true ); +} + +util::Date UnoDateFieldControl::getMax() +{ + return ImplGetPropertyValue_Date( BASEPROPERTY_DATEMAX ); +} + +void UnoDateFieldControl::setFirst( const util::Date& Date ) +{ + mnFirst = Date; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( Date ); + } +} + +util::Date UnoDateFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoDateFieldControl::setLast( const util::Date& Date ) +{ + mnLast = Date; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLast( Date ); + } +} + +util::Date UnoDateFieldControl::getLast() +{ + return mnLast; +} + +void UnoDateFieldControl::setLongFormat( sal_Bool bLong ) +{ + mbLongFormat = bLong ? TRISTATE_TRUE : TRISTATE_FALSE; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLongFormat( bLong ); + } +} + +sal_Bool UnoDateFieldControl::isLongFormat() +{ + return mbLongFormat == TRISTATE_TRUE; +} + +void UnoDateFieldControl::setEmpty() +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setEmpty(); + } +} + +sal_Bool UnoDateFieldControl::isEmpty() +{ + bool bEmpty = false; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + bEmpty = xField->isEmpty(); + } + return bEmpty; +} + +void UnoDateFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoDateFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoDateFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoDateFieldControl"; +} + +css::uno::Sequence<OUString> UnoDateFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlDateField", "stardiv.vcl.control.DateField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoDateFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoDateFieldControl()); +} + + + +UnoControlTimeFieldModel::UnoControlTimeFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXTimeField>(); +} + +OUString UnoControlTimeFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.TimeField"; +} + +uno::Any UnoControlTimeFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.TimeField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlTimeFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlTimeFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlTimeFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlTimeFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlTimeFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlTimeFieldModel", "stardiv.vcl.controlmodel.TimeField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTimeFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTimeFieldModel(context)); +} + + + +UnoTimeFieldControl::UnoTimeFieldControl() +{ + mnFirst = util::Time( 0, 0, 0, 0, false ); + mnLast = util::Time( 999999999, 59, 59, 23, false ); +} + +OUString UnoTimeFieldControl::GetComponentServiceName() const +{ + return "timefield"; +} + +// uno::XInterface +uno::Any UnoTimeFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XTimeField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoTimeFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoTimeFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XTimeField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoTimeFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); +} + +void UnoTimeFieldControl::textChanged( const awt::TextEvent& e ) +{ + // also change the text property (#i25106#) + uno::Reference< awt::XVclWindowPeer > xPeer( getPeer(), uno::UNO_QUERY ); + const OUString& sTextPropertyName = GetPropertyName( BASEPROPERTY_TEXT ); + ImplSetPropertyValue( sTextPropertyName, xPeer->getProperty( sTextPropertyName ), false ); + + // re-calc the Time property + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + uno::Any aValue; + if ( !xField->isEmpty() ) + aValue <<= xField->getTime(); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIME ), aValue, false ); + + // multiplex the event + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoTimeFieldControl::setTime( const util::Time& Time ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIME ), Any(Time), true ); +} + +util::Time UnoTimeFieldControl::getTime() +{ + return ImplGetPropertyValue_Time( BASEPROPERTY_TIME ); +} + +void UnoTimeFieldControl::setMin( const util::Time& Time ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIMEMIN ), uno::Any(Time), true ); +} + +util::Time UnoTimeFieldControl::getMin() +{ + return ImplGetPropertyValue_Time( BASEPROPERTY_TIMEMIN ); +} + +void UnoTimeFieldControl::setMax( const util::Time& Time ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIMEMAX ), uno::Any(Time), true ); +} + +util::Time UnoTimeFieldControl::getMax() +{ + return ImplGetPropertyValue_Time( BASEPROPERTY_TIMEMAX ); +} + +void UnoTimeFieldControl::setFirst( const util::Time& Time ) +{ + mnFirst = Time; + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + } +} + +util::Time UnoTimeFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoTimeFieldControl::setLast( const util::Time& Time ) +{ + mnLast = Time; + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnLast ); + } +} + +util::Time UnoTimeFieldControl::getLast() +{ + return mnLast; +} + +void UnoTimeFieldControl::setEmpty() +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setEmpty(); + } +} + +sal_Bool UnoTimeFieldControl::isEmpty() +{ + bool bEmpty = false; + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + bEmpty = xField->isEmpty(); + } + return bEmpty; +} + +void UnoTimeFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoTimeFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoTimeFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoTimeFieldControl"; +} + +css::uno::Sequence<OUString> UnoTimeFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlTimeField", "stardiv.vcl.control.TimeField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoTimeFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoTimeFieldControl()); +} + + + +UnoControlNumericFieldModel::UnoControlNumericFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXNumericField>(); +} + +OUString UnoControlNumericFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.NumericField"; +} + +uno::Any UnoControlNumericFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.NumericField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlNumericFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlNumericFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlNumericFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlNumericFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlNumericFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "stardiv.vcl.controlmodel.NumericField", "com.sun.star.awt.UnoControlNumericFieldModel" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlNumericFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlNumericFieldModel(context)); +} + + + +UnoNumericFieldControl::UnoNumericFieldControl() +{ + mnFirst = 0; + mnLast = 0x7FFFFFFF; +} + +OUString UnoNumericFieldControl::GetComponentServiceName() const +{ + return "numericfield"; +} + +// uno::XInterface +uno::Any UnoNumericFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XNumericField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoNumericFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoNumericFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XNumericField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoNumericFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); +} + + +void UnoNumericFieldControl::textChanged( const awt::TextEvent& e ) +{ + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), uno::Any(xField->getValue()), false ); + + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoNumericFieldControl::setValue( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), uno::Any(Value), true ); +} + +double UnoNumericFieldControl::getValue() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUE_DOUBLE ); +} + +void UnoNumericFieldControl::setMin( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMIN_DOUBLE ), uno::Any(Value), true ); +} + +double UnoNumericFieldControl::getMin() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMIN_DOUBLE ); +} + +void UnoNumericFieldControl::setMax( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMAX_DOUBLE ), uno::Any(Value), true ); +} + +double UnoNumericFieldControl::getMax() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMAX_DOUBLE ); +} + +void UnoNumericFieldControl::setFirst( double Value ) +{ + mnFirst = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + } +} + +double UnoNumericFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoNumericFieldControl::setLast( double Value ) +{ + mnLast = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLast( mnLast ); + } +} + +double UnoNumericFieldControl::getLast() +{ + return mnLast; +} + +void UnoNumericFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoNumericFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoNumericFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoNumericFieldControl"; +} + +css::uno::Sequence<OUString> UnoNumericFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlNumericField", "stardiv.vcl.control.NumericField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +void UnoNumericFieldControl::setSpinSize( double Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUESTEP_DOUBLE ), uno::Any(Digits), true ); +} + +double UnoNumericFieldControl::getSpinSize() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUESTEP_DOUBLE ); +} + +void UnoNumericFieldControl::setDecimalDigits( sal_Int16 Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DECIMALACCURACY ), uno::Any(Digits), true ); +} + +sal_Int16 UnoNumericFieldControl::getDecimalDigits() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_DECIMALACCURACY ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoNumericFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoNumericFieldControl()); +} + +UnoControlCurrencyFieldModel::UnoControlCurrencyFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<SVTXCurrencyField>(); +} + +OUString UnoControlCurrencyFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.CurrencyField"; +} + +uno::Any UnoControlCurrencyFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.CurrencyField" ) ); + } + if ( nPropId == BASEPROPERTY_CURSYM_POSITION ) + { + return uno::Any(false); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlCurrencyFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlCurrencyFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlCurrencyFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlCurrencyFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlCurrencyFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCurrencyFieldModel", "stardiv.vcl.controlmodel.CurrencyField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlCurrencyFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlCurrencyFieldModel(context)); +} + + + +UnoCurrencyFieldControl::UnoCurrencyFieldControl() +{ + mnFirst = 0; + mnLast = 0x7FFFFFFF; +} + +OUString UnoCurrencyFieldControl::GetComponentServiceName() const +{ + return "longcurrencyfield"; +} + +// uno::XInterface +uno::Any UnoCurrencyFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XCurrencyField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoCurrencyFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoCurrencyFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XCurrencyField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoCurrencyFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); +} + +void UnoCurrencyFieldControl::textChanged( const awt::TextEvent& e ) +{ + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), uno::Any(xField->getValue()), false ); + + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoCurrencyFieldControl::setValue( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), Any(Value), true ); +} + +double UnoCurrencyFieldControl::getValue() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUE_DOUBLE ); +} + +void UnoCurrencyFieldControl::setMin( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMIN_DOUBLE ), uno::Any(Value), true ); +} + +double UnoCurrencyFieldControl::getMin() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMIN_DOUBLE ); +} + +void UnoCurrencyFieldControl::setMax( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMAX_DOUBLE ), uno::Any(Value), true ); +} + +double UnoCurrencyFieldControl::getMax() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMAX_DOUBLE ); +} + +void UnoCurrencyFieldControl::setFirst( double Value ) +{ + mnFirst = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + } +} + +double UnoCurrencyFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoCurrencyFieldControl::setLast( double Value ) +{ + mnLast = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLast( mnLast ); + } +} + +double UnoCurrencyFieldControl::getLast() +{ + return mnLast; +} + +void UnoCurrencyFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoCurrencyFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoCurrencyFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoCurrencyFieldControl"; +} + +css::uno::Sequence<OUString> +UnoCurrencyFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCurrencyField", "stardiv.vcl.control.CurrencyField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +void UnoCurrencyFieldControl::setSpinSize( double Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUESTEP_DOUBLE ), uno::Any(Digits), true ); +} + +double UnoCurrencyFieldControl::getSpinSize() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUESTEP_DOUBLE ); +} + +void UnoCurrencyFieldControl::setDecimalDigits( sal_Int16 Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DECIMALACCURACY ), uno::Any(Digits), true ); +} + +sal_Int16 UnoCurrencyFieldControl::getDecimalDigits() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_DECIMALACCURACY ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoCurrencyFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoCurrencyFieldControl()); +} + + + +UnoControlPatternFieldModel::UnoControlPatternFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXPatternField>(); +} + +OUString UnoControlPatternFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.PatternField"; +} + +uno::Any UnoControlPatternFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.PatternField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlPatternFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlPatternFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlPatternFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlPatternFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlPatternFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlPatternFieldModel", "stardiv.vcl.controlmodel.PatternField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlPatternFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlPatternFieldModel(context)); +} + + + +UnoPatternFieldControl::UnoPatternFieldControl() +{ +} + +OUString UnoPatternFieldControl::GetComponentServiceName() const +{ + return "patternfield"; +} + +void UnoPatternFieldControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + sal_uInt16 nType = GetPropertyId( rPropName ); + if ( ( nType == BASEPROPERTY_TEXT ) || ( nType == BASEPROPERTY_EDITMASK ) || ( nType == BASEPROPERTY_LITERALMASK ) ) + { + // These masks cannot be set consecutively + OUString Text = ImplGetPropertyValue_UString( BASEPROPERTY_TEXT ); + OUString EditMask = ImplGetPropertyValue_UString( BASEPROPERTY_EDITMASK ); + OUString LiteralMask = ImplGetPropertyValue_UString( BASEPROPERTY_LITERALMASK ); + + uno::Reference < awt::XPatternField > xPF( getPeer(), uno::UNO_QUERY ); + if (xPF.is()) + { + // same comment as in UnoControl::ImplSetPeerProperty - see there + OUString sText( Text ); + ImplCheckLocalize( sText ); + xPF->setString( sText ); + xPF->setMasks( EditMask, LiteralMask ); + } + } + else + UnoSpinFieldControl::ImplSetPeerProperty( rPropName, rVal ); +} + + +// uno::XInterface +uno::Any UnoPatternFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XPatternField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoPatternFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoPatternFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XPatternField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoPatternFieldControl::setString( const OUString& rString ) +{ + setText( rString ); +} + +OUString UnoPatternFieldControl::getString() +{ + return getText(); +} + +void UnoPatternFieldControl::setMasks( const OUString& EditMask, const OUString& LiteralMask ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_EDITMASK ), uno::Any(EditMask), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LITERALMASK ), uno::Any(LiteralMask), true ); +} + +void UnoPatternFieldControl::getMasks( OUString& EditMask, OUString& LiteralMask ) +{ + EditMask = ImplGetPropertyValue_UString( BASEPROPERTY_EDITMASK ); + LiteralMask = ImplGetPropertyValue_UString( BASEPROPERTY_LITERALMASK ); +} + +void UnoPatternFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoPatternFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoPatternFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoPatternFieldControl"; +} + +css::uno::Sequence<OUString> UnoPatternFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlPatternField", "stardiv.vcl.control.PatternField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoPatternFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoPatternFieldControl()); +} + + + +UnoControlProgressBarModel::UnoControlProgressBarModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + 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_PROGRESSVALUE ); + ImplRegisterProperty( BASEPROPERTY_PROGRESSVALUE_MAX ); + ImplRegisterProperty( BASEPROPERTY_PROGRESSVALUE_MIN ); +} + +OUString UnoControlProgressBarModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.ProgressBar"; +} + +uno::Any UnoControlProgressBarModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.ProgressBar" ) ); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlProgressBarModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlProgressBarModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlProgressBarModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlProgressBarModel"; +} + +css::uno::Sequence<OUString> +UnoControlProgressBarModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlProgressBarModel", "stardiv.vcl.controlmodel.ProgressBar" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlProgressBarModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlProgressBarModel(context)); +} + + + +UnoProgressBarControl::UnoProgressBarControl() +{ +} + +OUString UnoProgressBarControl::GetComponentServiceName() const +{ + return "ProgressBar"; +} + +// uno::XInterface +uno::Any UnoProgressBarControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XProgressBar* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoProgressBarControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoProgressBarControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XProgressBar>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); +} + +// css::awt::XProgressBar +void UnoProgressBarControl::setForegroundColor( sal_Int32 nColor ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_FILLCOLOR ), uno::Any(nColor), true ); +} + +void UnoProgressBarControl::setBackgroundColor( sal_Int32 nColor ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_BACKGROUNDCOLOR ), uno::Any(nColor), true ); +} + +void UnoProgressBarControl::setValue( sal_Int32 nValue ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_PROGRESSVALUE ), uno::Any(nValue), true ); +} + +void UnoProgressBarControl::setRange( sal_Int32 nMin, sal_Int32 nMax ) +{ + uno::Any aMin; + uno::Any aMax; + + if ( nMin < nMax ) + { + // take correct min and max + aMin <<= nMin; + aMax <<= nMax; + } + else + { + // change min and max + aMin <<= nMax; + aMax <<= nMin; + } + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_PROGRESSVALUE_MIN ), aMin, true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_PROGRESSVALUE_MAX ), aMax, true ); +} + +sal_Int32 UnoProgressBarControl::getValue() +{ + return ImplGetPropertyValue_INT32( BASEPROPERTY_PROGRESSVALUE ); +} + +OUString UnoProgressBarControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoProgressBarControl"; +} + +css::uno::Sequence<OUString> UnoProgressBarControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlProgressBar", "stardiv.vcl.control.ProgressBar" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoProgressBarControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoProgressBarControl()); +} + + + +UnoControlFixedLineModel::UnoControlFixedLineModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_LABEL ); + ImplRegisterProperty( BASEPROPERTY_ORIENTATION ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); +} + +OUString UnoControlFixedLineModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.FixedLine"; +} + +uno::Any UnoControlFixedLineModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.FixedLine" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFixedLineModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFixedLineModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlFixedLineModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlFixedLineModel"; +} + +css::uno::Sequence<OUString> +UnoControlFixedLineModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedLineModel", "stardiv.vcl.controlmodel.FixedLine" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFixedLineModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFixedLineModel(context)); +} + + + +UnoFixedLineControl::UnoFixedLineControl() +{ + maComponentInfos.nWidth = 100; // ?? + maComponentInfos.nHeight = 100; // ?? +} + +OUString UnoFixedLineControl::GetComponentServiceName() const +{ + return "FixedLine"; +} + +sal_Bool UnoFixedLineControl::isTransparent() +{ + return true; +} + +OUString UnoFixedLineControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoFixedLineControl"; +} + +css::uno::Sequence<OUString> UnoFixedLineControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedLine", "stardiv.vcl.control.FixedLine" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFixedLineControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFixedLineControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontroltablemodel.cxx b/toolkit/source/controls/unocontroltablemodel.cxx new file mode 100644 index 0000000000..50f6cac3cf --- /dev/null +++ b/toolkit/source/controls/unocontroltablemodel.cxx @@ -0,0 +1,834 @@ +/* -*- 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 "unocontroltablemodel.hxx" +#include "unogridcolumnfacade.hxx" + +#include <controls/table/defaultinputhandler.hxx> +#include <controls/table/gridtablerenderer.hxx> + +#include <com/sun/star/awt/grid/XSortableGridData.hpp> +#include <com/sun/star/util/Color.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <cppuhelper/weakref.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> + + +namespace svt::table +{ + + + using css::uno::Reference; + using css::uno::Sequence; + using css::uno::UNO_QUERY_THROW; + using css::uno::UNO_QUERY; + using css::awt::grid::XGridColumn; + using css::uno::Exception; + using css::awt::grid::XGridDataModel; + using css::awt::grid::XGridColumnModel; + using css::uno::Any; + using css::style::VerticalAlignment_TOP; + using css::style::VerticalAlignment; + using css::awt::grid::GridDataEvent; + using css::awt::grid::XSortableGridData; + using css::beans::Pair; + + + //= UnoControlTableModel +#define DBG_CHECK_ME() \ + DBG_TESTSOLARMUTEX(); \ + + UnoControlTableModel::UnoControlTableModel() + :aColumns ( ) + ,bHasColumnHeaders ( true ) + ,bHasRowHeaders ( false ) + ,eVScrollMode ( ScrollbarShowNever ) + ,eHScrollMode ( ScrollbarShowNever ) + ,pRenderer ( ) + ,pInputHandler ( ) + ,nRowHeight ( 10 ) + ,nColumnHeaderHeight ( 10 ) + ,nRowHeaderWidth ( 10 ) + ,m_aGridLineColor ( ) + ,m_aHeaderBackgroundColor ( ) + ,m_aHeaderTextColor ( ) + ,m_aActiveSelectionBackColor ( ) + ,m_aInactiveSelectionBackColor ( ) + ,m_aActiveSelectionTextColor ( ) + ,m_aInactiveSelectionTextColor ( ) + ,m_aTextColor ( ) + ,m_aTextLineColor ( ) + ,m_aRowColors ( ) + ,m_eVerticalAlign ( VerticalAlignment_TOP ) + ,bEnabled ( true ) + { + pRenderer = std::make_shared<GridTableRenderer>( *this ); + pInputHandler = std::make_shared<DefaultInputHandler>(); + } + + + UnoControlTableModel::~UnoControlTableModel() + { + } + + + TableSize UnoControlTableModel::getColumnCount() const + { + DBG_CHECK_ME(); + return static_cast<TableSize>(aColumns.size()); + } + + + TableSize UnoControlTableModel::getRowCount() const + { + DBG_CHECK_ME(); + + TableSize nRowCount = 0; + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_THROW( xDataModel.is(), "no data model anymore!" ); + nRowCount = xDataModel->getRowCount(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return nRowCount; + } + + + bool UnoControlTableModel::hasColumnHeaders() const + { + DBG_CHECK_ME(); + return bHasColumnHeaders; + } + + + bool UnoControlTableModel::hasRowHeaders() const + { + DBG_CHECK_ME(); + return bHasRowHeaders; + } + + + void UnoControlTableModel::setRowHeaders(bool _bRowHeaders) + { + DBG_CHECK_ME(); + if ( bHasRowHeaders == _bRowHeaders ) + return; + + bHasRowHeaders = _bRowHeaders; + impl_notifyTableMetricsChanged(); + } + + + void UnoControlTableModel::setColumnHeaders(bool _bColumnHeaders) + { + DBG_CHECK_ME(); + if ( bHasColumnHeaders == _bColumnHeaders ) + return; + + bHasColumnHeaders = _bColumnHeaders; + impl_notifyTableMetricsChanged(); + } + + + PColumnModel UnoControlTableModel::getColumnModel( ColPos column ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN( ( column >= 0 ) && ( column < getColumnCount() ), + "DefaultTableModel::getColumnModel: invalid index!", PColumnModel() ); + return aColumns[ column ]; + } + + + void UnoControlTableModel::appendColumn( Reference< XGridColumn > const & i_column ) + { + DBG_CHECK_ME(); + insertColumn( aColumns.size(), i_column ); + } + + + void UnoControlTableModel::insertColumn( ColPos const i_position, Reference< XGridColumn > const & i_column ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_position >= 0 ) && ( o3tl::make_unsigned( i_position ) <= aColumns.size() ), + "UnoControlTableModel::insertColumn: illegal position!" ); + + const PColumnModel pColumn = std::make_shared<UnoGridColumnFacade>( *this, i_column ); + aColumns.insert( aColumns.begin() + i_position, pColumn ); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnInserted(); + } + } + + + void UnoControlTableModel::removeColumn( ColPos const i_position ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_position >= 0 ) && ( o3tl::make_unsigned( i_position ) <= aColumns.size() ), + "UnoControlTableModel::removeColumn: illegal position!" ); + + // remove the column + ColumnModels::iterator pos = aColumns.begin() + i_position; + const PColumnModel pColumn = *pos; + aColumns.erase( pos ); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnRemoved(); + } + + // dispose the column + UnoGridColumnFacade* pColumnImpl = dynamic_cast< UnoGridColumnFacade* >( pColumn.get() ); + OSL_ENSURE( pColumnImpl != nullptr, "UnoControlTableModel::removeColumn: illegal column implementation!" ); + if ( pColumnImpl ) + pColumnImpl->dispose(); + } + + + void UnoControlTableModel::removeAllColumns() + { + DBG_CHECK_ME(); + if ( aColumns.empty() ) + return; + + // dispose the column instances + for (auto const& col : aColumns) + { + UnoGridColumnFacade* pColumn = dynamic_cast< UnoGridColumnFacade* >( col.get() ); + if ( !pColumn ) + { + SAL_WARN( "svtools.uno", "UnoControlTableModel::removeAllColumns: illegal column implementation!" ); + continue; + } + + pColumn->dispose(); + } + aColumns.clear(); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->allColumnsRemoved(); + } + } + + + void UnoControlTableModel::impl_notifyTableMetricsChanged() const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->tableMetricsChanged(); + } + } + + + PTableRenderer UnoControlTableModel::getRenderer() const + { + DBG_CHECK_ME(); + return pRenderer; + } + + + PTableInputHandler UnoControlTableModel::getInputHandler() const + { + DBG_CHECK_ME(); + return pInputHandler; + } + + + TableMetrics UnoControlTableModel::getRowHeight() const + { + DBG_CHECK_ME(); + return nRowHeight; + } + + + void UnoControlTableModel::setRowHeight(TableMetrics _nRowHeight) + { + DBG_CHECK_ME(); + if ( nRowHeight == _nRowHeight ) + return; + + nRowHeight = _nRowHeight; + impl_notifyTableMetricsChanged(); + } + + + TableMetrics UnoControlTableModel::getColumnHeaderHeight() const + { + DBG_CHECK_ME(); + DBG_ASSERT( hasColumnHeaders(), "DefaultTableModel::getColumnHeaderHeight: invalid call!" ); + return nColumnHeaderHeight; + } + + + TableMetrics UnoControlTableModel::getRowHeaderWidth() const + { + DBG_CHECK_ME(); + DBG_ASSERT( hasRowHeaders(), "DefaultTableModel::getRowHeaderWidth: invalid call!" ); + return nRowHeaderWidth; + } + + void UnoControlTableModel::setColumnHeaderHeight(TableMetrics _nHeight) + { + DBG_CHECK_ME(); + if ( nColumnHeaderHeight == _nHeight ) + return; + + nColumnHeaderHeight = _nHeight; + impl_notifyTableMetricsChanged(); + } + + + void UnoControlTableModel::setRowHeaderWidth(TableMetrics _nWidth) + { + DBG_CHECK_ME(); + if ( nRowHeaderWidth == _nWidth ) + return; + + nRowHeaderWidth = _nWidth; + impl_notifyTableMetricsChanged(); + } + + + ScrollbarVisibility UnoControlTableModel::getVerticalScrollbarVisibility() const + { + DBG_CHECK_ME(); + return eVScrollMode; + } + + + ScrollbarVisibility UnoControlTableModel::getHorizontalScrollbarVisibility() const + { + DBG_CHECK_ME(); + return eHScrollMode; + } + + + void UnoControlTableModel::addTableModelListener( const PTableModelListener& i_listener ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( !!i_listener, "illegal NULL listener" ); + m_aListeners.push_back( i_listener ); + } + + + void UnoControlTableModel::removeTableModelListener( const PTableModelListener& i_listener ) + { + DBG_CHECK_ME(); + auto lookup = std::find(m_aListeners.begin(), m_aListeners.end(), i_listener); + if (lookup != m_aListeners.end()) + { + m_aListeners.erase( lookup ); + return; + } + OSL_ENSURE( false, "UnoControlTableModel::removeTableModelListener: listener is not registered - sure you're doing the right thing here?" ); + } + + + void UnoControlTableModel::setVerticalScrollbarVisibility( ScrollbarVisibility const i_visibility ) + { + DBG_CHECK_ME(); + eVScrollMode = i_visibility; + } + + + void UnoControlTableModel::setHorizontalScrollbarVisibility( ScrollbarVisibility const i_visibility ) + { + DBG_CHECK_ME(); + eHScrollMode = i_visibility; + } + + + void UnoControlTableModel::setDataModel( Reference< XGridDataModel > const & i_gridDataModel ) + { + DBG_CHECK_ME(); + m_aDataModel = i_gridDataModel; + // TODO: register as listener, so we're notified of row/data changes, and can multiplex them to our + // own listeners + } + + + Reference< XGridDataModel > UnoControlTableModel::getDataModel() const + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + return xDataModel; + } + + + bool UnoControlTableModel::hasDataModel() const + { + return getDataModel().is(); + } + + + void UnoControlTableModel::setColumnModel( Reference< XGridColumnModel > const & i_gridColumnModel ) + { + DBG_CHECK_ME(); + m_aColumnModel = i_gridColumnModel; + } + + + Reference< XGridColumnModel > UnoControlTableModel::getColumnModel() const + { + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + return xColumnModel; + } + + + bool UnoControlTableModel::hasColumnModel() const + { + return getColumnModel().is(); + } + + + void UnoControlTableModel::getCellContent( ColPos const i_col, RowPos const i_row, Any& o_cellContent ) + { + DBG_CHECK_ME(); + + o_cellContent.clear(); + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN_VOID( xDataModel.is(), "UnoControlTableModel::getCellContent: no data model anymore!" ); + + PColumnModel const pColumn = getColumnModel( i_col ); + UnoGridColumnFacade* pColumnImpl = dynamic_cast< UnoGridColumnFacade* >( pColumn.get() ); + ENSURE_OR_RETURN_VOID( pColumnImpl != nullptr, "UnoControlTableModel::getCellContent: no (valid) column at this position!" ); + sal_Int32 const nDataColumnIndex = pColumnImpl->getDataColumnIndex() >= 0 ? pColumnImpl->getDataColumnIndex() : i_col; + + if ( nDataColumnIndex >= xDataModel->getColumnCount() ) + { + // this is allowed, in case the column model has been dynamically extended, but the data model does + // not (yet?) know about it. + // So, handle it gracefully. + #if OSL_DEBUG_LEVEL > 0 + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + OSL_ENSURE( xColumnModel.is() && i_col < xColumnModel->getColumnCount(), + "UnoControlTableModel::getCellContent: request a column's value which the ColumnModel doesn't know about!" ); + #endif + } + else + { + o_cellContent = xDataModel->getCellData( nDataColumnIndex, i_row ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + void UnoControlTableModel::getCellToolTip( ColPos const i_col, RowPos const i_row, Any& o_cellToolTip ) + { + DBG_CHECK_ME(); + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_THROW( xDataModel.is(), "no data model anymore!" ); + + o_cellToolTip = xDataModel->getCellToolTip( i_col, i_row ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + Any UnoControlTableModel::getRowHeading( RowPos const i_rowPos ) const + { + DBG_CHECK_ME(); + + Any aRowHeading; + + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN( xDataModel.is(), "UnoControlTableModel::getRowHeading: no data model anymore!", aRowHeading ); + + try + { + aRowHeading = xDataModel->getRowHeading( i_rowPos ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return aRowHeading; + } + + + namespace + { + void lcl_setColor( Any const & i_color, ::std::optional< ::Color > & o_convertedColor ) + { + if ( !i_color.hasValue() ) + o_convertedColor.reset(); + else + { + Color nColor = COL_TRANSPARENT; + if ( i_color >>= nColor ) + { + o_convertedColor = nColor; + } + else + { + OSL_ENSURE( false, "lcl_setColor: could not extract color value!" ); + } + } + } + } + + + ::std::optional< ::Color > UnoControlTableModel::getLineColor() const + { + DBG_CHECK_ME(); + return m_aGridLineColor; + } + + + void UnoControlTableModel::setLineColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aGridLineColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getHeaderBackgroundColor() const + { + DBG_CHECK_ME(); + return m_aHeaderBackgroundColor; + } + + + void UnoControlTableModel::setHeaderBackgroundColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aHeaderBackgroundColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getHeaderTextColor() const + { + DBG_CHECK_ME(); + return m_aHeaderTextColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getActiveSelectionBackColor() const + { + DBG_CHECK_ME(); + return m_aActiveSelectionBackColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getInactiveSelectionBackColor() const + { + DBG_CHECK_ME(); + return m_aInactiveSelectionBackColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getActiveSelectionTextColor() const + { + DBG_CHECK_ME(); + return m_aActiveSelectionTextColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getInactiveSelectionTextColor() const + { + DBG_CHECK_ME(); + return m_aInactiveSelectionTextColor; + } + + + void UnoControlTableModel::setHeaderTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aHeaderTextColor ); + } + + + void UnoControlTableModel::setActiveSelectionBackColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aActiveSelectionBackColor ); + } + + + void UnoControlTableModel::setInactiveSelectionBackColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aInactiveSelectionBackColor ); + } + + + void UnoControlTableModel::setActiveSelectionTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aActiveSelectionTextColor ); + } + + + void UnoControlTableModel::setInactiveSelectionTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aInactiveSelectionTextColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getTextColor() const + { + DBG_CHECK_ME(); + return m_aTextColor; + } + + + void UnoControlTableModel::setTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aTextColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getTextLineColor() const + { + DBG_CHECK_ME(); + return m_aTextColor; + } + + + void UnoControlTableModel::setTextLineColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aTextLineColor ); + } + + + ::std::optional< ::std::vector< ::Color > > UnoControlTableModel::getRowBackgroundColors() const + { + DBG_CHECK_ME(); + return m_aRowColors; + } + + + void UnoControlTableModel::setRowBackgroundColors( css::uno::Any const & i_APIValue ) + { + DBG_CHECK_ME(); + Sequence< css::util::Color > aAPIColors; + if ( !( i_APIValue >>= aAPIColors ) ) + m_aRowColors.reset(); + else + { + ::std::vector< ::Color > aColors( aAPIColors.getLength() ); + std::transform(std::cbegin(aAPIColors), std::cend(aAPIColors), aColors.begin(), + [](const css::util::Color& rAPIColor) -> ::Color { return Color(ColorTransparency, rAPIColor); }); + m_aRowColors = aColors; + } + } + + + VerticalAlignment UnoControlTableModel::getVerticalAlign() const + { + DBG_CHECK_ME(); + return m_eVerticalAlign; + } + + + void UnoControlTableModel::setVerticalAlign( VerticalAlignment _xAlign ) + { + DBG_CHECK_ME(); + m_eVerticalAlign = _xAlign; + } + + + ColPos UnoControlTableModel::getColumnPos( UnoGridColumnFacade const & i_column ) const + { + DBG_CHECK_ME(); + ColPos nPos = 0; + for (auto const& col : aColumns) + { + if ( &i_column == col.get() ) + return nPos; + ++nPos; + } + OSL_ENSURE( false, "UnoControlTableModel::getColumnPos: column not found!" ); + return COL_INVALID; + } + + + ITableDataSort* UnoControlTableModel::getSortAdapter() + { + DBG_CHECK_ME(); + + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY ); + if ( xSortAccess.is() ) + return this; + return nullptr; + } + + + bool UnoControlTableModel::isEnabled() const + { + DBG_CHECK_ME(); + return bEnabled; + } + + + void UnoControlTableModel::setEnabled( bool _bEnabled ) + { + DBG_CHECK_ME(); + bEnabled = _bEnabled; + } + + + void UnoControlTableModel::sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) + { + DBG_CHECK_ME(); + + try + { + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY_THROW ); + xSortAccess->sortByColumn( i_column, i_sortDirection == ColumnSortAscending ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + ColumnSort UnoControlTableModel::getCurrentSortOrder() const + { + DBG_CHECK_ME(); + + ColumnSort currentSort; + try + { + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY_THROW ); + Pair< ::sal_Int32, sal_Bool > const aCurrentSortOrder( xSortAccess->getCurrentSortOrder() ); + currentSort.nColumnPos = aCurrentSortOrder.First; + currentSort.eSortDirection = aCurrentSortOrder.Second ? ColumnSortAscending : ColumnSortDescending; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return currentSort; + } + + + void UnoControlTableModel::notifyColumnChange( ColPos const i_columnPos, ColumnAttributeGroup const i_attributeGroup ) const + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_columnPos >= 0 ) && ( i_columnPos < getColumnCount() ), + "UnoControlTableModel::notifyColumnChange: invalid column index!" ); + + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnChanged( i_columnPos, i_attributeGroup ); + } + } + + + void UnoControlTableModel::notifyRowsInserted( GridDataEvent const & i_event ) const + { + // check sanity of the event + ENSURE_OR_RETURN_VOID( i_event.FirstRow >= 0, "UnoControlTableModel::notifyRowsInserted: invalid first row!" ); + ENSURE_OR_RETURN_VOID( i_event.LastRow >= i_event.FirstRow, "UnoControlTableModel::notifyRowsInserted: invalid row indexes!" ); + + // check own sanity + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + ENSURE_OR_RETURN_VOID( xColumnModel.is(), "UnoControlTableModel::notifyRowsInserted: no column model anymore!" ); + + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN_VOID( xDataModel.is(), "UnoControlTableModel::notifyRowsInserted: no data model anymore!" ); + + // implicitly add columns to the column model + // TODO: is this really a good idea? + sal_Int32 const dataColumnCount = xDataModel->getColumnCount(); + OSL_ENSURE( dataColumnCount > 0, "UnoControlTableModel::notifyRowsInserted: no columns at all?" ); + + sal_Int32 const modelColumnCount = xColumnModel->getColumnCount(); + if ( ( modelColumnCount == 0 ) && ( dataColumnCount > 0 ) ) + { + // TODO: shouldn't we clear the mutexes guard for this call? + xColumnModel->setDefaultColumns( dataColumnCount ); + } + + // multiplex the event to our own listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->rowsInserted( i_event.FirstRow, i_event.LastRow ); + } + } + + + void UnoControlTableModel::notifyRowsRemoved( GridDataEvent const & i_event ) const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->rowsRemoved( i_event.FirstRow, i_event.LastRow ); + } + } + + + void UnoControlTableModel::notifyDataChanged( css::awt::grid::GridDataEvent const & i_event ) const + { + RowPos const firstRow = i_event.FirstRow == -1 ? 0 : i_event.FirstRow; + RowPos const lastRow = i_event.FirstRow == -1 ? getRowCount() - 1 : i_event.LastRow; + + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->cellsUpdated( firstRow, lastRow ); + } + } + + + void UnoControlTableModel::notifyAllDataChanged() const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->cellsUpdated( 0, getRowCount() - 1 ); + } + } + + +} // svt::table + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontroltablemodel.hxx b/toolkit/source/controls/unocontroltablemodel.hxx new file mode 100644 index 0000000000..2619407941 --- /dev/null +++ b/toolkit/source/controls/unocontroltablemodel.hxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/table/tablemodel.hxx> +#include <controls/table/tablesort.hxx> +#include <tools/color.hxx> + +#include <com/sun/star/awt/grid/GridDataEvent.hpp> +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <cppuhelper/weakref.hxx> + + +namespace svt::table +{ + + + //= UnoControlTableModel + + class UnoGridColumnFacade; + class UnoControlTableModel : public ITableModel, public ITableDataSort + { + public: + UnoControlTableModel(); + virtual ~UnoControlTableModel() override; + + public: + // ITableModel overridables + virtual TableSize getColumnCount() const override; + virtual TableSize getRowCount() const override; + virtual bool hasColumnHeaders() const override; + virtual bool hasRowHeaders() const override; + virtual PColumnModel getColumnModel( ColPos column ) override; + virtual PTableRenderer getRenderer() const override; + virtual PTableInputHandler getInputHandler() const override; + virtual TableMetrics getRowHeight() const override; + virtual TableMetrics getColumnHeaderHeight() const override; + virtual TableMetrics getRowHeaderWidth() const override; + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const override; + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const override; + virtual void addTableModelListener( const PTableModelListener& i_listener ) override; + virtual void removeTableModelListener( const PTableModelListener& i_listener ) override; + virtual void getCellContent( ColPos const i_col, RowPos const i_row, css::uno::Any& o_cellContent ) override; + virtual void getCellToolTip( ColPos const i_col, RowPos const i_row, css::uno::Any & o_cellToolTip ) override; + virtual css::uno::Any getRowHeading( RowPos const i_rowPos ) const override; + virtual ::std::optional< ::Color > getLineColor() const override; + virtual ::std::optional< ::Color > getHeaderBackgroundColor() const override; + virtual ::std::optional< ::Color > getHeaderTextColor() const override; + virtual ::std::optional< ::Color > getActiveSelectionBackColor() const override; + virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const override; + virtual ::std::optional< ::Color > getActiveSelectionTextColor() const override; + virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const override; + virtual ::std::optional< ::Color > getTextColor() const override; + virtual ::std::optional< ::Color > getTextLineColor() const override; + virtual ::std::optional< ::std::vector< ::Color > > + getRowBackgroundColors() const override; + virtual css::style::VerticalAlignment + getVerticalAlign() const override; + virtual ITableDataSort* getSortAdapter() override; + virtual bool isEnabled() const override; + + // ITableDataSort overridables + virtual void sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) override; + virtual ColumnSort getCurrentSortOrder() const override; + + // column write access + void appendColumn( css::uno::Reference< css::awt::grid::XGridColumn > const & i_column ); + void insertColumn( ColPos const i_position, css::uno::Reference< css::awt::grid::XGridColumn > const & i_column ); + void removeColumn( ColPos const i_position ); + void removeAllColumns(); + + // other operations + void setVerticalScrollbarVisibility( ScrollbarVisibility const i_visibility ); + void setHorizontalScrollbarVisibility( ScrollbarVisibility const i_visibility ); + + void setDataModel( css::uno::Reference< css::awt::grid::XGridDataModel > const & i_gridDataModel ); + bool hasDataModel() const; + css::uno::Reference< css::awt::grid::XGridDataModel > + getDataModel() const; + void setColumnModel( css::uno::Reference< css::awt::grid::XGridColumnModel > const & i_gridColumnModel ); + bool hasColumnModel() const; + css::uno::Reference< css::awt::grid::XGridColumnModel > + getColumnModel() const; + + void setRowHeaders(bool _bRowHeaders); + void setColumnHeaders(bool _bColumnHeaders); + + void setRowHeight( TableMetrics _nHeight ); + void setRowHeaderWidth( TableMetrics _nWidth ); + void setColumnHeaderHeight( TableMetrics _nHeight ); + + void setLineColor( css::uno::Any const & i_color ); + void setHeaderBackgroundColor( css::uno::Any const & i_color ); + void setHeaderTextColor( css::uno::Any const & i_color ); + void setActiveSelectionBackColor( css::uno::Any const & i_color ); + void setInactiveSelectionBackColor( css::uno::Any const & i_color ); + void setActiveSelectionTextColor( css::uno::Any const & i_color ); + void setInactiveSelectionTextColor( css::uno::Any const & i_color ); + void setTextColor( css::uno::Any const & i_color ); + void setTextLineColor( css::uno::Any const & i_color ); + void setRowBackgroundColors( css::uno::Any const & i_APIValue ); + + void setVerticalAlign(css::style::VerticalAlignment _rAlign); + void setEnabled( bool _bEnabled ); + + // multiplexing of XGridDataListener events + void notifyRowsInserted( css::awt::grid::GridDataEvent const & i_event ) const; + void notifyRowsRemoved( css::awt::grid::GridDataEvent const & i_event ) const; + void notifyDataChanged( css::awt::grid::GridDataEvent const & i_event ) const; + + /// retrieves the index of a column within the model + ColPos getColumnPos( UnoGridColumnFacade const & i_column ) const; + + /// notifies a change in a column belonging to the model + void notifyColumnChange( ColPos const i_columnPos, ColumnAttributeGroup const i_attributeGroup ) const; + + /** notifies a change in all data represented by the model. To be used if you cannot specified the changed data + in more detail. + */ + void notifyAllDataChanged() const; + + private: + void impl_notifyTableMetricsChanged() const; + + typedef ::std::vector< PTableModelListener > ModellListeners; + typedef ::std::vector< PColumnModel > ColumnModels; + + ColumnModels aColumns; + bool bHasColumnHeaders; + bool bHasRowHeaders; + ScrollbarVisibility eVScrollMode; + ScrollbarVisibility eHScrollMode; + PTableRenderer pRenderer; + PTableInputHandler pInputHandler; + TableMetrics nRowHeight; + TableMetrics nColumnHeaderHeight; + TableMetrics nRowHeaderWidth; + ::std::optional< ::Color > m_aGridLineColor; + ::std::optional< ::Color > m_aHeaderBackgroundColor; + ::std::optional< ::Color > m_aHeaderTextColor; + ::std::optional< ::Color > m_aActiveSelectionBackColor; + ::std::optional< ::Color > m_aInactiveSelectionBackColor; + ::std::optional< ::Color > m_aActiveSelectionTextColor; + ::std::optional< ::Color > m_aInactiveSelectionTextColor; + ::std::optional< ::Color > m_aTextColor; + ::std::optional< ::Color > m_aTextLineColor; + ::std::optional< ::std::vector< ::Color > > m_aRowColors; + css::style::VerticalAlignment m_eVerticalAlign; + bool bEnabled; + ModellListeners m_aListeners; + css::uno::WeakReference< css::awt::grid::XGridDataModel > m_aDataModel; + css::uno::WeakReference< css::awt::grid::XGridColumnModel > m_aColumnModel; + }; + + +} // svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unogridcolumnfacade.cxx b/toolkit/source/controls/unogridcolumnfacade.cxx new file mode 100644 index 0000000000..a060c8cf3e --- /dev/null +++ b/toolkit/source/controls/unogridcolumnfacade.cxx @@ -0,0 +1,310 @@ +/* -*- 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 "unogridcolumnfacade.hxx" +#include "unocontroltablemodel.hxx" + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/XGridColumnListener.hpp> + +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <cppuhelper/implbase.hxx> + + +namespace svt::table +{ + + + using css::uno::Reference; + using css::awt::grid::XGridColumn; + using css::uno::Exception; + using css::awt::grid::XGridColumnListener; + using css::lang::EventObject; + using css::awt::grid::GridColumnEvent; + using css::style::HorizontalAlignment_LEFT; + using css::style::HorizontalAlignment; + + + namespace + { + template< class T1, class T2 > + void lcl_set( Reference< XGridColumn > const & i_column, void ( SAL_CALL XGridColumn::*i_setter )( T1 ), + T2 i_value ) + { + try + { + (i_column.get()->*i_setter) ( i_value ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + template< class ATTRIBUTE_TYPE > + ATTRIBUTE_TYPE lcl_get( Reference< XGridColumn > const & i_column, ATTRIBUTE_TYPE ( SAL_CALL XGridColumn::*i_getter )() ) + { + ATTRIBUTE_TYPE value = ATTRIBUTE_TYPE(); + try + { + value = (i_column.get()->*i_getter)(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return value; + } + } + + + //= ColumnChangeMultiplexer + + typedef ::cppu::WeakImplHelper < XGridColumnListener + > ColumnChangeMultiplexer_Base; + class ColumnChangeMultiplexer :public ColumnChangeMultiplexer_Base + { + public: + explicit ColumnChangeMultiplexer( UnoGridColumnFacade& i_colImpl ); + ColumnChangeMultiplexer(const ColumnChangeMultiplexer&) = delete; + ColumnChangeMultiplexer& operator=(const ColumnChangeMultiplexer&) = delete; + + void dispose(); + + protected: + virtual ~ColumnChangeMultiplexer() override; + + // XGridColumnListener + virtual void SAL_CALL columnChanged( const GridColumnEvent& i_event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& i_event ) override; + + private: + UnoGridColumnFacade* m_pColumnImplementation; + }; + + + ColumnChangeMultiplexer::ColumnChangeMultiplexer( UnoGridColumnFacade& i_colImpl ) + :m_pColumnImplementation( &i_colImpl ) + { + } + + + ColumnChangeMultiplexer::~ColumnChangeMultiplexer() + { + } + + + void ColumnChangeMultiplexer::dispose() + { + DBG_TESTSOLARMUTEX(); + m_pColumnImplementation = nullptr; + } + + + void SAL_CALL ColumnChangeMultiplexer::columnChanged( const GridColumnEvent& i_event ) + { + if ( i_event.AttributeName == "DataColumnIndex" ) + { + SolarMutexGuard aGuard; + if ( m_pColumnImplementation != nullptr ) + m_pColumnImplementation->dataColumnIndexChanged(); + return; + } + + ColumnAttributeGroup nChangedAttributes( ColumnAttributeGroup::NONE ); + + if ( i_event.AttributeName == "HorizontalAlign" ) + nChangedAttributes |= ColumnAttributeGroup::APPEARANCE; + + if ( i_event.AttributeName == "ColumnWidth" + || i_event.AttributeName == "MaxWidth" + || i_event.AttributeName == "MinWidth" + || i_event.AttributeName == "PreferredWidth" + || i_event.AttributeName == "Resizeable" + || i_event.AttributeName == "Flexibility" + ) + nChangedAttributes |= ColumnAttributeGroup::WIDTH; + + OSL_ENSURE( nChangedAttributes != ColumnAttributeGroup::NONE, + "ColumnChangeMultiplexer::columnChanged: unknown column attributed changed!" ); + + SolarMutexGuard aGuard; + if ( m_pColumnImplementation != nullptr ) + m_pColumnImplementation->columnChanged( nChangedAttributes ); + } + + + void SAL_CALL ColumnChangeMultiplexer::disposing( const EventObject& ) + { + } + + + //= UnoGridColumnFacade + + + UnoGridColumnFacade::UnoGridColumnFacade( UnoControlTableModel const & i_owner, Reference< XGridColumn > const & i_gridColumn ) + :m_pOwner( &i_owner ) + ,m_nDataColumnIndex( -1 ) + ,m_xGridColumn( i_gridColumn, css::uno::UNO_SET_THROW ) + ,m_pChangeMultiplexer( new ColumnChangeMultiplexer( *this ) ) + { + m_xGridColumn->addGridColumnListener( m_pChangeMultiplexer ); + impl_updateDataColumnIndex_nothrow(); + } + + + UnoGridColumnFacade::~UnoGridColumnFacade() + { + } + + + void UnoGridColumnFacade::dispose() + { + DBG_TESTSOLARMUTEX(); + ENSURE_OR_RETURN_VOID( m_pOwner != nullptr, "UnoGridColumnFacade::dispose: already disposed!" ); + + m_xGridColumn->removeGridColumnListener( m_pChangeMultiplexer ); + m_pChangeMultiplexer->dispose(); + m_pChangeMultiplexer.clear(); + m_xGridColumn.clear(); + m_pOwner = nullptr; + } + + + void UnoGridColumnFacade::impl_updateDataColumnIndex_nothrow() + { + m_nDataColumnIndex = -1; + ENSURE_OR_RETURN_VOID( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!" ); + try + { + m_nDataColumnIndex = m_xGridColumn->getDataColumnIndex(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + void UnoGridColumnFacade::dataColumnIndexChanged() + { + DBG_TESTSOLARMUTEX(); + impl_updateDataColumnIndex_nothrow(); + if ( m_pOwner != nullptr ) + m_pOwner->notifyAllDataChanged(); + } + + + void UnoGridColumnFacade::columnChanged( ColumnAttributeGroup const i_attributeGroup ) + { + DBG_TESTSOLARMUTEX(); + if ( m_pOwner != nullptr ) + m_pOwner->notifyColumnChange( m_pOwner->getColumnPos( *this ), i_attributeGroup ); + } + + + OUString UnoGridColumnFacade::getName() const + { + OUString sName; + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", sName ); + try + { + sName = m_xGridColumn->getTitle(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return sName; + } + + + OUString UnoGridColumnFacade::getHelpText() const + { + OUString sHelpText; + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", sHelpText ); + try + { + sHelpText = m_xGridColumn->getHelpText(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return sHelpText; + } + + + bool UnoGridColumnFacade::isResizable() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", false ); + return lcl_get( m_xGridColumn, &XGridColumn::getResizeable ); + } + + + sal_Int32 UnoGridColumnFacade::getFlexibility() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 1 ); + return lcl_get( m_xGridColumn, &XGridColumn::getFlexibility ); + } + + + TableMetrics UnoGridColumnFacade::getWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getColumnWidth ); + } + + + void UnoGridColumnFacade::setWidth( TableMetrics _nWidth ) + { + ENSURE_OR_RETURN_VOID( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!" ); + lcl_set( m_xGridColumn, &XGridColumn::setColumnWidth, _nWidth ); + } + + + TableMetrics UnoGridColumnFacade::getMinWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getMinWidth ); + } + + + TableMetrics UnoGridColumnFacade::getMaxWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getMaxWidth ); + } + + + css::style::HorizontalAlignment UnoGridColumnFacade::getHorizontalAlign() + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", HorizontalAlignment_LEFT ); + return lcl_get( m_xGridColumn, &XGridColumn::getHorizontalAlign ); + } + + +} // svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unogridcolumnfacade.hxx b/toolkit/source/controls/unogridcolumnfacade.hxx new file mode 100644 index 0000000000..580aee52bc --- /dev/null +++ b/toolkit/source/controls/unogridcolumnfacade.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/table/tablemodel.hxx> + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> + +#include <rtl/ref.hxx> + + +namespace svt::table +{ + + + //= UnoGridColumnFacade + + class ColumnChangeMultiplexer; + class UnoControlTableModel; + class UnoGridColumnFacade :public IColumnModel + { + public: + UnoGridColumnFacade( + UnoControlTableModel const & i_owner, + css::uno::Reference< css::awt::grid::XGridColumn > const & i_gridColumn + ); + virtual ~UnoGridColumnFacade() override; + UnoGridColumnFacade(const UnoGridColumnFacade&) = delete; + UnoGridColumnFacade& operator=(const UnoGridColumnFacade&) = delete; + + // IColumnModel overridables + virtual OUString getName() const override; + virtual OUString getHelpText() const override; + virtual bool isResizable() const override; + virtual sal_Int32 getFlexibility() const override; + virtual TableMetrics getWidth() const override; + virtual void setWidth( TableMetrics _nWidth ) override; + virtual TableMetrics getMinWidth() const override; + virtual TableMetrics getMaxWidth() const override; + virtual css::style::HorizontalAlignment getHorizontalAlign() override; + + /** disposes the column wrapper + + Note that the XGridColumn which is wrapped by the instance is <strong>not</strong> disposed, as we + do not own it. + */ + void dispose(); + + sal_Int32 + getDataColumnIndex() const { return m_nDataColumnIndex; } + + // callbacks for the XGridColumnListener + void columnChanged( ColumnAttributeGroup const i_attributeGroup ); + void dataColumnIndexChanged(); + + private: + void impl_updateDataColumnIndex_nothrow(); + + private: + UnoControlTableModel const * m_pOwner; + sal_Int32 m_nDataColumnIndex; + css::uno::Reference< css::awt::grid::XGridColumn > m_xGridColumn; + ::rtl::Reference< ColumnChangeMultiplexer > m_pChangeMultiplexer; + }; + + +} // svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |