diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /toolkit/source | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/source')
96 files changed, 54364 insertions, 0 deletions
diff --git a/toolkit/source/awt/animatedimagespeer.cxx b/toolkit/source/awt/animatedimagespeer.cxx new file mode 100644 index 0000000000..379a82388b --- /dev/null +++ b/toolkit/source/awt/animatedimagespeer.cxx @@ -0,0 +1,476 @@ +/* -*- 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/animatedimagespeer.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <o3tl/safeint.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/urlobj.hxx> +#include <vcl/toolkit/throbber.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <limits> +#include <string_view> + +namespace toolkit +{ + + + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::container::ContainerEvent; + using ::com::sun::star::awt::XAnimatedImages; + using ::com::sun::star::awt::Size; + using ::com::sun::star::graphic::XGraphicProvider; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::graphic::XGraphic; + + namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode; + + //= helper + + namespace + { + + OUString lcl_getHighContrastURL( OUString const& i_imageURL ) + { + INetURLObject aURL( i_imageURL ); + if ( aURL.GetProtocol() != INetProtocol::PrivSoffice ) + { + OSL_VERIFY( aURL.insertName( u"sifr", false, 0 ) ); + return aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + // the private: scheme is not considered to be hierarchical by INetURLObject, so manually insert the + // segment + const sal_Int32 separatorPos = i_imageURL.indexOf( '/' ); + ENSURE_OR_RETURN( separatorPos != -1, "lcl_getHighContrastURL: unsupported URL scheme - cannot automatically determine HC version!", i_imageURL ); + + OUString composer = OUString::Concat(i_imageURL.subView(0, separatorPos)) + "/sifr" + + i_imageURL.subView(separatorPos); + return composer; + } + + + bool lcl_ensureImage_throw( Reference< XGraphicProvider > const& i_graphicProvider, const bool i_isHighContrast, const AnimatedImagesPeer::CachedImage& i_cachedImage ) + { + if ( !i_cachedImage.xGraphic.is() ) + { + ::comphelper::NamedValueCollection aMediaProperties; + if ( i_isHighContrast ) + { + // try (to find) the high-contrast version of the graphic first + aMediaProperties.put( "URL", lcl_getHighContrastURL( i_cachedImage.sImageURL ) ); + i_cachedImage.xGraphic = i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + } + if ( !i_cachedImage.xGraphic.is() ) + { + aMediaProperties.put( "URL", i_cachedImage.sImageURL ); + i_cachedImage.xGraphic = i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + } + } + return i_cachedImage.xGraphic.is(); + } + + + Size lcl_getGraphicSizePixel( Reference< XGraphic > const& i_graphic ) + { + Size aSizePixel; + try + { + if ( i_graphic.is() ) + { + const Reference< XPropertySet > xGraphicProps( i_graphic, UNO_QUERY_THROW ); + OSL_VERIFY( xGraphicProps->getPropertyValue("SizePixel") >>= aSizePixel ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + return aSizePixel; + } + + + void lcl_init( Sequence< OUString > const& i_imageURLs, ::std::vector< AnimatedImagesPeer::CachedImage >& o_images ) + { + o_images.resize(0); + size_t count = size_t( i_imageURLs.getLength() ); + o_images.reserve( count ); + for ( const auto& rImageURL : i_imageURLs ) + { + o_images.emplace_back( AnimatedImagesPeer::CachedImage{ rImageURL, nullptr } ); + } + } + + + } + + + //= AnimatedImagesPeer + + + AnimatedImagesPeer::AnimatedImagesPeer() + { + } + + + AnimatedImagesPeer::~AnimatedImagesPeer() + { + } + + + void SAL_CALL AnimatedImagesPeer::startAnimation() + { + SolarMutexGuard aGuard; + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if (pThrobber) + pThrobber->start(); + } + + void SAL_CALL AnimatedImagesPeer::stopAnimation() + { + SolarMutexGuard aGuard; + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if (pThrobber) + pThrobber->stop(); + } + + sal_Bool SAL_CALL AnimatedImagesPeer::isAnimationRunning() + { + SolarMutexGuard aGuard; + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if (pThrobber) + return pThrobber->isRunning(); + return false; + } + + void SAL_CALL AnimatedImagesPeer::setProperty( const OUString& i_propertyName, const Any& i_value ) + { + SolarMutexGuard aGuard; + + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if ( !pThrobber ) + { + VCLXWindow::setProperty( i_propertyName, i_value ); + return; + } + + const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_STEP_TIME: + { + sal_Int32 nStepTime( 0 ); + if ( i_value >>= nStepTime ) + pThrobber->setStepTime( nStepTime ); + break; + } + case BASEPROPERTY_AUTO_REPEAT: + { + bool bRepeat( true ); + if ( i_value >>= bRepeat ) + pThrobber->setRepeat( bRepeat ); + break; + } + + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + sal_Int16 nScaleMode( ImageScaleMode::ANISOTROPIC ); + VclPtr<ImageControl> pImageControl = GetAsDynamic< ImageControl >(); + if ( pImageControl && ( i_value >>= nScaleMode ) ) + pImageControl->SetScaleMode( nScaleMode ); + } + break; + + default: + AnimatedImagesPeer_Base::setProperty( i_propertyName, i_value ); + break; + } + } + + + Any SAL_CALL AnimatedImagesPeer::getProperty( const OUString& i_propertyName ) + { + SolarMutexGuard aGuard; + + Any aReturn; + + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if ( !pThrobber ) + return VCLXWindow::getProperty( i_propertyName ); + + const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_STEP_TIME: + aReturn <<= pThrobber->getStepTime(); + break; + + case BASEPROPERTY_AUTO_REPEAT: + aReturn <<= pThrobber->getRepeat(); + break; + + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + VclPtr<ImageControl> pImageControl = GetAsDynamic<ImageControl>(); + aReturn <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::ANISOTROPIC ); + } + break; + + default: + aReturn = AnimatedImagesPeer_Base::getProperty( i_propertyName ); + break; + } + + return aReturn; + } + + + void AnimatedImagesPeer::ProcessWindowEvent( const VclWindowEvent& i_windowEvent ) + { + if ( i_windowEvent.GetId() == VclEventId::WindowResize ) + { + updateImageList_nothrow(); + } + + AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent ); + } + + + void AnimatedImagesPeer::impl_updateImages_nolck( const Reference< XInterface >& i_animatedImages ) + { + SolarMutexGuard aGuard; + + updateImageList_nothrow( Reference< XAnimatedImages >( i_animatedImages, UNO_QUERY_THROW ) ); + } + + + void SAL_CALL AnimatedImagesPeer::elementInserted( const ContainerEvent& i_event ) + { + SolarMutexGuard aGuard; + Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); + + sal_Int32 nPosition(0); + OSL_VERIFY( i_event.Accessor >>= nPosition ); + size_t position = size_t( nPosition ); + if ( position > maCachedImageSets.size() ) + { + OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" ); + updateImageList_nothrow( xAnimatedImages ); + } + + Sequence< OUString > aImageURLs; + OSL_VERIFY( i_event.Element >>= aImageURLs ); + ::std::vector< CachedImage > aImages; + lcl_init( aImageURLs, aImages ); + maCachedImageSets.insert( maCachedImageSets.begin() + position, aImages ); + updateImageList_nothrow(); + } + + + void SAL_CALL AnimatedImagesPeer::elementRemoved( const ContainerEvent& i_event ) + { + SolarMutexGuard aGuard; + Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); + + sal_Int32 nPosition(0); + OSL_VERIFY( i_event.Accessor >>= nPosition ); + size_t position = size_t( nPosition ); + if ( position >= maCachedImageSets.size() ) + { + OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" ); + updateImageList_nothrow( xAnimatedImages ); + } + + maCachedImageSets.erase( maCachedImageSets.begin() + position ); + updateImageList_nothrow(); + } + + + void SAL_CALL AnimatedImagesPeer::elementReplaced( const ContainerEvent& i_event ) + { + SolarMutexGuard aGuard; + Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); + + sal_Int32 nPosition(0); + OSL_VERIFY( i_event.Accessor >>= nPosition ); + size_t position = size_t( nPosition ); + if ( position >= maCachedImageSets.size() ) + { + OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" ); + updateImageList_nothrow( xAnimatedImages ); + } + + Sequence< OUString > aImageURLs; + OSL_VERIFY( i_event.Element >>= aImageURLs ); + ::std::vector< CachedImage > aImages; + lcl_init( aImageURLs, aImages ); + maCachedImageSets[ position ] = aImages; + updateImageList_nothrow(); + } + + + void SAL_CALL AnimatedImagesPeer::disposing( const EventObject& i_event ) + { + VCLXWindow::disposing( i_event ); + } + + + void SAL_CALL AnimatedImagesPeer::modified( const EventObject& i_event ) + { + impl_updateImages_nolck( i_event.Source ); + } + + + void SAL_CALL AnimatedImagesPeer::dispose( ) + { + AnimatedImagesPeer_Base::dispose(); + SolarMutexGuard aGuard; + maCachedImageSets.resize(0); + } + + void AnimatedImagesPeer::updateImageList_nothrow() + { + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if ( !pThrobber ) + return; + + try + { + // collect the image sizes of the different image sets + const Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + const Reference< XGraphicProvider > xGraphicProvider( css::graphic::GraphicProvider::create(xContext) ); + + const bool isHighContrast = pThrobber->GetSettings().GetStyleSettings().GetHighContrastMode(); + + sal_Int32 nPreferredSet = -1; + const size_t nImageSetCount = maCachedImageSets.size(); + if ( nImageSetCount < 2 ) + { + nPreferredSet = sal_Int32( nImageSetCount ) - 1; + } + else + { + ::std::vector< Size > aImageSizes( nImageSetCount ); + for ( size_t nImageSet = 0; nImageSet < nImageSetCount; ++nImageSet ) + { + ::std::vector< CachedImage > const& rImageSet( maCachedImageSets[ nImageSet ] ); + if ( ( rImageSet.empty() ) + || ( !lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rImageSet[0] ) ) + ) + { + aImageSizes[ nImageSet ] = Size( SAL_MAX_INT32, SAL_MAX_INT32 ); + } + else + { + aImageSizes[ nImageSet ] = lcl_getGraphicSizePixel( rImageSet[0].xGraphic ); + } + } + + // find the set with the smallest difference between window size and image size + const ::Size aWindowSizePixel = pThrobber->GetSizePixel(); + tools::Long nMinimalDistance = ::std::numeric_limits< tools::Long >::max(); + for ( ::std::vector< Size >::const_iterator check = aImageSizes.begin(); + check != aImageSizes.end(); + ++check + ) + { + if ( ( check->Width > aWindowSizePixel.Width() ) + || ( check->Height > aWindowSizePixel.Height() ) + ) + // do not use an image set which doesn't fit into the window + continue; + + const sal_Int64 distance = + ( aWindowSizePixel.Width() - check->Width ) * ( aWindowSizePixel.Width() - check->Width ) + + ( aWindowSizePixel.Height() - check->Height ) * ( aWindowSizePixel.Height() - check->Height ); + if ( distance < nMinimalDistance ) + { + nMinimalDistance = distance; + nPreferredSet = check - aImageSizes.begin(); + } + } + } + + // found a set? + std::vector< Image > aImages; + if ( ( nPreferredSet >= 0 ) && ( o3tl::make_unsigned( nPreferredSet ) < nImageSetCount ) ) + { + // => set the images + ::std::vector< CachedImage > const& rImageSet( maCachedImageSets[ nPreferredSet ] ); + aImages.resize( rImageSet.size() ); + sal_Int32 imageIndex = 0; + for ( const auto& rCachedImage : rImageSet ) + { + lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rCachedImage ); + aImages[ imageIndex++ ] = Image(rCachedImage.xGraphic); + } + } + pThrobber->setImageList( std::move(aImages) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } + + + void AnimatedImagesPeer::updateImageList_nothrow( const Reference< XAnimatedImages >& i_images ) + { + try + { + const sal_Int32 nImageSetCount = i_images->getImageSetCount(); + maCachedImageSets.resize(0); + for ( sal_Int32 set = 0; set < nImageSetCount; ++set ) + { + const Sequence< OUString > aImageURLs( i_images->getImageSet( set ) ); + ::std::vector< CachedImage > aImages; + lcl_init( aImageURLs, aImages ); + maCachedImageSets.push_back( aImages ); + } + + updateImageList_nothrow(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/asynccallback.cxx b/toolkit/source/awt/asynccallback.cxx new file mode 100644 index 0000000000..f6804d9c23 --- /dev/null +++ b/toolkit/source/awt/asynccallback.cxx @@ -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 . + */ + +#include <sal/config.h> + +#include <utility> +#include <vcl/svapp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/XRequestCallback.hpp> + +/// anonymous implementation namespace +namespace { + +class AsyncCallback: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::awt::XRequestCallback> +{ +public: + AsyncCallback() {} + AsyncCallback(const AsyncCallback&) = delete; + AsyncCallback& operator=(const AsyncCallback&) = delete; + + // css::lang::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; + + // css::awt::XRequestCallback: + virtual void SAL_CALL addCallback(const css::uno::Reference< css::awt::XCallback > & xCallback, const css::uno::Any & aData) override; + +private: + + struct CallbackData + { + CallbackData( css::uno::Reference< css::awt::XCallback > _xCallback, css::uno::Any aAny ) : + xCallback(std::move( _xCallback )), aData(std::move( aAny )) {} + + css::uno::Reference< css::awt::XCallback > xCallback; + css::uno::Any aData; + }; + + DECL_STATIC_LINK( AsyncCallback, Notify_Impl, void*, void ); + + virtual ~AsyncCallback() override {} +}; + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL AsyncCallback::getImplementationName() +{ + return "com.sun.star.awt.comp.AsyncCallback"; +} + +sal_Bool SAL_CALL AsyncCallback::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > SAL_CALL AsyncCallback::getSupportedServiceNames() +{ + return { "com.sun.star.awt.AsyncCallback" }; +} + +// css::awt::XRequestCallback: +void SAL_CALL AsyncCallback::addCallback(const css::uno::Reference< css::awt::XCallback > & xCallback, const css::uno::Any & aData) +{ + if ( Application::IsInMain() ) + { + // NOTE: We don't need SolarMutexGuard here as Application::PostUserEvent is thread-safe + CallbackData* pCallbackData = new CallbackData( xCallback, aData ); + Application::PostUserEvent( LINK( this, AsyncCallback, Notify_Impl ), pCallbackData ); + } +} + +// private asynchronous link to call reference to the callback object +IMPL_STATIC_LINK( AsyncCallback, Notify_Impl, void*, p, void ) +{ + CallbackData* pCallbackData = static_cast<CallbackData*>(p); + try + { + // Asynchronous execution + // Check pointer and reference before! + if ( pCallbackData && pCallbackData->xCallback.is() ) + pCallbackData->xCallback->notify( pCallbackData->aData ); + } + catch ( css::uno::Exception& ) + { + } + + delete pCallbackData; +} + +} // closing anonymous implementation namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_awt_comp_AsyncCallback_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new AsyncCallback()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/non-interactable-containers.xml b/toolkit/source/awt/non-interactable-containers.xml new file mode 100644 index 0000000000..00f3f2a93e --- /dev/null +++ b/toolkit/source/awt/non-interactable-containers.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<dialog xmlns="http://openoffice.org/2007/layout" + xmlns:cnt="http://openoffice.org/2007/layout/container" + title="Interactable Containers" optimumsize="true" + border="true" sizeable="true" moveable="true"> + <hbox> + <table columns="3" cnt:title="Page 1"> + <pushbutton cnt:x-expand="false" cnt:row-span="2" label="1,1" /> + <pushbutton cnt:y-expand="false" label="1,2" /> + <pushbutton cnt:y-expand="false" label="1,3" /> + <pushbutton cnt:col-span="2" label="2,1" /> + </table> + </hbox> +</dialog> diff --git a/toolkit/source/awt/scrollabledialog.cxx b/toolkit/source/awt/scrollabledialog.cxx new file mode 100644 index 0000000000..bcc8d18e07 --- /dev/null +++ b/toolkit/source/awt/scrollabledialog.cxx @@ -0,0 +1,173 @@ +/* -*- 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 <helper/scrollabledialog.hxx> +#include <vcl/settings.hxx> + +namespace toolkit +{ + +// Using WB_AUTOHSCROLL, WB_AUTOVSCROLL here sucks big time, there is a +// problem in the toolkit class where there are some clashing IDs +// ( css::awt::VclWindowPeerAttribute::VSCROLL has the same value +// as css::awt::WindowAttribute::NODECORATION and they are used +// in the same bitmap :-( WB_VSCROLL & WB_HSCROLL apparently are only for +// child classes ( whole thing is a mess if you ask me ) +ScrollableDialog::ScrollableDialog( vcl::Window* pParent, WinBits nStyle, Dialog::InitFlag eFlag ) + : Dialog( pParent, nStyle & ~( WB_AUTOHSCROLL | WB_AUTOVSCROLL ), eFlag ), + maHScrollBar( VclPtr<ScrollBar>::Create(this, WB_HSCROLL | WB_DRAG) ), + maVScrollBar( VclPtr<ScrollBar>::Create(this, WB_VSCROLL | WB_DRAG) ), + mbHasHoriBar( false ), + mbHasVertBar( false ), + maScrollVis( None ) +{ + Link<ScrollBar*,void> aLink( LINK( this, ScrollableDialog, ScrollBarHdl ) ); + maVScrollBar->SetScrollHdl( aLink ); + maHScrollBar->SetScrollHdl( aLink ); + + ScrollBarVisibility aVis = None; + + if ( nStyle & ( WB_AUTOHSCROLL | WB_AUTOVSCROLL ) ) + { + if ( nStyle & WB_AUTOHSCROLL ) + aVis = Hori; + if ( nStyle & WB_AUTOVSCROLL ) + { + if ( aVis == Hori ) + aVis = Both; + else + aVis = Vert; + } + } + setScrollVisibility( aVis ); + mnScrWidth = Dialog::GetSettings().GetStyleSettings().GetScrollBarSize(); +} + +void ScrollableDialog::setScrollVisibility( ScrollBarVisibility rVisState ) +{ + maScrollVis = rVisState; + if ( maScrollVis == Hori || maScrollVis == Both ) + { + mbHasHoriBar = true; + maHScrollBar->Show(); + } + if ( maScrollVis == Vert || maScrollVis == Both ) + { + mbHasVertBar = true; + maVScrollBar->Show(); + } + if ( mbHasHoriBar || mbHasVertBar ) + SetStyle( Dialog::GetStyle() | WB_CLIPCHILDREN ); +} + +ScrollableDialog::~ScrollableDialog() +{ + disposeOnce(); +} + +void ScrollableDialog::dispose() +{ + maHScrollBar.disposeAndClear(); + maVScrollBar.disposeAndClear(); + Dialog::dispose(); +} + +void ScrollableDialog::lcl_Scroll( tools::Long nX, tools::Long nY ) +{ + tools::Long nXScroll = mnScrollPos.X() - nX; + tools::Long nYScroll = mnScrollPos.Y() - nY; + mnScrollPos = Point( nX, nY ); + + tools::Rectangle aScrollableArea( 0, 0, maScrollArea.Width(), maScrollArea.Height() ); + Scroll(nXScroll, nYScroll, aScrollableArea ); + // Manually scroll all children ( except the scrollbars ) + for ( int index = 0; index < GetChildCount(); ++index ) + { + vcl::Window* pChild = GetChild( index ); + if ( pChild && pChild != maVScrollBar.get() && pChild != maHScrollBar.get() ) + { + Point aPos = pChild->GetPosPixel(); + aPos += Point( nXScroll, nYScroll ); + pChild->SetPosPixel( aPos ); + } + } +} + +IMPL_LINK( ScrollableDialog, ScrollBarHdl, ScrollBar*, pSB, void ) +{ + sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos()); + if( pSB == maVScrollBar.get() ) + lcl_Scroll(mnScrollPos.X(), nPos ); + else if( pSB == maHScrollBar.get() ) + lcl_Scroll(nPos, mnScrollPos.Y() ); +} + +void ScrollableDialog::SetScrollTop( tools::Long nTop ) +{ + Point aOld = mnScrollPos; + lcl_Scroll( mnScrollPos.X() , mnScrollPos.Y() - nTop ); + maHScrollBar->SetThumbPos( 0 ); + // new pos is 0,0 + mnScrollPos = aOld; +} +void ScrollableDialog::SetScrollLeft( tools::Long nLeft ) +{ + Point aOld = mnScrollPos; + lcl_Scroll( mnScrollPos.X() - nLeft , mnScrollPos.Y() ); + maVScrollBar->SetThumbPos( 0 ); + // new pos is 0,0 + mnScrollPos = aOld; +} + +void ScrollableDialog::SetScrollWidth( tools::Long nWidth ) +{ + maScrollArea.setWidth( nWidth ); + ResetScrollBars(); +} + +void ScrollableDialog::SetScrollHeight( tools::Long nHeight ) +{ + maScrollArea.setHeight( nHeight ); + ResetScrollBars(); +} + +void ScrollableDialog::Resize() +{ + ResetScrollBars(); +} + +void ScrollableDialog::ResetScrollBars() +{ + Size aOutSz = GetOutputSizePixel(); + + Point aVPos( aOutSz.Width() - mnScrWidth, 0 ); + Point aHPos( 0, aOutSz.Height() - mnScrWidth ); + + maVScrollBar->SetPosSizePixel( aVPos, Size( mnScrWidth, GetSizePixel().Height() - mnScrWidth ) ); + maHScrollBar->SetPosSizePixel( aHPos, Size( GetSizePixel().Width() - mnScrWidth, mnScrWidth ) ); + + maHScrollBar->SetRangeMax( maScrollArea.Width() + mnScrWidth ); + maHScrollBar->SetVisibleSize( GetSizePixel().Width() ); + + maVScrollBar->SetRangeMax( maScrollArea.Height() + mnScrWidth ); + maVScrollBar->SetVisibleSize( GetSizePixel().Height() ); +} + +} // toolkit +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/stylesettings.cxx b/toolkit/source/awt/stylesettings.cxx new file mode 100644 index 0000000000..75c2b687d2 --- /dev/null +++ b/toolkit/source/awt/stylesettings.cxx @@ -0,0 +1,926 @@ +/* -*- 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 "stylesettings.hxx" +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> + +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <vcl/event.hxx> +#include <vcl/window.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + + +namespace toolkit +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::awt::FontDescriptor; + using ::com::sun::star::awt::XStyleChangeListener; + + + IMPL_LINK( WindowStyleSettings, OnWindowEvent, VclWindowEvent&, rEvent, void ) + { + if ( rEvent.GetId() != VclEventId::WindowDataChanged ) + return; + const DataChangedEvent* pDataChangedEvent = static_cast< const DataChangedEvent* >( rEvent.GetData() ); + if ( !pDataChangedEvent || ( pDataChangedEvent->GetType() != DataChangedEventType::SETTINGS ) ) + return; + if ( !( pDataChangedEvent->GetFlags() & AllSettingsFlags::STYLE ) ) + return; + + EventObject aEvent( *pOwningWindow ); + aStyleChangeListeners.notifyEach( &XStyleChangeListener::styleSettingsChanged, aEvent ); + } + + + //= StyleMethodGuard + + namespace { + + class StyleMethodGuard + { + public: + explicit StyleMethodGuard( const VCLXWindow* pOwningWindow ) + { + if ( pOwningWindow == nullptr ) + throw DisposedException(); + } + + private: + SolarMutexGuard m_aGuard; + }; + + } + + //= WindowStyleSettings + + + WindowStyleSettings::WindowStyleSettings(::osl::Mutex& i_rListenerMutex, VCLXWindow& i_rOwningWindow ) + : pOwningWindow( &i_rOwningWindow ) + ,aStyleChangeListeners( i_rListenerMutex ) + { + VclPtr<vcl::Window> pWindow = i_rOwningWindow.GetWindow(); + if ( !pWindow ) + throw RuntimeException(); + pWindow->AddEventListener( LINK( this, WindowStyleSettings, OnWindowEvent ) ); + } + + + WindowStyleSettings::~WindowStyleSettings() + { + } + + + void WindowStyleSettings::dispose() + { + StyleMethodGuard aGuard( pOwningWindow ); + + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + OSL_ENSURE( pWindow, "WindowStyleSettings::dispose: window has been reset before we could revoke the listener!" ); + if ( pWindow ) + pWindow->RemoveEventListener( LINK( this, WindowStyleSettings, OnWindowEvent ) ); + + EventObject aEvent( *this ); + aStyleChangeListeners.disposeAndClear( aEvent ); + + pOwningWindow = nullptr; + } + + + sal_Int32 WindowStyleSettings::ImplGetStyleColor( Color const & (StyleSettings::*i_pGetter)() const ) const + { + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return sal_Int32((aStyleSettings.*i_pGetter)()); + } + + void WindowStyleSettings::ImplSetStyleColor( void (StyleSettings::*i_pSetter)( Color const & ), sal_Int32 i_nColor ) + { + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + AllSettings aAllSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + (aStyleSettings.*i_pSetter)( Color(ColorTransparency, i_nColor) ); + aAllSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aAllSettings ); + } + + FontDescriptor WindowStyleSettings::ImplGetStyleFont( vcl::Font const & (StyleSettings::*i_pGetter)() const ) const + { + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return VCLUnoHelper::CreateFontDescriptor( (aStyleSettings.*i_pGetter)() ); + } + + void WindowStyleSettings::ImplSetStyleFont( void (StyleSettings::*i_pSetter)( vcl::Font const &), + vcl::Font const & (StyleSettings::*i_pGetter)() const, const FontDescriptor& i_rFont ) + { + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + AllSettings aAllSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + const vcl::Font aNewFont = VCLUnoHelper::CreateFont( i_rFont, (aStyleSettings.*i_pGetter)() ); + (aStyleSettings.*i_pSetter)( aNewFont ); + aAllSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aAllSettings ); + } + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveBorderColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveBorderColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveBorderColor( ::sal_Int32 _activebordercolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveBorderColor, _activebordercolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveColor( ::sal_Int32 _activecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveColor, _activecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveTabColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveTabColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveTabColor( ::sal_Int32 _activetabcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveTabColor, _activetabcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveTextColor( ::sal_Int32 _activetextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveTextColor, _activetextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getButtonRolloverTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetButtonRolloverTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setButtonRolloverTextColor( ::sal_Int32 _buttonrollovertextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetButtonRolloverTextColor, _buttonrollovertextcolor ); + // Also need to set ActionButtonRolloverTextColor as this setting can't be + // set through the UNO interface otherwise. + // Previously this setting was used to set colors for both scenarios, + // but action button setting was added to differentiate the buttons from + // "normal" buttons in some themes. + ImplSetStyleColor( &StyleSettings::SetActionButtonRolloverTextColor, _buttonrollovertextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getButtonTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetButtonTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setButtonTextColor( ::sal_Int32 _buttontextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetButtonTextColor, _buttontextcolor ); + // Also need to set ActionButtonTextColor and DefaultActionButtonTextColor + // as this two settings can't be set through the UNO interface otherwise. + // Previously this setting was used to set colors for all three scenarios, + // but action button setting was added to differentiate the buttons from + // "normal" buttons in some themes. + ImplSetStyleColor( &StyleSettings::SetActionButtonTextColor, _buttontextcolor ); + ImplSetStyleColor( &StyleSettings::SetDefaultActionButtonTextColor, _buttontextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getCheckedColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetCheckedColor ); + } + + + void SAL_CALL WindowStyleSettings::setCheckedColor( ::sal_Int32 _checkedcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetCheckedColor, _checkedcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDarkShadowColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDarkShadowColor ); + } + + + void SAL_CALL WindowStyleSettings::setDarkShadowColor( ::sal_Int32 _darkshadowcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDarkShadowColor, _darkshadowcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDeactiveBorderColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDeactiveBorderColor ); + } + + + void SAL_CALL WindowStyleSettings::setDeactiveBorderColor( ::sal_Int32 _deactivebordercolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDeactiveBorderColor, _deactivebordercolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDeactiveColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDeactiveColor ); + } + + + void SAL_CALL WindowStyleSettings::setDeactiveColor( ::sal_Int32 _deactivecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDeactiveColor, _deactivecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDeactiveTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDeactiveTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setDeactiveTextColor( ::sal_Int32 _deactivetextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDeactiveTextColor, _deactivetextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDialogColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDialogColor ); + } + + + void SAL_CALL WindowStyleSettings::setDialogColor( ::sal_Int32 _dialogcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDialogColor, _dialogcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDialogTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDialogTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setDialogTextColor( ::sal_Int32 _dialogtextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDialogTextColor, _dialogtextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDisableColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDisableColor ); + } + + + void SAL_CALL WindowStyleSettings::setDisableColor( ::sal_Int32 _disablecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDisableColor, _disablecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFaceColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFaceColor ); + } + + + void SAL_CALL WindowStyleSettings::setFaceColor( ::sal_Int32 _facecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFaceColor, _facecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFaceGradientColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return sal_Int32(aStyleSettings.GetFaceGradientColor()); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFieldColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFieldColor ); + } + + + void SAL_CALL WindowStyleSettings::setFieldColor( ::sal_Int32 _fieldcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFieldColor, _fieldcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFieldRolloverTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFieldRolloverTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setFieldRolloverTextColor( ::sal_Int32 _fieldrollovertextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFieldRolloverTextColor, _fieldrollovertextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFieldTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFieldTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setFieldTextColor( ::sal_Int32 _fieldtextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFieldTextColor, _fieldtextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getGroupTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetGroupTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setGroupTextColor( ::sal_Int32 _grouptextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetGroupTextColor, _grouptextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHelpColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHelpColor ); + } + + + void SAL_CALL WindowStyleSettings::setHelpColor( ::sal_Int32 _helpcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHelpColor, _helpcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHelpTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHelpTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setHelpTextColor( ::sal_Int32 _helptextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHelpTextColor, _helptextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHighlightColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHighlightColor ); + } + + + void SAL_CALL WindowStyleSettings::setHighlightColor( ::sal_Int32 _highlightcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHighlightColor, _highlightcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHighlightTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHighlightTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setHighlightTextColor( ::sal_Int32 _highlighttextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHighlightTextColor, _highlighttextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getInactiveTabColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetInactiveTabColor ); + } + + + void SAL_CALL WindowStyleSettings::setInactiveTabColor( ::sal_Int32 _inactivetabcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetInactiveTabColor, _inactivetabcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getLabelTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetLabelTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setLabelTextColor( ::sal_Int32 _labeltextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetLabelTextColor, _labeltextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getLightColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetLightColor ); + } + + + void SAL_CALL WindowStyleSettings::setLightColor( ::sal_Int32 _lightcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetLightColor, _lightcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuBarColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuBarColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuBarColor( ::sal_Int32 _menubarcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuBarColor, _menubarcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuBarTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuBarTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuBarTextColor( ::sal_Int32 _menubartextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuBarTextColor, _menubartextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuBorderColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuBorderColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuBorderColor( ::sal_Int32 _menubordercolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuBorderColor, _menubordercolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuColor( ::sal_Int32 _menucolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuColor, _menucolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuHighlightColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuHighlightColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuHighlightColor( ::sal_Int32 _menuhighlightcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuHighlightColor, _menuhighlightcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuHighlightTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuHighlightTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuHighlightTextColor( ::sal_Int32 _menuhighlighttextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuHighlightTextColor, _menuhighlighttextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuTextColor( ::sal_Int32 _menutextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuTextColor, _menutextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMonoColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMonoColor ); + } + + + void SAL_CALL WindowStyleSettings::setMonoColor( ::sal_Int32 _monocolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMonoColor, _monocolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getRadioCheckTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetRadioCheckTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setRadioCheckTextColor( ::sal_Int32 _radiochecktextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetRadioCheckTextColor, _radiochecktextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getSeparatorColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return sal_Int32(aStyleSettings.GetSeparatorColor()); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getShadowColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetShadowColor ); + } + + + void SAL_CALL WindowStyleSettings::setShadowColor( ::sal_Int32 _shadowcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetShadowColor, _shadowcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getWindowColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetWindowColor ); + } + + + void SAL_CALL WindowStyleSettings::setWindowColor( ::sal_Int32 _windowcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetWindowColor, _windowcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getWindowTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetWindowTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setWindowTextColor( ::sal_Int32 _windowtextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetWindowTextColor, _windowtextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getWorkspaceColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetWorkspaceColor ); + } + + + void SAL_CALL WindowStyleSettings::setWorkspaceColor( ::sal_Int32 _workspacecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetWorkspaceColor, _workspacecolor ); + } + + + sal_Bool SAL_CALL WindowStyleSettings::getHighContrastMode() + { + StyleMethodGuard aGuard( pOwningWindow ); + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return aStyleSettings.GetHighContrastMode(); + } + + + void SAL_CALL WindowStyleSettings::setHighContrastMode( sal_Bool _highcontrastmode ) + { + StyleMethodGuard aGuard( pOwningWindow ); + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + AllSettings aAllSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + aStyleSettings.SetHighContrastMode( _highcontrastmode ); + aAllSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aAllSettings ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getApplicationFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetAppFont ); + } + + + void SAL_CALL WindowStyleSettings::setApplicationFont( const FontDescriptor& _applicationfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetAppFont, &StyleSettings::GetAppFont, _applicationfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getHelpFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetHelpFont ); + } + + + void SAL_CALL WindowStyleSettings::setHelpFont( const FontDescriptor& _helpfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetHelpFont, &StyleSettings::GetHelpFont, _helpfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getTitleFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetTitleFont ); + } + + + void SAL_CALL WindowStyleSettings::setTitleFont( const FontDescriptor& _titlefont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetTitleFont, &StyleSettings::GetTitleFont, _titlefont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getFloatTitleFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetFloatTitleFont ); + } + + + void SAL_CALL WindowStyleSettings::setFloatTitleFont( const FontDescriptor& _floattitlefont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetFloatTitleFont, &StyleSettings::GetFloatTitleFont, _floattitlefont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getMenuFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetMenuFont ); + } + + + void SAL_CALL WindowStyleSettings::setMenuFont( const FontDescriptor& _menufont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetMenuFont, &StyleSettings::GetMenuFont, _menufont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getToolFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetToolFont ); + } + + + void SAL_CALL WindowStyleSettings::setToolFont( const FontDescriptor& _toolfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetToolFont, &StyleSettings::GetToolFont, _toolfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getGroupFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetGroupFont ); + } + + + void SAL_CALL WindowStyleSettings::setGroupFont( const FontDescriptor& _groupfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetGroupFont, &StyleSettings::GetGroupFont, _groupfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getLabelFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetLabelFont ); + } + + + void SAL_CALL WindowStyleSettings::setLabelFont( const FontDescriptor& _labelfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetLabelFont, &StyleSettings::GetLabelFont, _labelfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getRadioCheckFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetRadioCheckFont ); + } + + + void SAL_CALL WindowStyleSettings::setRadioCheckFont( const FontDescriptor& _radiocheckfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetRadioCheckFont, &StyleSettings::GetRadioCheckFont, _radiocheckfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getPushButtonFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetPushButtonFont ); + } + + + void SAL_CALL WindowStyleSettings::setPushButtonFont( const FontDescriptor& _pushbuttonfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetPushButtonFont, &StyleSettings::GetPushButtonFont, _pushbuttonfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getFieldFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetFieldFont ); + } + + + void SAL_CALL WindowStyleSettings::setFieldFont( const FontDescriptor& _fieldfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetFieldFont, &StyleSettings::GetFieldFont, _fieldfont ); + } + + + void SAL_CALL WindowStyleSettings::addStyleChangeListener( const Reference< XStyleChangeListener >& i_rListener ) + { + StyleMethodGuard aGuard( pOwningWindow ); + if ( i_rListener.is() ) + aStyleChangeListeners.addInterface( i_rListener ); + } + + + void SAL_CALL WindowStyleSettings::removeStyleChangeListener( const Reference< XStyleChangeListener >& i_rListener ) + { + StyleMethodGuard aGuard( pOwningWindow ); + if ( i_rListener.is() ) + aStyleChangeListeners.removeInterface( i_rListener ); + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/stylesettings.hxx b/toolkit/source/awt/stylesettings.hxx new file mode 100644 index 0000000000..4e3f0b2df9 --- /dev/null +++ b/toolkit/source/awt/stylesettings.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 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_AWT_STYLESETTINGS_HXX +#define INCLUDED_TOOLKIT_SOURCE_AWT_STYLESETTINGS_HXX + +#include <com/sun/star/awt/XStyleSettings.hpp> + +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/implbase.hxx> +#include <tools/link.hxx> + +namespace osl +{ + class Mutex; +} +class Color; +class VCLXWindow; +class VclWindowEvent; +class StyleSettings; +namespace vcl { class Font; } + +namespace toolkit +{ + + + //= WindowStyleSettings + + typedef ::cppu::WeakImplHelper < css::awt::XStyleSettings + > WindowStyleSettings_Base; + class WindowStyleSettings : public WindowStyleSettings_Base + { + public: + WindowStyleSettings( ::osl::Mutex& i_rListenerMutex, VCLXWindow& i_rOwningWindow ); + virtual ~WindowStyleSettings() override; + + void dispose(); + + // XStyleSettings + virtual ::sal_Int32 SAL_CALL getActiveBorderColor() override; + virtual void SAL_CALL setActiveBorderColor( ::sal_Int32 _activebordercolor ) override; + virtual ::sal_Int32 SAL_CALL getActiveColor() override; + virtual void SAL_CALL setActiveColor( ::sal_Int32 _activecolor ) override; + virtual ::sal_Int32 SAL_CALL getActiveTabColor() override; + virtual void SAL_CALL setActiveTabColor( ::sal_Int32 _activetabcolor ) override; + virtual ::sal_Int32 SAL_CALL getActiveTextColor() override; + virtual void SAL_CALL setActiveTextColor( ::sal_Int32 _activetextcolor ) override; + virtual ::sal_Int32 SAL_CALL getButtonRolloverTextColor() override; + virtual void SAL_CALL setButtonRolloverTextColor( ::sal_Int32 _buttonrollovertextcolor ) override; + virtual ::sal_Int32 SAL_CALL getButtonTextColor() override; + virtual void SAL_CALL setButtonTextColor( ::sal_Int32 _buttontextcolor ) override; + virtual ::sal_Int32 SAL_CALL getCheckedColor() override; + virtual void SAL_CALL setCheckedColor( ::sal_Int32 _checkedcolor ) override; + virtual ::sal_Int32 SAL_CALL getDarkShadowColor() override; + virtual void SAL_CALL setDarkShadowColor( ::sal_Int32 _darkshadowcolor ) override; + virtual ::sal_Int32 SAL_CALL getDeactiveBorderColor() override; + virtual void SAL_CALL setDeactiveBorderColor( ::sal_Int32 _deactivebordercolor ) override; + virtual ::sal_Int32 SAL_CALL getDeactiveColor() override; + virtual void SAL_CALL setDeactiveColor( ::sal_Int32 _deactivecolor ) override; + virtual ::sal_Int32 SAL_CALL getDeactiveTextColor() override; + virtual void SAL_CALL setDeactiveTextColor( ::sal_Int32 _deactivetextcolor ) override; + virtual ::sal_Int32 SAL_CALL getDialogColor() override; + virtual void SAL_CALL setDialogColor( ::sal_Int32 _dialogcolor ) override; + virtual ::sal_Int32 SAL_CALL getDialogTextColor() override; + virtual void SAL_CALL setDialogTextColor( ::sal_Int32 _dialogtextcolor ) override; + virtual ::sal_Int32 SAL_CALL getDisableColor() override; + virtual void SAL_CALL setDisableColor( ::sal_Int32 _disablecolor ) override; + virtual ::sal_Int32 SAL_CALL getFaceColor() override; + virtual void SAL_CALL setFaceColor( ::sal_Int32 _facecolor ) override; + virtual ::sal_Int32 SAL_CALL getFaceGradientColor() override; + virtual ::sal_Int32 SAL_CALL getFieldColor() override; + virtual void SAL_CALL setFieldColor( ::sal_Int32 _fieldcolor ) override; + virtual ::sal_Int32 SAL_CALL getFieldRolloverTextColor() override; + virtual void SAL_CALL setFieldRolloverTextColor( ::sal_Int32 _fieldrollovertextcolor ) override; + virtual ::sal_Int32 SAL_CALL getFieldTextColor() override; + virtual void SAL_CALL setFieldTextColor( ::sal_Int32 _fieldtextcolor ) override; + virtual ::sal_Int32 SAL_CALL getGroupTextColor() override; + virtual void SAL_CALL setGroupTextColor( ::sal_Int32 _grouptextcolor ) override; + virtual ::sal_Int32 SAL_CALL getHelpColor() override; + virtual void SAL_CALL setHelpColor( ::sal_Int32 _helpcolor ) override; + virtual ::sal_Int32 SAL_CALL getHelpTextColor() override; + virtual void SAL_CALL setHelpTextColor( ::sal_Int32 _helptextcolor ) override; + virtual ::sal_Int32 SAL_CALL getHighlightColor() override; + virtual void SAL_CALL setHighlightColor( ::sal_Int32 _highlightcolor ) override; + virtual ::sal_Int32 SAL_CALL getHighlightTextColor() override; + virtual void SAL_CALL setHighlightTextColor( ::sal_Int32 _highlighttextcolor ) override; + virtual ::sal_Int32 SAL_CALL getInactiveTabColor() override; + virtual void SAL_CALL setInactiveTabColor( ::sal_Int32 _inactivetabcolor ) override; + virtual ::sal_Int32 SAL_CALL getLabelTextColor() override; + virtual void SAL_CALL setLabelTextColor( ::sal_Int32 _labeltextcolor ) override; + virtual ::sal_Int32 SAL_CALL getLightColor() override; + virtual void SAL_CALL setLightColor( ::sal_Int32 _lightcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuBarColor() override; + virtual void SAL_CALL setMenuBarColor( ::sal_Int32 _menubarcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuBarTextColor() override; + virtual void SAL_CALL setMenuBarTextColor( ::sal_Int32 _menubartextcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuBorderColor() override; + virtual void SAL_CALL setMenuBorderColor( ::sal_Int32 _menubordercolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuColor() override; + virtual void SAL_CALL setMenuColor( ::sal_Int32 _menucolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuHighlightColor() override; + virtual void SAL_CALL setMenuHighlightColor( ::sal_Int32 _menuhighlightcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuHighlightTextColor() override; + virtual void SAL_CALL setMenuHighlightTextColor( ::sal_Int32 _menuhighlighttextcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuTextColor() override; + virtual void SAL_CALL setMenuTextColor( ::sal_Int32 _menutextcolor ) override; + virtual ::sal_Int32 SAL_CALL getMonoColor() override; + virtual void SAL_CALL setMonoColor( ::sal_Int32 _monocolor ) override; + virtual ::sal_Int32 SAL_CALL getRadioCheckTextColor() override; + virtual void SAL_CALL setRadioCheckTextColor( ::sal_Int32 _radiochecktextcolor ) override; + virtual ::sal_Int32 SAL_CALL getSeparatorColor() override; + virtual ::sal_Int32 SAL_CALL getShadowColor() override; + virtual void SAL_CALL setShadowColor( ::sal_Int32 _shadowcolor ) override; + virtual ::sal_Int32 SAL_CALL getWindowColor() override; + virtual void SAL_CALL setWindowColor( ::sal_Int32 _windowcolor ) override; + virtual ::sal_Int32 SAL_CALL getWindowTextColor() override; + virtual void SAL_CALL setWindowTextColor( ::sal_Int32 _windowtextcolor ) override; + virtual ::sal_Int32 SAL_CALL getWorkspaceColor() override; + virtual void SAL_CALL setWorkspaceColor( ::sal_Int32 _workspacecolor ) override; + virtual sal_Bool SAL_CALL getHighContrastMode() override; + virtual void SAL_CALL setHighContrastMode( sal_Bool _highcontrastmode ) override; + virtual css::awt::FontDescriptor SAL_CALL getApplicationFont() override; + virtual void SAL_CALL setApplicationFont( const css::awt::FontDescriptor& _applicationfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getHelpFont() override; + virtual void SAL_CALL setHelpFont( const css::awt::FontDescriptor& _helpfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getTitleFont() override; + virtual void SAL_CALL setTitleFont( const css::awt::FontDescriptor& _titlefont ) override; + virtual css::awt::FontDescriptor SAL_CALL getFloatTitleFont() override; + virtual void SAL_CALL setFloatTitleFont( const css::awt::FontDescriptor& _floattitlefont ) override; + virtual css::awt::FontDescriptor SAL_CALL getMenuFont() override; + virtual void SAL_CALL setMenuFont( const css::awt::FontDescriptor& _menufont ) override; + virtual css::awt::FontDescriptor SAL_CALL getToolFont() override; + virtual void SAL_CALL setToolFont( const css::awt::FontDescriptor& _toolfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getGroupFont() override; + virtual void SAL_CALL setGroupFont( const css::awt::FontDescriptor& _groupfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getLabelFont() override; + virtual void SAL_CALL setLabelFont( const css::awt::FontDescriptor& _labelfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getRadioCheckFont() override; + virtual void SAL_CALL setRadioCheckFont( const css::awt::FontDescriptor& _radiocheckfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getPushButtonFont() override; + virtual void SAL_CALL setPushButtonFont( const css::awt::FontDescriptor& _pushbuttonfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getFieldFont() override; + virtual void SAL_CALL setFieldFont( const css::awt::FontDescriptor& _fieldfont ) override; + virtual void SAL_CALL addStyleChangeListener( const css::uno::Reference< css::awt::XStyleChangeListener >& Listener ) override; + virtual void SAL_CALL removeStyleChangeListener( const css::uno::Reference< css::awt::XStyleChangeListener >& Listener ) override; + + private: + void ImplSetStyleFont( void (StyleSettings::*i_pSetter)( vcl::Font const &), + vcl::Font const & (StyleSettings::*i_pGetter)() const, const css::awt::FontDescriptor& i_rFont ); + void ImplSetStyleColor( void (StyleSettings::*i_pSetter)( Color const & ), sal_Int32 i_nColor ); + sal_Int32 ImplGetStyleColor( Color const & (StyleSettings::*i_pGetter)() const ) const; + css::awt::FontDescriptor ImplGetStyleFont( vcl::Font const & (StyleSettings::*i_pGetter)() const ) const; + DECL_LINK( OnWindowEvent, VclWindowEvent&, void ); + + VCLXWindow* pOwningWindow; + ::comphelper::OInterfaceContainerHelper3<css::awt::XStyleChangeListener> aStyleChangeListeners; + }; + + +} // namespace toolkit + + +#endif // INCLUDED_TOOLKIT_SOURCE_AWT_STYLESETTINGS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxaccessiblecomponent.cxx b/toolkit/source/awt/vclxaccessiblecomponent.cxx new file mode 100644 index 0000000000..9c2321ede3 --- /dev/null +++ b/toolkit/source/awt/vclxaccessiblecomponent.cxx @@ -0,0 +1,854 @@ +/* -*- 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/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessiblecontexthelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <toolkit/awt/vclxaccessiblecomponent.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/convert.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/vclevent.hxx> +#include <vcl/window.hxx> +#include <vcl/toolkit/edit.hxx> +#include <vcl/settings.hxx> +#include <tools/debug.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/menu.hxx> + +using namespace ::com::sun::star; +using namespace ::comphelper; + +VCLXAccessibleComponent::VCLXAccessibleComponent( VCLXWindow* pVCLXWindow ) +{ + m_xVCLXWindow = pVCLXWindow; + + DBG_ASSERT( pVCLXWindow->GetWindow(), "VCLXAccessibleComponent - no window!" ); + m_xEventSource = pVCLXWindow->GetWindow(); + if ( m_xEventSource ) + { + m_xEventSource->AddEventListener( LINK( this, VCLXAccessibleComponent, WindowEventListener ) ); + m_xEventSource->AddChildEventListener( LINK( this, VCLXAccessibleComponent, WindowChildEventListener ) ); + } + + // announce the XAccessible of our creator to the base class + lateInit( pVCLXWindow ); +} + +VCLXWindow* VCLXAccessibleComponent::GetVCLXWindow() const +{ + return m_xVCLXWindow.get(); +} + +void VCLXAccessibleComponent::DisconnectEvents() +{ + if ( m_xEventSource ) + { + m_xEventSource->RemoveEventListener( LINK( this, VCLXAccessibleComponent, WindowEventListener ) ); + m_xEventSource->RemoveChildEventListener( LINK( this, VCLXAccessibleComponent, WindowChildEventListener ) ); + m_xEventSource.clear(); + } +} + +VCLXAccessibleComponent::~VCLXAccessibleComponent() +{ + ensureDisposed(); + DisconnectEvents(); +} + +OUString VCLXAccessibleComponent::getImplementationName() +{ + return "com.sun.star.comp.toolkit.AccessibleWindow"; +} + +sal_Bool VCLXAccessibleComponent::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > VCLXAccessibleComponent::getSupportedServiceNames() +{ + uno::Sequence< OUString > aNames { "com.sun.star.awt.AccessibleWindow" }; + return aNames; +} + +IMPL_LINK( VCLXAccessibleComponent, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + /* Ignore VclEventId::WindowEndPopupMode, because the UNO accessibility wrapper + * might have been destroyed by the previous VCLEventListener (if no AT tool + * is running), e.g. sub-toolbars in impress. + */ + if ( m_xVCLXWindow.is() /* #122218# */ && (rEvent.GetId() != VclEventId::WindowEndPopupMode) ) + { + DBG_ASSERT( rEvent.GetWindow(), "Window???" ); + if( !rEvent.GetWindow()->IsAccessibilityEventsSuppressed() || ( rEvent.GetId() == VclEventId::ObjectDying ) ) + { + ProcessWindowEvent( rEvent ); + } + } +} + +IMPL_LINK( VCLXAccessibleComponent, WindowChildEventListener, VclWindowEvent&, rEvent, void ) +{ + if ( m_xVCLXWindow.is() /* #i68079# */ ) + { + DBG_ASSERT( rEvent.GetWindow(), "Window???" ); + if( !rEvent.GetWindow()->IsAccessibilityEventsSuppressed() ) + { + // #103087# to prevent an early release of the component + uno::Reference< accessibility::XAccessibleContext > xHoldAlive = this; + + ProcessWindowChildEvent( rEvent ); + } + } +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::GetChildAccessible( const VclWindowEvent& rVclWindowEvent ) +{ + // checks if the data in the window event is our direct child + // and returns its accessible + + // MT: Change this later, normally a show/hide event shouldn't have the vcl::Window* in pData. + vcl::Window* pChildWindow = static_cast<vcl::Window *>(rVclWindowEvent.GetData()); + // tdf#141101/tdf#156561 Handle the event if this is either the a11y parent or the + // vcl::Window parent, since child events are sent for the vcl::Window hierarchy + // (s. Window::CallEventListeners) and e.g. DockingManager does manual partial reparenting + // that would cause child events to not be forwarded to the a11y level when + // not taking GetParent() into account here + if (pChildWindow && (GetWindow() == pChildWindow->GetAccessibleParentWindow() + || GetWindow() == pChildWindow->GetParent())) + return pChildWindow->GetAccessible( rVclWindowEvent.GetId() == VclEventId::WindowShow ); + else + return uno::Reference< accessibility::XAccessible > (); +} + +void VCLXAccessibleComponent::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent ) +{ + uno::Any aOldValue, aNewValue; + uno::Reference< accessibility::XAccessible > xAcc; + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowShow: // send create on show for direct accessible children + { + xAcc = GetChildAccessible( rVclWindowEvent ); + if( xAcc.is() ) + { + aNewValue <<= xAcc; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + + // CHILD event above results in a11y event listeners getting registered, + // so send state change event for SHOWING event after that + uno::Reference<XAccessibleContext> xChildContext = xAcc->getAccessibleContext(); + if (xChildContext.is()) + { + VCLXAccessibleComponent* pChildComponent = dynamic_cast<VCLXAccessibleComponent*>(xChildContext.get()); + if (pChildComponent) + { + css::uno::Any aNewStateValue; + aNewStateValue <<= accessibility::AccessibleStateType::SHOWING; + pChildComponent->NotifyAccessibleEvent(accessibility::AccessibleEventId::STATE_CHANGED, css::uno::Any(), aNewStateValue); + } + } + } + } + break; + case VclEventId::WindowHide: // send destroy on hide for direct accessible children + { + xAcc = GetChildAccessible( rVclWindowEvent ); + if( xAcc.is() ) + { + // send state change event for SHOWING before sending the CHILD event below, + // since that one results in a11y event listeners getting removed + uno::Reference<XAccessibleContext> xChildContext = xAcc->getAccessibleContext(); + if (xChildContext.is()) + { + VCLXAccessibleComponent* pChildComponent = dynamic_cast<VCLXAccessibleComponent*>(xChildContext.get()); + if (pChildComponent) + { + css::uno::Any aOldStateValue; + aOldStateValue <<= accessibility::AccessibleStateType::SHOWING; + pChildComponent->NotifyAccessibleEvent(accessibility::AccessibleEventId::STATE_CHANGED, aOldStateValue, css::uno::Any()); + } + } + + aOldValue <<= xAcc; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + break; + default: break; + } +} + +void VCLXAccessibleComponent::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + uno::Any aOldValue, aNewValue; + + vcl::Window* pAccWindow = rVclWindowEvent.GetWindow(); + assert(pAccWindow && "VCLXAccessibleComponent::ProcessWindowEvent - Window?"); + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ObjectDying: + { + DisconnectEvents(); + m_xVCLXWindow.clear(); + } + break; + case VclEventId::WindowChildDestroyed: + { + vcl::Window* pWindow = static_cast<vcl::Window*>(rVclWindowEvent.GetData()); + DBG_ASSERT( pWindow, "VclEventId::WindowChildDestroyed - Window=?" ); + if ( pWindow->GetAccessible( false ).is() ) + { + aOldValue <<= pWindow->GetAccessible( false ); + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowActivate: + { + sal_Int16 aAccessibleRole = getAccessibleRole(); + // avoid notification if a child frame is already active + // only one frame may be active at a given time + if ( !pAccWindow->HasActiveChildFrame() && + ( aAccessibleRole == accessibility::AccessibleRole::FRAME || + aAccessibleRole == accessibility::AccessibleRole::ALERT || + aAccessibleRole == accessibility::AccessibleRole::DIALOG ) ) // #i18891# + { + aNewValue <<= accessibility::AccessibleStateType::ACTIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowDeactivate: + { + sal_Int16 aAccessibleRole = getAccessibleRole(); + if ( aAccessibleRole == accessibility::AccessibleRole::FRAME || + aAccessibleRole == accessibility::AccessibleRole::ALERT || + aAccessibleRole == accessibility::AccessibleRole::DIALOG ) // #i18891# + { + aOldValue <<= accessibility::AccessibleStateType::ACTIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowGetFocus: + case VclEventId::ControlGetFocus: + { + if( (pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::ControlGetFocus) || + (!pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::WindowGetFocus) ) + { + // if multiple listeners were registered it is possible that the + // focus was changed during event processing (eg SfxTopWindow ) + // #106082# allow ChildPathFocus only for CompoundControls, for windows the focus must be in the window itself + if( (pAccWindow->IsCompoundControl() && pAccWindow->HasChildPathFocus()) || + (!pAccWindow->IsCompoundControl() && pAccWindow->HasFocus()) ) + { + aNewValue <<= accessibility::AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + } + break; + case VclEventId::WindowLoseFocus: + case VclEventId::ControlLoseFocus: + { + if( (pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::ControlLoseFocus) || + (!pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::WindowLoseFocus) ) + { + aOldValue <<= accessibility::AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowFrameTitleChanged: + { + OUString aOldName( *static_cast<OUString*>(rVclWindowEvent.GetData()) ); + OUString aNewName( getAccessibleName() ); + aOldValue <<= aOldName; + aNewValue <<= aNewName; + NotifyAccessibleEvent( accessibility::AccessibleEventId::NAME_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowEnabled: + { + aNewValue <<= accessibility::AccessibleStateType::ENABLED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + aNewValue <<= accessibility::AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowDisabled: + { + aOldValue <<= accessibility::AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + + aOldValue <<= accessibility::AccessibleStateType::ENABLED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowMove: + case VclEventId::WindowResize: + { + NotifyAccessibleEvent( accessibility::AccessibleEventId::BOUNDRECT_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowMenubarAdded: + { + MenuBar* pMenuBar = static_cast<MenuBar*>(rVclWindowEvent.GetData()); + if ( pMenuBar ) + { + uno::Reference< accessibility::XAccessible > xChild( pMenuBar->GetAccessible() ); + if ( xChild.is() ) + { + aNewValue <<= xChild; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + } + break; + case VclEventId::WindowMenubarRemoved: + { + MenuBar* pMenuBar = static_cast<MenuBar*>(rVclWindowEvent.GetData()); + if ( pMenuBar ) + { + uno::Reference< accessibility::XAccessible > xChild( pMenuBar->GetAccessible() ); + if ( xChild.is() ) + { + aOldValue <<= xChild; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + } + break; + case VclEventId::WindowMinimize: + { + aNewValue <<= accessibility::AccessibleStateType::ICONIFIED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowNormalize: + { + aOldValue <<= accessibility::AccessibleStateType::ICONIFIED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowHide: + case VclEventId::WindowShow: + // WindowHide and WindowShow are handled in ProcessWindowChildEvent so the right order + // regarding the CHILD event can be taken into account + default: + { + } + break; + } +} + +void VCLXAccessibleComponent::disposing() +{ + DisconnectEvents(); + + OAccessibleExtendedComponentHelper::disposing(); + + m_xVCLXWindow.clear(); +} + +vcl::Window* VCLXAccessibleComponent::GetWindow() const +{ + return GetVCLXWindow() ? GetVCLXWindow()->GetWindow() + : nullptr; +} + +void VCLXAccessibleComponent::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet ) +{ + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( !pWindow ) + return; + + vcl::Window *pLabeledBy = pWindow->GetAccessibleRelationLabeledBy(); + if ( pLabeledBy && pLabeledBy != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabeledBy->GetAccessible() }; + rRelationSet.AddRelation( accessibility::AccessibleRelation( accessibility::AccessibleRelationType::LABELED_BY, aSequence ) ); + } + + vcl::Window* pLabelFor = pWindow->GetAccessibleRelationLabelFor(); + if ( pLabelFor && pLabelFor != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabelFor->GetAccessible() }; + rRelationSet.AddRelation( accessibility::AccessibleRelation( accessibility::AccessibleRelationType::LABEL_FOR, aSequence ) ); + } + + vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf(); + if ( pMemberOf && pMemberOf != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pMemberOf->GetAccessible() }; + rRelationSet.AddRelation( accessibility::AccessibleRelation( accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) ); + } +} + +void VCLXAccessibleComponent::FillAccessibleStateSet( sal_Int64& rStateSet ) +{ + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsVisible() ) + { + rStateSet |= accessibility::AccessibleStateType::VISIBLE; + rStateSet |= accessibility::AccessibleStateType::SHOWING; + } + else + { + rStateSet |= accessibility::AccessibleStateType::INVALID; + } + + if ( pWindow->IsEnabled() ) + { + rStateSet |= accessibility::AccessibleStateType::ENABLED; + rStateSet |= accessibility::AccessibleStateType::SENSITIVE; + } + + if ( pWindow->HasChildPathFocus() && + ( getAccessibleRole() == accessibility::AccessibleRole::FRAME || + getAccessibleRole() == accessibility::AccessibleRole::ALERT || + getAccessibleRole() == accessibility::AccessibleRole::DIALOG ) ) // #i18891# + rStateSet |= accessibility::AccessibleStateType::ACTIVE; + + if ( pWindow->HasFocus() || ( pWindow->IsCompoundControl() && pWindow->HasChildPathFocus() ) ) + rStateSet |= accessibility::AccessibleStateType::FOCUSED; + + if ( pWindow->IsWait() ) + rStateSet |= accessibility::AccessibleStateType::BUSY; + + if ( pWindow->GetStyle() & WB_SIZEABLE ) + rStateSet |= accessibility::AccessibleStateType::RESIZABLE; + // 6. frame doesn't have MOVABLE state + // 10. for password text, where is the sensitive state? + if( ( getAccessibleRole() == accessibility::AccessibleRole::FRAME ||getAccessibleRole() == accessibility::AccessibleRole::DIALOG )&& pWindow->GetStyle() & WB_MOVEABLE ) + rStateSet |= accessibility::AccessibleStateType::MOVEABLE; + if( pWindow->IsDialog() ) + { + Dialog *pDlg = static_cast< Dialog* >( pWindow.get() ); + if( pDlg->IsInExecute() ) + rStateSet |= accessibility::AccessibleStateType::MODAL; + } + //If a combobox or list's edit child isn't read-only,EDITABLE state + //should be set. + if( pWindow && pWindow->GetType() == WindowType::COMBOBOX ) + { + if( !( pWindow->GetStyle() & WB_READONLY) || + !static_cast<Edit*>(pWindow.get())->IsReadOnly() ) + rStateSet |= accessibility::AccessibleStateType::EDITABLE; + } + + VclPtr<vcl::Window> pChild = pWindow->GetWindow( GetWindowType::FirstChild ); + + while( pWindow && pChild ) + { + VclPtr<vcl::Window> pWinTemp = pChild->GetWindow( GetWindowType::FirstChild ); + if( pWinTemp && pWinTemp->GetType() == WindowType::EDIT ) + { + if( !( pWinTemp->GetStyle() & WB_READONLY) || + !static_cast<Edit*>(pWinTemp.get())->IsReadOnly() ) + rStateSet |= accessibility::AccessibleStateType::EDITABLE; + break; + } + if( pChild->GetType() == WindowType::EDIT ) + { + if( !( pChild->GetStyle() & WB_READONLY) || + !static_cast<Edit*>(pChild.get())->IsReadOnly()) + rStateSet |= accessibility::AccessibleStateType::EDITABLE; + break; + } + pChild = pChild->GetWindow( GetWindowType::Next ); + } + } + else + { + rStateSet |= accessibility::AccessibleStateType::DEFUNC; + } + +/* + +MUST BE SET FROM DERIVED CLASSES: + +CHECKABLE +CHECKED +COLLAPSED +EXPANDED +EXPANDABLE +EDITABLE +FOCUSABLE +HORIZONTAL +VERTICAL +ICONIFIED +MULTILINE +MULTI_SELECTABLE +PRESSED +SELECTABLE +SELECTED +SINGLE_LINE +TRANSIENT + + */ +} + + +// accessibility::XAccessibleContext +sal_Int64 VCLXAccessibleComponent::getAccessibleChildCount() +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nChildren = 0; + if ( GetWindow() ) + nChildren = GetWindow()->GetAccessibleChildWindowCount(); + + return nChildren; +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::getAccessibleChild( sal_Int64 i ) +{ + OExternalLockGuard aGuard( this ); + + if ( i >= getAccessibleChildCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< accessibility::XAccessible > xAcc; + if ( GetWindow() ) + { + vcl::Window* pChild = GetWindow()->GetAccessibleChildWindow( static_cast<sal_uInt16>(i) ); + if ( pChild ) + xAcc = pChild->GetAccessible(); + } + + return xAcc; +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::getAccessibleParent( ) +{ + OExternalLockGuard aGuard( this ); + + uno::Reference< accessibility::XAccessible > xAcc; + if ( GetWindow() ) + { + vcl::Window* pParent = GetWindow()->GetAccessibleParentWindow(); + if ( pParent ) + xAcc = pParent->GetAccessible(); + } + return xAcc; +} + +sal_Int64 VCLXAccessibleComponent::getAccessibleIndexInParent( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nIndex = -1; + + if ( GetWindow() ) + { + vcl::Window* pParent = GetWindow()->GetAccessibleParentWindow(); + if ( pParent ) + { + // Iterate over all the parent's children and search for this object. + // this should be compatible with the code in SVX + uno::Reference< accessibility::XAccessible > xParentAcc( pParent->GetAccessible() ); + if ( xParentAcc.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xParentContext ( xParentAcc->getAccessibleContext() ); + if ( xParentContext.is() ) + { + sal_Int64 nChildCount = xParentContext->getAccessibleChildCount(); + for ( sal_Int64 i = 0; i < nChildCount; i++ ) + { + uno::Reference< accessibility::XAccessible > xChild( xParentContext->getAccessibleChild(i) ); + if ( xChild.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if ( xChildContext == static_cast<accessibility::XAccessibleContext*>(this) ) + { + nIndex = i; + break; + } + } + } + } + } + } + } + return nIndex; +} + +sal_Int16 VCLXAccessibleComponent::getAccessibleRole( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int16 nRole = 0; + + if ( GetWindow() ) + nRole = GetWindow()->GetAccessibleRole(); + + return nRole; +} + +OUString VCLXAccessibleComponent::getAccessibleDescription( ) +{ + OExternalLockGuard aGuard( this ); + + OUString aDescription; + + if ( GetWindow() ) + aDescription = GetWindow()->GetAccessibleDescription(); + + return aDescription; +} + +OUString VCLXAccessibleComponent::getAccessibleName( ) +{ + OExternalLockGuard aGuard( this ); + + OUString aName; + if ( GetWindow() ) + { + aName = GetWindow()->GetAccessibleName(); +#if OSL_DEBUG_LEVEL > 0 + // append window type to accessible name for debugging purposes + // if LIBO_APPEND_WINDOW_TYPE_TO_ACCESSIBLE_NAME environment variable is set + static const char* pEnvAppendType = getenv("LIBO_APPEND_WINDOW_TYPE_TO_ACCESSIBLE_NAME"); + if (pEnvAppendType && OUString::createFromAscii(pEnvAppendType) != u"0") + aName += " (Type = " + OUString::number(static_cast<sal_Int32>(GetWindow()->GetType())) + ")"; +#endif + } + return aName; +} + +OUString VCLXAccessibleComponent::getAccessibleId( ) +{ + OExternalLockGuard aGuard( this ); + + OUString aId; + if ( GetWindow() ) + { + const OUString &aWindowId = GetWindow()->get_id(); + aId = aWindowId; + } + return aId; +} + +uno::Reference< accessibility::XAccessibleRelationSet > VCLXAccessibleComponent::getAccessibleRelationSet( ) +{ + OExternalLockGuard aGuard( this ); + + rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper; + FillAccessibleRelationSet( *pRelationSetHelper ); + return pRelationSetHelper; +} + +sal_Int64 VCLXAccessibleComponent::getAccessibleStateSet( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nStateSet = 0; + FillAccessibleStateSet( nStateSet ); + return nStateSet; +} + +lang::Locale VCLXAccessibleComponent::getLocale() +{ + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + OExternalLockGuard aGuard( this ); + + uno::Reference< accessibility::XAccessible > xChild; + for ( sal_Int64 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + uno::Reference< accessibility::XAccessible > xAcc = getAccessibleChild( i ); + if ( xAcc.is() ) + { + uno::Reference< accessibility::XAccessibleComponent > xComp( xAcc->getAccessibleContext(), uno::UNO_QUERY ); + if ( xComp.is() ) + { + tools::Rectangle aRect = VCLRectangle( xComp->getBounds() ); + Point aPos = VCLPoint( rPoint ); + if ( aRect.Contains( aPos ) ) + { + xChild = xAcc; + break; + } + } + } + } + + return xChild; +} + +// accessibility::XAccessibleComponent +awt::Rectangle VCLXAccessibleComponent::implGetBounds() +{ + awt::Rectangle aBounds ( 0, 0, 0, 0 ); + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute(); + aBounds = AWTRectangle( aRect ); + vcl::Window* pParent = pWindow->GetAccessibleParentWindow(); + if ( pParent ) + { + AbsoluteScreenPixelRectangle aParentRect = pParent->GetWindowExtentsAbsolute(); + awt::Point aParentScreenLoc = AWTPoint( aParentRect.TopLeft() ); + aBounds.X -= aParentScreenLoc.X; + aBounds.Y -= aParentScreenLoc.Y; + } + } + + return aBounds; +} + +awt::Point VCLXAccessibleComponent::getLocationOnScreen( ) +{ + OExternalLockGuard aGuard( this ); + + awt::Point aPos; + if ( GetWindow() ) + { + AbsoluteScreenPixelRectangle aRect = GetWindow()->GetWindowExtentsAbsolute(); + aPos.X = aRect.Left(); + aPos.Y = aRect.Top(); + } + + return aPos; +} + +void VCLXAccessibleComponent::grabFocus( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nStates = getAccessibleStateSet(); + if ( m_xVCLXWindow.is() && ( nStates & accessibility::AccessibleStateType::FOCUSABLE ) ) + m_xVCLXWindow->setFocus(); +} + +sal_Int32 SAL_CALL VCLXAccessibleComponent::getForeground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + VclPtr<vcl::Window> pWindow = GetWindow(); + 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(); + // COL_AUTO is not very meaningful for AT + if ( nColor == COL_AUTO) + nColor = pWindow->GetTextColor(); + } + } + + return sal_Int32(nColor); +} + +sal_Int32 SAL_CALL VCLXAccessibleComponent::getBackground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsControlBackground() ) + nColor = pWindow->GetControlBackground(); + else + nColor = pWindow->GetBackground().GetColor(); + } + + return sal_Int32(nColor); +} + +// XAccessibleExtendedComponent + +uno::Reference< awt::XFont > SAL_CALL VCLXAccessibleComponent::getFont( ) +{ + OExternalLockGuard aGuard( this ); + + uno::Reference< awt::XFont > xFont; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + uno::Reference< awt::XDevice > xDev( pWindow->GetComponentInterface(), uno::UNO_QUERY ); + if ( xDev.is() ) + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + rtl::Reference<VCLXFont> pVCLXFont = new VCLXFont; + pVCLXFont->Init( *xDev, aFont ); + xFont = pVCLXFont; + } + } + + return xFont; +} + +OUString SAL_CALL VCLXAccessibleComponent::getTitledBorderText( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sRet; + if ( GetWindow() ) + sRet = GetWindow()->GetText(); + + return sRet; +} + +OUString SAL_CALL VCLXAccessibleComponent::getToolTipText( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sRet; + if ( GetWindow() ) + sRet = GetWindow()->GetQuickHelpText(); + + return sRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxbitmap.cxx b/toolkit/source/awt/vclxbitmap.cxx new file mode 100644 index 0000000000..1faedb6235 --- /dev/null +++ b/toolkit/source/awt/vclxbitmap.cxx @@ -0,0 +1,60 @@ +/* -*- 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/vclxbitmap.hxx> +#include <tools/stream.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/BitmapTools.hxx> + + + + +// css::awt::XBitmap +css::awt::Size VCLXBitmap::getSize() +{ + std::scoped_lock aGuard( GetMutex() ); + + css::awt::Size aSize( maBitmap.GetSizePixel().Width(), maBitmap.GetSizePixel().Height() ); + return aSize; +} + +css::uno::Sequence< sal_Int8 > VCLXBitmap::getDIB() +{ + std::scoped_lock aGuard( GetMutex() ); + + SvMemoryStream aMem; + WriteDIB(maBitmap.GetBitmap(), aMem, false, true); + return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() ); +} + +css::uno::Sequence< sal_Int8 > VCLXBitmap::getMaskDIB() +{ + std::scoped_lock aGuard( GetMutex() ); + + return vcl::bitmap::GetMaskDIB(maBitmap); +} + +sal_Int64 SAL_CALL VCLXBitmap::estimateUsage() +{ + std::scoped_lock aGuard( GetMutex() ); + + return maBitmap.GetSizeBytes(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxcontainer.cxx b/toolkit/source/awt/vclxcontainer.cxx new file mode 100644 index 0000000000..de7518d418 --- /dev/null +++ b/toolkit/source/awt/vclxcontainer.cxx @@ -0,0 +1,264 @@ +/* -*- 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/vclxcontainer.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/tabpage.hxx> +#include <tools/debug.hxx> +#include <helper/scrollabledialog.hxx> +#include <helper/property.hxx> + +void VCLXContainer::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXContainer::VCLXContainer() +{ +} + +VCLXContainer::~VCLXContainer() +{ +} + + +// css::awt::XVclContainer +void VCLXContainer::addVclContainerListener( const css::uno::Reference< css::awt::XVclContainerListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetContainerListeners().addInterface( rxListener ); +} + +void VCLXContainer::removeVclContainerListener( const css::uno::Reference< css::awt::XVclContainerListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetContainerListeners().removeInterface( rxListener ); +} + +css::uno::Sequence< css::uno::Reference< css::awt::XWindow > > VCLXContainer::getWindows( ) +{ + SolarMutexGuard aGuard; + + // Request container interface from all children + css::uno::Sequence< css::uno::Reference< css::awt::XWindow > > aSeq; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + sal_uInt16 nChildren = pWindow->GetChildCount(); + if ( nChildren ) + { + aSeq = css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >( nChildren ); + css::uno::Reference< css::awt::XWindow > * pChildRefs = aSeq.getArray(); + for ( sal_uInt16 n = 0; n < nChildren; n++ ) + { + vcl::Window* pChild = pWindow->GetChild( n ); + css::uno::Reference< css::awt::XWindowPeer > xWP = pChild->GetComponentInterface(); + css::uno::Reference< css::awt::XWindow > xW( xWP, css::uno::UNO_QUERY ); + pChildRefs[n] = xW; + } + } + } + return aSeq; +} + + +// css::awt::XVclContainerPeer +void VCLXContainer::enableDialogControl( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( bEnable ) + nStyle |= WB_DIALOGCONTROL; + else + nStyle &= (~WB_DIALOGCONTROL); + pWindow->SetStyle( nStyle ); + } +} + +void VCLXContainer::setTabOrder( const css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& Components, const css::uno::Sequence< css::uno::Any >& Tabs, sal_Bool bGroupControl ) +{ + SolarMutexGuard aGuard; + + sal_uInt32 nCount = Components.getLength(); + DBG_ASSERT( nCount == static_cast<sal_uInt32>(Tabs.getLength()), "setTabOrder: TabCount != ComponentCount" ); + const css::uno::Reference< css::awt::XWindow > * pComps = Components.getConstArray(); + const css::uno::Any* pTabs = Tabs.getConstArray(); + + vcl::Window* pPrevWin = nullptr; + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + // css::style::TabStop + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( pComps[n] ); + // May be NULL if a css::uno::Sequence is originated from TabController and is missing a peer! + if ( pWin ) + { + // Order windows before manipulating their style, because elements such as the + // RadioButton considers the PREV-window in StateChanged. + if ( pPrevWin ) + pWin->SetZOrder( pPrevWin, ZOrderFlags::Behind ); + + WinBits nStyle = pWin->GetStyle(); + nStyle &= ~(WB_TABSTOP|WB_NOTABSTOP|WB_GROUP); + if ( pTabs[n].getValueType().getTypeClass() == css::uno::TypeClass_BOOLEAN ) + { + bool bTab = false; + pTabs[n] >>= bTab; + nStyle |= ( bTab ? WB_TABSTOP : WB_NOTABSTOP ); + } + pWin->SetStyle( nStyle ); + + if ( bGroupControl ) + { + if ( n == 0 ) + pWin->SetDialogControlStart( true ); + else + pWin->SetDialogControlStart( false ); + } + + pPrevWin = pWin; + } + } +} + +void VCLXContainer::setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& Components ) +{ + SolarMutexGuard aGuard; + + sal_uInt32 nCount = Components.getLength(); + const css::uno::Reference< css::awt::XWindow > * pComps = Components.getConstArray(); + + vcl::Window* pPrevWin = nullptr; + vcl::Window* pPrevRadio = nullptr; + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( pComps[n] ); + if ( pWin ) + { + vcl::Window* pSortBehind = pPrevWin; + // #57096# Sort all radios consecutively + bool bNewPrevWin = true; + if ( pWin->GetType() == WindowType::RADIOBUTTON ) + { + if ( pPrevRadio ) + { + // This RadioButton was sorted before PrevWin + bNewPrevWin = ( pPrevWin == pPrevRadio ); + pSortBehind = pPrevRadio; + } + pPrevRadio = pWin; + } + + // Z-Order + if ( pSortBehind ) + pWin->SetZOrder( pSortBehind, ZOrderFlags::Behind ); + + WinBits nStyle = pWin->GetStyle(); + if ( n == 0 ) + nStyle |= WB_GROUP; + else + nStyle &= (~WB_GROUP); + pWin->SetStyle( nStyle ); + + // Add WB_GROUP after the last group + if ( n == ( nCount - 1 ) ) + { + vcl::Window* pBehindLast = pWin->GetWindow( GetWindowType::Next ); + if ( pBehindLast ) + { + WinBits nLastStyle = pBehindLast->GetStyle(); + nLastStyle |= WB_GROUP; + pBehindLast->SetStyle( nLastStyle ); + } + } + + if ( bNewPrevWin ) + pPrevWin = pWin; + } + } +} + +void SAL_CALL VCLXContainer::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_SCROLLHEIGHT: + case BASEPROPERTY_SCROLLWIDTH: + case BASEPROPERTY_SCROLLTOP: + case BASEPROPERTY_SCROLLLEFT: + { + sal_Int32 nVal =0; + Value >>= nVal; + Size aSize( nVal, nVal ); + VclPtr<vcl::Window> pWindow = GetWindow(); + MapMode aMode( MapUnit::MapAppFont ); + toolkit::ScrollableDialog* pScrollable = dynamic_cast< toolkit::ScrollableDialog* >( pWindow.get() ); + TabPage* pScrollTabPage = dynamic_cast< TabPage* >( pWindow.get() ); + if ( pWindow && (pScrollable || pScrollTabPage) ) + { + aSize = pWindow->LogicToPixel( aSize, aMode ); + switch ( nPropType ) + { + case BASEPROPERTY_SCROLLHEIGHT: + pScrollable ? pScrollable->SetScrollHeight( aSize.Height() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollHeight( aSize.Height() ) : (void)0; + break; + case BASEPROPERTY_SCROLLWIDTH: + pScrollable ? pScrollable->SetScrollWidth( aSize.Width() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollWidth( aSize.Width() ) : (void)0; + break; + case BASEPROPERTY_SCROLLTOP: + pScrollable ? pScrollable->SetScrollTop( aSize.Height() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollTop( aSize.Height() ) : (void)0; + break; + case BASEPROPERTY_SCROLLLEFT: + pScrollable ? pScrollable->SetScrollLeft( aSize.Width() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollLeft( aSize.Width() ) : (void)0; + break; + default: + break; + } + break; + } + break; + } + + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxdevice.cxx b/toolkit/source/awt/vclxdevice.cxx new file mode 100644 index 0000000000..c4ed981467 --- /dev/null +++ b/toolkit/source/awt/vclxdevice.cxx @@ -0,0 +1,251 @@ +/* -*- 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/util/MeasureUnit.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include <toolkit/awt/vclxdevice.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <awt/vclxbitmap.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/metric.hxx> + + +VCLXDevice::VCLXDevice() +{ +} + +VCLXDevice::~VCLXDevice() +{ + //TODO: why was this empty, and everything done in ~VCLXVirtualDevice? + SolarMutexGuard g; + mpOutputDevice.reset(); +} + +// css::awt::XDevice, +css::uno::Reference< css::awt::XGraphics > VCLXDevice::createGraphics( ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XGraphics > xRef; + + if ( mpOutputDevice ) + xRef = mpOutputDevice->CreateUnoGraphics(); + + return xRef; +} + +css::uno::Reference< css::awt::XDevice > VCLXDevice::createDevice( sal_Int32 nWidth, sal_Int32 nHeight ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XDevice > xRef; + if ( GetOutputDevice() ) + { + rtl::Reference<VCLXVirtualDevice> pVDev = new VCLXVirtualDevice; + VclPtrInstance<VirtualDevice> pVclVDev( *GetOutputDevice() ); + pVclVDev->SetOutputSizePixel( Size( nWidth, nHeight ) ); + pVDev->SetVirtualDevice( pVclVDev ); + xRef = pVDev; + } + return xRef; +} + +css::awt::DeviceInfo VCLXDevice::getInfo() +{ + SolarMutexGuard aGuard; + + css::awt::DeviceInfo aInfo; + + if (mpOutputDevice) + aInfo = mpOutputDevice->GetDeviceInfo(); + + return aInfo; +} + +css::uno::Sequence< css::awt::FontDescriptor > VCLXDevice::getFontDescriptors( ) +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< css::awt::FontDescriptor> aFonts; + if( mpOutputDevice ) + { + int nFonts = mpOutputDevice->GetFontFaceCollectionCount(); + if ( nFonts ) + { + aFonts = css::uno::Sequence< css::awt::FontDescriptor>( nFonts ); + css::awt::FontDescriptor* pFonts = aFonts.getArray(); + for ( int n = 0; n < nFonts; n++ ) + pFonts[n] = VCLUnoHelper::CreateFontDescriptor( mpOutputDevice->GetFontMetricFromCollection( n ) ); + } + } + return aFonts; +} + +css::uno::Reference< css::awt::XFont > VCLXDevice::getFont( const css::awt::FontDescriptor& rDescriptor ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XFont > xRef; + if( mpOutputDevice ) + { + rtl::Reference<VCLXFont> pMetric = new VCLXFont; + pMetric->Init( *this, VCLUnoHelper::CreateFont( rDescriptor, mpOutputDevice->GetFont() ) ); + xRef = pMetric; + } + return xRef; +} + +css::uno::Reference< css::awt::XBitmap > VCLXDevice::createBitmap( sal_Int32 nX, sal_Int32 nY, sal_Int32 nWidth, sal_Int32 nHeight ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XBitmap > xBmp; + if( mpOutputDevice ) + { + BitmapEx aBmp = mpOutputDevice->GetBitmapEx( Point( nX, nY ), Size( nWidth, nHeight ) ); + + rtl::Reference<VCLXBitmap> pBmp = new VCLXBitmap; + pBmp->SetBitmap( aBmp ); + xBmp = pBmp; + } + return xBmp; +} + +css::uno::Reference< css::awt::XDisplayBitmap > VCLXDevice::createDisplayBitmap( const css::uno::Reference< css::awt::XBitmap >& rxBitmap ) +{ + SolarMutexGuard aGuard; + + BitmapEx aBmp = VCLUnoHelper::GetBitmap( rxBitmap ); + rtl::Reference<VCLXBitmap> pBmp = new VCLXBitmap; + pBmp->SetBitmap( aBmp ); + return pBmp; +} + +VCLXVirtualDevice::~VCLXVirtualDevice() +{ + SolarMutexGuard aGuard; + + mpOutputDevice.disposeAndClear(); +} + +// Interface implementation of css::awt::XUnitConversion + +css::awt::Point SAL_CALL VCLXDevice::convertPointToLogic( const css::awt::Point& aPoint, ::sal_Int16 TargetUnit ) +{ + SolarMutexGuard aGuard; + if (TargetUnit == css::util::MeasureUnit::PERCENT ) + { + // percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Point aAWTPoint(0,0); + // X,Y + + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(TargetUnit)); + ::Point aVCLPoint = VCLUnoHelper::ConvertToVCLPoint(aPoint); + ::Point aDevPoint = mpOutputDevice->PixelToLogic(aVCLPoint, aMode ); + aAWTPoint = VCLUnoHelper::ConvertToAWTPoint(aDevPoint); + } + + return aAWTPoint; +} + + +css::awt::Point SAL_CALL VCLXDevice::convertPointToPixel( const css::awt::Point& aPoint, ::sal_Int16 SourceUnit ) +{ + SolarMutexGuard aGuard; + if (SourceUnit == css::util::MeasureUnit::PERCENT || + SourceUnit == css::util::MeasureUnit::PIXEL ) + { + // pixel or percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Point aAWTPoint(0,0); + + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(SourceUnit)); + ::Point aVCLPoint = VCLUnoHelper::ConvertToVCLPoint(aPoint); + ::Point aDevPoint = mpOutputDevice->LogicToPixel(aVCLPoint, aMode ); + aAWTPoint = VCLUnoHelper::ConvertToAWTPoint(aDevPoint); + } + + return aAWTPoint; +} + +css::awt::Size SAL_CALL VCLXDevice::convertSizeToLogic( const css::awt::Size& aSize, ::sal_Int16 TargetUnit ) +{ + SolarMutexGuard aGuard; + if (TargetUnit == css::util::MeasureUnit::PERCENT) + { + // percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Size aAWTSize(0,0); + // Width, Height + + + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(TargetUnit)); + ::Size aVCLSize = VCLUnoHelper::ConvertToVCLSize(aSize); + ::Size aDevSz = mpOutputDevice->PixelToLogic(aVCLSize, aMode ); + aAWTSize = VCLUnoHelper::ConvertToAWTSize(aDevSz); + } + + return aAWTSize; +} + +css::awt::Size SAL_CALL VCLXDevice::convertSizeToPixel( const css::awt::Size& aSize, ::sal_Int16 SourceUnit ) +{ + SolarMutexGuard aGuard; + if (SourceUnit == css::util::MeasureUnit::PERCENT || + SourceUnit == css::util::MeasureUnit::PIXEL) + { + // pixel or percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Size aAWTSize(0,0); + // Width, Height + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(SourceUnit)); + ::Size aVCLSize = VCLUnoHelper::ConvertToVCLSize(aSize); + ::Size aDevSz = mpOutputDevice->LogicToPixel(aVCLSize, aMode ); + aAWTSize = VCLUnoHelper::ConvertToAWTSize(aDevSz); + } + + return aAWTSize; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxfont.cxx b/toolkit/source/awt/vclxfont.cxx new file mode 100644 index 0000000000..01e7aaae79 --- /dev/null +++ b/toolkit/source/awt/vclxfont.cxx @@ -0,0 +1,189 @@ +/* -*- 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/awt/XDevice.hpp> + +#include <comphelper/sequence.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/kernarray.hxx> +#include <vcl/metric.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> + +VCLXFont::VCLXFont() +{ + mpFontMetric = nullptr; +} + +VCLXFont::~VCLXFont() +{ +} + +void VCLXFont::Init( css::awt::XDevice& rxDev, const vcl::Font& rFont ) +{ + mxDevice = &rxDev; + + mpFontMetric.reset(); + + maFont = rFont; +} + +bool VCLXFont::ImplAssertValidFontMetric() +{ + if ( !mpFontMetric && mxDevice.is() ) + { + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + mpFontMetric.reset( new FontMetric( pOutDev->GetFontMetric() ) ); + pOutDev->SetFont( aOldFont ); + } + } + return mpFontMetric != nullptr; +} + +css::awt::FontDescriptor VCLXFont::getFontDescriptor( ) +{ + std::unique_lock aGuard( maMutex ); + + return VCLUnoHelper::CreateFontDescriptor( maFont ); + +} + +css::awt::SimpleFontMetric VCLXFont::getFontMetric( ) +{ + std::unique_lock aGuard( maMutex ); + + css::awt::SimpleFontMetric aFM; + if ( ImplAssertValidFontMetric() ) + aFM = VCLUnoHelper::CreateFontMetric( *mpFontMetric ); + return aFM; +} + +sal_Int16 VCLXFont::getCharWidth( sal_Unicode c ) +{ + std::unique_lock aGuard( maMutex ); + + sal_Int16 nRet = -1; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + + nRet = sal::static_int_cast< sal_Int16 >( + pOutDev->GetTextWidth( OUString(c) )); + + pOutDev->SetFont( aOldFont ); + } + return nRet; +} + +css::uno::Sequence< sal_Int16 > VCLXFont::getCharWidths( sal_Unicode nFirst, sal_Unicode nLast ) +{ + std::unique_lock aGuard( maMutex ); + + css::uno::Sequence<sal_Int16> aSeq; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + + sal_Int16 nCount = nLast-nFirst + 1; + aSeq = css::uno::Sequence<sal_Int16>( nCount ); + for ( sal_uInt16 n = 0; n < nCount; n++ ) + { + aSeq.getArray()[n] = sal::static_int_cast< sal_Int16 >( + pOutDev->GetTextWidth( + OUString(static_cast< sal_Unicode >(nFirst+n)) )); + } + + pOutDev->SetFont( aOldFont ); + } + return aSeq; +} + +sal_Int32 VCLXFont::getStringWidth( const OUString& str ) +{ + std::unique_lock aGuard( maMutex ); + + sal_Int32 nRet = -1; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + nRet = pOutDev->GetTextWidth( str ); + pOutDev->SetFont( aOldFont ); + } + return nRet; +} + +sal_Int32 VCLXFont::getStringWidthArray( const OUString& str, css::uno::Sequence< sal_Int32 >& rDXArray ) +{ + std::unique_lock aGuard( maMutex ); + + sal_Int32 nRet = -1; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + KernArray aDXA; + nRet = pOutDev->GetTextArray( str, &aDXA ); + rDXArray.realloc(aDXA.size()); + sal_Int32* pArray = rDXArray.getArray(); + for (size_t i = 0, nLen = aDXA.size(); i < nLen; ++i) + pArray[i] = aDXA[i]; + pOutDev->SetFont( aOldFont ); + } + return nRet; +} + +void VCLXFont::getKernPairs( css::uno::Sequence< sal_Unicode >& /*rnChars1*/, css::uno::Sequence< sal_Unicode >& /*rnChars2*/, css::uno::Sequence< sal_Int16 >& /*rnKerns*/ ) +{ + // NOTE: this empty method is just used for keeping the related UNO-API stable +} + +// css::awt::XFont2 +sal_Bool VCLXFont::hasGlyphs( const OUString& aText ) +{ + std::unique_lock aGuard( maMutex ); + SolarMutexGuard aSolarGuard; + + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + if ( pOutDev->HasGlyphs( maFont, aText ) == -1 ) + { + return true; + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxgraphics.cxx b/toolkit/source/awt/vclxgraphics.cxx new file mode 100644 index 0000000000..06df80a421 --- /dev/null +++ b/toolkit/source/awt/vclxgraphics.cxx @@ -0,0 +1,487 @@ +/* -*- 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 <awt/vclxgraphics.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/image.hxx> +#include <vcl/kernarray.hxx> +#include <vcl/gradient.hxx> +#include <vcl/metric.hxx> +#include <tools/debug.hxx> + +using namespace com::sun::star; + + + +VCLXGraphics::VCLXGraphics() + : mpOutputDevice(nullptr) + , meRasterOp(RasterOp::OverPaint) +{ +} + +VCLXGraphics::~VCLXGraphics() +{ + std::vector< VCLXGraphics* > *pLst = mpOutputDevice ? mpOutputDevice->GetUnoGraphicsList() : nullptr; + if ( pLst ) + { + auto it = std::find(pLst->begin(), pLst->end(), this); + if (it != pLst->end()) + pLst->erase( it ); + } + + mpClipRegion.reset(); + + SolarMutexGuard g; + mpOutputDevice.reset(); +} + +void VCLXGraphics::SetOutputDevice( OutputDevice* pOutDev ) +{ + mpOutputDevice = pOutDev; + mxDevice = nullptr; + initAttrs(); +} + +void VCLXGraphics::Init( OutputDevice* pOutDev ) +{ + DBG_ASSERT( !mpOutputDevice, "VCLXGraphics::Init already has pOutDev !" ); + mpOutputDevice = pOutDev; + + initAttrs(); + mpClipRegion = nullptr; + + // Register at OutputDevice + std::vector< VCLXGraphics* > *pLst = mpOutputDevice->GetUnoGraphicsList(); + if ( !pLst ) + pLst = mpOutputDevice->CreateUnoGraphicsList(); + pLst->push_back( this ); +} + +void VCLXGraphics::initAttrs() +{ + if ( !mpOutputDevice ) + return; + + maFont = mpOutputDevice->GetFont(); + maTextColor = mpOutputDevice->GetTextColor(); /* COL_BLACK */ + maTextFillColor = mpOutputDevice->GetTextFillColor(); /* COL_TRANSPARENT */ + maLineColor = mpOutputDevice->GetLineColor(); /* COL_BLACK */ + maFillColor = mpOutputDevice->GetFillColor(); /* COL_WHITE */ + meRasterOp = mpOutputDevice->GetRasterOp(); /* RasterOp::OverPaint */ +} + +void VCLXGraphics::InitOutputDevice( InitOutDevFlags nFlags ) +{ + if(!mpOutputDevice) + return; + + SolarMutexGuard aVclGuard; + + if ( nFlags & InitOutDevFlags::FONT ) + { + mpOutputDevice->SetFont( maFont ); + mpOutputDevice->SetTextColor( maTextColor ); + mpOutputDevice->SetTextFillColor( maTextFillColor ); + } + + if ( nFlags & InitOutDevFlags::COLORS ) + { + mpOutputDevice->SetLineColor( maLineColor ); + mpOutputDevice->SetFillColor( maFillColor ); + } + + mpOutputDevice->SetRasterOp( meRasterOp ); + + if( mpClipRegion ) + mpOutputDevice->SetClipRegion( *mpClipRegion ); + else + mpOutputDevice->SetClipRegion(); +} + +uno::Reference< awt::XDevice > VCLXGraphics::getDevice() +{ + SolarMutexGuard aGuard; + + if( !mxDevice.is() && mpOutputDevice ) + { + rtl::Reference<VCLXDevice> pDev = new VCLXDevice; + pDev->SetOutputDevice( mpOutputDevice ); + mxDevice = pDev; + } + return mxDevice; +} + +awt::SimpleFontMetric VCLXGraphics::getFontMetric() +{ + SolarMutexGuard aGuard; + + awt::SimpleFontMetric aM; + if( mpOutputDevice ) + { + mpOutputDevice->SetFont( maFont ); + aM = VCLUnoHelper::CreateFontMetric( mpOutputDevice->GetFontMetric() ); + } + return aM; +} + +void VCLXGraphics::setFont( const uno::Reference< awt::XFont >& rxFont ) +{ + SolarMutexGuard aGuard; + + maFont = VCLUnoHelper::CreateFont( rxFont ); +} + +void VCLXGraphics::selectFont( const awt::FontDescriptor& rDescription ) +{ + SolarMutexGuard aGuard; + + maFont = VCLUnoHelper::CreateFont( rDescription, vcl::Font() ); +} + +void VCLXGraphics::setTextColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maTextColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setTextFillColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maTextFillColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setLineColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maLineColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setFillColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maFillColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setRasterOp( awt::RasterOperation eROP ) +{ + SolarMutexGuard aGuard; + + meRasterOp = static_cast<RasterOp>(eROP); +} + +void VCLXGraphics::setClipRegion( const uno::Reference< awt::XRegion >& rxRegion ) +{ + SolarMutexGuard aGuard; + + if ( rxRegion.is() ) + mpClipRegion.reset( new vcl::Region( VCLUnoHelper::GetRegion( rxRegion ) ) ); + else + mpClipRegion.reset(); +} + +void VCLXGraphics::intersectClipRegion( const uno::Reference< awt::XRegion >& rxRegion ) +{ + SolarMutexGuard aGuard; + + if ( rxRegion.is() ) + { + vcl::Region aRegion( VCLUnoHelper::GetRegion( rxRegion ) ); + if ( !mpClipRegion ) + mpClipRegion.reset( new vcl::Region( aRegion ) ); + else + mpClipRegion->Intersect( aRegion ); + } +} + +void VCLXGraphics::push( ) +{ + SolarMutexGuard aGuard; + + + if( mpOutputDevice ) + mpOutputDevice->Push(); +} + +void VCLXGraphics::pop( ) +{ + SolarMutexGuard aGuard; + + + if( mpOutputDevice ) + mpOutputDevice->Pop(); +} + +void VCLXGraphics::clear( + const awt::Rectangle& aRect ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + const ::tools::Rectangle aVCLRect = VCLUnoHelper::ConvertToVCLRect( aRect ); + mpOutputDevice->Erase( aVCLRect ); + } +} + +void VCLXGraphics::copy( const uno::Reference< awt::XDevice >& rxSource, sal_Int32 nSourceX, sal_Int32 nSourceY, sal_Int32 nSourceWidth, sal_Int32 nSourceHeight, sal_Int32 nDestX, sal_Int32 nDestY, sal_Int32 nDestWidth, sal_Int32 nDestHeight ) +{ + SolarMutexGuard aGuard; + + if ( mpOutputDevice ) + { + VCLXDevice* pFromDev = dynamic_cast<VCLXDevice*>( rxSource.get() ); + DBG_ASSERT( pFromDev, "VCLXGraphics::copy - invalid device" ); + if ( pFromDev ) + { + InitOutputDevice( InitOutDevFlags::NONE ); + mpOutputDevice->DrawOutDev( Point( nDestX, nDestY ), Size( nDestWidth, nDestHeight ), + Point( nSourceX, nSourceY ), Size( nSourceWidth, nSourceHeight ), *pFromDev->GetOutputDevice() ); + } + } +} + +void VCLXGraphics::draw( const uno::Reference< awt::XDisplayBitmap >& rxBitmapHandle, sal_Int32 nSourceX, sal_Int32 nSourceY, sal_Int32 nSourceWidth, sal_Int32 nSourceHeight, sal_Int32 nDestX, sal_Int32 nDestY, sal_Int32 nDestWidth, sal_Int32 nDestHeight ) +{ + SolarMutexGuard aGuard; + + if( !mpOutputDevice ) + return; + + InitOutputDevice( InitOutDevFlags::NONE); + uno::Reference< awt::XBitmap > xBitmap( rxBitmapHandle, uno::UNO_QUERY ); + BitmapEx aBmpEx = VCLUnoHelper::GetBitmap( xBitmap ); + + Point aPos(nDestX - nSourceX, nDestY - nSourceY); + Size aSz = aBmpEx.GetSizePixel(); + + if(nDestWidth != nSourceWidth) + { + float zoomX = static_cast<float>(nDestWidth) / static_cast<float>(nSourceWidth); + aSz.setWidth( static_cast<tools::Long>(static_cast<float>(aSz.Width()) * zoomX) ); + } + + if(nDestHeight != nSourceHeight) + { + float zoomY = static_cast<float>(nDestHeight) / static_cast<float>(nSourceHeight); + aSz.setHeight( static_cast<tools::Long>(static_cast<float>(aSz.Height()) * zoomY) ); + } + + if(nSourceX || nSourceY || aSz.Width() != nSourceWidth || aSz.Height() != nSourceHeight) + mpOutputDevice->IntersectClipRegion(vcl::Region(tools::Rectangle(nDestX, nDestY, nDestX + nDestWidth - 1, nDestY + nDestHeight - 1))); + + mpOutputDevice->DrawBitmapEx( aPos, aSz, aBmpEx ); +} + +void VCLXGraphics::drawPixel( sal_Int32 x, sal_Int32 y ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPixel( Point( x, y ) ); + } +} + +void VCLXGraphics::drawLine( sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawLine( Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawRect( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawRect( tools::Rectangle( Point( x, y ), Size( width, height ) ) ); + } +} + +void VCLXGraphics::drawRoundedRect( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 nHorzRound, sal_Int32 nVertRound ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawRect( tools::Rectangle( Point( x, y ), Size( width, height ) ), nHorzRound, nVertRound ); + } +} + +void VCLXGraphics::drawPolyLine( const uno::Sequence< sal_Int32 >& DataX, const uno::Sequence< sal_Int32 >& DataY ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPolyLine( VCLUnoHelper::CreatePolygon( DataX, DataY ) ); + } +} + +void VCLXGraphics::drawPolygon( const uno::Sequence< sal_Int32 >& DataX, const uno::Sequence< sal_Int32 >& DataY ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPolygon( VCLUnoHelper::CreatePolygon( DataX, DataY ) ); + } +} + +void VCLXGraphics::drawPolyPolygon( const uno::Sequence< uno::Sequence< sal_Int32 > >& DataX, const uno::Sequence< uno::Sequence< sal_Int32 > >& DataY ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + sal_uInt16 nPolys = static_cast<sal_uInt16>(DataX.getLength()); + tools::PolyPolygon aPolyPoly( nPolys ); + for ( sal_uInt16 n = 0; n < nPolys; n++ ) + aPolyPoly[n] = VCLUnoHelper::CreatePolygon( DataX.getConstArray()[n], DataY.getConstArray()[n] ); + + mpOutputDevice->DrawPolyPolygon( aPolyPoly ); + } +} + +void VCLXGraphics::drawEllipse( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawEllipse( tools::Rectangle( Point( x, y ), Size( width, height ) ) ); + } +} + +void VCLXGraphics::drawArc( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawArc( tools::Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawPie( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPie( tools::Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawChord( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawChord( tools::Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawGradient( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, const awt::Gradient& rGradient ) +{ + SolarMutexGuard aGuard; + + if( !mpOutputDevice ) + return; + + InitOutputDevice( InitOutDevFlags::COLORS ); + Gradient aGradient(rGradient.Style, Color(ColorTransparency, rGradient.StartColor), Color(ColorTransparency, rGradient.EndColor)); + aGradient.SetAngle(Degree10(rGradient.Angle)); + aGradient.SetBorder(rGradient.Border); + aGradient.SetOfsX(rGradient.XOffset); + aGradient.SetOfsY(rGradient.YOffset); + aGradient.SetStartIntensity(rGradient.StartIntensity); + aGradient.SetEndIntensity(rGradient.EndIntensity); + aGradient.SetSteps(rGradient.StepCount); + mpOutputDevice->DrawGradient( tools::Rectangle( Point( x, y ), Size( width, height ) ), aGradient ); +} + +void VCLXGraphics::drawText( sal_Int32 x, sal_Int32 y, const OUString& rText ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS |InitOutDevFlags::FONT); + mpOutputDevice->DrawText( Point( x, y ), rText ); + } +} + +void VCLXGraphics::drawTextArray( sal_Int32 x, sal_Int32 y, const OUString& rText, const uno::Sequence< sal_Int32 >& rLongs ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS|InitOutDevFlags::FONT ); + KernArray aDXA; + aDXA.reserve(rText.getLength()); + for(int i = 0; i < rText.getLength(); ++i) + aDXA.push_back(rLongs[i]); + mpOutputDevice->DrawTextArray( Point( x, y ), rText, aDXA, {}, 0, rText.getLength()); + } +} + + +void VCLXGraphics::drawImage( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int16 nStyle, const uno::Reference< graphic::XGraphic >& xGraphic ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice && xGraphic.is() ) + { + Image aImage( xGraphic ); + if ( !!aImage ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawImage( Point( x, y ), Size( width, height ), aImage, static_cast<DrawImageFlags>(nStyle) ); + } + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxmenu.cxx b/toolkit/source/awt/vclxmenu.cxx new file mode 100644 index 0000000000..37785849c5 --- /dev/null +++ b/toolkit/source/awt/vclxmenu.cxx @@ -0,0 +1,869 @@ +/* -*- 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 <toolkit/awt/vclxmenu.hxx> +#include <toolkit/helper/convert.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <tools/debug.hxx> +#include <vcl/dialoghelper.hxx> +#include <vcl/graph.hxx> +#include <vcl/menu.hxx> +#include <vcl/keycod.hxx> +#include <vcl/image.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#include <com/sun/star/awt/KeyModifier.hpp> + +VCLXMenu::VCLXMenu() + : maMenuListeners( *this ) + , mnDefaultItem(0) +{ + mpMenu = nullptr; +} + +VCLXMenu::VCLXMenu( Menu* pMenu ) + : maMenuListeners( *this ) + , mnDefaultItem(0) +{ + mpMenu = pMenu; +} + +VCLXMenu::~VCLXMenu() +{ + maPopupMenuRefs.clear(); + if ( mpMenu ) + { + SolarMutexGuard g; + mpMenu->RemoveEventListener( LINK( this, VCLXMenu, MenuEventListener ) ); + mpMenu.disposeAndClear(); + } +} + +bool VCLXMenu::IsPopupMenu() const +{ + return (mpMenu && ! mpMenu->IsMenuBar()); +} + +void VCLXMenu::ImplCreateMenu( bool bPopup ) +{ + DBG_ASSERT( !mpMenu, "CreateMenu: Menu exists!" ); + + if ( bPopup ) + mpMenu = VclPtr<PopupMenu>::Create(); + else + mpMenu = VclPtr<MenuBar>::Create(); + + mpMenu->AddEventListener( LINK( this, VCLXMenu, MenuEventListener ) ); +} + +void VCLXMenu::ImplAddListener() +{ + assert(mpMenu); + mpMenu->AddEventListener( LINK( this, VCLXMenu, MenuEventListener ) ); +} + +IMPL_LINK( VCLXMenu, MenuEventListener, VclMenuEvent&, rMenuEvent, void ) +{ + DBG_ASSERT( rMenuEvent.GetMenu() && mpMenu, "Menu???" ); + + if ( rMenuEvent.GetMenu() != mpMenu ) // Also called for the root menu + return; + + switch ( rMenuEvent.GetId() ) + { + case VclEventId::MenuSelect: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemSelected( aEvent ); + } + } + break; + case VclEventId::ObjectDying: + { + mpMenu = nullptr; + } + break; + case VclEventId::MenuHighlight: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemHighlighted( aEvent ); + } + } + break; + case VclEventId::MenuActivate: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemActivated( aEvent ); + } + } + break; + case VclEventId::MenuDeactivate: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemDeactivated( aEvent ); + } + } + break; + + // ignore accessibility events + case VclEventId::MenuEnable: + case VclEventId::MenuInsertItem: + case VclEventId::MenuRemoveItem: + case VclEventId::MenuSubmenuActivate: + case VclEventId::MenuSubmenuDeactivate: + case VclEventId::MenuSubmenuChanged: + case VclEventId::MenuDehighlight: + case VclEventId::MenuDisable: + case VclEventId::MenuItemRoleChanged: + case VclEventId::MenuItemTextChanged: + case VclEventId::MenuItemChecked: + case VclEventId::MenuItemUnchecked: + case VclEventId::MenuShow: + case VclEventId::MenuHide: + break; + + default: OSL_FAIL( "MenuEventListener - Unknown event!" ); + } +} + + +OUString SAL_CALL VCLXMenu::getImplementationName( ) +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + OUString implName( "stardiv.Toolkit." ); + if ( bIsPopupMenu ) + implName += "VCLXPopupMenu"; + else + implName += "VCLXMenuBar"; + + return implName; +} + +css::uno::Sequence< OUString > SAL_CALL VCLXMenu::getSupportedServiceNames( ) +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + if ( bIsPopupMenu ) + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.PopupMenu", + "stardiv.vcl.PopupMenu"}; + else + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.MenuBar", + "stardiv.vcl.MenuBar"}; +} + +sal_Bool SAL_CALL VCLXMenu::supportsService(const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Any VCLXMenu::queryInterface( + const css::uno::Type & rType ) +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + css::uno::Any aRet; + + if ( bIsPopupMenu ) + aRet = ::cppu::queryInterface( rType, + static_cast< css::awt::XMenu* >(static_cast<css::awt::XMenuBar*>(this)), + static_cast< css::awt::XPopupMenu* >(this), + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this) ); + else + aRet = ::cppu::queryInterface( rType, + static_cast< css::awt::XMenu* >(static_cast<css::awt::XMenuBar*>(this)), + static_cast< css::awt::XMenuBar* >(this), + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this) ); + + return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType )); +} + + +css::uno::Sequence< css::uno::Type > VCLXMenu::getTypes() +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + if ( bIsPopupMenu ) + { + static cppu::OTypeCollection collectionPopupMenu( + cppu::UnoType<css::lang::XTypeProvider>::get(), cppu::UnoType<css::awt::XMenu>::get(), + cppu::UnoType<css::awt::XPopupMenu>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get()); + return collectionPopupMenu.getTypes(); + } + else + { + static cppu::OTypeCollection collectionMenuBar( + cppu::UnoType<css::lang::XTypeProvider>::get(), cppu::UnoType<css::awt::XMenu>::get(), + cppu::UnoType<css::awt::XMenuBar>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get()); + return collectionMenuBar.getTypes(); + } +} + + +css::uno::Sequence< sal_Int8 > VCLXMenu::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void VCLXMenu::addMenuListener( + const css::uno::Reference< css::awt::XMenuListener >& rxListener ) +{ + std::unique_lock aGuard( maMutex ); + + maMenuListeners.addInterface( rxListener ); +} + +void VCLXMenu::removeMenuListener( + const css::uno::Reference< css::awt::XMenuListener >& rxListener ) +{ + std::unique_lock aGuard( maMutex ); + + maMenuListeners.removeInterface( rxListener ); +} + +void VCLXMenu::insertItem( + sal_Int16 nItemId, + const OUString& aText, + sal_Int16 nItemStyle, + sal_Int16 nPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->InsertItem(nItemId, aText, static_cast<MenuItemBits>(nItemStyle), {}, nPos); +} + +void VCLXMenu::removeItem( + sal_Int16 nPos, + sal_Int16 nCount ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if (!mpMenu) + return; + + sal_Int32 nItemCount = static_cast<sal_Int32>(mpMenu->GetItemCount()); + if ((nCount > 0) && (nPos >= 0) && (nPos < nItemCount)) + { + sal_Int16 nP = sal::static_int_cast< sal_Int16 >( + std::min( static_cast<int>(nPos+nCount), static_cast<int>(nItemCount) )); + while( nP-nPos > 0 ) + mpMenu->RemoveItem( --nP ); + } +} + +sal_Int16 VCLXMenu::getItemCount( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu ? mpMenu->GetItemCount() : 0; +} + +sal_Int16 VCLXMenu::getItemId( + sal_Int16 nPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu ? mpMenu->GetItemId( nPos ) : 0; +} + +sal_Int16 VCLXMenu::getItemPos( + sal_Int16 nId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu ? mpMenu->GetItemPos( nId ) : 0; +} + +void VCLXMenu::enableItem( + sal_Int16 nItemId, + sal_Bool bEnable ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->EnableItem( nItemId, bEnable ); +} + +sal_Bool VCLXMenu::isItemEnabled( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu && mpMenu->IsItemEnabled( nItemId ); +} + +void VCLXMenu::setItemText( + sal_Int16 nItemId, + const OUString& aText ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->SetItemText( nItemId, aText ); +} + +OUString VCLXMenu::getItemText( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString aItemText; + if ( mpMenu ) + aItemText = mpMenu->GetItemText( nItemId ); + return aItemText; +} + +void VCLXMenu::setPopupMenu( + sal_Int16 nItemId, + const css::uno::Reference< css::awt::XPopupMenu >& rxPopupMenu ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + VCLXMenu* pVCLMenu = dynamic_cast<VCLXMenu*>( rxPopupMenu.get() ); + DBG_ASSERT( pVCLMenu && pVCLMenu->GetMenu() && pVCLMenu->IsPopupMenu(), "setPopupMenu: Invalid Menu!" ); + + if ( mpMenu && pVCLMenu && pVCLMenu->GetMenu() && pVCLMenu->IsPopupMenu() ) + { + maPopupMenuRefs.push_back( rxPopupMenu ); + + mpMenu->SetPopupMenu( nItemId, static_cast<PopupMenu*>( pVCLMenu->GetMenu() ) ); + } +} + +css::uno::Reference< css::awt::XPopupMenu > VCLXMenu::getPopupMenu( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::uno::Reference< css::awt::XPopupMenu > aRef; + Menu* pMenu = mpMenu ? mpMenu->GetPopupMenu( nItemId ) : nullptr; + if ( pMenu ) + { + for ( size_t n = maPopupMenuRefs.size(); n; ) + { + css::uno::Reference< css::awt::XPopupMenu >& rRef = maPopupMenuRefs[ --n ]; + Menu* pM = static_cast<VCLXMenu*>(rRef.get())->GetMenu(); + if ( pM == pMenu ) + { + aRef = rRef; + break; + } + } + // it seems the popup menu is not insert into maPopupMenuRefs + // if the popup men is not created by stardiv.Toolkit.VCLXPopupMenu + if( !aRef.is() ) + { + aRef = new VCLXPopupMenu( static_cast<PopupMenu*>(pMenu) ); + } + } + return aRef; +} + +// css::awt::XPopupMenu +void VCLXMenu::insertSeparator( + sal_Int16 nPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->InsertSeparator({}, nPos); +} + +void VCLXMenu::setDefaultItem( + sal_Int16 nItemId ) +{ + std::unique_lock aGuard( maMutex ); + + mnDefaultItem = nItemId; +} + +sal_Int16 VCLXMenu::getDefaultItem( ) +{ + std::unique_lock aGuard( maMutex ); + + return mnDefaultItem; +} + +void VCLXMenu::checkItem( + sal_Int16 nItemId, + sal_Bool bCheck ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->CheckItem( nItemId, bCheck ); +} + +sal_Bool VCLXMenu::isItemChecked( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu && mpMenu->IsItemChecked( nItemId ); +} + +sal_Int16 VCLXMenu::execute( + const css::uno::Reference< css::awt::XWindowPeer >& rxWindowPeer, + const css::awt::Rectangle& rPos, + sal_Int16 nFlags ) +{ + SolarMutexGuard aSolarGuard; + auto pMenu = mpMenu; + { + std::unique_lock aGuard( maMutex ); + if ( !mpMenu || !IsPopupMenu() ) + return 0; + } + // cannot call this with mutex locked because it will call back into us + return static_cast<PopupMenu*>(pMenu.get())->Execute( + VCLUnoHelper::GetWindow( rxWindowPeer ), + VCLRectangle( rPos ), + static_cast<PopupMenuFlags>(nFlags) | PopupMenuFlags::NoMouseUpClose ); +} + + +void SAL_CALL VCLXMenu::setCommand( + sal_Int16 nItemId, + const OUString& aCommand ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->SetItemCommand( nItemId, aCommand ); +} + +OUString SAL_CALL VCLXMenu::getCommand( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString aItemCommand; + if ( mpMenu ) + aItemCommand = mpMenu->GetItemCommand( nItemId ); + return aItemCommand; +} + +void SAL_CALL VCLXMenu::setHelpCommand( + sal_Int16 nItemId, + const OUString& aHelp ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->SetHelpCommand( nItemId, aHelp ); +} + +OUString SAL_CALL VCLXMenu::getHelpCommand( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString aHelpCommand; + if ( mpMenu ) + aHelpCommand = mpMenu->GetHelpCommand( nItemId ); + return aHelpCommand; +} + + +namespace +{ + Image lcl_XGraphic2VCLImage( + const css::uno::Reference< css::graphic::XGraphic >& xGraphic, + bool bResize ) + { + Image aImage; + if ( !xGraphic.is() ) + return aImage; + + aImage = Image( xGraphic ); + const ::Size aCurSize = aImage.GetSizePixel(); + const sal_Int32 nCurWidth = aCurSize.Width(); + const sal_Int32 nCurHeight = aCurSize.Height(); + constexpr sal_Int32 nIdeal( 16 ); + + if ( nCurWidth > 0 && nCurHeight > 0 ) + { + if ( bResize && ( nCurWidth > nIdeal || nCurHeight > nIdeal ) ) + { + sal_Int32 nIdealWidth = std::min(nCurWidth, nIdeal); + sal_Int32 nIdealHeight = std::min(nCurHeight, nIdeal); + + ::Size aNewSize( nIdealWidth, nIdealHeight ); + + bool bModified( false ); + BitmapEx aBitmapEx = aImage.GetBitmapEx(); + bModified = aBitmapEx.Scale( aNewSize, BmpScaleFlag::BestQuality ); + + if ( bModified ) + aImage = Image( aBitmapEx ); + } + } + return aImage; + } + + /** Copied from include/svtools/acceleratorexecute.hxx */ + css::awt::KeyEvent lcl_VCLKey2AWTKey( + const vcl::KeyCode& aVCLKey) + { + css::awt::KeyEvent aAWTKey; + aAWTKey.Modifiers = 0; + aAWTKey.KeyCode = static_cast<sal_Int16>(aVCLKey.GetCode()); + + if (aVCLKey.IsShift()) + aAWTKey.Modifiers |= css::awt::KeyModifier::SHIFT; + if (aVCLKey.IsMod1()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD1; + if (aVCLKey.IsMod2()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD2; + if (aVCLKey.IsMod3()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD3; + + return aAWTKey; + } + + vcl::KeyCode lcl_AWTKey2VCLKey(const css::awt::KeyEvent& aAWTKey) + { + bool bShift = ((aAWTKey.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT ); + bool bMod1 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1 ); + bool bMod2 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2 ); + bool bMod3 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3 ); + sal_uInt16 nKey = static_cast<sal_uInt16>(aAWTKey.KeyCode); + + return vcl::KeyCode(nKey, bShift, bMod1, bMod2, bMod3); + } + +} + + +sal_Bool SAL_CALL VCLXMenu::isPopupMenu( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + return IsPopupMenu(); +} + +void SAL_CALL VCLXMenu::clear( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + if ( mpMenu ) + mpMenu->Clear(); +} + + +css::awt::MenuItemType SAL_CALL VCLXMenu::getItemType( + ::sal_Int16 nItemPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::awt::MenuItemType aMenuItemType = + css::awt::MenuItemType_DONTKNOW; + if ( mpMenu ) + { + aMenuItemType = static_cast<css::awt::MenuItemType>(mpMenu->GetItemType( nItemPos )); + } + + return aMenuItemType; +} + +void SAL_CALL VCLXMenu::hideDisabledEntries( + sal_Bool bHide ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + if ( mpMenu ) + { + if ( bHide ) + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() | MenuFlags::HideDisabledEntries ); + else + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() & ~MenuFlags::HideDisabledEntries ); + } +} + + +sal_Bool SAL_CALL VCLXMenu::isInExecute( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() ) + return vcl::IsInPopupMenuExecute(); + else + return false; +} + + +void SAL_CALL VCLXMenu::endExecute() +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() ) + static_cast<PopupMenu*>( mpMenu.get() )->EndExecute(); +} + + +void SAL_CALL VCLXMenu::enableAutoMnemonics( + sal_Bool bEnable ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + if ( mpMenu ) + { + if ( !bEnable ) + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() | MenuFlags::NoAutoMnemonics ); + else + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() & ~MenuFlags::NoAutoMnemonics ); + } +} + + +void SAL_CALL VCLXMenu::setAcceleratorKeyEvent( + ::sal_Int16 nItemId, + const css::awt::KeyEvent& aKeyEvent ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + vcl::KeyCode aVCLKeyCode = lcl_AWTKey2VCLKey( aKeyEvent ); + mpMenu->SetAccelKey( nItemId, aVCLKeyCode ); + } +} + + +css::awt::KeyEvent SAL_CALL VCLXMenu::getAcceleratorKeyEvent( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::awt::KeyEvent aKeyEvent; + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + vcl::KeyCode nKeyCode = mpMenu->GetAccelKey( nItemId ); + aKeyEvent = lcl_VCLKey2AWTKey( nKeyCode ); + } + + return aKeyEvent; +} + + +void SAL_CALL VCLXMenu::setHelpText( + ::sal_Int16 nItemId, + const OUString& sHelpText ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + mpMenu->SetHelpText( nItemId, sHelpText ); + } +} + + +OUString SAL_CALL VCLXMenu::getHelpText( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString sHelpText; + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + sHelpText = mpMenu->GetHelpText( nItemId ); + } + + return sHelpText; +} + + +void SAL_CALL VCLXMenu::setTipHelpText( + ::sal_Int16 nItemId, + const OUString& sTipHelpText ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + mpMenu->SetTipHelpText( nItemId, sTipHelpText ); + } +} + + +OUString SAL_CALL VCLXMenu::getTipHelpText( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString sTipHelpText; + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + sTipHelpText = mpMenu->GetTipHelpText( nItemId ); + } + return sTipHelpText; +} + + +void SAL_CALL VCLXMenu::setItemImage( + ::sal_Int16 nItemId, + const css::uno::Reference< css::graphic::XGraphic >& xGraphic, + sal_Bool bScale ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + Image aImage = lcl_XGraphic2VCLImage( xGraphic, bScale ); + mpMenu->SetItemImage( nItemId, aImage ); + } +} + + +css::uno::Reference< css::graphic::XGraphic > SAL_CALL +VCLXMenu::getItemImage( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::uno::Reference< css::graphic::XGraphic > rxGraphic; + + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + Image aImage = mpMenu->GetItemImage( nItemId ); + if ( !!aImage ) + rxGraphic = Graphic(aImage.GetBitmapEx()).GetXGraphic(); + } + return rxGraphic; +} + +void VCLXMenu::setUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard(maMutex); + + mpMenu->SetUserValue(nItemId, nUserValue, aFunc); +} + +void* VCLXMenu::getUserValue(sal_uInt16 nItemId) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard(maMutex); + + return mpMenu->GetUserValue(nItemId); +} + +VCLXMenuBar::VCLXMenuBar() +{ + ImplCreateMenu( false ); +} + +VCLXMenuBar::VCLXMenuBar( MenuBar* pMenuBar ) : VCLXMenu( static_cast<Menu *>(pMenuBar) ) +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXMenuBar_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXMenuBar()); +} + +VCLXPopupMenu::VCLXPopupMenu() +{ + ImplCreateMenu( true ); +} + +VCLXPopupMenu::VCLXPopupMenu( PopupMenu* pPopMenu ) : VCLXMenu( static_cast<Menu *>(pPopMenu) ) +{ + ImplAddListener(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXPopupMenu_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXPopupMenu()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxpointer.cxx b/toolkit/source/awt/vclxpointer.cxx new file mode 100644 index 0000000000..b541610413 --- /dev/null +++ b/toolkit/source/awt/vclxpointer.cxx @@ -0,0 +1,70 @@ +/* -*- 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 <awt/vclxpointer.hxx> +#include <cppuhelper/supportsservice.hxx> + +VCLXPointer::VCLXPointer() : maPointer(PointerStyle::Arrow) +{ +} + +VCLXPointer::~VCLXPointer() +{ +} + +void VCLXPointer::setType( sal_Int32 nType ) +{ + std::scoped_lock aGuard( maMutex ); + + maPointer = static_cast<PointerStyle>(nType); +} + +sal_Int32 VCLXPointer::getType() +{ + std::scoped_lock aGuard( maMutex ); + + return static_cast<sal_Int32>(maPointer); +} + +OUString VCLXPointer::getImplementationName() +{ + return "stardiv.Toolkit.VCLXPointer"; +} + +sal_Bool VCLXPointer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> VCLXPointer::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.Pointer", "stardiv.vcl.Pointer"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXPointer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXPointer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxprinter.cxx b/toolkit/source/awt/vclxprinter.cxx new file mode 100644 index 0000000000..6ace061795 --- /dev/null +++ b/toolkit/source/awt/vclxprinter.cxx @@ -0,0 +1,379 @@ +/* -*- 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 <awt/vclxprinter.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/string.hxx> + +#include <vcl/print.hxx> +#include <vcl/jobset.hxx> +#include <vcl/oldprintadaptor.hxx> +#include <vcl/svapp.hxx> + +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <o3tl/string_view.hxx> + +#include <toolkit/awt/vclxdevice.hxx> + + +#define BINARYSETUPMARKER 0x23864691 + +#define PROPERTY_Orientation 0 +#define PROPERTY_Horizontal 1 + +// ---------------------------------------------------- +// class VCLXPrinterPropertySet +// ---------------------------------------------------- + +IMPLEMENT_FORWARD_XINTERFACE2( VCLXPrinterPropertySet, VCLXPrinterPropertySet_Base, OPropertySetHelper ) +IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXPrinterPropertySet, VCLXPrinterPropertySet_Base, ::cppu::OPropertySetHelper ) + +VCLXPrinterPropertySet::VCLXPrinterPropertySet( const OUString& rPrinterName ) + : OPropertySetHelper( m_aBHelper ) + , mxPrinter(VclPtrInstance< Printer >(rPrinterName)) +{ + SolarMutexGuard aSolarGuard; + + mnOrientation = 0; + mbHorizontal = false; +} + +VCLXPrinterPropertySet::~VCLXPrinterPropertySet() +{ + SolarMutexGuard aSolarGuard; + mxPrinter.reset(); +} + +css::uno::Reference< css::awt::XDevice > const & VCLXPrinterPropertySet::GetDevice() +{ + if ( !mxPrnDevice.is() ) + { + rtl::Reference<VCLXDevice> pDev = new VCLXDevice; + pDev->SetOutputDevice( GetPrinter() ); + mxPrnDevice = pDev; + } + return mxPrnDevice; +} + +css::uno::Reference< css::beans::XPropertySetInfo > VCLXPrinterPropertySet::getPropertySetInfo( ) +{ + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& VCLXPrinterPropertySet::getInfoHelper() +{ + static ::cppu::OPropertyArrayHelper s_PropertyArrayHelper( + css::uno::Sequence<css::beans::Property>{ + css::beans::Property( "Orientation", PROPERTY_Orientation, cppu::UnoType<sal_Int16>::get(), 0 ), + css::beans::Property( "Horizontal", PROPERTY_Horizontal, cppu::UnoType<bool>::get(), 0 )}, + false); + + return s_PropertyArrayHelper; +} + +sal_Bool VCLXPrinterPropertySet::convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bDifferent = false; + switch ( nHandle ) + { + case PROPERTY_Orientation: + { + sal_Int16 n; + if( ( rValue >>= n ) && ( n != mnOrientation ) ) + { + rConvertedValue <<= n; + rOldValue <<= mnOrientation; + bDifferent = true; + } + } + break; + case PROPERTY_Horizontal: + { + bool b; + if( ( rValue >>= b ) && ( b != mbHorizontal ) ) + { + rConvertedValue <<= b; + rOldValue <<= mbHorizontal; + bDifferent = true; + } + } + break; + default: + { + OSL_FAIL( "VCLXPrinterPropertySet_Impl::convertFastPropertyValue - invalid Handle" ); + } + } + return bDifferent; +} + +void VCLXPrinterPropertySet::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + switch( nHandle ) + { + case PROPERTY_Orientation: + { + rValue >>= mnOrientation; + } + break; + case PROPERTY_Horizontal: + { + rValue >>= mbHorizontal; + } + break; + default: + { + OSL_FAIL( "VCLXPrinterPropertySet_Impl::convertFastPropertyValue - invalid Handle" ); + } + } +} + +void VCLXPrinterPropertySet::getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const +{ + ::osl::MutexGuard aGuard( const_cast<VCLXPrinterPropertySet*>(this)->m_aMutex ); + + switch( nHandle ) + { + case PROPERTY_Orientation: + rValue <<= mnOrientation; + break; + case PROPERTY_Horizontal: + rValue <<= mbHorizontal; + break; + default: + { + OSL_FAIL( "VCLXPrinterPropertySet_Impl::convertFastPropertyValue - invalid Handle" ); + } + } +} + +// css::awt::XPrinterPropertySet +void VCLXPrinterPropertySet::setHorizontal( sal_Bool bHorizontal ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + css::uno::Any aValue; + aValue <<= bHorizontal; + setFastPropertyValue( PROPERTY_Horizontal, aValue ); +} + +css::uno::Sequence< OUString > VCLXPrinterPropertySet::getFormDescriptions( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + const sal_uInt16 nPaperBinCount = GetPrinter()->GetPaperBinCount(); + css::uno::Sequence< OUString > aDescriptions( nPaperBinCount ); + for ( sal_uInt16 n = 0; n < nPaperBinCount; n++ ) + { + // Format: <DisplayFormName;FormNameId;DisplayPaperBinName;PaperBinNameId;DisplayPaperName;PaperNameId> + OUString aDescr = "*;*;" + GetPrinter()->GetPaperBinName( n ) + ";" + + OUString::number(n) + ";*;*"; + + aDescriptions.getArray()[n] = aDescr; + } + return aDescriptions; +} + +void VCLXPrinterPropertySet::selectForm( const OUString& rFormDescription ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_uInt16 nPaperBin = sal::static_int_cast< sal_uInt16 >( + o3tl::toInt32(o3tl::getToken(rFormDescription, 3, ';' ))); + GetPrinter()->SetPaperBin( nPaperBin ); +} + +css::uno::Sequence< sal_Int8 > VCLXPrinterPropertySet::getBinarySetup( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvMemoryStream aMem; + aMem.WriteUInt32( BINARYSETUPMARKER ); + WriteJobSetup( aMem, GetPrinter()->GetJobSetup() ); + return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() ); +} + +void VCLXPrinterPropertySet::setBinarySetup( const css::uno::Sequence< sal_Int8 >& data ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvMemoryStream aMem( const_cast<signed char*>(data.getConstArray()), data.getLength(), StreamMode::READ ); + sal_uInt32 nMarker; + aMem.ReadUInt32( nMarker ); + DBG_ASSERT( nMarker == BINARYSETUPMARKER, "setBinarySetup - invalid!" ); + if ( nMarker == BINARYSETUPMARKER ) + { + JobSetup aSetup; + ReadJobSetup( aMem, aSetup ); + GetPrinter()->SetJobSetup( aSetup ); + } +} + + +// ---------------------------------------------------- +// class VCLXPrinter +// ---------------------------------------------------- +VCLXPrinter::VCLXPrinter( const OUString& rPrinterName ) + : VCLXPrinter_Base( rPrinterName ) +{ +} + +VCLXPrinter::~VCLXPrinter() +{ +} + +sal_Bool VCLXPrinter::start( const OUString& /*rJobName*/, sal_Int16 /*nCopies*/, sal_Bool /*bCollate*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxPrinter) + { + maInitJobSetup = mxPrinter->GetJobSetup(); + mxListener = std::make_shared<vcl::OldStylePrintAdaptor>(mxPrinter, nullptr); + } + + return true; +} + +void VCLXPrinter::end( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxListener) + { + Printer::PrintJob(mxListener, maInitJobSetup); + mxListener.reset(); + } +} + +void VCLXPrinter::terminate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + mxListener.reset(); +} + +css::uno::Reference< css::awt::XDevice > VCLXPrinter::startPage( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxListener) + { + mxListener->StartPage(); + } + return GetDevice(); +} + +void VCLXPrinter::endPage( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxListener) + { + mxListener->EndPage(); + } +} + + +// ---------------------------------------------------- +// class VCLXInfoPrinter +// ---------------------------------------------------- + +VCLXInfoPrinter::VCLXInfoPrinter( const OUString& rPrinterName ) + : VCLXInfoPrinter_Base( rPrinterName ) +{ +} + +VCLXInfoPrinter::~VCLXInfoPrinter() +{ +} + +// css::awt::XInfoPrinter +css::uno::Reference< css::awt::XDevice > VCLXInfoPrinter::createDevice( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return GetDevice(); +} + +// ---------------------------------------------------- +// class VCLXPrinterServer +// ---------------------------------------------------- + +// css::awt::XPrinterServer2 +css::uno::Sequence< OUString > VCLXPrinterServer::getPrinterNames( ) +{ + const std::vector<OUString>& rQueues = Printer::GetPrinterQueues(); + sal_uInt32 nPrinters = rQueues.size(); + + css::uno::Sequence< OUString > aNames( nPrinters ); + for ( sal_uInt32 n = 0; n < nPrinters; n++ ) + aNames.getArray()[n] = rQueues[n]; + + return aNames; +} + +OUString VCLXPrinterServer::getDefaultPrinterName() +{ + return Printer::GetDefaultPrinterName(); +} + +css::uno::Reference< css::awt::XPrinter > VCLXPrinterServer::createPrinter( const OUString& rPrinterName ) +{ + css::uno::Reference< css::awt::XPrinter > xP = new VCLXPrinter( rPrinterName ); + return xP; +} + +css::uno::Reference< css::awt::XInfoPrinter > VCLXPrinterServer::createInfoPrinter( const OUString& rPrinterName ) +{ + css::uno::Reference< css::awt::XInfoPrinter > xP = new VCLXInfoPrinter( rPrinterName ); + return xP; +} + +OUString VCLXPrinterServer::getImplementationName() +{ + return "stardiv.Toolkit.VCLXPrinterServer"; +} + +sal_Bool VCLXPrinterServer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> VCLXPrinterServer::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.PrinterServer", "stardiv.vcl.PrinterServer"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXPrinterServer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXPrinterServer); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxregion.cxx b/toolkit/source/awt/vclxregion.cxx new file mode 100644 index 0000000000..fff7e7969d --- /dev/null +++ b/toolkit/source/awt/vclxregion.cxx @@ -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 . + */ + +#include <awt/vclxregion.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> + + + +VCLXRegion::VCLXRegion() +{ +} + +VCLXRegion::~VCLXRegion() +{ +} + +css::awt::Rectangle VCLXRegion::getBounds() +{ + std::scoped_lock aGuard( maMutex ); + + return AWTRectangle( maRegion.GetBoundRect() ); +} + +void VCLXRegion::clear() +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.SetEmpty(); +} + +void VCLXRegion::move( sal_Int32 nHorzMove, sal_Int32 nVertMove ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Move( nHorzMove, nVertMove ); +} + +void VCLXRegion::unionRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Union( VCLRectangle( rRect ) ); +} + +void VCLXRegion::intersectRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Intersect( VCLRectangle( rRect ) ); +} + +void VCLXRegion::excludeRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Exclude( VCLRectangle( rRect ) ); +} + +void VCLXRegion::xOrRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.XOr( VCLRectangle( rRect ) ); +} + +void VCLXRegion::unionRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.Union( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +void VCLXRegion::intersectRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.Intersect( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +void VCLXRegion::excludeRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.Exclude( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +void VCLXRegion::xOrRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.XOr( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +css::uno::Sequence< css::awt::Rectangle > VCLXRegion::getRectangles() +{ + std::scoped_lock aGuard( maMutex ); + + RectangleVector aRectangles; + maRegion.GetRegionRectangles(aRectangles); + +// sal_uLong nRects = maRegion.GetRectCount(); + css::uno::Sequence< css::awt::Rectangle > aRects(aRectangles.size()); + sal_uInt32 a(0); + + for(const auto& rRect : aRectangles) + { + aRects.getArray()[a++] = AWTRectangle(rRect); + } + + //Rectangle aRect; + //sal_uInt32 nR = 0; + //RegionHandle h = maRegion.BeginEnumRects(); + //while ( maRegion.GetEnumRects( h, aRect ) ) + // aRects.getArray()[nR++] = AWTRectangle( aRect ); + //maRegion.EndEnumRects( h ); + + return aRects; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxspinbutton.cxx b/toolkit/source/awt/vclxspinbutton.cxx new file mode 100644 index 0000000000..cc4747dc20 --- /dev/null +++ b/toolkit/source/awt/vclxspinbutton.cxx @@ -0,0 +1,331 @@ +/* -*- 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/vclxspinbutton.hxx> +#include <helper/property.hxx> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> + +#include <vcl/toolkit/spin.hxx> +#include <vcl/svapp.hxx> +#include "vclxwindows_internal.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; + + + namespace + { + void lcl_modifyStyle( vcl::Window* _pWindow, WinBits _nStyleBits, bool _bShouldBePresent ) + { + WinBits nStyle = _pWindow->GetStyle(); + if ( _bShouldBePresent ) + nStyle |= _nStyleBits; + else + nStyle &= ~_nStyleBits; + _pWindow->SetStyle( nStyle ); + } + } + + VCLXSpinButton::VCLXSpinButton() + :maAdjustmentListeners( *this ) + { + } + + + VCLXSpinButton::~VCLXSpinButton() + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( VCLXSpinButton, VCLXWindow, VCLXSpinButton_Base ) + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXSpinButton, VCLXWindow, VCLXSpinButton_Base ) + + + void SAL_CALL VCLXSpinButton::dispose( ) + { + { + SolarMutexGuard aGuard; + + EventObject aDisposeEvent; + aDisposeEvent.Source = *this; + maAdjustmentListeners.disposeAndClear( aDisposeEvent ); + } + + VCLXWindow::dispose(); + } + + + void SAL_CALL VCLXSpinButton::addAdjustmentListener( const Reference< XAdjustmentListener >& listener ) + { + if ( listener.is() ) + maAdjustmentListeners.addInterface( listener ); + } + + + void SAL_CALL VCLXSpinButton::removeAdjustmentListener( const Reference< XAdjustmentListener >& listener ) + { + if ( listener.is() ) + maAdjustmentListeners.removeInterface( listener ); + } + + namespace + { + typedef void (SpinButton::*SetSpinButtonValue) (tools::Long); + typedef tools::Long (SpinButton::*GetSpinButtonValue) () const; + + + void lcl_setSpinButtonValue(vcl::Window* _pWindow, SetSpinButtonValue _pSetter, sal_Int32 _nValue ) + { + SolarMutexGuard aGuard; + SpinButton* pSpinButton = static_cast< SpinButton* >( _pWindow ); + if ( pSpinButton ) + (pSpinButton->*_pSetter)( _nValue ); + } + + + sal_Int32 lcl_getSpinButtonValue(const vcl::Window* _pWindow, GetSpinButtonValue _pGetter ) + { + SolarMutexGuard aGuard; + + sal_Int32 nValue = 0; + + const SpinButton* pSpinButton = static_cast< const SpinButton* >( _pWindow ); + if ( pSpinButton ) + nValue = (pSpinButton->*_pGetter)( ); + return nValue; + } + } + + + void SAL_CALL VCLXSpinButton::setValue( sal_Int32 n ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetValue, n ); + } + + + void SAL_CALL VCLXSpinButton::setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) + { + SolarMutexGuard aGuard; + + setMinimum( minValue ); + setMaximum( maxValue ); + setValue( currentValue ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getValue( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetValue ); + } + + + void SAL_CALL VCLXSpinButton::setMinimum( sal_Int32 minValue ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetRangeMin, minValue ); + } + + + void SAL_CALL VCLXSpinButton::setMaximum( sal_Int32 maxValue ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetRangeMax, maxValue ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getMinimum( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetRangeMin ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getMaximum( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetRangeMax ); + } + + + void SAL_CALL VCLXSpinButton::setSpinIncrement( sal_Int32 spinIncrement ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetValueStep, spinIncrement ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getSpinIncrement( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetValueStep ); + } + + + void SAL_CALL VCLXSpinButton::setOrientation( sal_Int32 orientation ) + { + SolarMutexGuard aGuard; + + lcl_modifyStyle( GetWindow(), WB_HSCROLL, orientation == ScrollBarOrientation::HORIZONTAL ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getOrientation( ) + { + return ( 0 != ( GetWindow()->GetStyle() & WB_HSCROLL ) ) + ? ScrollBarOrientation::HORIZONTAL + : ScrollBarOrientation::VERTICAL; + } + + + void VCLXSpinButton::ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent ) + { + SolarMutexClearableGuard aGuard; + Reference< XSpinValue > xKeepAlive( this ); + VclPtr<SpinButton> pSpinButton = GetAs<SpinButton>(); + if ( !pSpinButton ) + return; + + switch ( _rVclWindowEvent.GetId() ) + { + case VclEventId::SpinbuttonUp: + case VclEventId::SpinbuttonDown: + if ( maAdjustmentListeners.getLength() ) + { + AdjustmentEvent aEvent; + aEvent.Source = *this; + aEvent.Value = pSpinButton->GetValue(); + + aGuard.clear(); + maAdjustmentListeners.adjustmentValueChanged( aEvent ); + } + break; + + default: + xKeepAlive.clear(); + aGuard.clear(); + VCLXWindow::ProcessWindowEvent( _rVclWindowEvent ); + break; + } + } + + + void SAL_CALL VCLXSpinButton::setProperty( const OUString& PropertyName, const Any& Value ) + { + SolarMutexGuard aGuard; + + sal_Int32 nValue = 0; + bool bIsLongValue = ( Value >>= nValue ); + + if ( !GetWindow() ) + return; + + sal_uInt16 nPropertyId = GetPropertyId( PropertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_BACKGROUNDCOLOR: + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + setButtonLikeFaceColor( GetWindow(), Value); + break; + + case BASEPROPERTY_SPINVALUE: + if ( bIsLongValue ) + setValue( nValue ); + break; + + case BASEPROPERTY_SPINVALUE_MIN: + if ( bIsLongValue ) + setMinimum( nValue ); + break; + + case BASEPROPERTY_SPINVALUE_MAX: + if ( bIsLongValue ) + setMaximum( nValue ); + break; + + case BASEPROPERTY_SPININCREMENT: + if ( bIsLongValue ) + setSpinIncrement( nValue ); + break; + + case BASEPROPERTY_ORIENTATION: + if ( bIsLongValue ) + lcl_modifyStyle( GetWindow(), WB_HSCROLL, nValue == ScrollBarOrientation::HORIZONTAL ); + break; + + default: + VCLXWindow::setProperty( PropertyName, Value ); + } + } + + + Any SAL_CALL VCLXSpinButton::getProperty( const OUString& PropertyName ) + { + SolarMutexGuard aGuard; + + Any aReturn; + + if ( GetWindow() ) + { + sal_uInt16 nPropertyId = GetPropertyId( PropertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_BACKGROUNDCOLOR: + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + aReturn = getButtonLikeFaceColor( GetWindow() ); + break; + + case BASEPROPERTY_SPINVALUE: + aReturn <<= getValue( ); + break; + + case BASEPROPERTY_SPINVALUE_MIN: + aReturn <<= getMinimum( ); + break; + + case BASEPROPERTY_SPINVALUE_MAX: + aReturn <<= getMaximum( ); + break; + + case BASEPROPERTY_SPININCREMENT: + aReturn <<= getSpinIncrement( ); + break; + + case BASEPROPERTY_ORIENTATION: + aReturn <<= static_cast<sal_Int32>( ( 0 != ( GetWindow()->GetStyle() & WB_HSCROLL ) ) + ? ScrollBarOrientation::HORIZONTAL + : ScrollBarOrientation::VERTICAL + ); + break; + + default: + aReturn = VCLXWindow::getProperty( PropertyName ); + } + } + return aReturn; + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxsystemdependentwindow.cxx b/toolkit/source/awt/vclxsystemdependentwindow.cxx new file mode 100644 index 0000000000..2ba59470a8 --- /dev/null +++ b/toolkit/source/awt/vclxsystemdependentwindow.cxx @@ -0,0 +1,90 @@ +/* -*- 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/lang/SystemDependent.hpp> + +#if defined UNX && ! defined MACOSX +#include <com/sun/star/awt/SystemDependentXWindow.hpp> +#endif + +#include <awt/vclxsystemdependentwindow.hxx> + +#ifdef MACOSX +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif + +#include <vcl/svapp.hxx> +#include <vcl/syschild.hxx> +#include <vcl/sysdata.hxx> + + + +VCLXSystemDependentWindow::VCLXSystemDependentWindow() +{ +} + +VCLXSystemDependentWindow::~VCLXSystemDependentWindow() +{ +} + +css::uno::Any VCLXSystemDependentWindow::getWindowHandle( const css::uno::Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) +{ + SolarMutexGuard aGuard; + + // TODO, check the process id + css::uno::Any aRet; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + const SystemEnvData* pSysData = static_cast<SystemChildWindow *>(pWindow.get())->GetSystemData(); + if( pSysData ) + { +#if defined(_WIN32) + if( SystemType == css::lang::SystemDependent::SYSTEM_WIN32 ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->hWnd); + } +#elif defined(MACOSX) + if( SystemType == css::lang::SystemDependent::SYSTEM_MAC ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->mpNSView); + } +#elif defined(ANDROID) + // Nothing + (void) SystemType; +#elif defined(IOS) + // Nothing + (void) SystemType; +#elif defined(UNX) + if( SystemType == css::lang::SystemDependent::SYSTEM_XWINDOW ) + { + css::awt::SystemDependentXWindow aSD; + aSD.DisplayPointer = sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_IntPtr >(pSysData->pDisplay)); + aSD.WindowHandle = pSysData->GetWindowHandle(pWindow->ImplGetFrame()); + aRet <<= aSD; + } +#endif + } + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxtabpagecontainer.cxx b/toolkit/source/awt/vclxtabpagecontainer.cxx new file mode 100644 index 0000000000..afd3d843b7 --- /dev/null +++ b/toolkit/source/awt/vclxtabpagecontainer.cxx @@ -0,0 +1,233 @@ +/* -*- 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/vclxtabpagecontainer.hxx> +#include <com/sun/star/awt/tab/XTabPageModel.hpp> +#include <com/sun/star/awt/XControl.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <helper/property.hxx> +#include <vcl/image.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <helper/tkresmgr.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; + + +void VCLXTabPageContainer::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXTabPageContainer::VCLXTabPageContainer() : + m_aTabPageListeners( *this ) +{ +} + +VCLXTabPageContainer::~VCLXTabPageContainer() +{ + SAL_INFO("toolkit", __FUNCTION__); +} + +void SAL_CALL VCLXTabPageContainer::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabControl = GetAs<TabControl>(); + if ( pTabControl ) + { + TabPage *pTabPage = pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( pTabControl->GetCurPageId( ) ) ); + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if (pTabPage && pDev) + { + ::Point aPos( nX, nY ); + aPos = pDev->PixelToLogic( aPos ); + pTabPage->Draw( pDev, aPos, SystemTextColorFlags::NONE ); + } + } + + VCLXWindow::draw( nX, nY ); +} + +void SAL_CALL VCLXTabPageContainer::setProperty(const OUString& PropertyName, const Any& Value ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabPage = GetAs<TabControl>(); + if ( pTabPage ) + VCLXWindow::setProperty( PropertyName, Value ); +} + +::sal_Int16 SAL_CALL VCLXTabPageContainer::getActiveTabPageID() +{ + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + return pTabCtrl ? pTabCtrl->GetCurPageId( ) : 0; +} + +void SAL_CALL VCLXTabPageContainer::setActiveTabPageID( ::sal_Int16 _activetabpageid ) +{ + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + if ( pTabCtrl ) + pTabCtrl->SelectTabPage(_activetabpageid); +} + +::sal_Int16 SAL_CALL VCLXTabPageContainer::getTabPageCount( ) +{ + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + return pTabCtrl ? pTabCtrl->GetPageCount() : 0; +} + +sal_Bool SAL_CALL VCLXTabPageContainer::isTabPageActive( ::sal_Int16 tabPageIndex ) +{ + return (getActiveTabPageID() == tabPageIndex); +} + +Reference< css::awt::tab::XTabPage > SAL_CALL VCLXTabPageContainer::getTabPage( ::sal_Int16 tabPageIndex ) +{ + return (tabPageIndex >= 0 && o3tl::make_unsigned(tabPageIndex) < m_aTabPages.size()) ? m_aTabPages[tabPageIndex] : nullptr; +} + +Reference< css::awt::tab::XTabPage > SAL_CALL VCLXTabPageContainer::getTabPageByID( ::sal_Int16 tabPageID ) +{ + SolarMutexGuard aGuard; + Reference< css::awt::tab::XTabPage > xTabPage; + for(const auto& rTabPage : m_aTabPages) + { + Reference< awt::XControl > xControl(rTabPage,UNO_QUERY ); + Reference< awt::tab::XTabPageModel > xP( xControl->getModel(), UNO_QUERY ); + if ( tabPageID == xP->getTabPageID() ) + { + xTabPage = rTabPage; + break; + } + } + return xTabPage; +} + +void SAL_CALL VCLXTabPageContainer::addTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + m_aTabPageListeners.addInterface( listener ); +} + +void SAL_CALL VCLXTabPageContainer::removeTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + m_aTabPageListeners.removeInterface( listener ); +} + +void VCLXTabPageContainer::ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent ) +{ + SolarMutexClearableGuard aGuard; + VclPtr<TabControl> pTabControl = GetAs<TabControl>(); + if ( !pTabControl ) + return; + + switch ( _rVclWindowEvent.GetId() ) + { + case VclEventId::TabpageActivate: + { + sal_uLong page = reinterpret_cast<sal_uLong>(_rVclWindowEvent.GetData()); + awt::tab::TabPageActivatedEvent aEvent(nullptr,page); + m_aTabPageListeners.tabPageActivated(aEvent); + break; + } + default: + aGuard.clear(); + VCLXWindow::ProcessWindowEvent( _rVclWindowEvent ); + break; + } +} +void SAL_CALL VCLXTabPageContainer::disposing( const css::lang::EventObject& /*Source*/ ) +{ +} +void SAL_CALL VCLXTabPageContainer::elementInserted( const css::container::ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + Reference< css::awt::tab::XTabPage > xTabPage(Event.Element,uno::UNO_QUERY); + if ( !pTabCtrl || !xTabPage.is() ) + return; + + Reference< awt::XControl > xControl(xTabPage,UNO_QUERY ); + Reference< awt::tab::XTabPageModel > xP( xControl->getModel(), UNO_QUERY ); + sal_Int16 nPageID = xP->getTabPageID(); + + if (!xControl->getPeer().is()) + throw RuntimeException("No peer for tabpage container!"); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xControl->getPeer()); + TabPage* pPage = static_cast<TabPage*>(pWindow.get()); + pTabCtrl->InsertPage(nPageID,pPage->GetText()); + + pPage->Hide(); + pTabCtrl->SetTabPage(nPageID,pPage); + pTabCtrl->SetHelpText(nPageID,xP->getToolTip()); + pTabCtrl->SetPageImage(nPageID,TkResMgr::getImageFromURL(xP->getImageURL())); + pTabCtrl->SelectTabPage(nPageID); + pTabCtrl->SetPageEnabled(nPageID,xP->getEnabled()); + m_aTabPages.push_back(xTabPage); + +} +void SAL_CALL VCLXTabPageContainer::elementRemoved( const css::container::ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + Reference< css::awt::tab::XTabPage > xTabPage(Event.Element,uno::UNO_QUERY); + if ( pTabCtrl && xTabPage.is() ) + { + Reference< awt::XControl > xControl(xTabPage,UNO_QUERY ); + Reference< awt::tab::XTabPageModel > xP( xControl->getModel(), UNO_QUERY ); + pTabCtrl->RemovePage(xP->getTabPageID()); + std::erase(m_aTabPages,xTabPage); + } +} +void SAL_CALL VCLXTabPageContainer::elementReplaced( const css::container::ContainerEvent& /*Event*/ ) +{ +} + +void VCLXTabPageContainer::propertiesChange(const::css::uno::Sequence<PropertyChangeEvent>& rEvents) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + if (!pTabCtrl) + return; + + for (const beans::PropertyChangeEvent& rEvent : rEvents) { + // handle property changes for tab pages + Reference< css::awt::tab::XTabPageModel > xTabPageModel(rEvent.Source, uno::UNO_QUERY); + if (!xTabPageModel.is()) + continue; + + const sal_Int16 nId = xTabPageModel->getTabPageID(); + if (rEvent.PropertyName == GetPropertyName(BASEPROPERTY_ENABLED)) { + pTabCtrl->SetPageEnabled(nId, xTabPageModel->getEnabled()); + } else if (rEvent.PropertyName == GetPropertyName(BASEPROPERTY_TITLE)) { + pTabCtrl->SetPageText(nId, xTabPageModel->getTitle()); + } else if (rEvent.PropertyName == GetPropertyName(BASEPROPERTY_IMAGEURL)) { + pTabCtrl->SetPageImage(nId, TkResMgr::getImageFromURL(xTabPageModel->getImageURL())); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxtoolkit.cxx b/toolkit/source/awt/vclxtoolkit.cxx new file mode 100644 index 0000000000..fc6b7b9df8 --- /dev/null +++ b/toolkit/source/awt/vclxtoolkit.cxx @@ -0,0 +1,2624 @@ +/* -*- 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 <string_view> +#include <thread> + +#ifdef _WIN32 +#include <prewin.h> +#include <postwin.h> +#endif +#include <config_features.h> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/awt/WindowClass.hpp> +#include <com/sun/star/awt/MessageBoxButtons.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/awt/FocusEvent.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/lang/EventObject.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/awt/XToolkitExperimental.hpp> +#include <com/sun/star/awt/XToolkitRobot.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/bootstrap.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <osl/conditn.hxx> +#include <osl/module.h> +#include <osl/thread.hxx> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <rtl/process.h> +#include <sal/log.hxx> +#include <tools/link.hxx> +#include <vcl/idletask.hxx> +#include <vcl/wintypes.hxx> + +#ifdef MACOSX +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif + +#include <utility> +#include <vcl/sysdata.hxx> +#include <vcl/textrectinfo.hxx> +#include <vcl/toolkit/vclmedit.hxx> + +#include <toolkit/awt/vclxwindows.hxx> +#include <awt/vclxwindows.hxx> +#include <awt/vclxsystemdependentwindow.hxx> +#include <awt/vclxregion.hxx> +#include <awt/vclxtabpagecontainer.hxx> +#include <awt/vclxtopwindow.hxx> + +#include <awt/animatedimagespeer.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <helper/property.hxx> + +#include <toolkit/helper/convert.hxx> +#include <controls/filectrl.hxx> +#include <controls/svmedit.hxx> +#include <controls/table/tablecontrol.hxx> +#include <controls/treecontrolpeer.hxx> +#include <vcl/toolkit/button.hxx> +#include <vcl/toolkit/calendar.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/ctrl.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/dockingarea.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/toolkit/edit.hxx> +#include <vcl/event.hxx> +#include <vcl/toolkit/field.hxx> +#include <vcl/toolkit/fixed.hxx> +#include <vcl/toolkit/fixedhyper.hxx> +#include <vcl/toolkit/floatwin.hxx> +#include <vcl/toolkit/fmtfield.hxx> +#include <vcl/toolkit/prgsbar.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/toolkit/lstbox.hxx> +#include <vcl/toolkit/longcurr.hxx> +#include <vcl/toolkit/menubtn.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/split.hxx> +#include <vcl/splitwin.hxx> +#include <vcl/status.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syschild.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/virdev.hxx> +#include <vcl/window.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/toolkit/group.hxx> +#include <vcl/toolkit/imgctrl.hxx> +#include <vcl/toolkit/morebtn.hxx> +#include <vcl/toolkit/roadmap.hxx> +#include <vcl/toolkit/spin.hxx> +#include <vcl/toolkit/tabdlg.hxx> +#include <vcl/toolkit/throbber.hxx> +#if HAVE_FEATURE_OPENGL +#include <vcl/opengl/OpenGLWrapper.hxx> +#endif +#include <awt/vclxspinbutton.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/profilezone.hxx> + +#include <helper/msgbox.hxx> +#include <helper/scrollabledialog.hxx> +#include <helper/unowrapper.hxx> + +#if defined(_WIN32) +#define SYSTEM_DEPENDENT_TYPE css::lang::SystemDependent::SYSTEM_WIN32 +#elif defined(MACOSX) +#define SYSTEM_DEPENDENT_TYPE css::lang::SystemDependent::SYSTEM_MAC +#elif defined(UNX) +#define SYSTEM_DEPENDENT_TYPE css::lang::SystemDependent::SYSTEM_XWINDOW +#endif + +void MessBox::ImplInitButtons() +{ + ButtonDialogFlags nOKFlags = ButtonDialogFlags::OK; + ButtonDialogFlags nCancelFlags = ButtonDialogFlags::Cancel; + ButtonDialogFlags nRetryFlags = ButtonDialogFlags::NONE; + ButtonDialogFlags nYesFlags = ButtonDialogFlags::NONE; + ButtonDialogFlags nNoFlags = ButtonDialogFlags::NONE; + + if ( mnMessBoxStyle & MessBoxStyle::OkCancel ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultCancel ) + nCancelFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else // MessBoxStyle::DefaultOk + nOKFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::OK, RET_OK, nOKFlags ); + AddButton( StandardButtonType::Cancel, RET_CANCEL, nCancelFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::YesNo ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultYes ) + nYesFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else // MessBoxStyle::DefaultNo + nNoFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + nNoFlags |= ButtonDialogFlags::Cancel; + + AddButton( StandardButtonType::Yes, RET_YES, nYesFlags ); + AddButton( StandardButtonType::No, RET_NO, nNoFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::YesNoCancel ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultYes ) + nYesFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else if ( mnMessBoxStyle & MessBoxStyle::DefaultNo ) + nNoFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else + nCancelFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::Yes, RET_YES, nYesFlags ); + AddButton( StandardButtonType::No, RET_NO, nNoFlags ); + AddButton( StandardButtonType::Cancel, RET_CANCEL, nCancelFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::RetryCancel ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultCancel ) + nCancelFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else // MessBoxStyle::DefaultRetry + nRetryFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::Retry, RET_RETRY, nRetryFlags ); + AddButton( StandardButtonType::Cancel, RET_CANCEL, nCancelFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::AbortRetryIgnore ) + { + ButtonDialogFlags nAbortFlags = ButtonDialogFlags::NONE; + ButtonDialogFlags nIgnoreFlags = ButtonDialogFlags::NONE; + + if ( mnMessBoxStyle & MessBoxStyle::DefaultCancel ) + nAbortFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else if ( mnMessBoxStyle & MessBoxStyle::DefaultRetry ) + nRetryFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else if ( mnMessBoxStyle & MessBoxStyle::DefaultIgnore ) + nIgnoreFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::Abort, RET_CANCEL, nAbortFlags ); + AddButton( StandardButtonType::Retry, RET_RETRY, nRetryFlags ); + AddButton( StandardButtonType::Ignore, RET_IGNORE, nIgnoreFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::Ok ) + { + nOKFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::OK, RET_OK, nOKFlags ); + } +} + +MessBox::MessBox(vcl::Window* pParent, MessBoxStyle nMessBoxStyle, WinBits nWinBits, + const OUString& rTitle, OUString aMessage) : + ButtonDialog( WindowType::MESSBOX ), + mbHelpBtn( false ), + mnMessBoxStyle( nMessBoxStyle ), + maMessText(std::move( aMessage )) +{ + ImplLOKNotifier(pParent); + ImplInitDialog(pParent, nWinBits | WB_MOVEABLE | WB_HORZ | WB_CENTER); + ImplInitButtons(); + + if ( !rTitle.isEmpty() ) + SetText( rTitle ); +} + +MessBox::~MessBox() +{ + disposeOnce(); +} + +void MessBox::dispose() +{ + mpVCLMultiLineEdit.disposeAndClear(); + mpFixedImage.disposeAndClear(); + ButtonDialog::dispose(); +} + +void MessBox::ImplPosControls() +{ + if ( !GetHelpId().isEmpty() ) + { + if ( !mbHelpBtn ) + { + AddButton( StandardButtonType::Help, RET_HELP, ButtonDialogFlags::Help, 3 ); + mbHelpBtn = true; + } + } + else + { + if ( mbHelpBtn ) + { + RemoveButton( RET_HELP ); + mbHelpBtn = false; + } + } + + TextRectInfo aTextInfo; + tools::Rectangle aRect( 0, 0, 30000, 30000 ); + tools::Rectangle aFormatRect; + Point aTextPos( IMPL_DIALOG_OFFSET, IMPL_DIALOG_OFFSET+IMPL_MSGBOX_OFFSET_EXTRA_Y ); + Size aImageSize; + Size aPageSize; + Size aMEditSize; + tools::Long nTitleWidth; + tools::Long nButtonSize = ImplGetButtonSize(); + tools::Long nMaxLineWidth; + tools::Long nWidth; + WinBits nWinStyle = WB_LEFT | WB_NOLABEL; + DrawTextFlags nTextStyle = DrawTextFlags::MultiLine | DrawTextFlags::Top | DrawTextFlags::Left; + + mpVCLMultiLineEdit.disposeAndClear(); + mpFixedImage.disposeAndClear(); + + // Clean up message text with tabs + OUString aMessText(maMessText.replaceAll("\t", " ")); + + //If window too small, we make dialog box be wider + tools::Long nMaxWidth = 630 * GetDPIScaleFactor(); + + // MessagBox should be at least as wide as to see the title + // Extra-Width for Close button, because Close button is set after this call + nTitleWidth = CalcTitleWidth(); + + nMaxWidth -= (IMPL_DIALOG_OFFSET*2)+(IMPL_MSGBOX_OFFSET_EXTRA_X*2); + + // for an image, get its size, create a suitable control and position it + aImageSize = maImage.GetSizePixel(); + if ( aImageSize.Width() ) + { + aImageSize.AdjustWidth(4 ); + aImageSize.AdjustHeight(4 ); + aTextPos.AdjustX(aImageSize.Width()+IMPL_SEP_MSGBOX_IMAGE ); + mpFixedImage = VclPtr<FixedImage>::Create( this ); + mpFixedImage->SetPosSizePixel( Point( IMPL_DIALOG_OFFSET-2+IMPL_MSGBOX_OFFSET_EXTRA_X, + IMPL_DIALOG_OFFSET-2+IMPL_MSGBOX_OFFSET_EXTRA_Y ), + aImageSize ); + mpFixedImage->SetImage( maImage ); + mpFixedImage->Show(); + nMaxWidth -= aImageSize.Width()+IMPL_SEP_MSGBOX_IMAGE; + } + else + aTextPos.AdjustX(IMPL_MSGBOX_OFFSET_EXTRA_X ); + + // Determine maximum line length without wordbreak + aFormatRect = GetTextRect( aRect, aMessText, nTextStyle, &aTextInfo ); + nMaxLineWidth = aFormatRect.GetWidth(); + nTextStyle |= DrawTextFlags::WordBreak; + + // Determine the width for text formatting + if ( nMaxLineWidth > 450 ) + nWidth = 450; + else if ( nMaxLineWidth > 300 ) + nWidth = nMaxLineWidth+5; + else + nWidth = 300; + + nWidth *= GetDPIScaleFactor(); + + if ( nButtonSize > nWidth ) + nWidth = nButtonSize-(aTextPos.X()-IMPL_DIALOG_OFFSET); + if ( nWidth > nMaxWidth ) + nWidth = nMaxWidth; + + aRect.SetRight( nWidth ); + aFormatRect = GetTextRect( aRect, aMessText, nTextStyle, &aTextInfo ); + if ( aTextInfo.GetMaxLineWidth() > nWidth ) + { + nWidth = aTextInfo.GetMaxLineWidth()+8; + aRect.SetRight( nWidth ); + aFormatRect = GetTextRect( aRect, aMessText, nTextStyle, &aTextInfo ); + } + + // get Style for VCLMultiLineEdit + aMEditSize.setWidth( aTextInfo.GetMaxLineWidth()+1 ); + aMEditSize.setHeight( aFormatRect.GetHeight() ); + aPageSize.setWidth( aImageSize.Width() ); + if ( aMEditSize.Height() < aImageSize.Height() ) + { + nWinStyle |= WB_VCENTER; + aPageSize.setHeight( aImageSize.Height() ); + aMEditSize.setHeight( aImageSize.Height() ); + } + else + { + nWinStyle |= WB_TOP; + aPageSize.setHeight( aMEditSize.Height() ); + } + if ( aImageSize.Width() ) + aPageSize.AdjustWidth(IMPL_SEP_MSGBOX_IMAGE ); + aPageSize.AdjustWidth((IMPL_DIALOG_OFFSET*2)+(IMPL_MSGBOX_OFFSET_EXTRA_X*2) ); + aPageSize.AdjustWidth(aMEditSize.Width()+1 ); + aPageSize.AdjustHeight((IMPL_DIALOG_OFFSET*2)+(IMPL_MSGBOX_OFFSET_EXTRA_Y*2) ); + + if ( aPageSize.Width() < IMPL_MINSIZE_MSGBOX_WIDTH ) + aPageSize.setWidth( IMPL_MINSIZE_MSGBOX_WIDTH ); + if ( aPageSize.Width() < nTitleWidth ) + aPageSize.setWidth( nTitleWidth ); + + mpVCLMultiLineEdit = VclPtr<VclMultiLineEdit>::Create( this, nWinStyle ); + mpVCLMultiLineEdit->SetText( aMessText ); + mpVCLMultiLineEdit->SetPosSizePixel( aTextPos, aMEditSize ); + mpVCLMultiLineEdit->Show(); + mpVCLMultiLineEdit->SetPaintTransparent(true); + mpVCLMultiLineEdit->EnableCursor(false); + SetPageSizePixel( aPageSize ); +} + +void MessBox::StateChanged( StateChangedType nType ) +{ + if ( nType == StateChangedType::InitShow ) + { + ImplPosControls(); + } + ButtonDialog::StateChanged( nType ); +} + +Size MessBox::GetOptimalSize() const +{ + // FIXME: base me on the font size ? + return Size( 250, 100 ); +} + + +namespace { + +class Pause : public Idle +{ +public: + explicit Pause(sal_Int32 nPauseMilliseconds) : + Idle("pause"), + m_nPauseMilliseconds(nPauseMilliseconds) + { + SetPriority(TaskPriority::HIGHEST); + Start(); + } + + virtual void Invoke() override + { + SolarMutexGuard aSolarGuard; + std::this_thread::sleep_for(std::chrono::milliseconds(m_nPauseMilliseconds)); + Stop(); + delete this; + } + + sal_Int32 m_nPauseMilliseconds; +}; + +class VCLXToolkit : public cppu::BaseMutex, + public cppu::WeakComponentImplHelper< + css::awt::XToolkitExperimental, + css::awt::XToolkitRobot, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::datatransfer::clipboard::XClipboard > mxClipboard; + css::uno::Reference< css::datatransfer::clipboard::XClipboard > mxSelection; + + ::comphelper::OInterfaceContainerHelper3<css::awt::XTopWindowListener> m_aTopWindowListeners; + ::comphelper::OInterfaceContainerHelper3<css::awt::XKeyHandler> m_aKeyHandlers; + ::comphelper::OInterfaceContainerHelper3<css::awt::XFocusListener> m_aFocusListeners; + ::Link<VclSimpleEvent&,void> m_aEventListenerLink; + ::Link<VclWindowEvent&,bool> m_aKeyListenerLink; + bool m_bEventListener; + bool m_bKeyListener; + + DECL_LINK(eventListenerHandler, ::VclSimpleEvent&, void); + + DECL_LINK(keyListenerHandler, ::VclWindowEvent&, bool); + + void callTopWindowListeners( + ::VclSimpleEvent const * pEvent, + void (SAL_CALL css::awt::XTopWindowListener::* pFn)( + css::lang::EventObject const &)); + + bool callKeyHandlers(::VclSimpleEvent const * pEvent, bool bPressed); + + void callFocusListeners(::VclSimpleEvent const * pEvent, bool bGained); + +protected: + ::osl::Mutex& GetMutex() { return m_aMutex; } + + virtual void SAL_CALL disposing() override; + + static vcl::Window* ImplCreateWindow( rtl::Reference<VCLXWindow>* ppNewComp, const css::awt::WindowDescriptor& rDescriptor, vcl::Window* pParent, + WinBits nWinBits, MessBoxStyle nMessBoxStyle ); + css::uno::Reference< css::awt::XWindowPeer > ImplCreateWindow( const css::awt::WindowDescriptor& Descriptor, + MessBoxStyle nForceMessBoxStyle ); + +public: + + VCLXToolkit(); + + // css::awt::XToolkitExperimental + virtual void SAL_CALL processEventsToIdle() override; + + virtual sal_Int64 SAL_CALL getOpenGLBufferSwapCounter() override; + + virtual void SAL_CALL setDeterministicScheduling(sal_Bool bDeterministicMode) override; + + virtual void SAL_CALL pause(sal_Int32 nMilliseconds) override; + + virtual void SAL_CALL startRecording() override; + + virtual void SAL_CALL stopRecording() override; + + css::uno::Sequence< OUString > SAL_CALL getRecordingAndClear() override; + + virtual void SAL_CALL waitUntilAllIdlesDispatched() override; + + // css::awt::XToolkit + css::uno::Reference< css::awt::XWindowPeer > SAL_CALL getDesktopWindow( ) override; + css::awt::Rectangle SAL_CALL getWorkArea( ) override; + css::uno::Reference< css::awt::XWindowPeer > SAL_CALL createWindow( const css::awt::WindowDescriptor& Descriptor ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XWindowPeer > > SAL_CALL createWindows( const css::uno::Sequence< css::awt::WindowDescriptor >& Descriptors ) override; + css::uno::Reference< css::awt::XDevice > SAL_CALL createScreenCompatibleDevice( sal_Int32 Width, sal_Int32 Height ) override; + css::uno::Reference< css::awt::XRegion > SAL_CALL createRegion( ) override; + + // css::awt::XSystemChildFactory + css::uno::Reference< css::awt::XWindowPeer > SAL_CALL createSystemChild( const css::uno::Any& Parent, const css::uno::Sequence< sal_Int8 >& ProcessId, sal_Int16 SystemType ) override; + + // css::awt::XMessageBoxFactory + virtual css::uno::Reference< css::awt::XMessageBox > SAL_CALL createMessageBox( const css::uno::Reference< css::awt::XWindowPeer >& aParent, css::awt::MessageBoxType eType, ::sal_Int32 aButtons, const OUString& aTitle, const OUString& aMessage ) override; + + // css::awt::XDataTransfer + css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > SAL_CALL getDragGestureRecognizer( const css::uno::Reference< css::awt::XWindow >& window ) override; + css::uno::Reference< css::datatransfer::dnd::XDragSource > SAL_CALL getDragSource( const css::uno::Reference< css::awt::XWindow >& window ) override; + css::uno::Reference< css::datatransfer::dnd::XDropTarget > SAL_CALL getDropTarget( const css::uno::Reference< css::awt::XWindow >& window ) override; + css::uno::Reference< css::datatransfer::clipboard::XClipboard > SAL_CALL getClipboard( const OUString& clipboardName ) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // css::awt::XExtendedToolkit: + + virtual ::sal_Int32 SAL_CALL getTopWindowCount() override; + + virtual css::uno::Reference< css::awt::XTopWindow > + SAL_CALL getTopWindow(::sal_Int32 nIndex) override; + + virtual css::uno::Reference< css::awt::XTopWindow > + SAL_CALL getActiveTopWindow() override; + + virtual void SAL_CALL addTopWindowListener( + css::uno::Reference< + css::awt::XTopWindowListener > const & rListener) override; + + virtual void SAL_CALL removeTopWindowListener( + css::uno::Reference< + css::awt::XTopWindowListener > const & rListener) override; + + virtual void SAL_CALL addKeyHandler( + css::uno::Reference< + css::awt::XKeyHandler > const & rHandler) override; + + virtual void SAL_CALL removeKeyHandler( + css::uno::Reference< + css::awt::XKeyHandler > const & rHandler) override; + + virtual void SAL_CALL addFocusListener( + css::uno::Reference< + css::awt::XFocusListener > const & rListener) override; + + virtual void SAL_CALL removeFocusListener( + css::uno::Reference< + css::awt::XFocusListener > const & rListener) override; + + virtual void SAL_CALL fireFocusGained( + css::uno::Reference< + css::uno::XInterface > const & source) override; + + virtual void SAL_CALL fireFocusLost( + css::uno::Reference< + css::uno::XInterface > const & source) override; + + // css::awt::XReschedule: + virtual void SAL_CALL reschedule() override; + + // css::awt::XFontMappingUse: + virtual void SAL_CALL startTrackingFontMappingUse() override; + + virtual css::uno::Sequence<css::awt::XFontMappingUseItem> SAL_CALL finishTrackingFontMappingUse() override; + + // css:awt:XToolkitRobot + virtual void SAL_CALL keyPress( const css::awt::KeyEvent & aKeyEvent ) override; + + virtual void SAL_CALL keyRelease( const css::awt::KeyEvent & aKeyEvent ) override; + + virtual void SAL_CALL mousePress( const css::awt::MouseEvent & aMouseEvent ) override; + + virtual void SAL_CALL mouseRelease( const css::awt::MouseEvent & aMouseEvent ) override; + + virtual void SAL_CALL mouseMove( const css::awt::MouseEvent & aMouseEvent ) override; + +}; + +std::pair<WinBits,MessBoxStyle> ImplGetWinBits( sal_uInt32 nComponentAttribs, WindowType nCompType ) +{ + WinBits nWinBits = 0; + MessBoxStyle nStyle = MessBoxStyle::NONE; + + bool bMessBox = false; + if ( ( nCompType == WindowType::INFOBOX ) || + ( nCompType == WindowType::MESSBOX ) || + ( nCompType == WindowType::QUERYBOX ) || + ( nCompType == WindowType::WARNINGBOX ) || + ( nCompType == WindowType::ERRORBOX ) ) + { + bMessBox = true; + } + + bool bDecoratedWindow = false; + if ( bMessBox + || ( nCompType == WindowType::DIALOG ) + || ( nCompType == WindowType::MODELESSDIALOG ) + || ( nCompType == WindowType::DOCKINGWINDOW ) + || ( nCompType == WindowType::TABDIALOG ) + || ( nCompType == WindowType::BUTTONDIALOG ) + || ( nCompType == WindowType::SYSTEMCHILDWINDOW ) + ) + { + bDecoratedWindow = true; + } + + if( nComponentAttribs & css::awt::WindowAttribute::BORDER ) + nWinBits |= WB_BORDER; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::NOBORDER ) + nWinBits |= WB_NOBORDER; + if( nComponentAttribs & css::awt::WindowAttribute::SIZEABLE ) + nWinBits |= WB_SIZEABLE; + if( nComponentAttribs & css::awt::WindowAttribute::MOVEABLE ) + nWinBits |= WB_MOVEABLE; + if( nComponentAttribs & css::awt::WindowAttribute::CLOSEABLE ) + nWinBits |= WB_CLOSEABLE; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::HSCROLL ) + nWinBits |= WB_HSCROLL; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::VSCROLL ) + nWinBits |= WB_VSCROLL; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::LEFT ) + nWinBits |= WB_LEFT; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::CENTER ) + nWinBits |= WB_CENTER; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::RIGHT ) + nWinBits |= WB_RIGHT; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::SPIN ) + nWinBits |= WB_SPIN; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::SORT ) + nWinBits |= WB_SORT; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DROPDOWN ) + nWinBits |= WB_DROPDOWN; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEFBUTTON ) + nWinBits |= WB_DEFBUTTON; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::READONLY ) + nWinBits |= WB_READONLY; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::CLIPCHILDREN ) + nWinBits |= WB_CLIPCHILDREN; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::GROUP ) + nWinBits |= WB_GROUP; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::NOLABEL ) //added for issue79712 + nWinBits |= WB_NOLABEL; + + // These bits are not unique + if ( bMessBox ) + { + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::OK ) + nStyle |= MessBoxStyle::Ok; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::OK_CANCEL ) + nStyle |= MessBoxStyle::OkCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::YES_NO ) + nStyle |= MessBoxStyle::YesNo; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::YES_NO_CANCEL ) + nStyle |= MessBoxStyle::YesNoCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::RETRY_CANCEL ) + nStyle |= MessBoxStyle::RetryCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_OK ) + nStyle |= MessBoxStyle::DefaultOk; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_CANCEL ) + nStyle |= MessBoxStyle::DefaultCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_RETRY ) + nStyle |= MessBoxStyle::DefaultRetry; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_YES ) + nStyle |= MessBoxStyle::DefaultYes; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_NO ) + nStyle |= MessBoxStyle::DefaultNo; + } + if ( nCompType == WindowType::MULTILINEEDIT || nCompType == WindowType::DIALOG + || nCompType == WindowType::GROUPBOX || nCompType == WindowType::TABPAGE ) + { + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::AUTOHSCROLL ) + nWinBits |= WB_AUTOHSCROLL; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::AUTOVSCROLL ) + nWinBits |= WB_AUTOVSCROLL; + } + + + if ( bDecoratedWindow ) + { + if( nComponentAttribs & css::awt::WindowAttribute::NODECORATION ) + { + // No decoration removes several window attributes and must + // set WB_NOBORDER! + nWinBits &= ~WB_BORDER; + nWinBits &= ~WB_SIZEABLE; + nWinBits &= ~WB_MOVEABLE; + nWinBits &= ~WB_CLOSEABLE; + nWinBits |= WB_NOBORDER; + } + } + + return { nWinBits, nStyle }; +} + +struct ComponentInfo +{ + std::u16string_view sName; + WindowType nWinType; +}; + +ComponentInfo const aComponentInfos [] = +{ + { std::u16string_view(u"animatedimages"), WindowType::CONTROL }, + { std::u16string_view(u"buttondialog"), WindowType::BUTTONDIALOG }, + { std::u16string_view(u"cancelbutton"), WindowType::CANCELBUTTON }, + { std::u16string_view(u"checkbox"), WindowType::CHECKBOX }, + { std::u16string_view(u"combobox"), WindowType::COMBOBOX }, + { std::u16string_view(u"control"), WindowType::CONTROL }, + { std::u16string_view(u"currencybox"), WindowType::CURRENCYBOX }, + { std::u16string_view(u"currencyfield"), WindowType::CURRENCYFIELD }, + { std::u16string_view(u"datebox"), WindowType::DATEBOX }, + { std::u16string_view(u"datefield"), WindowType::CONTROL }, + { std::u16string_view(u"dialog"), WindowType::DIALOG }, + { std::u16string_view(u"dockingarea"), WindowType::DOCKINGAREA }, + { std::u16string_view(u"dockingwindow"), WindowType::DOCKINGWINDOW }, + { std::u16string_view(u"edit"), WindowType::EDIT }, + { std::u16string_view(u"errorbox"), WindowType::ERRORBOX }, + { std::u16string_view(u"filecontrol"), WindowType::CONTROL }, + { std::u16string_view(u"fixedbitmap"), WindowType::FIXEDBITMAP }, + { std::u16string_view(u"fixedhyperlink"), WindowType::CONTROL }, + { std::u16string_view(u"fixedimage"), WindowType::FIXEDIMAGE }, + { std::u16string_view(u"fixedline"), WindowType::FIXEDLINE }, + { std::u16string_view(u"fixedtext"), WindowType::FIXEDTEXT }, + { std::u16string_view(u"floatingwindow"), WindowType::FLOATINGWINDOW }, + { std::u16string_view(u"formattedfield"), WindowType::CONTROL }, + { std::u16string_view(u"frame"), WindowType::GROUPBOX }, + { std::u16string_view(u"framewindow"), WindowType::TOOLKIT_FRAMEWINDOW }, + { std::u16string_view(u"grid"), WindowType::CONTROL }, + { std::u16string_view(u"groupbox"), WindowType::GROUPBOX }, + { std::u16string_view(u"helpbutton"), WindowType::HELPBUTTON }, + { std::u16string_view(u"imagebutton"), WindowType::IMAGEBUTTON }, + { std::u16string_view(u"infobox"), WindowType::INFOBOX }, + { std::u16string_view(u"listbox"), WindowType::LISTBOX }, + { std::u16string_view(u"longcurrencybox"), WindowType::LONGCURRENCYBOX }, + { std::u16string_view(u"longcurrencyfield"), WindowType::CONTROL }, + { std::u16string_view(u"menubutton"), WindowType::MENUBUTTON }, + { std::u16string_view(u"messbox"), WindowType::MESSBOX }, + { std::u16string_view(u"metricbox"), WindowType::METRICBOX }, + { std::u16string_view(u"metricfield"), WindowType::METRICFIELD }, + { std::u16string_view(u"modelessdialog"), WindowType::MODELESSDIALOG }, + { std::u16string_view(u"morebutton"), WindowType::MOREBUTTON }, + { std::u16string_view(u"multilineedit"), WindowType::MULTILINEEDIT }, + { std::u16string_view(u"multilistbox"), WindowType::MULTILISTBOX }, + { std::u16string_view(u"numericbox"), WindowType::NUMERICBOX }, + { std::u16string_view(u"numericfield"), WindowType::CONTROL }, + { std::u16string_view(u"okbutton"), WindowType::OKBUTTON }, + { std::u16string_view(u"patternbox"), WindowType::PATTERNBOX }, + { std::u16string_view(u"patternfield"), WindowType::PATTERNFIELD }, + { std::u16string_view(u"progressbar"), WindowType::CONTROL }, + { std::u16string_view(u"pushbutton"), WindowType::PUSHBUTTON }, + { std::u16string_view(u"querybox"), WindowType::QUERYBOX }, + { std::u16string_view(u"radiobutton"), WindowType::RADIOBUTTON }, + { std::u16string_view(u"roadmap"), WindowType::CONTROL }, + { std::u16string_view(u"scrollbar"), WindowType::SCROLLBAR }, + { std::u16string_view(u"scrollbarbox"), WindowType::SCROLLBARBOX }, + { std::u16string_view(u"spinbutton"), WindowType::SPINBUTTON }, + { std::u16string_view(u"spinfield"), WindowType::SPINFIELD }, + { std::u16string_view(u"splitter"), WindowType::SPLITTER }, + { std::u16string_view(u"splitwindow"), WindowType::SPLITWINDOW }, + { std::u16string_view(u"statusbar"), WindowType::STATUSBAR }, + { std::u16string_view(u"systemchildwindow"), WindowType::TOOLKIT_SYSTEMCHILDWINDOW }, + { std::u16string_view(u"tabcontrol"), WindowType::TABCONTROL }, + { std::u16string_view(u"tabdialog"), WindowType::TABDIALOG }, + { std::u16string_view(u"tabpage"), WindowType::TABPAGE }, + { std::u16string_view(u"tabpagecontainer"), WindowType::CONTROL }, + { std::u16string_view(u"tabpagemodel"), WindowType::TABPAGE }, + { std::u16string_view(u"timebox"), WindowType::TIMEBOX }, + { std::u16string_view(u"timefield"), WindowType::TIMEFIELD }, + { std::u16string_view(u"toolbox"), WindowType::TOOLBOX }, + { std::u16string_view(u"tree"), WindowType::CONTROL }, + { std::u16string_view(u"tristatebox"), WindowType::TRISTATEBOX }, + { std::u16string_view(u"warningbox"), WindowType::WARNINGBOX }, + { std::u16string_view(u"window"), WindowType::WINDOW }, + { std::u16string_view(u"workwindow"), WindowType::WORKWINDOW } +}; + +bool ComponentInfoFindCompare( const ComponentInfo & lhs, const OUString & s) +{ + return rtl_ustr_compareIgnoreAsciiCase_WithLength(s.pData->buffer, s.pData->length, + lhs.sName.data(), lhs.sName.size()) > 0; +} + +WindowType ImplGetComponentType( const OUString& rServiceName ) +{ + static bool bSorted = false; + if( !bSorted ) + { + assert( std::is_sorted( std::begin(aComponentInfos), std::end(aComponentInfos), + [](const ComponentInfo & lhs, const ComponentInfo & rhs) { + return + rtl_ustr_compare_WithLength( + lhs.sName.data(), lhs.sName.size(), rhs.sName.data(), + rhs.sName.size()) + < 0; + } ) ); + bSorted = true; + } + + OUString sSearch; + if ( !rServiceName.isEmpty() ) + sSearch = rServiceName; + else + sSearch = "window"; + + auto it = std::lower_bound( std::begin(aComponentInfos), std::end(aComponentInfos), sSearch, + ComponentInfoFindCompare ); + if (it != std::end(aComponentInfos) && + rtl_ustr_compareIgnoreAsciiCase_WithLength(sSearch.pData->buffer, sSearch.pData->length, it->sName.data(), it->sName.size()) == 0) + return it->nWinType; + return WindowType::NONE; +} + +struct MessageBoxTypeInfo +{ + css::awt::MessageBoxType eType; + const char *pName; + sal_Int32 nLen; +}; + +const MessageBoxTypeInfo aMessageBoxTypeInfo[] = +{ + { css::awt::MessageBoxType_MESSAGEBOX, RTL_CONSTASCII_STRINGPARAM("messbox") }, + { css::awt::MessageBoxType_INFOBOX, RTL_CONSTASCII_STRINGPARAM("infobox") }, + { css::awt::MessageBoxType_WARNINGBOX, RTL_CONSTASCII_STRINGPARAM("warningbox") }, + { css::awt::MessageBoxType_ERRORBOX, RTL_CONSTASCII_STRINGPARAM("errorbox") }, + { css::awt::MessageBoxType_QUERYBOX, RTL_CONSTASCII_STRINGPARAM("querybox") }, + { css::awt::MessageBoxType::MessageBoxType_MAKE_FIXED_SIZE, nullptr, 0 } +}; + +bool lcl_convertMessageBoxType( + OUString &sType, + css::awt::MessageBoxType eType ) +{ + const MessageBoxTypeInfo *pMap = aMessageBoxTypeInfo; + css::awt::MessageBoxType eVal = css::awt::MessageBoxType::MessageBoxType_MAKE_FIXED_SIZE; + + while ( pMap->pName ) + { + if ( pMap->eType == eType ) + { + eVal = eType; + sType = OUString( pMap->pName, pMap->nLen, RTL_TEXTENCODING_ASCII_US ); + break; + } + pMap++; + } + + return ( eVal != css::awt::MessageBoxType::MessageBoxType_MAKE_FIXED_SIZE ); +} + +#ifndef IOS + +sal_Int32 nVCLToolkitInstanceCount = 0; +bool bInitedByVCLToolkit = false; + +osl::Mutex & getInitMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} + +osl::Condition & getInitCondition() +{ + static osl::Condition aCondition; + return aCondition; +} + +extern "C" +{ +static void ToolkitWorkerFunction( void* pArgs ) +{ + osl_setThreadName("VCLXToolkit VCL main thread"); + + css::uno::Reference<css::lang::XMultiServiceFactory> xServiceManager; + try + { + xServiceManager = ::comphelper::getProcessServiceFactory(); + } + catch (const css::uno::DeploymentException&) + { + } + if (!xServiceManager.is()) + { + css::uno::Reference<css::uno::XComponentContext> xContext = + ::cppu::defaultBootstrap_InitialComponentContext(); + + xServiceManager.set( xContext->getServiceManager(), css::uno::UNO_QUERY_THROW ); + // set global process service factory used by unotools config helpers + ::comphelper::setProcessServiceFactory( xServiceManager ); + } + + VCLXToolkit * pTk = static_cast<VCLXToolkit *>(pArgs); + bInitedByVCLToolkit = !IsVCLInit() && InitVCL(); + if( bInitedByVCLToolkit ) + { + UnoWrapper* pUnoWrapper = new UnoWrapper( pTk ); + UnoWrapperBase::SetUnoWrapper( pUnoWrapper ); + } + getInitCondition().set(); + if( bInitedByVCLToolkit ) + { + { + SolarMutexGuard aGuard; + Application::Execute(); + } + try + { + pTk->dispose(); + } + catch( css::uno::Exception & ) + { + } + DeInitVCL(); + } + else + { + // having the thread join itself is pretty stupid. + // but we can't get the osl_Thread to destroy here so just leak it. + } +} +} + +#endif + +// constructor, which might initialize VCL +VCLXToolkit::VCLXToolkit(): + cppu::WeakComponentImplHelper< + css::awt::XToolkitExperimental, + css::awt::XToolkitRobot, + css::lang::XServiceInfo>( GetMutex() ), + m_aTopWindowListeners(rBHelper.rMutex), + m_aKeyHandlers(rBHelper.rMutex), + m_aFocusListeners(rBHelper.rMutex), + m_aEventListenerLink(LINK(this, VCLXToolkit, eventListenerHandler)), + m_aKeyListenerLink(LINK(this, VCLXToolkit, keyListenerHandler)), + m_bEventListener(false), + m_bKeyListener(false) +{ +#ifndef IOS + osl::Guard< osl::Mutex > aGuard( getInitMutex() ); + nVCLToolkitInstanceCount++; + if( ( nVCLToolkitInstanceCount == 1 ) && ( !Application::IsInMain() ) ) + { + // setup execute thread + CreateMainLoopThread( ToolkitWorkerFunction, this ); + getInitCondition().wait(); + } +#endif +} + +void SAL_CALL VCLXToolkit::disposing() +{ +#ifndef IOS + { + osl::Guard< osl::Mutex > aGuard( getInitMutex() ); + if( --nVCLToolkitInstanceCount == 0 ) + { + if( bInitedByVCLToolkit ) + { + Application::Quit(); + JoinMainLoopThread(); + bInitedByVCLToolkit = false; + } + } + } +#endif + if (m_bEventListener) + { + ::Application::RemoveEventListener(m_aEventListenerLink); + m_bEventListener = false; + } + if (m_bKeyListener) + { + ::Application::RemoveKeyListener(m_aKeyListenerLink); + m_bKeyListener = false; + } + css::lang::EventObject aEvent( + getXWeak()); + m_aTopWindowListeners.disposeAndClear(aEvent); + m_aKeyHandlers.disposeAndClear(aEvent); + m_aFocusListeners.disposeAndClear(aEvent); +} + + +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::getDesktopWindow( ) +{ + css::uno::Reference< css::awt::XWindowPeer > xRef; + // 07/00: AppWindow doesn't exist anymore... + return xRef; +} + +css::awt::Rectangle VCLXToolkit::getWorkArea( ) +{ + sal_Int32 nDisplay = Application::GetDisplayBuiltInScreen(); + AbsoluteScreenPixelRectangle aWorkRect = Application::GetScreenPosSizePixel( nDisplay ); + css::awt::Rectangle aNotherRect; + aNotherRect.X = aWorkRect.Left(); + aNotherRect.Y = aWorkRect.Top(); + aNotherRect.Width = aWorkRect.getOpenWidth(); + aNotherRect.Height = aWorkRect.getOpenHeight(); + return aNotherRect; +} + +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::createWindow( const css::awt::WindowDescriptor& rDescriptor ) +{ + return ImplCreateWindow( rDescriptor, MessBoxStyle::NONE ); +} + +css::uno::Reference< css::awt::XDevice > VCLXToolkit::createScreenCompatibleDevice( sal_Int32 Width, sal_Int32 Height ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + rtl::Reference<VCLXVirtualDevice> pVDev = new VCLXVirtualDevice; + + SolarMutexGuard aSolarGuard; + + VclPtrInstance<VirtualDevice> pV; + pV->SetOutputSizePixel( Size( Width, Height ) ); + pVDev->SetVirtualDevice( pV ); + + return pVDev; +} + +css::uno::Reference< css::awt::XRegion > VCLXToolkit::createRegion( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Reference< css::awt::XRegion > xRef = new VCLXRegion; + return xRef; +} + +class InfoBox : public MessBox +{ +public: + InfoBox(vcl::Window* pParent, const OUString& rMessage) + : MessBox(pParent, MessBoxStyle::Ok | MessBoxStyle::DefaultOk, 0, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardInfoBoxText()); + SetImage(GetStandardInfoBoxImage()); + } +}; + +class ErrorBox : public MessBox +{ +public: + ErrorBox(vcl::Window* pParent, MessBoxStyle nStyle, WinBits nWinBits, const OUString& rMessage) + : MessBox(pParent, nStyle, nWinBits, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardErrorBoxText()); + SetImage(GetStandardErrorBoxImage()); + } +}; + +class QueryBox : public MessBox +{ +public: + QueryBox(vcl::Window* pParent, MessBoxStyle nStyle, WinBits nWinBits, const OUString& rMessage) + : MessBox(pParent, nStyle, nWinBits, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardQueryBoxText()); + SetImage(GetStandardQueryBoxImage()); + } +}; + +class WarningBox : public MessBox +{ +public: + WarningBox(vcl::Window* pParent, MessBoxStyle nStyle, WinBits nWinBits, const OUString& rMessage) + : MessBox(pParent, nStyle, nWinBits, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardWarningBoxText()); + SetImage(GetStandardWarningBoxImage()); + } +}; + + +struct RMItemData +{ + bool b_Enabled; + sal_Int32 n_ID; + OUString Label; +}; + +typedef ::cppu::ImplInheritanceHelper < VCLXGraphicControl + , css::container::XContainerListener + , css::beans::XPropertyChangeListener + , css::awt::XItemEventBroadcaster + > SVTXRoadmap_Base; +class SVTXRoadmap final : public SVTXRoadmap_Base +{ +public: + SVTXRoadmap(); + + void SAL_CALL disposing( const css::lang::EventObject& Source ) override { VCLXWindow::disposing( Source ); } + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // XContainerListener + void SAL_CALL elementInserted( const css::container::ContainerEvent& rEvent ) override; + void SAL_CALL elementRemoved( const css::container::ContainerEvent& rEvent ) override; + void SAL_CALL elementReplaced( const css::container::ContainerEvent& rEvent ) override; + + // XItemEventBroadcaster + virtual void SAL_CALL addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override; + virtual void SAL_CALL removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + +private: + + // VCLXGraphicControl overridables + virtual void ImplSetNewImage() override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } + + static RMItemData GetRMItemData( const css::container::ContainerEvent& _rEvent ); + + virtual void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + + virtual ~SVTXRoadmap() override; + + ItemListenerMultiplexer maItemListeners; +}; + + + + +SVTXRoadmap::SVTXRoadmap() : maItemListeners( *this ) +{ +} + +SVTXRoadmap::~SVTXRoadmap() +{ +} + +void SVTXRoadmap::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::RoadmapItemSelected: + { + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_Int16 CurItemID = pField->GetCurrentRoadmapItemID(); + css::awt::ItemEvent aEvent; + aEvent.Selected = CurItemID; + aEvent.Highlighted = CurItemID; + aEvent.ItemId = CurItemID; + maItemListeners.itemStateChanged( aEvent ); + } + } + break; + default: + SVTXRoadmap_Base::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +void SVTXRoadmap::propertyChange( const css::beans::PropertyChangeEvent& evt ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( !pField ) + return; + + css::uno::Reference< css::uno::XInterface > xRoadmapItem = evt.Source; + sal_Int32 nID = 0; + css::uno::Reference< css::beans::XPropertySet > xPropertySet( xRoadmapItem, css::uno::UNO_QUERY ); + css::uno::Any aValue = xPropertySet->getPropertyValue("ID"); + aValue >>= nID; + + OUString sPropertyName = evt.PropertyName; + if ( sPropertyName == "Enabled" ) + { + bool bEnable = false; + evt.NewValue >>= bEnable; + pField->EnableRoadmapItem( static_cast<vcl::RoadmapTypes::ItemId>(nID) , bEnable ); + } + else if ( sPropertyName == "Label" ) + { + OUString sLabel; + evt.NewValue >>= sLabel; + pField->ChangeRoadmapItemLabel( static_cast<vcl::RoadmapTypes::ItemId>(nID) , sLabel ); + } + else if ( sPropertyName == "ID" ) + { + sal_Int32 nNewID = 0; + evt.NewValue >>= nNewID; + evt.OldValue >>= nID; + pField->ChangeRoadmapItemID( static_cast<vcl::RoadmapTypes::ItemId>(nID), static_cast<vcl::RoadmapTypes::ItemId>(nNewID) ); + } +// else + // TODO handle Interactive appropriately +} + +void SVTXRoadmap::addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) +{ + maItemListeners.addInterface( l ); +} + +void SVTXRoadmap::removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) +{ + maItemListeners.removeInterface( l ); +} + +RMItemData SVTXRoadmap::GetRMItemData( const css::container::ContainerEvent& _rEvent ) +{ + RMItemData aCurRMItemData; + css::uno::Reference< css::uno::XInterface > xRoadmapItem; + _rEvent.Element >>= xRoadmapItem; + css::uno::Reference< css::beans::XPropertySet > xPropertySet( xRoadmapItem, css::uno::UNO_QUERY ); + if ( xPropertySet.is() ) + { + css::uno::Any aValue = xPropertySet->getPropertyValue("Label"); + aValue >>= aCurRMItemData.Label; + aValue = xPropertySet->getPropertyValue("ID"); + aValue >>= aCurRMItemData.n_ID; + aValue = xPropertySet->getPropertyValue("Enabled"); + aValue >>= aCurRMItemData.b_Enabled; + } + else + { + aCurRMItemData.b_Enabled = false; + aCurRMItemData.n_ID = 0; + } + return aCurRMItemData; +} + +void SVTXRoadmap::elementInserted( const css::container::ContainerEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + RMItemData CurItemData = GetRMItemData( _rEvent ); + sal_Int32 InsertIndex = 0; + _rEvent.Accessor >>= InsertIndex; + pField->InsertRoadmapItem( InsertIndex, CurItemData.Label, static_cast<vcl::RoadmapTypes::ItemId>(CurItemData.n_ID), CurItemData.b_Enabled ); + } +} + +void SVTXRoadmap::elementRemoved( const css::container::ContainerEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_Int32 DelIndex = 0; + _rEvent.Accessor >>= DelIndex; + pField->DeleteRoadmapItem(DelIndex); + } +} + +void SVTXRoadmap::elementReplaced( const css::container::ContainerEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + RMItemData CurItemData = GetRMItemData( _rEvent ); + sal_Int32 ReplaceIndex = 0; + _rEvent.Accessor >>= ReplaceIndex; + pField->ReplaceRoadmapItem( ReplaceIndex, CurItemData.Label, static_cast<vcl::RoadmapTypes::ItemId>(CurItemData.n_ID), CurItemData.b_Enabled ); + } +} + +void SVTXRoadmap::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_COMPLETE: + { + bool b = false; + Value >>= b; + pField->SetRoadmapComplete( b); + } + break; + + case BASEPROPERTY_ACTIVATED: + { + bool b = false; + Value >>= b; + pField->SetRoadmapInteractive( b); + } + break; + + case BASEPROPERTY_CURRENTITEMID: + { + sal_Int32 nId = 0; + Value >>= nId; + pField->SelectRoadmapItemByID( static_cast<vcl::RoadmapTypes::ItemId>(nId) ); + } + break; + + case BASEPROPERTY_TEXT: + { + OUString aStr; + Value >>= aStr; + pField->SetText( aStr ); + pField->Invalidate(); + } + break; + + default: + SVTXRoadmap_Base::setProperty( PropertyName, Value ); + break; + } + + } + else + SVTXRoadmap_Base::setProperty( PropertyName, Value ); +} + + +css::uno::Any SVTXRoadmap::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aReturn; + + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_COMPLETE: + aReturn <<= pField->IsRoadmapComplete(); + break; + case BASEPROPERTY_ACTIVATED: + aReturn <<= pField->IsRoadmapInteractive(); + break; + case BASEPROPERTY_CURRENTITEMID: + aReturn <<= pField->GetCurrentRoadmapItemID(); + break; + default: + aReturn = SVTXRoadmap_Base::getProperty(PropertyName); + break; + } + } + return aReturn; +} + +void SVTXRoadmap::ImplSetNewImage() +{ + OSL_PRECOND( GetWindow(), "SVTXRoadmap::ImplSetNewImage: window is required to be not-NULL!" ); + VclPtr< ::vcl::ORoadmap > pButton = GetAs< ::vcl::ORoadmap >(); + pButton->SetRoadmapBitmap( GetImage().GetBitmapEx() ); +} + +void SVTXRoadmap::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_COMPLETE, + BASEPROPERTY_ACTIVATED, + BASEPROPERTY_CURRENTITEMID, + BASEPROPERTY_TEXT, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +vcl::Window* VCLXToolkit::ImplCreateWindow( rtl::Reference<VCLXWindow>* ppNewComp, + const css::awt::WindowDescriptor& rDescriptor, + vcl::Window* pParent, WinBits nWinBits, MessBoxStyle nMessBoxStyle ) +{ + OUString aServiceName = rDescriptor.WindowServiceName.toAsciiLowerCase(); + + VclPtr<vcl::Window> pNewWindow; + WindowType nType = ImplGetComponentType( aServiceName ); + bool bFrameControl = false; + if ( aServiceName == "frame" ) + bFrameControl = true; + if ( aServiceName == "tabcontrolnotabs" ) + { + nWinBits |= WB_NOBORDER; + nType = ImplGetComponentType( "tabcontrol" ); + } + if ( !pParent ) + { + // If the component needs a parent, then return NULL, + // some time later css::uno::Exception... + bool bException = true; + if ( ( nType == WindowType::DIALOG ) + || ( nType == WindowType::MODELESSDIALOG ) + || ( nType == WindowType::MESSBOX ) + || ( nType == WindowType::INFOBOX ) + || ( nType == WindowType::WARNINGBOX ) + || ( nType == WindowType::ERRORBOX ) + || ( nType == WindowType::QUERYBOX ) + ) + bException = false; + else if ( ( nType == WindowType::WINDOW ) || + ( nType == WindowType::WORKWINDOW ) || + ( nType == WindowType::TOOLKIT_FRAMEWINDOW ) ) + { + if ( rDescriptor.Type == css::awt::WindowClass_TOP ) + bException = false; + } + + if ( bException ) + { + *ppNewComp = nullptr; + return nullptr; + } + } + + if ( nType != WindowType::NONE ) + { + SolarMutexGuard aVclGuard; + switch ( nType ) + { + case WindowType::CANCELBUTTON: + pNewWindow = VclPtr<CancelButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::CHECKBOX: + pNewWindow = VclPtr<CheckBox>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXCheckBox; + break; + case WindowType::COMBOBOX: + pNewWindow = VclPtr<ComboBox>::Create( pParent, nWinBits|WB_AUTOHSCROLL ); + static_cast<ComboBox*>(pNewWindow.get())->EnableAutoSize( false ); + *ppNewComp = new VCLXComboBox; + break; + case WindowType::CURRENCYBOX: + pNewWindow = VclPtr<CurrencyBox>::Create( pParent, nWinBits ); + break; + case WindowType::CURRENCYFIELD: + pNewWindow = VclPtr<CurrencyField>::Create( pParent, nWinBits ); + static_cast<CurrencyField*>(pNewWindow.get())->EnableEmptyFieldValue( true ); + *ppNewComp = new VCLXNumericField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<CurrencyField*>(pNewWindow.get())) ); + break; + case WindowType::DATEBOX: + pNewWindow = VclPtr<DateBox>::Create( pParent, nWinBits ); + break; + case WindowType::DOCKINGAREA: + pNewWindow = VclPtr<DockingAreaWindow>::Create( pParent ); + break; + case WindowType::MULTILINEEDIT: + pNewWindow = VclPtr<MultiLineEdit>::Create(pParent, nWinBits|WB_IGNORETAB); + static_cast<MultiLineEdit*>(pNewWindow.get())->DisableSelectionOnFocus(); + *ppNewComp = new VCLXMultiLineEdit; + break; + case WindowType::EDIT: + pNewWindow = VclPtr<Edit>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXEdit; + break; + case WindowType::ERRORBOX: + pNewWindow = VclPtr<ErrorBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::FIXEDBITMAP: + pNewWindow = VclPtr<FixedBitmap>::Create( pParent, nWinBits ); + break; + case WindowType::FIXEDIMAGE: + pNewWindow = VclPtr<ImageControl>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXImageControl; + break; + case WindowType::FIXEDLINE: + pNewWindow = VclPtr<FixedLine>::Create( pParent, nWinBits ); + break; + case WindowType::FIXEDTEXT: + pNewWindow = VclPtr<FixedText>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXFixedText; + break; + case WindowType::FLOATINGWINDOW: + pNewWindow = VclPtr<FloatingWindow>::Create( pParent, nWinBits ); + break; + case WindowType::GROUPBOX: + pNewWindow = VclPtr<GroupBox>::Create( pParent, nWinBits ); + if ( bFrameControl ) + { + GroupBox* pGroupBox = static_cast< GroupBox* >( pNewWindow.get() ); + *ppNewComp = new VCLXFrame; + // Frame control needs to receive + // Mouse events + pGroupBox->SetMouseTransparent( false ); + } + break; + case WindowType::HELPBUTTON: + pNewWindow = VclPtr<HelpButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::IMAGEBUTTON: + pNewWindow = VclPtr<ImageButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::INFOBOX: + pNewWindow = VclPtr<InfoBox>::Create( pParent, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::LISTBOX: + pNewWindow = VclPtr<ListBox>::Create( pParent, nWinBits|WB_SIMPLEMODE|WB_AUTOHSCROLL ); + static_cast<ListBox*>(pNewWindow.get())->EnableAutoSize( false ); + *ppNewComp = new VCLXListBox; + break; + case WindowType::LONGCURRENCYBOX: + pNewWindow = VclPtr<LongCurrencyBox>::Create( pParent, nWinBits ); + break; + case WindowType::MENUBUTTON: + pNewWindow = VclPtr<MenuButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::MESSBOX: + pNewWindow = VclPtr<MessBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString(), OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::METRICBOX: + pNewWindow = VclPtr<MetricBox>::Create( pParent, nWinBits ); + break; + case WindowType::METRICFIELD: + pNewWindow = VclPtr<MetricField>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXMetricField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<MetricField*>(pNewWindow.get())) ); + break; + case WindowType::DIALOG: + case WindowType::MODELESSDIALOG: + { + // Modal/Modeless only via Show/Execute + if ( (pParent == nullptr ) && ( rDescriptor.ParentIndex == -1 ) ) + pNewWindow = VclPtr<toolkit::ScrollableDialog>::Create( nullptr, nWinBits, Dialog::InitFlag::NoParent ); + else + pNewWindow = VclPtr<toolkit::ScrollableDialog>::Create( pParent, nWinBits ); + // #i70217# Don't always create a new component object. It's possible that VCL has called + // GetComponentInterface( sal_True ) in the Dialog ctor itself (see Window::IsTopWindow() ) + // which creates a component object. + css::uno::Reference< css::awt::XWindowPeer > xWinPeer = pNewWindow->GetComponentInterface( false ); + if ( xWinPeer.is() ) + *ppNewComp = dynamic_cast< VCLXDialog* >( xWinPeer.get() ); + else + *ppNewComp = new VCLXDialog; + } + break; + case WindowType::MOREBUTTON: + pNewWindow = VclPtr<MoreButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::MULTILISTBOX: + pNewWindow = VclPtr<MultiListBox>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXListBox; + break; + case WindowType::NUMERICBOX: + pNewWindow = VclPtr<NumericBox>::Create( pParent, nWinBits ); + break; + case WindowType::OKBUTTON: + pNewWindow = VclPtr<OKButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::PATTERNBOX: + pNewWindow = VclPtr<PatternBox>::Create( pParent, nWinBits ); + break; + case WindowType::PATTERNFIELD: + pNewWindow = VclPtr<PatternField>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXPatternField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<PatternField*>(pNewWindow.get())) ); + break; + case WindowType::PUSHBUTTON: + pNewWindow = VclPtr<PushButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::QUERYBOX: + pNewWindow = VclPtr<QueryBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::RADIOBUTTON: + pNewWindow = VclPtr<RadioButton>::Create(pParent, false, nWinBits); + *ppNewComp = new VCLXRadioButton; + + // by default, disable RadioCheck + // Since the VCLXRadioButton really cares for its RadioCheck settings, this is important: + // if we enable it, the VCLXRadioButton will use RadioButton::Check instead of RadioButton::SetState + // This leads to a strange behaviour if the control is newly created: when settings the initial + // state to "checked", the RadioButton::Check (called because RadioCheck=sal_True) will uncheck + // _all_other_ radio buttons in the same group. However, at this moment the grouping of the controls + // is not really valid: the controls are grouped after they have been created, but we're still in + // the creation process, so the RadioButton::Check relies on invalid grouping information. + // 07.08.2001 - #87254# - frank.schoenheit@sun.com + static_cast<RadioButton*>(pNewWindow.get())->EnableRadioCheck( false ); + break; + case WindowType::SCROLLBAR: + pNewWindow = VclPtr<ScrollBar>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXScrollBar; + break; + case WindowType::SCROLLBARBOX: + pNewWindow = VclPtr<ScrollBarBox>::Create( pParent, nWinBits ); + break; + case WindowType::SPINBUTTON: + pNewWindow = VclPtr<SpinButton>::Create( pParent, nWinBits ); + *ppNewComp = new ::toolkit::VCLXSpinButton; + break; + case WindowType::SPINFIELD: + pNewWindow = VclPtr<SpinField>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXNumericField; + break; + case WindowType::SPLITTER: + pNewWindow = VclPtr<Splitter>::Create( pParent, nWinBits ); + break; + case WindowType::SPLITWINDOW: + pNewWindow = VclPtr<SplitWindow>::Create( pParent, nWinBits ); + break; + case WindowType::STATUSBAR: + pNewWindow = VclPtr<StatusBar>::Create( pParent, nWinBits ); + break; + case WindowType::TOOLKIT_SYSTEMCHILDWINDOW: + pNewWindow = VclPtr<SystemChildWindow>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXSystemDependentWindow(); + break; + case WindowType::TABCONTROL: + pNewWindow = VclPtr<TabControl>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXMultiPage; + break; + case WindowType::TABDIALOG: + pNewWindow = VclPtr<TabDialog>::Create( pParent, nWinBits ); + break; + case WindowType::TABPAGE: + { + pNewWindow = VclPtr<TabPage>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXTabPage; + } + break; + case WindowType::TIMEBOX: + pNewWindow = VclPtr<TimeBox>::Create( pParent, nWinBits ); + break; + case WindowType::TIMEFIELD: + pNewWindow = VclPtr<TimeField>::Create( pParent, nWinBits ); + static_cast<TimeField*>(pNewWindow.get())->EnableEmptyFieldValue( true ); + *ppNewComp = new VCLXTimeField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<TimeField*>(pNewWindow.get())) ); + break; + case WindowType::TOOLBOX: + pNewWindow = VclPtr<ToolBox>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXToolBox; + break; + case WindowType::TRISTATEBOX: + pNewWindow = VclPtr<CheckBox>::Create( pParent, nWinBits ); + static_cast<CheckBox*>(pNewWindow.get())->EnableTriState(true); + break; + case WindowType::WARNINGBOX: + pNewWindow = VclPtr<WarningBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::WORKWINDOW: + case WindowType::WINDOW: + case WindowType::TOOLKIT_FRAMEWINDOW: + case WindowType::DOCKINGWINDOW: + if ( rDescriptor.Type == css::awt::WindowClass_TOP ) + { + if (nType == WindowType::DOCKINGWINDOW ) + pNewWindow = VclPtr<DockingWindow>::Create( pParent, nWinBits ); + else + { + if ((pParent == nullptr) && rDescriptor.Parent.is()) + { + // try to get a system dependent window handle + css::uno::Reference< css::awt::XSystemDependentWindowPeer > xSystemDepParent(rDescriptor.Parent, css::uno::UNO_QUERY); + + if (xSystemDepParent.is()) + { + sal_Int8 processID[16]; + + rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(processID) ); + + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + css::uno::Sequence<sal_Int8> processIdSeq(processID, 16); + + css::uno::Any anyHandle = xSystemDepParent->getWindowHandle(processIdSeq, SYSTEM_DEPENDENT_TYPE); + + // use sal_Int64 here to accommodate all int types + // uno::Any shift operator will upcast if necessary + sal_Int64 nWindowHandle = 0; + bool bXEmbed = false; + + bool bUseParentData = true; + if( ! (anyHandle >>= nWindowHandle) ) + { + css::uno::Sequence< css::beans::NamedValue > aProps; + if( anyHandle >>= aProps ) + { + for( const css::beans::NamedValue& rProp : std::as_const(aProps) ) + { + if ( rProp.Name == "WINDOW" ) + rProp.Value >>= nWindowHandle; + else if ( rProp.Name == "XEMBED" ) + rProp.Value >>= bXEmbed; + } + } + else + bUseParentData = false; + } + + if( bUseParentData ) + { + SystemParentData aParentData; + aParentData.nSize = sizeof( aParentData ); + #if defined MACOSX + aParentData.pView = reinterpret_cast<NSView*>(nWindowHandle); + #elif defined ANDROID + // Nothing + #elif defined IOS + // Nothing + #elif defined UNX + aParentData.aWindow = nWindowHandle; + aParentData.bXEmbedSupport = bXEmbed; + #elif defined _WIN32 + aParentData.hWnd = reinterpret_cast<HWND>(nWindowHandle); + #endif + pNewWindow = VclPtr<WorkWindow>::Create( &aParentData ); + } + } + } + + if (!pNewWindow) + pNewWindow = VclPtr<WorkWindow>::Create( pParent, nWinBits ); + } + + *ppNewComp = new VCLXTopWindow(); + } + else if ( rDescriptor.Type == css::awt::WindowClass_CONTAINER ) + { + if (nType == WindowType::DOCKINGWINDOW ) + pNewWindow = VclPtr<DockingWindow>::Create( pParent, nWinBits ); + else + pNewWindow = VclPtr<vcl::Window>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXContainer; + } + else + { + if (nType == WindowType::DOCKINGWINDOW ) + pNewWindow = VclPtr<DockingWindow>::Create( pParent, nWinBits ); + else + pNewWindow = VclPtr<vcl::Window>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXWindow; + } + break; + case WindowType::CONTROL: + if ( aServiceName == "tabpagecontainer" ) + { + // TabControl has a special case for tabs without border: they are displayed + // in a different way, so we need to ensure that this style is not set, so + // we can guarantee normal tab behavior + pNewWindow = VclPtr<TabControl>::Create( pParent, nWinBits & (~WB_NOBORDER)); + *ppNewComp = new VCLXTabPageContainer; + } + else if ( aServiceName == "animatedimages" ) + { + pNewWindow = VclPtr<Throbber>::Create( pParent, nWinBits ); + *ppNewComp = new ::toolkit::AnimatedImagesPeer; + } + else if (aServiceName == "roadmap") + { + pNewWindow = VclPtr<::vcl::ORoadmap>::Create( pParent, WB_TABSTOP ); + *ppNewComp = new SVTXRoadmap; + } + else if (aServiceName == "fixedhyperlink") + { + pNewWindow = VclPtr<FixedHyperlink>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXFixedHyperlink; + } + else if (aServiceName == "progressbar") + { + pNewWindow = VclPtr<ProgressBar>::Create( pParent, nWinBits, ProgressBar::BarStyle::Progress ); + *ppNewComp = new VCLXProgressBar; + } + else if (aServiceName == "filecontrol") + { + pNewWindow = VclPtr<FileControl>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXFileControl; + } + else if (aServiceName == "tree") + { + rtl::Reference<TreeControlPeer> pPeer = new TreeControlPeer; + *ppNewComp = pPeer; + pNewWindow = pPeer->createVclControl( pParent, nWinBits ); + } + else if (aServiceName == "formattedfield") + { + pNewWindow = VclPtr<FormattedField>::Create( pParent, nWinBits ); + *ppNewComp = new SVTXFormattedField; + } + else if (aServiceName == "numericfield") + { + pNewWindow = VclPtr<DoubleNumericField>::Create( pParent, nWinBits ); + *ppNewComp = new SVTXNumericField; + } + else if (aServiceName == "longcurrencyfield") + { + pNewWindow = VclPtr<DoubleCurrencyField>::Create( pParent, nWinBits ); + *ppNewComp = new SVTXCurrencyField; + } + else if (aServiceName == "datefield") + { + pNewWindow = VclPtr<CalendarField>::Create(pParent, nWinBits); + static_cast<CalendarField*>(pNewWindow.get())->EnableToday(); + static_cast<CalendarField*>(pNewWindow.get())->EnableNone(); + static_cast<CalendarField*>(pNewWindow.get())->EnableEmptyFieldValue( true ); + rtl::Reference<SVTXDateField> newComp = new SVTXDateField; + *ppNewComp = newComp; + newComp->SetFormatter( static_cast<FormatterBase*>(static_cast<DateField*>(pNewWindow.get())) ); + } + else if (aServiceName == "grid") + { + pNewWindow = VclPtr<::svt::table::TableControl>::Create(pParent, nWinBits); + *ppNewComp = new SVTXGridControl; + } + break; + default: + OSL_ENSURE( false, "VCLXToolkit::ImplCreateWindow: unknown window type!" ); + break; + } + } + + // tdf#126717 default that formcontrols show accelerators + if (Control* pControl = dynamic_cast<Control*>(pNewWindow.get())) + pControl->SetShowAccelerator(true); + return pNewWindow; +} + +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::ImplCreateWindow( + const css::awt::WindowDescriptor& rDescriptor, + MessBoxStyle nForceMessBoxStyle ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + SolarMutexGuard aSolarGuard; + + css::uno::Reference< css::awt::XVclWindowPeer > xRef; + + VclPtr<vcl::Window> pParent; + if ( rDescriptor.Parent.is() ) + { + VCLXWindow* pParentComponent = dynamic_cast<VCLXWindow*>( rDescriptor.Parent.get() ); + + // #103939# Don't throw assertion, may be it's a system dependent window, used in ImplCreateWindow. + // DBG_ASSERT( pParentComponent, "ParentComponent not valid" ); + + if ( pParentComponent ) + pParent = pParentComponent->GetWindow(); + } + std::pair<WinBits, MessBoxStyle> aPair = ImplGetWinBits( rDescriptor.WindowAttributes, + ImplGetComponentType( rDescriptor.WindowServiceName ) ); + WinBits nWinBits = aPair.first; + aPair.second |= nForceMessBoxStyle; + + rtl::Reference<VCLXWindow> pNewComp; + + vcl::Window* pNewWindow = ImplCreateWindow( &pNewComp, rDescriptor, pParent, nWinBits, aPair.second ); + + DBG_ASSERT( pNewWindow, "createWindow: Unknown Component!" ); + SAL_INFO_IF( !pNewComp, "toolkit", "createWindow: No special Interface!" ); + + if ( pNewWindow ) + { + pNewWindow->SetCreatedWithToolkit( true ); + //pNewWindow->SetPosPixel( Point() ); // do not force (0,0) position, keep default pos instead + + if ( rDescriptor.WindowAttributes & css::awt::WindowAttribute::MINSIZE ) + { + pNewWindow->SetSizePixel( Size() ); + } + else if ( rDescriptor.WindowAttributes & css::awt::WindowAttribute::FULLSIZE ) + { + if ( pParent ) + pNewWindow->SetSizePixel( pParent->GetOutputSizePixel() ); + } + else if ( !VCLUnoHelper::IsZero( rDescriptor.Bounds ) ) + { + tools::Rectangle aRect = VCLRectangle( rDescriptor.Bounds ); + pNewWindow->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() ); + } + + if ( !pNewComp ) + { + // Default-Interface + xRef = pNewWindow->GetComponentInterface(); + } + else + { + xRef = pNewComp; + pNewWindow->SetComponentInterface( xRef ); + } + DBG_ASSERT( pNewWindow->GetComponentInterface( false ) == xRef, + "VCLXToolkit::createWindow: did #133706# resurge?" ); + + if ( rDescriptor.WindowAttributes & css::awt::WindowAttribute::SHOW ) + pNewWindow->Show(); + } + + return xRef; +} + +css::uno::Sequence< css::uno::Reference< css::awt::XWindowPeer > > VCLXToolkit::createWindows( const css::uno::Sequence< css::awt::WindowDescriptor >& rDescriptors ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + sal_uInt32 nComponents = rDescriptors.getLength(); + css::uno::Sequence< css::uno::Reference< css::awt::XWindowPeer > > aSeq( nComponents ); + for ( sal_uInt32 n = 0; n < nComponents; n++ ) + { + css::awt::WindowDescriptor aDescr = rDescriptors.getConstArray()[n]; + + if ( aDescr.ParentIndex == -1 ) + aDescr.Parent = nullptr; + else if ( ( aDescr.ParentIndex >= 0 ) && ( o3tl::make_unsigned(aDescr.ParentIndex) < n ) ) + aDescr.Parent = aSeq.getConstArray()[aDescr.ParentIndex]; + aSeq.getArray()[n] = createWindow( aDescr ); + } + return aSeq; +} + +// css::awt::XSystemChildFactory +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::createSystemChild( const css::uno::Any& Parent, const css::uno::Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 nSystemType ) +{ + VclPtr<vcl::Window> pChildWindow; + if ( nSystemType == SYSTEM_DEPENDENT_TYPE ) + { + // use sal_Int64 here to accommodate all int types + // uno::Any shift operator will upcast if necessary + sal_Int64 nWindowHandle = 0; + bool bXEmbed = false; + + bool bUseParentData = true; + if( ! (Parent >>= nWindowHandle) ) + { + css::uno::Sequence< css::beans::NamedValue > aProps; + if( Parent >>= aProps ) + { + for( const css::beans::NamedValue& rProp : std::as_const(aProps) ) + { + if ( rProp.Name == "WINDOW" ) + rProp.Value >>= nWindowHandle; + else if ( rProp.Name == "XEMBED" ) + rProp.Value >>= bXEmbed; + } + } + else + bUseParentData = false; + } + + if( bUseParentData ) + { + SystemParentData aParentData; + aParentData.nSize = sizeof( aParentData ); + #if defined MACOSX + aParentData.pView = reinterpret_cast<NSView*>(nWindowHandle); + #elif defined ANDROID + // Nothing + #elif defined IOS + // Nothing + #elif defined UNX + aParentData.aWindow = nWindowHandle; + aParentData.bXEmbedSupport = bXEmbed; + #elif defined _WIN32 + aParentData.hWnd = reinterpret_cast<HWND>(nWindowHandle); + #endif + SolarMutexGuard aGuard; + try + { + pChildWindow.reset( VclPtr<WorkWindow>::Create( &aParentData ) ); + } + catch ( const css::uno::RuntimeException & ) + { + // system child window could not be created + DBG_UNHANDLED_EXCEPTION("toolkit"); + pChildWindow.clear(); + } + } + } + else if (nSystemType == css::lang::SystemDependent::SYSTEM_JAVA) + { + SolarMutexGuard aGuard; + pChildWindow.reset(VclPtr<WorkWindow>::Create(nullptr, Parent)); + } + + css::uno::Reference< css::awt::XVclWindowPeer > xPeer; + if ( pChildWindow ) + { + rtl::Reference<VCLXTopWindow> pPeer = new VCLXTopWindow; + SolarMutexGuard aGuard; + pPeer->SetWindow( pChildWindow ); + xPeer = pPeer; + pChildWindow->SetWindowPeer(xPeer, pPeer.get()); + } + + return xPeer; +} + +// css::awt::XMessageBoxFactory +css::uno::Reference< css::awt::XMessageBox > SAL_CALL VCLXToolkit::createMessageBox( + const css::uno::Reference< css::awt::XWindowPeer >& aParent, + css::awt::MessageBoxType eType, + ::sal_Int32 aButtons, + const OUString& aTitle, + const OUString& aMessage ) +{ + css::awt::WindowDescriptor aDescriptor; + + sal_Int32 nWindowAttributes = css::awt::WindowAttribute::BORDER|css::awt::WindowAttribute::MOVEABLE|css::awt::WindowAttribute::CLOSEABLE; + + // Map button definitions to window attributes + if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_OK ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::OK; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_OK_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::OK_CANCEL; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_YES_NO ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::YES_NO; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_YES_NO_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::YES_NO_CANCEL; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_RETRY_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::RETRY_CANCEL; + + // Map default button definitions to window attributes + if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_OK ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_OK; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_CANCEL; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_YES ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_YES; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_NO ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_NO; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_RETRY ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_RETRY; + + // No more bits for VclWindowPeerAttribute possible. Mapping must be + // done explicitly using VCL methods + MessBoxStyle nAddWinBits = MessBoxStyle::NONE; + if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_ABORT_IGNORE_RETRY ) + nAddWinBits |= MessBoxStyle::AbortRetryIgnore; + if ( sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_IGNORE ) + nAddWinBits |= MessBoxStyle::DefaultIgnore; + + OUString aType; + lcl_convertMessageBoxType( aType, eType ); + + aDescriptor.Type = css::awt::WindowClass_MODALTOP; + aDescriptor.WindowServiceName = aType; + aDescriptor.ParentIndex = -1; + aDescriptor.Parent = aParent; + aDescriptor.WindowAttributes = nWindowAttributes; + css::uno::Reference< css::awt::XMessageBox > xMsgBox( + ImplCreateWindow( aDescriptor, nAddWinBits ), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XWindow > xWindow( xMsgBox, css::uno::UNO_QUERY ); + if ( xMsgBox.is() && xWindow.is() ) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + SolarMutexGuard aGuard; + xMsgBox->setCaptionText( aTitle ); + xMsgBox->setMessageText( aMessage ); + } + } + + return xMsgBox; +} + +css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > SAL_CALL VCLXToolkit::getDragGestureRecognizer( const css::uno::Reference< css::awt::XWindow >& window ) +{ + SolarMutexGuard g; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( window ); + + if( pWindow ) + return pWindow->GetDragGestureRecognizer(); + + return css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer >(); +} + +css::uno::Reference< css::datatransfer::dnd::XDragSource > SAL_CALL VCLXToolkit::getDragSource( const css::uno::Reference< css::awt::XWindow >& window ) +{ + SolarMutexGuard g; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( window ); + + if( pWindow ) + return pWindow->GetDragSource(); + + return css::uno::Reference< css::datatransfer::dnd::XDragSource >(); +} + +css::uno::Reference< css::datatransfer::dnd::XDropTarget > SAL_CALL VCLXToolkit::getDropTarget( const css::uno::Reference< css::awt::XWindow >& window ) +{ + SolarMutexGuard g; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( window ); + + if( pWindow ) + return pWindow->GetDropTarget(); + + return css::uno::Reference< css::datatransfer::dnd::XDropTarget >(); +} + +css::uno::Reference< css::datatransfer::clipboard::XClipboard > SAL_CALL VCLXToolkit::getClipboard( const OUString& clipboardName ) +{ + if( clipboardName.isEmpty() ) + { + if( !mxClipboard.is() ) + { + // remember clipboard here + mxClipboard = css::datatransfer::clipboard::SystemClipboard::create( + comphelper::getProcessComponentContext()); + } + + return mxClipboard; + } + + else if( clipboardName == "Selection" ) + { + return mxSelection; + } + + return css::uno::Reference< css::datatransfer::clipboard::XClipboard >(); +} + +// XServiceInfo +OUString VCLXToolkit::getImplementationName() +{ + return "stardiv.Toolkit.VCLXToolkit"; +} + +sal_Bool VCLXToolkit::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > VCLXToolkit::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.Toolkit", "stardiv.vcl.VclToolkit"}; +} + +// css::awt::XExtendedToolkit: + +// virtual +::sal_Int32 SAL_CALL VCLXToolkit::getTopWindowCount() +{ + return static_cast< ::sal_Int32 >(::Application::GetTopWindowCount()); + // XXX numeric overflow +} + +// virtual +css::uno::Reference< css::awt::XTopWindow > SAL_CALL +VCLXToolkit::getTopWindow(::sal_Int32 nIndex) +{ + vcl::Window * p = ::Application::GetTopWindow(static_cast< tools::Long >(nIndex)); + // XXX numeric overflow + return css::uno::Reference< css::awt::XTopWindow >( + p == nullptr ? nullptr : static_cast< css::awt::XWindow * >(p->GetWindowPeer()), + css::uno::UNO_QUERY); +} + +// virtual +css::uno::Reference< css::awt::XTopWindow > SAL_CALL +VCLXToolkit::getActiveTopWindow() +{ + vcl::Window * p = ::Application::GetActiveTopWindow(); + return css::uno::Reference< css::awt::XTopWindow >( + p == nullptr ? nullptr : static_cast< css::awt::XWindow * >(p->GetWindowPeer()), + css::uno::UNO_QUERY); +} + +// virtual +void SAL_CALL VCLXToolkit::addTopWindowListener( + css::uno::Reference< css::awt::XTopWindowListener > const & rListener) +{ + OSL_ENSURE(rListener.is(), "Null rListener"); + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rListener->disposing( + css::lang::EventObject( + getXWeak())); + } + else if (m_aTopWindowListeners.addInterface(rListener) == 1 + && !m_bEventListener) + { + m_bEventListener = true; + ::Application::AddEventListener(m_aEventListenerLink); + } +} + +// virtual +void SAL_CALL VCLXToolkit::removeTopWindowListener( + css::uno::Reference< css::awt::XTopWindowListener > const & rListener) +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose) + && m_aTopWindowListeners.removeInterface(rListener) == 0 + && m_aFocusListeners.getLength() == 0 && m_bEventListener) + { + ::Application::RemoveEventListener(m_aEventListenerLink); + m_bEventListener = false; + } +} + +// virtual +void SAL_CALL VCLXToolkit::addKeyHandler( + css::uno::Reference< css::awt::XKeyHandler > const & rHandler) +{ + OSL_ENSURE(rHandler.is(), "Null rHandler"); + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rHandler->disposing( + css::lang::EventObject( + getXWeak())); + } + else if (m_aKeyHandlers.addInterface(rHandler) == 1 && !m_bKeyListener) + { + m_bKeyListener = true; + ::Application::AddKeyListener(m_aKeyListenerLink); + } +} + +// virtual +void SAL_CALL VCLXToolkit::removeKeyHandler( + css::uno::Reference< css::awt::XKeyHandler > const & rHandler) +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose) + && m_aKeyHandlers.removeInterface(rHandler) == 0 && m_bKeyListener) + { + ::Application::RemoveKeyListener(m_aKeyListenerLink); + m_bKeyListener = false; + } +} + +// virtual +void SAL_CALL VCLXToolkit::addFocusListener( + css::uno::Reference< css::awt::XFocusListener > const & rListener) +{ + OSL_ENSURE(rListener.is(), "Null rListener"); + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rListener->disposing( + css::lang::EventObject( + getXWeak())); + } + else if (m_aFocusListeners.addInterface(rListener) == 1 + && !m_bEventListener) + { + m_bEventListener = true; + ::Application::AddEventListener(m_aEventListenerLink); + } +} + +// virtual +void SAL_CALL VCLXToolkit::removeFocusListener( + css::uno::Reference< css::awt::XFocusListener > const & rListener) +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose) + && m_aFocusListeners.removeInterface(rListener) == 0 + && m_aTopWindowListeners.getLength() == 0 && m_bEventListener) + { + ::Application::RemoveEventListener(m_aEventListenerLink); + m_bEventListener = false; + } +} + +// virtual +void SAL_CALL VCLXToolkit::fireFocusGained( + css::uno::Reference< + css::uno::XInterface > const &) +{ +} + +// virtual +void SAL_CALL VCLXToolkit::fireFocusLost( + css::uno::Reference< + css::uno::XInterface > const &) +{ +} + + +IMPL_LINK(VCLXToolkit, eventListenerHandler, ::VclSimpleEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowShow: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowOpened); + break; + case VclEventId::WindowHide: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowClosed); + break; + case VclEventId::WindowActivate: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowActivated); + break; + case VclEventId::WindowDeactivate: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowDeactivated); + break; + case VclEventId::WindowClose: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowClosing); + break; + case VclEventId::WindowGetFocus: + callFocusListeners(&rEvent, true); + break; + case VclEventId::WindowLoseFocus: + callFocusListeners(&rEvent, false); + break; + case VclEventId::WindowMinimize: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowMinimized); + break; + case VclEventId::WindowNormalize: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowNormalized); + break; + default: break; + } +} + +IMPL_LINK(VCLXToolkit, keyListenerHandler, ::VclWindowEvent&, rEvent, bool) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowKeyInput: + return callKeyHandlers(&rEvent, true); + case VclEventId::WindowKeyUp: + return callKeyHandlers(&rEvent, false); + default: break; + } + return false; +} + +void VCLXToolkit::callTopWindowListeners( + ::VclSimpleEvent const * pEvent, + void (SAL_CALL css::awt::XTopWindowListener::* pFn)( + css::lang::EventObject const &)) +{ + vcl::Window * pWindow + = static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow(); + if (!pWindow->IsTopWindow()) + return; + + std::vector< css::uno::Reference< css::awt::XTopWindowListener > > + aListeners(m_aTopWindowListeners.getElements()); + if (aListeners.empty()) + return; + + css::lang::EventObject aAwtEvent( + static_cast< css::awt::XWindow * >(pWindow->GetWindowPeer())); + for (const css::uno::Reference<css::awt::XTopWindowListener> & xListener : aListeners) + { + try + { + (xListener.get()->*pFn)(aAwtEvent); + } + catch (const css::uno::RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } +} + +bool VCLXToolkit::callKeyHandlers(::VclSimpleEvent const * pEvent, + bool bPressed) +{ + std::vector< css::uno::Reference< css::awt::XKeyHandler > > + aHandlers(m_aKeyHandlers.getElements()); + + if (!aHandlers.empty()) + { + vcl::Window * pWindow = static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow(); + + // See implementation in vclxwindow.cxx for mapping between VCL and UNO AWT event + ::KeyEvent * pKeyEvent = static_cast< ::KeyEvent * >( + static_cast< ::VclWindowEvent const * >(pEvent)->GetData()); + css::awt::KeyEvent aAwtEvent( + static_cast< css::awt::XWindow * >(pWindow->GetWindowPeer()), + (pKeyEvent->GetKeyCode().IsShift() + ? css::awt::KeyModifier::SHIFT : 0) + | (pKeyEvent->GetKeyCode().IsMod1() + ? css::awt::KeyModifier::MOD1 : 0) + | (pKeyEvent->GetKeyCode().IsMod2() + ? css::awt::KeyModifier::MOD2 : 0) + | (pKeyEvent->GetKeyCode().IsMod3() + ? css::awt::KeyModifier::MOD3 : 0), + pKeyEvent->GetKeyCode().GetCode(), pKeyEvent->GetCharCode(), + sal::static_int_cast< sal_Int16 >( + pKeyEvent->GetKeyCode().GetFunction())); + for (const css::uno::Reference<css::awt::XKeyHandler> & xHandler : aHandlers) + { + try + { + if (bPressed ? xHandler->keyPressed(aAwtEvent) + : xHandler->keyReleased(aAwtEvent)) + return true; + } + catch (const css::uno::RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } + } + return false; +} + +void VCLXToolkit::callFocusListeners(::VclSimpleEvent const * pEvent, + bool bGained) +{ + vcl::Window * pWindow + = static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow(); + if (!pWindow->IsTopWindow()) + return; + + std::vector< css::uno::Reference< css::awt::XFocusListener > > + aListeners(m_aFocusListeners.getElements()); + if (aListeners.empty()) + return; + + // Ignore the interior of compound controls when determining the + // window that gets the focus next (see implementation in + // vclxwindow.cxx for mapping between VCL and UNO AWT event): + css::uno::Reference< css::uno::XInterface > xNext; + vcl::Window * pFocus = ::Application::GetFocusWindow(); + for (vcl::Window * p = pFocus; p != nullptr; p = p->GetParent()) + if (!p->IsCompoundControl()) + { + pFocus = p; + break; + } + if (pFocus != nullptr) + xNext = pFocus->GetComponentInterface(); + css::awt::FocusEvent aAwtEvent( + static_cast< css::awt::XWindow * >(pWindow->GetWindowPeer()), + static_cast<sal_Int16>(pWindow->GetGetFocusFlags()), + xNext, false); + for (const css::uno::Reference<css::awt::XFocusListener> & xListener : aListeners) + { + try + { + bGained ? xListener->focusGained(aAwtEvent) + : xListener->focusLost(aAwtEvent); + } + catch (const css::uno::RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } +} + +// css::awt::XReschedule: + +void SAL_CALL VCLXToolkit::reschedule() +{ + SolarMutexGuard aSolarGuard; + Application::Reschedule(true); +} + +// css::awt::XFontMappingUse: +void SAL_CALL VCLXToolkit::startTrackingFontMappingUse() +{ + SolarMutexGuard aSolarGuard; + OutputDevice::StartTrackingFontMappingUse(); +} + +css::uno::Sequence<css::awt::XFontMappingUseItem> +SAL_CALL VCLXToolkit::finishTrackingFontMappingUse() +{ + SolarMutexGuard aSolarGuard; + OutputDevice::FontMappingUseData data = OutputDevice::FinishTrackingFontMappingUse(); + css::uno::Sequence<css::awt::XFontMappingUseItem> ret( data.size()); + css::awt::XFontMappingUseItem* retData = ret.getArray(); + for( size_t i = 0; i < data.size(); ++i ) + { + retData[ i ].originalFont = data[ i ].mOriginalFont; + retData[ i ].usedFonts = comphelper::arrayToSequence<OUString,OUString> + (data[ i ].mUsedFonts.data(), data[ i ].mUsedFonts.size()); + retData[ i ].count = data[ i ].mCount; + } + return ret; +} + +// css::awt::XToolkitExperimental + +void SAL_CALL VCLXToolkit::processEventsToIdle() +{ + SolarMutexGuard aSolarGuard; + comphelper::ProfileZone aZone("processEvents"); + Scheduler::ProcessEventsToIdle(); +} + +sal_Int64 SAL_CALL VCLXToolkit::getOpenGLBufferSwapCounter() +{ +#if HAVE_FEATURE_OPENGL + return OpenGLWrapper::getBufferSwapCounter(); +#else + return 0; +#endif +} + +void SAL_CALL VCLXToolkit::setDeterministicScheduling(sal_Bool bDeterministicMode) +{ + SolarMutexGuard aSolarGuard; + Scheduler::SetDeterministicMode(bDeterministicMode); +} + +void SAL_CALL VCLXToolkit::pause(sal_Int32 nMilliseconds) +{ + new Pause(nMilliseconds); +} + +void SAL_CALL VCLXToolkit::startRecording() +{ + comphelper::TraceEvent::startRecording(); +} + +void SAL_CALL VCLXToolkit::stopRecording() +{ + comphelper::TraceEvent::stopRecording(); +} + +css::uno::Sequence< OUString > VCLXToolkit::getRecordingAndClear() +{ + return comphelper::ProfileZone::getRecordingAndClear(); +} + +void VCLXToolkit::waitUntilAllIdlesDispatched() +{ + IdleTask::waitUntilIdleDispatched(); +} + +// css:awt:XToolkitRobot + +void SAL_CALL VCLXToolkit::keyPress( const css::awt::KeyEvent & aKeyEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aKeyEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::KeyEvent aVCLKeyEvent = VCLUnoHelper::createVCLKeyEvent( aKeyEvent ); + ::Application::PostKeyEvent( VclEventId::WindowKeyInput, pWindow, &aVCLKeyEvent ); +} + +void SAL_CALL VCLXToolkit::keyRelease( const css::awt::KeyEvent & aKeyEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aKeyEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::KeyEvent aVCLKeyEvent = VCLUnoHelper::createVCLKeyEvent( aKeyEvent ); + ::Application::PostKeyEvent( VclEventId::WindowKeyUp, pWindow, &aVCLKeyEvent ); +} + + +void SAL_CALL VCLXToolkit::mousePress( const css::awt::MouseEvent & aMouseEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aMouseEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::MouseEvent aVCLMouseEvent = VCLUnoHelper::createVCLMouseEvent( aMouseEvent ); + ::Application::PostMouseEvent( VclEventId::WindowMouseButtonDown, pWindow, &aVCLMouseEvent ); +} + +void SAL_CALL VCLXToolkit::mouseRelease( const css::awt::MouseEvent & aMouseEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aMouseEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::MouseEvent aVCLMouseEvent = VCLUnoHelper::createVCLMouseEvent( aMouseEvent ); + ::Application::PostMouseEvent( VclEventId::WindowMouseButtonUp, pWindow, &aVCLMouseEvent ); +} + +void SAL_CALL VCLXToolkit::mouseMove( const css::awt::MouseEvent & aMouseEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aMouseEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::MouseEvent aVCLMouseEvent = VCLUnoHelper::createVCLMouseEvent( aMouseEvent ); + ::Application::PostMouseEvent( VclEventId::WindowMouseMove, pWindow, &aVCLMouseEvent ); +} + + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXToolkit_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXToolkit()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxtopwindow.cxx b/toolkit/source/awt/vclxtopwindow.cxx new file mode 100644 index 0000000000..c0ce4d891d --- /dev/null +++ b/toolkit/source/awt/vclxtopwindow.cxx @@ -0,0 +1,229 @@ +/* -*- 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/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/awt/SystemDependentXWindow.hpp> + +#if defined ( MACOSX ) +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif + +#include <vcl/sysdata.hxx> +#include <o3tl/safeint.hxx> + +#include <awt/vclxtopwindow.hxx> +#include <toolkit/awt/vclxmenu.hxx> + +#include <vcl/wrkwin.hxx> +#include <vcl/syswin.hxx> +#include <vcl/menu.hxx> +#include <vcl/svapp.hxx> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::lang::IndexOutOfBoundsException; + + +css::uno::Any VCLXTopWindow::getWindowHandle( const css::uno::Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) +{ + SolarMutexGuard aGuard; + + // TODO, check the process id + css::uno::Any aRet; + vcl::Window* pWindow = VCLXContainer::GetWindow(); + if ( pWindow ) + { + const SystemEnvData* pSysData = static_cast<SystemWindow *>(pWindow)->GetSystemData(); + if( pSysData ) + { +#if defined (_WIN32) + if( SystemType == css::lang::SystemDependent::SYSTEM_WIN32 ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->hWnd); + } +#elif defined(MACOSX) + if( SystemType == css::lang::SystemDependent::SYSTEM_MAC ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->mpNSView); + } +#elif defined(ANDROID) + // Nothing + (void) SystemType; +#elif defined(IOS) + // Nothing + (void) SystemType; +#elif defined(UNX) + if( SystemType == css::lang::SystemDependent::SYSTEM_XWINDOW ) + { + css::awt::SystemDependentXWindow aSD; + aSD.DisplayPointer = sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_IntPtr >(pSysData->pDisplay)); + aSD.WindowHandle = pSysData->GetWindowHandle(pWindow->ImplGetFrame()); + aRet <<= aSD; + } +#endif + } + } + return aRet; +} + +void VCLXTopWindow::addTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetTopWindowListeners().addInterface( rxListener ); +} + +void VCLXTopWindow::removeTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetTopWindowListeners().removeInterface( rxListener ); +} + +void VCLXTopWindow::toFront( ) +{ + SolarMutexGuard aGuard; + + vcl::Window* pWindow = VCLXContainer::GetWindow(); + if (pWindow) + pWindow->ToTop( ToTopFlags::RestoreWhenMin ); +} + +void VCLXTopWindow::toBack( ) +{ +} + +void VCLXTopWindow::setMenuBar( const css::uno::Reference< css::awt::XMenuBar >& rxMenu ) +{ + SolarMutexGuard aGuard; + + vcl::Window* pWindow = VCLXContainer::GetWindow(); + if ( pWindow ) + { + SystemWindow* pSystemWindow = static_cast<SystemWindow*>( pWindow ); + pSystemWindow->SetMenuBar( nullptr ); + if ( rxMenu.is() ) + { + VCLXMenu* pMenu = dynamic_cast<VCLXMenu*>( rxMenu.get() ); + if ( pMenu && !pMenu->IsPopupMenu() ) + pSystemWindow->SetMenuBar( static_cast<MenuBar*>( pMenu->GetMenu() )); + } + } +} + + +sal_Bool SAL_CALL VCLXTopWindow::getIsMaximized() +{ + SolarMutexGuard aGuard; + + const WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return false; + + return pWindow->IsMaximized(); +} + + +void SAL_CALL VCLXTopWindow::setIsMaximized( sal_Bool _ismaximized ) +{ + SolarMutexGuard aGuard; + + WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return; + + pWindow->Maximize( _ismaximized ); +} + + +sal_Bool SAL_CALL VCLXTopWindow::getIsMinimized() +{ + SolarMutexGuard aGuard; + + const WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return false; + + return pWindow->IsMinimized(); +} + + +void SAL_CALL VCLXTopWindow::setIsMinimized( sal_Bool _isMinimized ) +{ + SolarMutexGuard aGuard; + + WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return; + + _isMinimized ? pWindow->Minimize() : pWindow->Restore(); +} + + +::sal_Int32 SAL_CALL VCLXTopWindow::getDisplay() +{ + SolarMutexGuard aGuard; + + const SystemWindow* pWindow = VCLXContainer::GetAsDynamic<SystemWindow>(); + if ( !pWindow ) + return 0; + + return pWindow->GetScreenNumber(); +} + + +void SAL_CALL VCLXTopWindow::setDisplay( ::sal_Int32 _display ) +{ + SolarMutexGuard aGuard; + + if ( ( _display < 0 ) || ( o3tl::make_unsigned(_display) >= Application::GetScreenCount() ) ) + throw IndexOutOfBoundsException(); + + SystemWindow* pWindow = VCLXContainer::GetAsDynamic<SystemWindow>(); + if ( !pWindow ) + return; + + pWindow->SetScreenNumber( _display ); +} + + + + +void VCLXTopWindow::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXTopWindow::VCLXTopWindow() +{ +} + +VCLXTopWindow::~VCLXTopWindow() +{ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindow.cxx b/toolkit/source/awt/vclxwindow.cxx new file mode 100644 index 0000000000..2448eaf646 --- /dev/null +++ b/toolkit/source/awt/vclxwindow.cxx @@ -0,0 +1,2611 @@ +/* -*- 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 <stdarg.h> +#include <memory> +#include <com/sun/star/awt/WindowEvent.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/awt/MouseWheelBehavior.hpp> +#include <com/sun/star/awt/Style.hpp> +#include <com/sun/star/awt/DockingEvent.hpp> +#include <com/sun/star/awt/EndDockingEvent.hpp> +#include <com/sun/star/awt/EndPopupModeEvent.hpp> +#include <com/sun/star/awt/XWindowListener2.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <toolkit/awt/vclxwindow.hxx> +#include <awt/vclxpointer.hxx> +#include <toolkit/awt/vclxwindows.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> +#include <helper/property.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <utility> +#include <vcl/toolkit/floatwin.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <tools/color.hxx> +#include <tools/fract.hxx> +#include <tools/debug.hxx> +#include <vcl/event.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/pdfextoutdevdata.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/ctrl.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> +#include <comphelper/flagguard.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/profilezone.hxx> +#include "stylesettings.hxx" +#include <tools/urlobj.hxx> + +#include <helper/accessibilityclient.hxx> +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::lang::EventObject; +using ::com::sun::star::awt::XWindowListener2; +using ::com::sun::star::awt::XDockableWindowListener; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::XStyleSettings; +using ::com::sun::star::lang::DisposedException; +using ::com::sun::star::style::VerticalAlignment; +using ::com::sun::star::style::VerticalAlignment_TOP; +using ::com::sun::star::style::VerticalAlignment_MIDDLE; +using ::com::sun::star::style::VerticalAlignment_BOTTOM; + +namespace WritingMode2 = ::com::sun::star::text::WritingMode2; + + +//= VCLXWindowImpl + +class VCLXWindowImpl +{ +private: + typedef ::std::vector< VCLXWindow::Callback > CallbackArray; + +private: + VCLXWindow& mrAntiImpl; + ::toolkit::AccessibilityClient maAccFactory; + bool mbDisposed; + bool mbDrawingOntoParent; // no bit mask, is passed around by reference + bool mbEnableVisible; + bool mbDirectVisible; + + ::osl::Mutex maListenerContainerMutex; + ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2> maWindow2Listeners; + ::comphelper::OInterfaceContainerHelper3<XDockableWindowListener> maDockableWindowListeners; + EventListenerMultiplexer maEventListeners; + FocusListenerMultiplexer maFocusListeners; + WindowListenerMultiplexer maWindowListeners; + KeyListenerMultiplexer maKeyListeners; + MouseListenerMultiplexer maMouseListeners; + MouseMotionListenerMultiplexer maMouseMotionListeners; + PaintListenerMultiplexer maPaintListeners; + VclContainerListenerMultiplexer maContainerListeners; + TopWindowListenerMultiplexer maTopWindowListeners; + + CallbackArray maCallbackEvents; + ImplSVEvent * mnCallbackEventId; + +public: + bool mbDisposing : 1; + bool mbDesignMode : 1; + bool mbSynthesizingVCLEvent : 1; + bool mbWithDefaultProps : 1; + + sal_uLong mnListenerLockLevel; + sal_Int16 mnWritingMode; + sal_Int16 mnContextWritingMode; + + std::unique_ptr<UnoPropertyArrayHelper> + mpPropHelper; + + css::uno::Reference< css::accessibility::XAccessibleContext > + mxAccessibleContext; + css::uno::Reference< css::awt::XGraphics > + mxViewGraphics; + rtl::Reference< toolkit::WindowStyleSettings > + mxWindowStyleSettings; + +public: + bool& getDrawingOntoParent_ref() { return mbDrawingOntoParent; } + +public: + /** ctor + @param _pAntiImpl + the <type>VCLXWindow</type> instance which the object belongs to. Must + live longer then the object just being constructed. + */ + VCLXWindowImpl( VCLXWindow& _rAntiImpl, bool _bWithDefaultProps ); + + VCLXWindowImpl( const VCLXWindowImpl& ) = delete; + const VCLXWindowImpl& operator=(const VCLXWindowImpl&) = delete; + + /** synchronously mbEnableVisible + */ + void setEnableVisible( bool bEnableVisible ) { mbEnableVisible = bEnableVisible; } + bool isEnableVisible() const { return mbEnableVisible; } + /** synchronously mbDirectVisible; + */ + void setDirectVisible( bool bDirectVisible ) { mbDirectVisible = bDirectVisible; } + bool isDirectVisible() const { return mbDirectVisible; } + + /** impl-version of VCLXWindow::ImplExecuteAsyncWithoutSolarLock + */ + void callBackAsync( const VCLXWindow::Callback& i_callback ); + + /** notifies the object that its VCLXWindow is being disposed + */ + void disposing(); + + ::toolkit::AccessibilityClient& getAccessibleFactory() + { + return maAccFactory; + } + + Reference< XStyleSettings > getStyleSettings(); + + /** returns the container of registered XWindowListener2 listeners + */ + ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2>& getWindow2Listeners() { return maWindow2Listeners; } + ::comphelper::OInterfaceContainerHelper3<XDockableWindowListener>& getDockableWindowListeners() { return maDockableWindowListeners; } + EventListenerMultiplexer& getEventListeners() { return maEventListeners; } + FocusListenerMultiplexer& getFocusListeners() { return maFocusListeners; } + WindowListenerMultiplexer& getWindowListeners() { return maWindowListeners; } + KeyListenerMultiplexer& getKeyListeners() { return maKeyListeners; } + MouseListenerMultiplexer& getMouseListeners() { return maMouseListeners; } + MouseMotionListenerMultiplexer& getMouseMotionListeners() { return maMouseMotionListeners; } + PaintListenerMultiplexer& getPaintListeners() { return maPaintListeners; } + VclContainerListenerMultiplexer& getContainerListeners() { return maContainerListeners; } + TopWindowListenerMultiplexer& getTopWindowListeners() { return maTopWindowListeners; } + +private: + DECL_LINK( OnProcessCallbacks, void*, void ); +}; + + +VCLXWindowImpl::VCLXWindowImpl( VCLXWindow& _rAntiImpl, bool _bWithDefaultProps ) + :mrAntiImpl( _rAntiImpl ) + ,mbDisposed( false ) + ,mbDrawingOntoParent( false ) + ,mbEnableVisible(true) + ,mbDirectVisible(true) + ,maWindow2Listeners( maListenerContainerMutex ) + ,maDockableWindowListeners( maListenerContainerMutex ) + ,maEventListeners( _rAntiImpl ) + ,maFocusListeners( _rAntiImpl ) + ,maWindowListeners( _rAntiImpl ) + ,maKeyListeners( _rAntiImpl ) + ,maMouseListeners( _rAntiImpl ) + ,maMouseMotionListeners( _rAntiImpl ) + ,maPaintListeners( _rAntiImpl ) + ,maContainerListeners( _rAntiImpl ) + ,maTopWindowListeners( _rAntiImpl ) + ,mnCallbackEventId( nullptr ) + ,mbDisposing( false ) + ,mbDesignMode( false ) + ,mbSynthesizingVCLEvent( false ) + ,mbWithDefaultProps( _bWithDefaultProps ) + ,mnListenerLockLevel( 0 ) + ,mnWritingMode( WritingMode2::CONTEXT ) + ,mnContextWritingMode( WritingMode2::CONTEXT ) +{ +} + +void VCLXWindowImpl::disposing() +{ + SolarMutexGuard aGuard; + + assert(!mbDisposed); + + mbDisposed = true; + + if ( mnCallbackEventId ) + { + Application::RemoveUserEvent( mnCallbackEventId ); + mnCallbackEventId = nullptr; + // we acquired our VCLXWindow once before posting the event, release this one ref now + mrAntiImpl.release(); + } + maCallbackEvents.clear(); + + css::lang::EventObject aEvent; + aEvent.Source = mrAntiImpl; + + maDockableWindowListeners.disposeAndClear( aEvent ); + maEventListeners.disposeAndClear( aEvent ); + maFocusListeners.disposeAndClear( aEvent ); + maWindowListeners.disposeAndClear( aEvent ); + maKeyListeners.disposeAndClear( aEvent ); + maMouseListeners.disposeAndClear( aEvent ); + maMouseMotionListeners.disposeAndClear( aEvent ); + maPaintListeners.disposeAndClear( aEvent ); + maContainerListeners.disposeAndClear( aEvent ); + maTopWindowListeners.disposeAndClear( aEvent ); + maWindow2Listeners.disposeAndClear( aEvent ); + + if ( mxWindowStyleSettings ) + mxWindowStyleSettings->dispose(); + mxWindowStyleSettings.clear(); +} + + +void VCLXWindowImpl::callBackAsync( const VCLXWindow::Callback& i_callback ) +{ + DBG_TESTSOLARMUTEX(); + maCallbackEvents.push_back( i_callback ); + if ( !mnCallbackEventId ) + { + // ensure our VCLXWindow is not destroyed while the event is underway + mrAntiImpl.acquire(); + mnCallbackEventId = Application::PostUserEvent( LINK( this, VCLXWindowImpl, OnProcessCallbacks ) ); + } +} + + +IMPL_LINK_NOARG(VCLXWindowImpl, OnProcessCallbacks, void*, void) +{ + const Reference< uno::XInterface > xKeepAlive( mrAntiImpl ); + + SAL_INFO("toolkit.controls", "OnProcessCallbacks grabbing solarmutex"); + + // work on a copy of the callback array + CallbackArray aCallbacksCopy; + { + SolarMutexGuard aGuard; + aCallbacksCopy.swap(maCallbackEvents); + + // we acquired our VCLXWindow once before posting the event, release this one ref now + mrAntiImpl.release(); + + assert( mnCallbackEventId && "should not be possible to call us if the event was removed"); + + mnCallbackEventId = nullptr; + } + + { + SAL_INFO("toolkit.controls", "OnProcessCallbacks relinquished solarmutex"); + SolarMutexReleaser aReleaseSolar; + for (const auto& rCallback : aCallbacksCopy) + { + rCallback(); + } + } +} + +Reference< XStyleSettings > VCLXWindowImpl::getStyleSettings() +{ + SolarMutexGuard aGuard; + if ( mbDisposed ) + throw DisposedException( OUString(), mrAntiImpl ); + if ( !mxWindowStyleSettings.is() ) + mxWindowStyleSettings = new ::toolkit::WindowStyleSettings( maListenerContainerMutex, mrAntiImpl ); + return mxWindowStyleSettings; +} + + +// Uses an out-parameter instead of return value, due to the object reference + +static void ImplInitWindowEvent( css::awt::WindowEvent& rEvent, vcl::Window const * pWindow ) +{ + Point aPos = pWindow->GetPosPixel(); + Size aSz = pWindow->GetSizePixel(); + + rEvent.X = aPos.X(); + rEvent.Y = aPos.Y(); + + rEvent.Width = aSz.Width(); + rEvent.Height = aSz.Height(); + + pWindow->GetBorder( rEvent.LeftInset, rEvent.TopInset, rEvent.RightInset, rEvent.BottomInset ); +} + +VCLXWindow::VCLXWindow( bool _bWithDefaultProps ) +{ + mpImpl.reset( new VCLXWindowImpl( *this, _bWithDefaultProps ) ); +} + +VCLXWindow::~VCLXWindow() +{ + assert(mpImpl->mbDisposing && "forgot to call dispose()"); +} + + +void VCLXWindow::ImplExecuteAsyncWithoutSolarLock( const Callback& i_callback ) +{ + if (mpImpl->mbDisposing) + return; + mpImpl->callBackAsync( i_callback ); +} + + +::toolkit::IAccessibleFactory& VCLXWindow::getAccessibleFactory() +{ + return mpImpl->getAccessibleFactory().getFactory(); +} + +void VCLXWindow::SetWindow( const VclPtr<vcl::Window> &pWindow ) +{ + assert(!mpImpl->mbDisposing || !pWindow); + + if ( GetWindow() ) + { + GetWindow()->RemoveEventListener( LINK( this, VCLXWindow, WindowEventListener ) ); +// GetWindow()->DbgAssertNoEventListeners(); + } + + SetOutputDevice( pWindow ? pWindow->GetOutDev() : nullptr ); + + if ( GetWindow() ) + { + GetWindow()->AddEventListener( LINK( this, VCLXWindow, WindowEventListener ) ); + bool bDirectVisible = pWindow && pWindow->IsVisible(); + mpImpl->setDirectVisible( bDirectVisible ); + } +} + +void VCLXWindow::suspendVclEventListening( ) +{ + ++mpImpl->mnListenerLockLevel; +} + +void VCLXWindow::resumeVclEventListening( ) +{ + DBG_ASSERT( mpImpl->mnListenerLockLevel, "VCLXWindow::resumeVclEventListening: not suspended!" ); + --mpImpl->mnListenerLockLevel; +} + +void VCLXWindow::notifyWindowRemoved( vcl::Window const & _rWindow ) +{ + if ( mpImpl->getContainerListeners().getLength() ) + { + awt::VclContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Child = static_cast< XWindow* >( _rWindow.GetWindowPeer() ); + mpImpl->getContainerListeners().windowRemoved( aEvent ); + } +} + +IMPL_LINK( VCLXWindow, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + if ( mpImpl->mbDisposing || mpImpl->mnListenerLockLevel ) + return; + + DBG_ASSERT( rEvent.GetWindow() && GetWindow(), "Window???" ); + ProcessWindowEvent( rEvent ); +} + +namespace +{ + struct CallWindow2Listener + { + CallWindow2Listener( ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2>& i_rWindow2Listeners, const bool i_bEnabled, EventObject i_Event ) + :m_rWindow2Listeners( i_rWindow2Listeners ) + ,m_bEnabled( i_bEnabled ) + ,m_aEvent(std::move( i_Event )) + { + } + + void operator()() + { + m_rWindow2Listeners.notifyEach( m_bEnabled ? &XWindowListener2::windowEnabled : &XWindowListener2::windowDisabled, m_aEvent ); + } + + ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2>& m_rWindow2Listeners; + const bool m_bEnabled; + const EventObject m_aEvent; + }; +} + +void VCLXWindow::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + if (mpImpl->mbDisposing) + return; + css::uno::Reference< css::uno::XInterface > xThis( getXWeak() ); + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowEnabled: + case VclEventId::WindowDisabled: + { + Callback aCallback = CallWindow2Listener( + mpImpl->getWindow2Listeners(), + ( VclEventId::WindowEnabled == rVclWindowEvent.GetId() ), + EventObject( *this ) + ); + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + break; + + case VclEventId::WindowPaint: + { + if ( mpImpl->getPaintListeners().getLength() ) + { + css::awt::PaintEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.UpdateRect = AWTRectangle( *static_cast<tools::Rectangle*>(rVclWindowEvent.GetData()) ); + aEvent.Count = 0; + mpImpl->getPaintListeners().windowPaint( aEvent ); + } + } + break; + case VclEventId::WindowMove: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowMoved( aEvent ); + } + } + break; + case VclEventId::WindowResize: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowResized( aEvent ); + } + } + break; + case VclEventId::WindowShow: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowShown( aEvent ); + } + + // For TopWindows this means opened... + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowOpened( aEvent ); + } + } + break; + case VclEventId::WindowHide: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowHidden( aEvent ); + } + + // For TopWindows this means closed... + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowClosed( aEvent ); + } + } + break; + case VclEventId::WindowActivate: + case VclEventId::WindowDeactivate: + { + if (!mpImpl->getTopWindowListeners().getLength()) + return; + + // Suppress events which are unlikely to be interesting to our listeners. + vcl::Window* pWin = static_cast<vcl::Window*>(rVclWindowEvent.GetData()); + bool bSuppress = false; + + while (pWin) + { + // Either the event came from the same window, from its + // child, or from a child of its border window (e.g. + // menubar or notebookbar). + if (pWin->GetWindow(GetWindowType::Client) == GetWindow()) + return; + + if (pWin->IsMenuFloatingWindow()) + bSuppress = true; + + if (pWin->GetType() == WindowType::FLOATINGWINDOW && + static_cast<FloatingWindow*>(pWin)->IsInPopupMode()) + bSuppress = true; + + // Otherwise, don't suppress if the event came from a different frame. + if (!bSuppress && pWin->GetWindow(GetWindowType::Frame) == pWin) + break; + + pWin = pWin->GetWindow(GetWindowType::RealParent); + } + + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + if (rVclWindowEvent.GetId() == VclEventId::WindowActivate) + mpImpl->getTopWindowListeners().windowActivated( aEvent ); + else + mpImpl->getTopWindowListeners().windowDeactivated( aEvent ); + } + break; + case VclEventId::WindowClose: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::closed, aEvent ); + } + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowClosing( aEvent ); + } + } + break; + case VclEventId::ControlGetFocus: + case VclEventId::WindowGetFocus: + { + if ( ( rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::ControlGetFocus + ) + || ( !rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::WindowGetFocus + ) + ) + { + if ( mpImpl->getFocusListeners().getLength() ) + { + css::awt::FocusEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FocusFlags = static_cast<sal_Int16>(rVclWindowEvent.GetWindow()->GetGetFocusFlags()); + aEvent.Temporary = false; + mpImpl->getFocusListeners().focusGained( aEvent ); + } + } + } + break; + case VclEventId::ControlLoseFocus: + case VclEventId::WindowLoseFocus: + { + if ( ( rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::ControlLoseFocus + ) + || ( !rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::WindowLoseFocus + ) + ) + { + if ( mpImpl->getFocusListeners().getLength() ) + { + css::awt::FocusEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FocusFlags = static_cast<sal_Int16>(rVclWindowEvent.GetWindow()->GetGetFocusFlags()); + aEvent.Temporary = false; + + vcl::Window* pNext = Application::GetFocusWindow(); + if ( pNext ) + { + // Don't care about internals if this control is compound + vcl::Window* pNextC = pNext; + while ( pNextC && !pNextC->IsCompoundControl() ) + pNextC = pNextC->GetParent(); + if ( pNextC ) + pNext = pNextC; + + pNext->GetComponentInterface(); + aEvent.NextFocus = cppu::getXWeak(pNext->GetWindowPeer()); + } + mpImpl->getFocusListeners().focusLost( aEvent ); + } + } + } + break; + case VclEventId::WindowMinimize: + { + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowMinimized( aEvent ); + } + } + break; + case VclEventId::WindowNormalize: + { + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowNormalized( aEvent ); + } + } + break; + case VclEventId::WindowKeyInput: + { + if ( mpImpl->getKeyListeners().getLength() ) + { + css::awt::KeyEvent aEvent( VCLUnoHelper::createKeyEvent( + *static_cast<KeyEvent*>(rVclWindowEvent.GetData()), *this + ) ); + mpImpl->getKeyListeners().keyPressed( aEvent ); + } + } + break; + case VclEventId::WindowKeyUp: + { + if ( mpImpl->getKeyListeners().getLength() ) + { + css::awt::KeyEvent aEvent( VCLUnoHelper::createKeyEvent( + *static_cast<KeyEvent*>(rVclWindowEvent.GetData()), *this + ) ); + mpImpl->getKeyListeners().keyReleased( aEvent ); + } + } + break; + case VclEventId::WindowCommand: + { + CommandEvent* pCmdEvt = static_cast<CommandEvent*>(rVclWindowEvent.GetData()); + if ( mpImpl->getMouseListeners().getLength() && ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu ) ) + { + // CommandEventId::ContextMenu: send as mousePressed with PopupTrigger = true ... + Point aWhere = static_cast< CommandEvent* >( rVclWindowEvent.GetData() )->GetMousePosPixel(); + if ( !pCmdEvt->IsMouseEvent() ) + { // for keyboard events, we set the coordinates to -1,-1. This is a slight HACK, but the current API + // handles a context menu command as special case of a mouse event, which is simply wrong. + // Without extending the API, we would not have another chance to notify listeners of a + // keyboard-triggered context menu request + aWhere = Point( -1, -1 ); + } + + MouseEvent aMEvt( aWhere, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT, 0 ); + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( aMEvt, *this ) ); + aEvent.PopupTrigger = true; + + Callback aCallback = [ this, aEvent ]() + { this->mpImpl->getMouseListeners().mousePressed( aEvent ); }; + + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + case VclEventId::WindowMouseMove: + { + MouseEvent* pMouseEvt = static_cast<MouseEvent*>(rVclWindowEvent.GetData()); + if ( mpImpl->getMouseListeners().getLength() && ( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() ) ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *pMouseEvt, *this ) ); + bool const isEnter(pMouseEvt->IsEnterWindow()); + Callback aCallback = [ this, isEnter, aEvent ]() + { MouseListenerMultiplexer& rMouseListeners = this->mpImpl->getMouseListeners(); + isEnter + ? rMouseListeners.mouseEntered(aEvent) + : rMouseListeners.mouseExited(aEvent); }; + + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + + if ( mpImpl->getMouseMotionListeners().getLength() && !pMouseEvt->IsEnterWindow() && !pMouseEvt->IsLeaveWindow() ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *pMouseEvt, *this ) ); + aEvent.ClickCount = 0; + if ( pMouseEvt->GetMode() & MouseEventModifiers::SIMPLEMOVE ) + mpImpl->getMouseMotionListeners().mouseMoved( aEvent ); + else + mpImpl->getMouseMotionListeners().mouseDragged( aEvent ); + } + } + break; + case VclEventId::WindowMouseButtonDown: + { + if ( mpImpl->getMouseListeners().getLength() ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *static_cast<MouseEvent*>(rVclWindowEvent.GetData()), *this ) ); + Callback aCallback = [ this, aEvent ]() + { this->mpImpl->getMouseListeners().mousePressed( aEvent ); }; + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + case VclEventId::WindowMouseButtonUp: + { + if ( mpImpl->getMouseListeners().getLength() ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *static_cast<MouseEvent*>(rVclWindowEvent.GetData()), *this ) ); + + Callback aCallback = [ this, aEvent ]() + { this->mpImpl->getMouseListeners().mouseReleased( aEvent ); }; + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + case VclEventId::WindowStartDocking: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + DockingData *pData = static_cast<DockingData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::DockingEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.TrackingRectangle = AWTRectangle( pData->maTrackRect ); + aEvent.MousePos.X = pData->maMousePos.X(); + aEvent.MousePos.Y = pData->maMousePos.Y(); + aEvent.bLiveMode = false; + aEvent.bInteractive = true; + + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::startDocking, aEvent ); + } + } + } + break; + case VclEventId::WindowDocking: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + DockingData *pData = static_cast<DockingData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::DockingEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.TrackingRectangle = AWTRectangle( pData->maTrackRect ); + aEvent.MousePos.X = pData->maMousePos.X(); + aEvent.MousePos.Y = pData->maMousePos.Y(); + aEvent.bLiveMode = false; + aEvent.bInteractive = true; + + Reference< XDockableWindowListener > xFirstListener; + ::comphelper::OInterfaceIteratorHelper3 aIter( mpImpl->getDockableWindowListeners() ); + while ( aIter.hasMoreElements() && !xFirstListener.is() ) + { + xFirstListener = aIter.next(); + } + + css::awt::DockingData aDockingData = + xFirstListener->docking( aEvent ); + pData->maTrackRect = VCLRectangle( aDockingData.TrackingRectangle ); + pData->mbFloating = aDockingData.bFloating; + } + } + } + break; + case VclEventId::WindowEndDocking: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + EndDockingData *pData = static_cast<EndDockingData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::EndDockingEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.WindowRectangle = AWTRectangle( pData->maWindowRect ); + aEvent.bFloating = pData->mbFloating; + aEvent.bCancelled = pData->mbCancelled; + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::endDocking, aEvent ); + } + } + } + break; + case VclEventId::WindowPrepareToggleFloating: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + sal_Bool *p_bFloating = static_cast<sal_Bool*>(rVclWindowEvent.GetData()); + + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + + Reference< XDockableWindowListener > xFirstListener; + ::comphelper::OInterfaceIteratorHelper3 aIter( mpImpl->getDockableWindowListeners() ); + while ( aIter.hasMoreElements() && !xFirstListener.is() ) + { + xFirstListener = aIter.next(); + } + + *p_bFloating = xFirstListener->prepareToggleFloatingMode( aEvent ); + } + } + break; + case VclEventId::WindowToggleFloating: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::toggleFloatingMode, aEvent ); + } + } + break; + case VclEventId::WindowEndPopupMode: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + EndPopupModeData *pData = static_cast<EndPopupModeData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::EndPopupModeEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FloatingPosition.X = pData->maFloatingPos.X(); + aEvent.FloatingPosition.Y = pData->maFloatingPos.Y(); + aEvent.bTearoff = pData->mbTearoff; + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::endPopupMode, aEvent ); + } + } + } + break; + default: break; + } +} + +uno::Reference< accessibility::XAccessibleContext > VCLXWindow::CreateAccessibleContext() +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return nullptr; + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXWindow::SetSynthesizingVCLEvent( bool _b ) +{ + mpImpl->mbSynthesizingVCLEvent = _b; +} + +bool VCLXWindow::IsSynthesizingVCLEvent() const +{ + return mpImpl->mbSynthesizingVCLEvent; +} + +Size VCLXWindow::ImplCalcWindowSize( const Size& rOutSz ) const +{ + Size aSz = rOutSz; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + sal_Int32 nLeft, nTop, nRight, nBottom; + pWindow->GetBorder( nLeft, nTop, nRight, nBottom ); + aSz.AdjustWidth(nLeft+nRight ); + aSz.AdjustHeight(nTop+nBottom ); + } + return aSz; +} + + +// css::lang::Component +void VCLXWindow::dispose( ) +{ + SolarMutexGuard aGuard; + + if ( mpImpl->mbDisposing ) + return; + + mpImpl->mbDisposing = true; + + mpImpl->mxViewGraphics = nullptr; + + mpImpl->disposing(); + + if ( VclPtr<vcl::Window> pWindow = GetWindow() ) + { + pWindow->RemoveEventListener( LINK( this, VCLXWindow, WindowEventListener ) ); + pWindow->SetWindowPeer( nullptr, nullptr ); + pWindow->SetAccessible( nullptr ); + + SetOutputDevice( nullptr ); + pWindow.disposeAndClear(); + } + + // #i14103# dispose the accessible context after the window has been destroyed, + // otherwise the old value in the child event fired in VCLXAccessibleComponent::ProcessWindowEvent() + // for VclEventId::WindowChildDestroyed contains a reference to an already disposed accessible object + try + { + css::uno::Reference< css::lang::XComponent > xComponent( mpImpl->mxAccessibleContext, css::uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const css::uno::Exception& ) + { + OSL_FAIL( "VCLXWindow::dispose: could not dispose the accessible context!" ); + } + mpImpl->mxAccessibleContext.clear(); +} + +void VCLXWindow::addEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) // called during dispose by accessibility stuff + return; + mpImpl->getEventListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getEventListeners().removeInterface( rxListener ); +} + + +// css::awt::XWindow +void VCLXWindow::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("setPosSize"); + + if ( GetWindow() ) + { + if( vcl::Window::GetDockingManager()->IsDockable( GetWindow() ) ) + vcl::Window::GetDockingManager()->SetPosSizePixel( GetWindow() , X, Y, Width, Height, static_cast<PosSizeFlags>(Flags) ); + else + GetWindow()->setPosSizePixel( X, Y, Width, Height, static_cast<PosSizeFlags>(Flags) ); + } +} + +css::awt::Rectangle VCLXWindow::getPosSize( ) +{ + SolarMutexGuard aGuard; + + css::awt::Rectangle aBounds; + if ( GetWindow() ) + { + if( vcl::Window::GetDockingManager()->IsDockable( GetWindow() ) ) + aBounds = AWTRectangle( vcl::Window::GetDockingManager()->GetPosSizePixel( GetWindow() ) ); + else + aBounds = AWTRectangle( tools::Rectangle( GetWindow()->GetPosPixel(), GetWindow()->GetSizePixel() ) ); + } + + return aBounds; +} + +void VCLXWindow::setVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + mpImpl->setDirectVisible( bVisible ); + pWindow->Show( bVisible && mpImpl->isEnableVisible() ); + } +} + +void VCLXWindow::setEnable( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->Enable( bEnable, false ); // #95824# without children! + pWindow->EnableInput( bEnable ); + } +} + +void VCLXWindow::setFocus( ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->GrabFocus(); +} + +void VCLXWindow::addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + + mpImpl->getWindowListeners().addInterface( rxListener ); + + Reference< XWindowListener2 > xListener2( rxListener, UNO_QUERY ); + if ( xListener2.is() ) + mpImpl->getWindow2Listeners().addInterface( xListener2 ); + + // #100119# Get all resize events, even if height or width 0, or invisible + if ( GetWindow() ) + GetWindow()->EnableAllResize(); +} + +void VCLXWindow::removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (mpImpl->mbDisposing) + return; + + Reference< XWindowListener2 > xListener2( rxListener, UNO_QUERY ); + if ( xListener2.is() ) + mpImpl->getWindow2Listeners().removeInterface( xListener2 ); + + mpImpl->getWindowListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getFocusListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getFocusListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getKeyListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getKeyListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseMotionListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseMotionListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getPaintListeners().addInterface( rxListener ); +} + +void VCLXWindow::removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getPaintListeners().removeInterface( rxListener ); +} + +// css::awt::XWindowPeer +css::uno::Reference< css::awt::XToolkit > VCLXWindow::getToolkit( ) +{ + // no guard. nothing to guard here. + // 82463 - 12/21/00 - fs + return Application::GetVCLToolkit(); +} + +void VCLXWindow::setPointer( const css::uno::Reference< css::awt::XPointer >& rxPointer ) +{ + SolarMutexGuard aGuard; + + VCLXPointer* pPointer = dynamic_cast<VCLXPointer*>( rxPointer.get() ); + if ( pPointer && GetWindow() ) + GetWindow()->SetPointer( pPointer->GetPointer() ); +} + +void VCLXWindow::setBackground( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + if ( !GetWindow() ) + return; + + Color aColor(ColorTransparency, nColor); + GetWindow()->SetBackground( aColor ); + GetWindow()->SetControlBackground( aColor ); + + WindowType eWinType = GetWindow()->GetType(); + if ( ( eWinType == WindowType::WINDOW ) || + ( eWinType == WindowType::WORKWINDOW ) || + ( eWinType == WindowType::FLOATINGWINDOW ) ) + { + GetWindow()->Invalidate(); + } +} + +void VCLXWindow::invalidate( sal_Int16 nInvalidateFlags ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->Invalidate( static_cast<InvalidateFlags>(nInvalidateFlags) ); +} + +void VCLXWindow::invalidateRect( const css::awt::Rectangle& rRect, sal_Int16 nInvalidateFlags ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->Invalidate( VCLRectangle(rRect), static_cast<InvalidateFlags>(nInvalidateFlags) ); +} + + +// css::awt::XVclWindowPeer +sal_Bool VCLXWindow::isChild( const css::uno::Reference< css::awt::XWindowPeer >& rxPeer ) +{ + SolarMutexGuard aGuard; + + bool bIsChild = false; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + VclPtr<vcl::Window> pPeerWindow = VCLUnoHelper::GetWindow( rxPeer ); + bIsChild = pPeerWindow && pWindow->IsChild( pPeerWindow ); + } + + return bIsChild; +} + +void VCLXWindow::setDesignMode( sal_Bool bOn ) +{ + SolarMutexGuard aGuard; + + mpImpl->mbDesignMode = bOn; +} + +sal_Bool VCLXWindow::isDesignMode( ) +{ + SolarMutexGuard aGuard; + return mpImpl->mbDesignMode; +} + +void VCLXWindow::enableClipSiblings( sal_Bool bClip ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->EnableClipSiblings( bClip ); +} + +void VCLXWindow::setForeground( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + { + GetWindow()->SetControlForeground( Color(ColorTransparency, nColor) ); + } +} + +void VCLXWindow::setControlFont( const css::awt::FontDescriptor& rFont ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->SetControlFont( VCLUnoHelper::CreateFont( rFont, GetWindow()->GetControlFont() ) ); +} + +void VCLXWindow::getStyles( sal_Int16 nType, css::awt::FontDescriptor& Font, sal_Int32& ForegroundColor, sal_Int32& BackgroundColor ) +{ + SolarMutexGuard aGuard; + + if ( !GetWindow() ) + return; + + const StyleSettings& rStyleSettings = GetWindow()->GetSettings().GetStyleSettings(); + + switch ( nType ) + { + case css::awt::Style::FRAME: + { + Font = VCLUnoHelper::CreateFontDescriptor( rStyleSettings.GetAppFont() ); + ForegroundColor = sal_Int32(rStyleSettings.GetWindowTextColor()); + BackgroundColor = sal_Int32(rStyleSettings.GetWindowColor()); + } + break; + case css::awt::Style::DIALOG: + { + Font = VCLUnoHelper::CreateFontDescriptor( rStyleSettings.GetAppFont() ); + ForegroundColor = sal_Int32(rStyleSettings.GetDialogTextColor()); + BackgroundColor = sal_Int32(rStyleSettings.GetDialogColor()); + } + break; + default: OSL_FAIL( "VCLWindow::getStyles() - unknown Type" ); + } +} + +namespace toolkit +{ + static void setColorSettings( vcl::Window* _pWindow, const css::uno::Any& _rValue, + void (StyleSettings::*pSetter)( const Color& ), const Color& (StyleSettings::*pGetter)( ) const ) + { + sal_Int32 nColor = 0; + if ( !( _rValue >>= nColor ) ) + nColor = sal_Int32((Application::GetSettings().GetStyleSettings().*pGetter)()); + + AllSettings aSettings = _pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + + (aStyleSettings.*pSetter)( Color( ColorTransparency, nColor ) ); + + aSettings.SetStyleSettings( aStyleSettings ); + _pWindow->SetSettings( aSettings, true ); + } +} + +// Terminated by BASEPROPERTY_NOTFOUND (or 0) +void VCLXWindow::PushPropertyIds( std::vector< sal_uInt16 > &rIds, + int nFirstId, ...) +{ + va_list pVarArgs; + va_start( pVarArgs, nFirstId ); + + for ( int nId = nFirstId; nId != BASEPROPERTY_NOTFOUND; + nId = va_arg( pVarArgs, int ) ) + rIds.push_back( static_cast<sal_uInt16>(nId) ); + + va_end( pVarArgs ); +} + +void VCLXWindow::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds, bool bWithDefaults ) +{ + // These are common across ~all VCLXWindow derived classes + if( bWithDefaults ) + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_TEXT, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_ENABLEVISIBLE, // for visibility + BASEPROPERTY_TABSTOP, + 0); + + // lovely hack from: + // void UnoControlModel::ImplRegisterProperty( sal_uInt16 nPropId ) + if( std::find(rIds.begin(), rIds.end(), BASEPROPERTY_FONTDESCRIPTOR) != rIds.end() ) + { + // 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... + + rIds.push_back( BASEPROPERTY_TEXTCOLOR ); + rIds.push_back( BASEPROPERTY_TEXTLINECOLOR ); + rIds.push_back( BASEPROPERTY_FONTRELIEF ); + rIds.push_back( BASEPROPERTY_FONTEMPHASISMARK ); + } +} + +void VCLXWindow::GetPropertyIds( std::vector< sal_uInt16 >& _out_rIds ) +{ + return ImplGetPropertyIds( _out_rIds, mpImpl->mbWithDefaultProps ); +} + +ListenerMultiplexerBase<css::awt::XVclContainerListener>& VCLXWindow::GetContainerListeners() +{ + return mpImpl->getContainerListeners(); +} + +ListenerMultiplexerBase<css::awt::XTopWindowListener>& VCLXWindow::GetTopWindowListeners() +{ + return mpImpl->getTopWindowListeners(); +} + +namespace +{ + void lcl_updateWritingMode( vcl::Window& _rWindow, const sal_Int16 _nWritingMode, const sal_Int16 _nContextWritingMode ) + { + bool bEnableRTL = false; + switch ( _nWritingMode ) + { + case WritingMode2::LR_TB: bEnableRTL = false; break; + case WritingMode2::RL_TB: bEnableRTL = true; break; + case WritingMode2::CONTEXT: + { + // consult our ContextWritingMode. If it has an explicit RTL/LTR value, then use + // it. If it doesn't (but is CONTEXT itself), then just ask the parent window of our + // own window for its RTL mode + switch ( _nContextWritingMode ) + { + case WritingMode2::LR_TB: bEnableRTL = false; break; + case WritingMode2::RL_TB: bEnableRTL = true; break; + case WritingMode2::CONTEXT: + { + const vcl::Window* pParent = _rWindow.GetParent(); + OSL_ENSURE( pParent, "lcl_updateWritingMode: cannot determine context's writing mode!" ); + if ( pParent ) + bEnableRTL = pParent->IsRTLEnabled(); + } + break; + } + } + break; + default: + OSL_FAIL( "lcl_updateWritingMode: unsupported WritingMode!" ); + } // switch ( nWritingMode ) + + _rWindow.EnableRTL( bEnableRTL ); + } +} + +void VCLXWindow::setProperty( const OUString& PropertyName, const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( !pWindow ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + WindowType eWinType = pWindow->GetType(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_REFERENCE_DEVICE: + { + Control* pControl = dynamic_cast< Control* >( pWindow.get() ); + OSL_ENSURE( pControl, "VCLXWindow::setProperty( RefDevice ): need a Control for this!" ); + if ( !pControl ) + break; + Reference< XDevice > xDevice( Value, UNO_QUERY ); + OutputDevice* pDevice = VCLUnoHelper::GetOutputDevice( xDevice ); + pControl->SetReferenceDevice( pDevice ); + } + break; + + case BASEPROPERTY_CONTEXT_WRITING_MODE: + { + OSL_VERIFY( Value >>= mpImpl->mnContextWritingMode ); + if ( mpImpl->mnWritingMode == WritingMode2::CONTEXT ) + lcl_updateWritingMode( *pWindow, mpImpl->mnWritingMode, mpImpl->mnContextWritingMode ); + } + break; + + case BASEPROPERTY_WRITING_MODE: + { + bool bProperType = ( Value >>= mpImpl->mnWritingMode ); + OSL_ENSURE( bProperType, "VCLXWindow::setProperty( 'WritingMode' ): illegal value type!" ); + if ( bProperType ) + lcl_updateWritingMode( *pWindow, mpImpl->mnWritingMode, mpImpl->mnContextWritingMode ); + } + break; + + case BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR: + { + sal_uInt16 nWheelBehavior( css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY ); + OSL_VERIFY( Value >>= nWheelBehavior ); + + AllSettings aSettings = pWindow->GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + MouseWheelBehaviour nVclBehavior( MouseWheelBehaviour::FocusOnly ); + switch ( nWheelBehavior ) + { + case css::awt::MouseWheelBehavior::SCROLL_DISABLED: nVclBehavior = MouseWheelBehaviour::Disable; break; + case css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY: nVclBehavior = MouseWheelBehaviour::FocusOnly; break; + case css::awt::MouseWheelBehavior::SCROLL_ALWAYS: nVclBehavior = MouseWheelBehaviour::ALWAYS; break; + default: + OSL_FAIL( "VCLXWindow::setProperty( 'MouseWheelBehavior' ): illegal property value!" ); + } + + aMouseSettings.SetWheelBehavior( nVclBehavior ); + aSettings.SetMouseSettings( aMouseSettings ); + pWindow->SetSettings( aSettings, true ); + } + break; + + case BASEPROPERTY_NATIVE_WIDGET_LOOK: + { + bool bEnable( true ); + OSL_VERIFY( Value >>= bEnable ); + pWindow->EnableNativeWidget( bEnable ); + } + break; + + case BASEPROPERTY_PLUGINPARENT: + { + // set parent handle + SetSystemParent_Impl( Value ); + } + break; + + case BASEPROPERTY_ENABLED: + { + bool b = bool(); + if ( Value >>= b ) + setEnable( b ); + } + break; + case BASEPROPERTY_ENABLEVISIBLE: + { + bool b = false; + if ( Value >>= b ) + { + if( b != mpImpl->isEnableVisible() ) + { + mpImpl->setEnableVisible( b ); + pWindow->Show( b && mpImpl->isDirectVisible() ); + } + } + } + break; + case BASEPROPERTY_TEXT: + case BASEPROPERTY_LABEL: + case BASEPROPERTY_TITLE: + { + OUString aText; + if ( Value >>= aText ) + { + switch (eWinType) + { + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: + case WindowType::HELPBUTTON: + // Standard Button: overwrite only if not empty. + if (!aText.isEmpty()) + pWindow->SetText( aText ); + break; + + default: + pWindow->SetText( aText ); + break; + } + } + } + break; + case BASEPROPERTY_ACCESSIBLENAME: + { + OUString aText; + if ( Value >>= aText ) + pWindow->SetAccessibleName( aText ); + } + break; + case BASEPROPERTY_HELPURL: + { + OUString aURL; + if ( Value >>= aURL ) + { + INetURLObject aHelpURL( aURL ); + if ( aHelpURL.GetProtocol() == INetProtocol::Hid ) + pWindow->SetHelpId( aHelpURL.GetURLPath() ); + else + pWindow->SetHelpId( aURL ); + } + } + break; + case BASEPROPERTY_HELPTEXT: + { + OUString aHelpText; + if ( Value >>= aHelpText ) + { + pWindow->SetQuickHelpText( aHelpText ); + } + } + break; + case BASEPROPERTY_FONTDESCRIPTOR: + { + if ( bVoid ) + pWindow->SetControlFont( vcl::Font() ); + else + { + css::awt::FontDescriptor aFont; + if ( Value >>= aFont ) + pWindow->SetControlFont( VCLUnoHelper::CreateFont( aFont, pWindow->GetControlFont() ) ); + } + } + break; + case BASEPROPERTY_FONTRELIEF: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + { + vcl::Font aFont = pWindow->GetControlFont(); + aFont.SetRelief( static_cast<FontRelief>(n) ); + pWindow->SetControlFont( aFont ); + } + } + break; + case BASEPROPERTY_FONTEMPHASISMARK: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + { + vcl::Font aFont = pWindow->GetControlFont(); + aFont.SetEmphasisMark( static_cast<FontEmphasisMark>(n) ); + pWindow->SetControlFont( aFont ); + } + } + break; + case BASEPROPERTY_BACKGROUNDCOLOR: + if ( bVoid ) + { + switch ( eWinType ) + { + // set dialog color for default + case WindowType::DIALOG: + case WindowType::MESSBOX: + case WindowType::INFOBOX: + case WindowType::WARNINGBOX: + case WindowType::ERRORBOX: + case WindowType::QUERYBOX: + case WindowType::TABPAGE: + { + Color aColor = pWindow->GetSettings().GetStyleSettings().GetDialogColor(); + pWindow->SetBackground( aColor ); + pWindow->SetControlBackground( aColor ); + break; + } + + case WindowType::FIXEDTEXT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::GROUPBOX: + case WindowType::FIXEDLINE: + { + // support transparency only for special controls + pWindow->SetBackground(); + pWindow->SetControlBackground(); + pWindow->SetPaintTransparent( true ); + break; + } + + default: + { + // default code which enables transparency for + // compound controls. It's not real transparency + // as most of these controls repaint their client + // area completely new. + if ( pWindow->IsCompoundControl() ) + pWindow->SetBackground(); + pWindow->SetControlBackground(); + break; + } + } + } + else + { + Color aColor; + if ( Value >>= aColor ) + { + pWindow->SetControlBackground( aColor ); + pWindow->SetBackground( aColor ); + switch ( eWinType ) + { + // reset paint transparent mode + case WindowType::FIXEDTEXT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::GROUPBOX: + case WindowType::FIXEDLINE: + pWindow->SetPaintTransparent( false ); + break; + default: + break; + } + pWindow->Invalidate(); // Invalidate if control does not respond to it + } + } + break; + case BASEPROPERTY_TEXTCOLOR: + if ( bVoid ) + { + pWindow->SetControlForeground(); + } + else + { + Color nColor ; + if ( Value >>= nColor ) + { + pWindow->SetTextColor( nColor ); + pWindow->SetControlForeground( nColor ); + } + } + break; + case BASEPROPERTY_TEXTLINECOLOR: + if ( bVoid ) + { + pWindow->SetTextLineColor(); + } + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->SetTextLineColor( nColor ); + } + break; + case BASEPROPERTY_FILLCOLOR: + if ( bVoid ) + pWindow->GetOutDev()->SetFillColor(); + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->GetOutDev()->SetFillColor( nColor ); + } + break; + case BASEPROPERTY_LINECOLOR: + if ( bVoid ) + pWindow->GetOutDev()->SetLineColor(); + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->GetOutDev()->SetLineColor( nColor ); + } + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + { + Color nColor = 0; + if ( bVoid ) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + + AllSettings aSettings(pWindow->GetSettings()); + StyleSettings aStyle(aSettings.GetStyleSettings()); + aStyle.SetHighlightColor(nColor); + aSettings.SetStyleSettings(aStyle); + pWindow->SetSettings(aSettings); + } + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + { + Color nColor = 0; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightTextColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + + AllSettings aSettings(pWindow->GetSettings()); + StyleSettings aStyle(aSettings.GetStyleSettings()); + aStyle.SetHighlightTextColor(nColor); + aSettings.SetStyleSettings(aStyle); + pWindow->SetSettings(aSettings); + } + break; + case BASEPROPERTY_BORDER: + { + WinBits nStyle = pWindow->GetStyle(); + sal_uInt16 nTmp = 0; + Value >>= nTmp; + // clear any dodgy bits passed in, can come from dodgy extensions + nTmp &= o3tl::typed_flags<WindowBorderStyle>::mask; + WindowBorderStyle nBorder = static_cast<WindowBorderStyle>(nTmp); + if ( !bool(nBorder) ) + { + pWindow->SetStyle( nStyle & ~WB_BORDER ); + } + else + { + pWindow->SetStyle( nStyle | WB_BORDER ); + pWindow->SetBorderStyle( nBorder ); + } + } + break; + case BASEPROPERTY_TABSTOP: + { + WinBits nStyle = pWindow->GetStyle() & ~WB_TABSTOP; + if ( !bVoid ) + { + bool bTab = false; + Value >>= bTab; + if ( bTab ) + nStyle |= WB_TABSTOP; + else + nStyle |= WB_NOTABSTOP; + } + pWindow->SetStyle( nStyle ); + } + break; + case BASEPROPERTY_VERTICALALIGN: + { + VerticalAlignment eAlign = css::style::VerticalAlignment::VerticalAlignment_MAKE_FIXED_SIZE; + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_TOP|WB_VCENTER|WB_BOTTOM); + if ( !bVoid ) + Value >>= eAlign; + switch ( eAlign ) + { + case VerticalAlignment_TOP: + nStyle |= WB_TOP; + break; + case VerticalAlignment_MIDDLE: + nStyle |= WB_VCENTER; + break; + case VerticalAlignment_BOTTOM: + nStyle |= WB_BOTTOM; + break; + default: ; // for warning free code, MAKE_FIXED_SIZE + } + pWindow->SetStyle( nStyle ); + } + break; + case BASEPROPERTY_ALIGN: + { + sal_Int16 nAlign = PROPERTY_ALIGN_LEFT; + switch ( eWinType ) + { + case WindowType::COMBOBOX: + case WindowType::PUSHBUTTON: + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: + case WindowType::HELPBUTTON: + nAlign = PROPERTY_ALIGN_CENTER; + [[fallthrough]]; + case WindowType::FIXEDTEXT: + case WindowType::EDIT: + case WindowType::MULTILINEEDIT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::LISTBOX: + { + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_LEFT|WB_CENTER|WB_RIGHT); + if ( !bVoid ) + Value >>= nAlign; + if ( nAlign == PROPERTY_ALIGN_LEFT ) + nStyle |= WB_LEFT; + else if ( nAlign == PROPERTY_ALIGN_CENTER ) + nStyle |= WB_CENTER; + else + nStyle |= WB_RIGHT; + pWindow->SetStyle( nStyle ); + } + break; + default: break; + } + } + break; + case BASEPROPERTY_MULTILINE: + { + if ( ( eWinType == WindowType::FIXEDTEXT ) + || ( eWinType == WindowType::CHECKBOX ) + || ( eWinType == WindowType::RADIOBUTTON ) + || ( eWinType == WindowType::PUSHBUTTON ) + || ( eWinType == WindowType::OKBUTTON ) + || ( eWinType == WindowType::CANCELBUTTON ) + || ( eWinType == WindowType::HELPBUTTON ) + ) + { + WinBits nStyle = pWindow->GetStyle(); + bool bMulti = false; + Value >>= bMulti; + if ( bMulti ) + nStyle |= WB_WORDBREAK; + else + nStyle &= ~WB_WORDBREAK; + pWindow->SetStyle( nStyle ); + } + } + break; + case BASEPROPERTY_ORIENTATION: + { + if ( eWinType == WindowType::FIXEDLINE) + { + sal_Int32 nOrientation = 0; + if ( Value >>= nOrientation ) + { + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_HORZ|WB_VERT); + if ( nOrientation == 0 ) + nStyle |= WB_HORZ; + else + nStyle |= WB_VERT; + + pWindow->SetStyle( nStyle ); + } + } + } + break; + case BASEPROPERTY_AUTOMNEMONICS: + { + bool bAutoMnemonics = false; + Value >>= bAutoMnemonics; + AllSettings aSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + if ( aStyleSettings.GetAutoMnemonic() != bAutoMnemonics ) + { + aStyleSettings.SetAutoMnemonic( bAutoMnemonics ); + aSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aSettings ); + } + } + break; + case BASEPROPERTY_MOUSETRANSPARENT: + { + bool bMouseTransparent = false; + Value >>= bMouseTransparent; + pWindow->SetMouseTransparent( bMouseTransparent ); + } + break; + case BASEPROPERTY_PAINTTRANSPARENT: + { + bool bPaintTransparent = false; + Value >>= bPaintTransparent; + pWindow->SetPaintTransparent( bPaintTransparent ); +// pWindow->SetBackground(); + } + break; + + case BASEPROPERTY_REPEAT: + { + bool bRepeat( false ); + Value >>= bRepeat; + + WinBits nStyle = pWindow->GetStyle(); + if ( bRepeat ) + nStyle |= WB_REPEAT; + else + nStyle &= ~WB_REPEAT; + pWindow->SetStyle( nStyle ); + } + break; + + case BASEPROPERTY_REPEAT_DELAY: + { + sal_Int32 nRepeatDelay = 0; + if ( Value >>= nRepeatDelay ) + { + AllSettings aSettings = pWindow->GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + aMouseSettings.SetButtonRepeat( nRepeatDelay ); + aSettings.SetMouseSettings( aMouseSettings ); + + pWindow->SetSettings( aSettings, true ); + } + } + break; + + case BASEPROPERTY_SYMBOL_COLOR: + ::toolkit::setColorSettings( pWindow, Value, &StyleSettings::SetButtonTextColor, &StyleSettings::GetButtonTextColor ); + break; + + case BASEPROPERTY_BORDERCOLOR: + ::toolkit::setColorSettings( pWindow, Value, &StyleSettings::SetMonoColor, &StyleSettings::GetMonoColor); + break; + } +} + +css::uno::Any VCLXWindow::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( GetWindow() ) + { + if (PropertyName == "ParentIs100thmm") + { + bool bParentIs100thmm = false; + VclPtr<vcl::Window> pWindow = GetWindow(); + if (pWindow) + { + pWindow = pWindow->GetParent(); + if(pWindow && MapUnit::Map100thMM == pWindow->GetMapMode().GetMapUnit()) + { + bParentIs100thmm = true; + } + } + aProp <<= bParentIs100thmm; + return aProp; + } + WindowType eWinType = GetWindow()->GetType(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_REFERENCE_DEVICE: + { + VclPtr<Control> pControl = GetAsDynamic<Control >(); + OSL_ENSURE( pControl, "VCLXWindow::setProperty( RefDevice ): need a Control for this!" ); + if ( !pControl ) + break; + + rtl::Reference<VCLXDevice> pDevice = new VCLXDevice; + pDevice->SetOutputDevice( pControl->GetReferenceDevice() ); + aProp <<= Reference< XDevice >( pDevice ); + } + break; + + case BASEPROPERTY_CONTEXT_WRITING_MODE: + aProp <<= mpImpl->mnContextWritingMode; + break; + + case BASEPROPERTY_WRITING_MODE: + aProp <<= mpImpl->mnWritingMode; + break; + + case BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR: + { + MouseWheelBehaviour nVclBehavior = GetWindow()->GetSettings().GetMouseSettings().GetWheelBehavior(); + sal_uInt16 nBehavior = css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY; + switch ( nVclBehavior ) + { + case MouseWheelBehaviour::Disable: nBehavior = css::awt::MouseWheelBehavior::SCROLL_DISABLED; break; + case MouseWheelBehaviour::FocusOnly: nBehavior = css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY; break; + case MouseWheelBehaviour::ALWAYS: nBehavior = css::awt::MouseWheelBehavior::SCROLL_ALWAYS; break; + default: + OSL_FAIL( "VCLXWindow::getProperty( 'MouseWheelBehavior' ): illegal VCL value!" ); + } + aProp <<= nBehavior; + } + break; + + case BASEPROPERTY_NATIVE_WIDGET_LOOK: + aProp <<= GetWindow()->IsNativeWidgetEnabled(); + break; + + case BASEPROPERTY_ENABLED: + aProp <<= GetWindow()->IsEnabled(); + break; + + case BASEPROPERTY_ENABLEVISIBLE: + aProp <<= mpImpl->isEnableVisible(); + break; + + case BASEPROPERTY_HIGHCONTRASTMODE: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetHighContrastMode(); + break; + + case BASEPROPERTY_TEXT: + case BASEPROPERTY_LABEL: + case BASEPROPERTY_TITLE: + { + OUString aText = GetWindow()->GetText(); + aProp <<= aText; + } + break; + case BASEPROPERTY_ACCESSIBLENAME: + { + OUString aText = GetWindow()->GetAccessibleName(); + aProp <<= aText; + } + break; + case BASEPROPERTY_HELPTEXT: + { + OUString aText = GetWindow()->GetQuickHelpText(); + aProp <<= aText; + } + break; + case BASEPROPERTY_HELPURL: + aProp <<= GetWindow()->GetHelpId(); + break; + case BASEPROPERTY_FONTDESCRIPTOR: + { + vcl::Font aFont = GetWindow()->GetControlFont(); + css::awt::FontDescriptor aFD = VCLUnoHelper::CreateFontDescriptor( aFont ); + aProp <<= aFD; + } + break; + case BASEPROPERTY_BACKGROUNDCOLOR: + aProp <<= GetWindow()->GetControlBackground(); + break; + case BASEPROPERTY_DISPLAYBACKGROUNDCOLOR: + aProp <<= GetWindow()->GetBackgroundColor(); + break; + case BASEPROPERTY_FONTRELIEF: + aProp <<= static_cast<sal_Int16>(GetWindow()->GetControlFont().GetRelief()); + break; + case BASEPROPERTY_FONTEMPHASISMARK: + aProp <<= static_cast<sal_Int16>(GetWindow()->GetControlFont().GetEmphasisMark()); + break; + case BASEPROPERTY_TEXTCOLOR: + aProp <<= GetWindow()->GetControlForeground(); + break; + case BASEPROPERTY_TEXTLINECOLOR: + aProp <<= GetWindow()->GetTextLineColor(); + break; + case BASEPROPERTY_FILLCOLOR: + aProp <<= GetWindow()->GetOutDev()->GetFillColor(); + break; + case BASEPROPERTY_LINECOLOR: + aProp <<= GetWindow()->GetOutDev()->GetLineColor(); + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetHighlightColor(); + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetHighlightTextColor(); + break; + case BASEPROPERTY_BORDER: + { + WindowBorderStyle nBorder = WindowBorderStyle::NONE; + if ( GetWindow()->GetStyle() & WB_BORDER ) + nBorder = GetWindow()->GetBorderStyle(); + aProp <<= static_cast<sal_uInt16>(nBorder); + } + break; + case BASEPROPERTY_TABSTOP: + aProp <<= ( GetWindow()->GetStyle() & WB_TABSTOP ) != 0; + break; + case BASEPROPERTY_VERTICALALIGN: + { + WinBits nStyle = GetWindow()->GetStyle(); + if ( nStyle & WB_TOP ) + aProp <<= VerticalAlignment_TOP; + else if ( nStyle & WB_VCENTER ) + aProp <<= VerticalAlignment_MIDDLE; + else if ( nStyle & WB_BOTTOM ) + aProp <<= VerticalAlignment_BOTTOM; + } + break; + case BASEPROPERTY_ALIGN: + { + switch ( eWinType ) + { + case WindowType::FIXEDTEXT: + case WindowType::EDIT: + case WindowType::MULTILINEEDIT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::LISTBOX: + case WindowType::COMBOBOX: + case WindowType::PUSHBUTTON: + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: + case WindowType::HELPBUTTON: + { + WinBits nStyle = GetWindow()->GetStyle(); + if ( nStyle & WB_LEFT ) + aProp <<= sal_Int16(PROPERTY_ALIGN_LEFT); + else if ( nStyle & WB_CENTER ) + aProp <<= sal_Int16(PROPERTY_ALIGN_CENTER); + else if ( nStyle & WB_RIGHT ) + aProp <<= sal_Int16(PROPERTY_ALIGN_RIGHT); + } + break; + default: break; + } + } + break; + case BASEPROPERTY_MULTILINE: + { + if ( ( eWinType == WindowType::FIXEDTEXT ) + || ( eWinType == WindowType::CHECKBOX ) + || ( eWinType == WindowType::RADIOBUTTON ) + || ( eWinType == WindowType::PUSHBUTTON ) + || ( eWinType == WindowType::OKBUTTON ) + || ( eWinType == WindowType::CANCELBUTTON ) + || ( eWinType == WindowType::HELPBUTTON ) + ) + aProp <<= ( GetWindow()->GetStyle() & WB_WORDBREAK ) != 0; + } + break; + case BASEPROPERTY_AUTOMNEMONICS: + { + bool bAutoMnemonics = GetWindow()->GetSettings().GetStyleSettings().GetAutoMnemonic(); + aProp <<= bAutoMnemonics; + } + break; + case BASEPROPERTY_MOUSETRANSPARENT: + { + bool bMouseTransparent = GetWindow()->IsMouseTransparent(); + aProp <<= bMouseTransparent; + } + break; + case BASEPROPERTY_PAINTTRANSPARENT: + { + bool bPaintTransparent = GetWindow()->IsPaintTransparent(); + aProp <<= bPaintTransparent; + } + break; + + case BASEPROPERTY_REPEAT: + aProp <<= ( 0 != ( GetWindow()->GetStyle() & WB_REPEAT ) ); + break; + + case BASEPROPERTY_REPEAT_DELAY: + { + sal_Int32 nButtonRepeat = GetWindow()->GetSettings().GetMouseSettings().GetButtonRepeat(); + aProp <<= nButtonRepeat; + } + break; + + case BASEPROPERTY_SYMBOL_COLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetButtonTextColor(); + break; + + case BASEPROPERTY_BORDERCOLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetMonoColor(); + break; + } + } + return aProp; +} + + +// css::awt::XLayoutConstrains +css::awt::Size VCLXWindow::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + // Use this method only for those components which can be created through + // css::awt::Toolkit , but do not have an interface + + Size aSz; + if ( GetWindow() ) + { + WindowType nWinType = GetWindow()->GetType(); + switch ( nWinType ) + { + case WindowType::CONTROL: + aSz.setWidth( GetWindow()->GetTextWidth( GetWindow()->GetText() )+2*12 ); + aSz.setHeight( GetWindow()->GetTextHeight()+2*6 ); + break; + + case WindowType::PATTERNBOX: + case WindowType::NUMERICBOX: + case WindowType::METRICBOX: + case WindowType::CURRENCYBOX: + case WindowType::DATEBOX: + case WindowType::TIMEBOX: + case WindowType::LONGCURRENCYBOX: + aSz.setWidth( GetWindow()->GetTextWidth( GetWindow()->GetText() )+2*2 ); + aSz.setHeight( GetWindow()->GetTextHeight()+2*2 ); + break; + case WindowType::SCROLLBARBOX: + return VCLXScrollBar::implGetMinimumSize( GetWindow() ); + default: + aSz = GetWindow()->get_preferred_size(); + } + } + + return css::awt::Size( aSz.Width(), aSz.Height() ); +} + +css::awt::Size VCLXWindow::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXWindow::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aNewSize( rNewSize ); + css::awt::Size aMinSize = getMinimumSize(); + + if ( aNewSize.Width < aMinSize.Width ) + aNewSize.Width = aMinSize.Width; + if ( aNewSize.Height < aMinSize.Height ) + aNewSize.Height = aMinSize.Height; + + return aNewSize; +} + + +// css::awt::XView +sal_Bool VCLXWindow::setGraphics( const css::uno::Reference< css::awt::XGraphics >& rxDevice ) +{ + SolarMutexGuard aGuard; + + if ( VCLUnoHelper::GetOutputDevice( rxDevice ) ) + mpImpl->mxViewGraphics = rxDevice; + else + mpImpl->mxViewGraphics = nullptr; + + return mpImpl->mxViewGraphics.is(); +} + +css::uno::Reference< css::awt::XGraphics > VCLXWindow::getGraphics( ) +{ + SolarMutexGuard aGuard; + + return mpImpl->mxViewGraphics; +} + +css::awt::Size VCLXWindow::getSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + if ( GetWindow() ) + aSz = GetWindow()->GetSizePixel(); + return css::awt::Size( aSz.Width(), aSz.Height() ); +} + +void VCLXWindow::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( !pWindow ) + return; + + if ( !(isDesignMode() || mpImpl->isEnableVisible()) ) + return; + + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( mpImpl->mxViewGraphics ); + if (!pDev) + pDev = pWindow->GetParent()->GetOutDev(); + TabPage* pTabPage = dynamic_cast< TabPage* >( pWindow.get() ); + if ( pTabPage ) + { + Point aPos( nX, nY ); + aPos = pDev->PixelToLogic( aPos ); + pTabPage->Draw( pDev, aPos, SystemTextColorFlags::NONE ); + return; + } + + Point aPos( nX, nY ); + + if ( pWindow->GetParent() && !pWindow->IsSystemWindow() && ( pWindow->GetParent()->GetOutDev() == pDev ) ) + { + // #i40647# don't draw here if this is a recursive call + // sometimes this is called recursively, because the Update call on the parent + // (strangely) triggers another paint. Prevent a stack overflow here + // Yes, this is only fixing symptoms for the moment... + // #i40647# / 2005-01-18 / frank.schoenheit@sun.com + if ( !mpImpl->getDrawingOntoParent_ref() ) + { + ::comphelper::FlagGuard aDrawingflagGuard( mpImpl->getDrawingOntoParent_ref() ); + + bool bWasVisible = pWindow->IsVisible(); + Point aOldPos( pWindow->GetPosPixel() ); + + if ( bWasVisible && aOldPos == aPos ) + { + pWindow->PaintImmediately(); + return; + } + + pWindow->SetPosPixel( aPos ); + + // Update parent first to avoid painting the parent upon the update + // of this window, as it may otherwise cause the parent + // to hide this window again + if( pWindow->GetParent() ) + pWindow->GetParent()->PaintImmediately(); + + pWindow->Show(); + pWindow->PaintImmediately(); + pWindow->SetParentUpdateMode( false ); + pWindow->Hide(); + pWindow->SetParentUpdateMode( true ); + + pWindow->SetPosPixel( aOldPos ); + if ( bWasVisible ) + pWindow->Show(); + } + } + else if ( pDev ) + { + Point aP = pDev->PixelToLogic( aPos ); + + vcl::PDFExtOutDevData* pPDFExport = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData()); + bool bDrawSimple = ( pDev->GetOutDevType() == OUTDEV_PRINTER ) + || ( pDev->GetOutDevViewType() == OutDevViewType::PrintPreview ) + || ( pPDFExport != nullptr ); + if ( bDrawSimple ) + { + pWindow->Draw( pDev, aP, SystemTextColorFlags::NoControls ); + } + else + { + bool bOldNW =pWindow->IsNativeWidgetEnabled(); + if( bOldNW ) + pWindow->EnableNativeWidget(false); + pWindow->PaintToDevice( pDev, aP ); + if( bOldNW ) + pWindow->EnableNativeWidget(); + } + } +} + +void VCLXWindow::setZoom( float fZoomX, float /*fZoomY*/ ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + { + // Fraction::Fraction takes a double, but we have a float only. + // The implicit conversion from float to double can result in a precision loss, i.e. 1.2 is converted to + // 1.200000000047something. To prevent this, we convert explicitly to double, and round it. + double nZoom( fZoomX ); + Fraction aZoom(::rtl::math::round(nZoom, 4)); + aZoom.ReduceInaccurate(10); // to avoid runovers and BigInt mapping + GetWindow()->SetZoom(aZoom); + } +} + +// css::lang::XEventListener +void SAL_CALL VCLXWindow::disposing( const css::lang::EventObject& _rSource ) +{ + SolarMutexGuard aGuard; + + if (mpImpl->mbDisposing) + return; + + // check if it comes from our AccessibleContext + uno::Reference< uno::XInterface > aAC( mpImpl->mxAccessibleContext, uno::UNO_QUERY ); + uno::Reference< uno::XInterface > xSource( _rSource.Source, uno::UNO_QUERY ); + + if ( aAC.get() == xSource.get() ) + { // yep, it does + mpImpl->mxAccessibleContext.clear(); + } +} + +// css::accessibility::XAccessible +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXWindow::getAccessibleContext( ) +{ + SolarMutexGuard aGuard; + + // already disposed + if (mpImpl->mbDisposing) + return uno::Reference< accessibility::XAccessibleContext >(); + + if ( !mpImpl->mxAccessibleContext.is() && GetWindow() ) + { + mpImpl->mxAccessibleContext = CreateAccessibleContext(); + + // add as event listener to this component + // in case somebody disposes it, we do not want to have a (though weak) reference to a dead + // object + uno::Reference< lang::XComponent > xComp( mpImpl->mxAccessibleContext, uno::UNO_QUERY ); + if ( xComp.is() ) + xComp->addEventListener( this ); + } + + return mpImpl->mxAccessibleContext; +} + +// css::awt::XDockable +void SAL_CALL VCLXWindow::addDockableWindowListener( const css::uno::Reference< css::awt::XDockableWindowListener >& xListener ) +{ + SolarMutexGuard aGuard; + + if (!mpImpl->mbDisposing && xListener.is() ) + mpImpl->getDockableWindowListeners().addInterface( xListener ); + +} + +void SAL_CALL VCLXWindow::removeDockableWindowListener( const css::uno::Reference< css::awt::XDockableWindowListener >& xListener ) +{ + SolarMutexGuard aGuard; + + if (!mpImpl->mbDisposing) + mpImpl->getDockableWindowListeners().removeInterface( xListener ); +} + +void SAL_CALL VCLXWindow::enableDocking( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->EnableDocking( bEnable ); +} + +sal_Bool SAL_CALL VCLXWindow::isFloating( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow ) + return vcl::Window::GetDockingManager()->IsFloating( pWindow ); + else + return false; +} + +void SAL_CALL VCLXWindow::setFloatingMode( sal_Bool bFloating ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow ) + vcl::Window::GetDockingManager()->SetFloatingMode( pWindow, bFloating ); +} + +sal_Bool SAL_CALL VCLXWindow::isLocked( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow ) + return vcl::Window::GetDockingManager()->IsLocked( pWindow ); + else + return false; +} + +void SAL_CALL VCLXWindow::lock( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow && !vcl::Window::GetDockingManager()->IsFloating( pWindow ) ) + vcl::Window::GetDockingManager()->Lock( pWindow ); +} + +void SAL_CALL VCLXWindow::unlock( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow && !vcl::Window::GetDockingManager()->IsFloating( pWindow ) ) + vcl::Window::GetDockingManager()->Unlock( pWindow ); +} + +void SAL_CALL VCLXWindow::startPopupMode( const css::awt::Rectangle& ) +{ + // deprecated +} + +sal_Bool SAL_CALL VCLXWindow::isInPopupMode( ) +{ + // deprecated + return false; +} + + +// css::awt::XWindow2 + +void SAL_CALL VCLXWindow::setOutputSize( const css::awt::Size& aSize ) +{ + SolarMutexGuard aGuard; + if( VclPtr<vcl::Window> pWindow = GetWindow() ) + pWindow->SetOutputSizePixel( VCLSize( aSize ) ); +} + +css::awt::Size SAL_CALL VCLXWindow::getOutputSize( ) +{ + SolarMutexGuard aGuard; + if( VclPtr<vcl::Window> pWindow = GetWindow() ) + return AWTSize( pWindow->GetOutputSizePixel() ); + else + return css::awt::Size(); +} + +sal_Bool SAL_CALL VCLXWindow::isVisible( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->IsVisible(); + else + return false; +} + +sal_Bool SAL_CALL VCLXWindow::isActive( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->IsActive(); + else + return false; + +} + +sal_Bool SAL_CALL VCLXWindow::isEnabled( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->IsEnabled(); + else + return false; +} + +sal_Bool SAL_CALL VCLXWindow::hasFocus( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->HasFocus(); + else + return false; +} + +// css::beans::XPropertySetInfo + +UnoPropertyArrayHelper * +VCLXWindow::GetPropHelper() +{ + SolarMutexGuard aGuard; + if ( mpImpl->mpPropHelper == nullptr ) + { + std::vector< sal_uInt16 > aIDs; + GetPropertyIds( aIDs ); + mpImpl->mpPropHelper.reset( new UnoPropertyArrayHelper( aIDs ) ); + } + return mpImpl->mpPropHelper.get(); +} + +css::uno::Sequence< css::beans::Property > SAL_CALL +VCLXWindow::getProperties() +{ + return GetPropHelper()->getProperties(); +} +css::beans::Property SAL_CALL +VCLXWindow::getPropertyByName( const OUString& rName ) +{ + return GetPropHelper()->getPropertyByName( rName ); +} + +sal_Bool SAL_CALL +VCLXWindow::hasPropertyByName( const OUString& rName ) +{ + return GetPropHelper()->hasPropertyByName( rName ); +} + +Reference< XStyleSettings > SAL_CALL VCLXWindow::getStyleSettings() +{ + return mpImpl->getStyleSettings(); +} + +bool VCLXWindow::IsDisposed() const +{ + return mpImpl->mbDisposing; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindow1.cxx b/toolkit/source/awt/vclxwindow1.cxx new file mode 100644 index 0000000000..edc78f7d92 --- /dev/null +++ b/toolkit/source/awt/vclxwindow1.cxx @@ -0,0 +1,90 @@ +/* -*- 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 <toolkit/awt/vclxwindow.hxx> +#include <com/sun/star/beans/NamedValue.hpp> +#include <vcl/wrkwin.hxx> +#include <vcl/window.hxx> + +#ifdef _WIN32 +#include <prewin.h> +#include <postwin.h> +#elif defined(MACOSX) +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif +#include <vcl/sysdata.hxx> + +/// helper method to set a window handle into a SystemParentData struct +void VCLXWindow::SetSystemParent_Impl(const css::uno::Any& rHandle) +{ + // does only work for WorkWindows + VclPtr<vcl::Window> pWindow = GetWindow(); + if (pWindow->GetType() != WindowType::WORKWINDOW) + { + throw css::uno::RuntimeException("not a work window"); + } + + // use sal_Int64 here to accommodate all int types + // uno::Any shift operator will upcast if necessary + sal_Int64 nHandle = 0; + bool bXEmbed = false; + bool bThrow = false; + if (!(rHandle >>= nHandle)) + { + css::uno::Sequence<css::beans::NamedValue> aProps; + if (rHandle >>= aProps) + { + for (const css::beans::NamedValue& rProp : std::as_const(aProps)) + { + if (rProp.Name == "WINDOW") + rProp.Value >>= nHandle; + else if (rProp.Name == "XEMBED") + rProp.Value >>= bXEmbed; + } + } + else + bThrow = true; + } + if (bThrow) + { + throw css::uno::RuntimeException("incorrect window handle type"); + } + // create system parent data + SystemParentData aSysParentData; + aSysParentData.nSize = sizeof(SystemParentData); +#if defined(_WIN32) + aSysParentData.hWnd = reinterpret_cast<HWND>(nHandle); +#elif defined(MACOSX) + aSysParentData.pView = reinterpret_cast<NSView*>(nHandle); +#elif defined(ANDROID) + // Nothing +#elif defined(IOS) + // Nothing +#elif defined(UNX) + aSysParentData.aWindow = nHandle; + aSysParentData.bXEmbedSupport = bXEmbed; +#endif + + // set system parent + static_cast<WorkWindow*>(pWindow.get())->SetPluginParent(&aSysParentData); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindows.cxx b/toolkit/source/awt/vclxwindows.cxx new file mode 100644 index 0000000000..d6ba5e48a6 --- /dev/null +++ b/toolkit/source/awt/vclxwindows.cxx @@ -0,0 +1,7877 @@ +/* -*- 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 <toolkit/awt/vclxwindows.hxx> +#include <toolkit/helper/accessiblefactory.hxx> +#include <com/sun/star/awt/LineEndFormat.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/convert.hxx> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> +#include <com/sun/star/awt/XItemList.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> + +#include <awt/vclxwindows.hxx> +#include <controls/filectrl.hxx> +#include <controls/svmedit.hxx> +#include <vcl/toolkit/button.hxx> +#include <vcl/toolkit/fmtfield.hxx> +#include <vcl/graph.hxx> +#include <vcl/toolkit/lstbox.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/toolkit/field.hxx> +#include <vcl/toolkit/fixedhyper.hxx> +#include <vcl/toolkit/imgctrl.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/toolkit/prgsbar.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/svapp.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/settings.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> + +#include <helper/imagealign.hxx> +#include <helper/msgbox.hxx> +#include <helper/tkresmgr.hxx> +#include "vclxwindows_internal.hxx" +#include <svl/numformat.hxx> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::lang::EventObject; +using ::com::sun::star::awt::ItemListEvent; +using ::com::sun::star::awt::XItemList; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::graphic::XGraphicProvider; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt::VisualEffect; +namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode; + +static double ImplCalcLongValue( double nValue, sal_uInt16 nDigits ) +{ + double n = nValue; + for ( sal_uInt16 d = 0; d < nDigits; d++ ) + n *= 10; + return n; +} + +static double ImplCalcDoubleValue( double nValue, sal_uInt16 nDigits ) +{ + double n = nValue; + for ( sal_uInt16 d = 0; d < nDigits; d++ ) + n /= 10; + return n; +} + +namespace toolkit +{ + /** sets the "face color" for button like controls (scroll bar, spin button) + */ + void setButtonLikeFaceColor( vcl::Window* _pWindow, const css::uno::Any& _rColorValue ) + { + AllSettings aSettings = _pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + + if ( !_rColorValue.hasValue() ) + { + const StyleSettings& aAppStyle = Application::GetSettings().GetStyleSettings(); + aStyleSettings.SetFaceColor( aAppStyle.GetFaceColor( ) ); + aStyleSettings.SetCheckedColor( aAppStyle.GetCheckedColor( ) ); + aStyleSettings.SetLightBorderColor( aAppStyle.GetLightBorderColor() ); + aStyleSettings.SetLightColor( aAppStyle.GetLightColor() ); + aStyleSettings.SetShadowColor( aAppStyle.GetShadowColor() ); + aStyleSettings.SetDarkShadowColor( aAppStyle.GetDarkShadowColor() ); + } + else + { + Color nBackgroundColor; + _rColorValue >>= nBackgroundColor; + aStyleSettings.SetFaceColor( nBackgroundColor ); + + // for the real background (everything except the buttons and the thumb), + // use an average between the desired color and "white" + Color aWhite( COL_WHITE ); + Color aCheckedBackground( nBackgroundColor ); + aCheckedBackground.SetRed( ( aCheckedBackground.GetRed() + aWhite.GetRed() ) / 2 ); + aCheckedBackground.SetGreen( ( aCheckedBackground.GetGreen() + aWhite.GetGreen() ) / 2 ); + aCheckedBackground.SetBlue( ( aCheckedBackground.GetBlue() + aWhite.GetBlue() ) / 2 ); + aStyleSettings.SetCheckedColor( aCheckedBackground ); + + sal_Int32 nBackgroundLuminance = nBackgroundColor.GetLuminance(); + sal_Int32 nWhiteLuminance = COL_WHITE.GetLuminance(); + + Color aLightShadow( nBackgroundColor ); + aLightShadow.IncreaseLuminance( static_cast<sal_uInt8>( ( nWhiteLuminance - nBackgroundLuminance ) * 2 / 3 ) ); + aStyleSettings.SetLightBorderColor( aLightShadow ); + + Color aLight( nBackgroundColor ); + aLight.IncreaseLuminance( static_cast<sal_uInt8>( ( nWhiteLuminance - nBackgroundLuminance ) * 1 / 3 ) ); + aStyleSettings.SetLightColor( aLight ); + + Color aShadow( nBackgroundColor ); + aShadow.DecreaseLuminance( static_cast<sal_uInt8>( nBackgroundLuminance * 1 / 3 ) ); + aStyleSettings.SetShadowColor( aShadow ); + + Color aDarkShadow( nBackgroundColor ); + aDarkShadow.DecreaseLuminance( static_cast<sal_uInt8>( nBackgroundLuminance * 2 / 3 ) ); + aStyleSettings.SetDarkShadowColor( aDarkShadow ); + } + + aSettings.SetStyleSettings( aStyleSettings ); + _pWindow->SetSettings( aSettings, true ); + } + + Any getButtonLikeFaceColor( const vcl::Window* _pWindow ) + { + Color nBackgroundColor = _pWindow->GetSettings().GetStyleSettings().GetFaceColor(); + return Any( sal_Int32(nBackgroundColor) ); + } + + static void adjustBooleanWindowStyle( const Any& _rValue, vcl::Window* _pWindow, WinBits _nBits, bool _bInverseSemantics ) + { + WinBits nStyle = _pWindow->GetStyle(); + bool bValue( false ); + OSL_VERIFY( _rValue >>= bValue ); + if ( bValue != _bInverseSemantics ) + nStyle |= _nBits; + else + nStyle &= ~_nBits; + _pWindow->SetStyle( nStyle ); + } + + static void setVisualEffect( const Any& _rValue, vcl::Window* _pWindow ) + { + AllSettings aSettings = _pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + + sal_Int16 nStyle = LOOK3D; + OSL_VERIFY( _rValue >>= nStyle ); + switch ( nStyle ) + { + case FLAT: + aStyleSettings.SetOptions( aStyleSettings.GetOptions() | StyleSettingsOptions::Mono ); + break; + case LOOK3D: + default: + aStyleSettings.SetOptions( aStyleSettings.GetOptions() & ~StyleSettingsOptions::Mono ); + } + aSettings.SetStyleSettings( aStyleSettings ); + _pWindow->SetSettings( aSettings ); + } + + static Any getVisualEffect( vcl::Window const * _pWindow ) + { + Any aEffect; + + StyleSettings aStyleSettings = _pWindow->GetSettings().GetStyleSettings(); + if ( aStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) + aEffect <<= sal_Int16(FLAT); + else + aEffect <<= sal_Int16(LOOK3D); + return aEffect; + } +} + + + + +void VCLXGraphicControl::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +void VCLXGraphicControl::ImplSetNewImage() +{ + OSL_PRECOND( GetWindow(), "VCLXGraphicControl::ImplSetNewImage: window is required to be not-NULL!" ); + VclPtr< Button > pButton = GetAsDynamic< Button >(); + pButton->SetModeImage( GetImage() ); +} + +void VCLXGraphicControl::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + { + Size aOldSize = GetWindow()->GetSizePixel(); + VCLXWindow::setPosSize( X, Y, Width, Height, Flags ); + if ( ( aOldSize.Width() != Width ) || ( aOldSize.Height() != Height ) ) + ImplSetNewImage(); + } +} + +void VCLXGraphicControl::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !GetWindow() ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + OSL_VERIFY( Value >>= xGraphic ); + maImage = Image( xGraphic ); + ImplSetNewImage(); + } + break; + + case BASEPROPERTY_IMAGEALIGN: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + sal_Int16 nAlignment = sal_Int16(); + if ( Value >>= nAlignment ) + GetAs< Button >()->SetImageAlign( static_cast< ImageAlign >( nAlignment ) ); + } + } + break; + case BASEPROPERTY_IMAGEPOSITION: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + sal_Int16 nImagePosition = 2; + OSL_VERIFY( Value >>= nImagePosition ); + GetAs<Button>()->SetImageAlign( ::toolkit::translateImagePosition( nImagePosition ) ); + } + } + break; + default: + VCLXWindow::setProperty( PropertyName, Value ); + break; + } +} + +css::uno::Any VCLXGraphicControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( !GetWindow() ) + return aProp; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + aProp <<= Graphic(maImage.GetBitmapEx()).GetXGraphic(); + break; + case BASEPROPERTY_IMAGEALIGN: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + aProp <<= ::toolkit::getCompatibleImageAlign( + GetAs<Button>()->GetImageAlign() ); + } + } + break; + case BASEPROPERTY_IMAGEPOSITION: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + aProp <<= ::toolkit::translateImagePosition( + GetAs< Button >()->GetImageAlign() ); + } + } + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + break; + } + return aProp; +} + + + + +void VCLXButton::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTBUTTON, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEALIGN, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_PUSHBUTTONTYPE, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_STATE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TOGGLE, + BASEPROPERTY_FOCUSONCLICK, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_ALIGN, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +VCLXButton::VCLXButton() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ +} + +VCLXButton::~VCLXButton() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXButton::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXButton::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maActionListeners.disposeAndClear( aObj ); + maItemListeners.disposeAndClear( aObj ); + VCLXGraphicControl::dispose(); +} + +void VCLXButton::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXButton::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXButton::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXButton::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXButton::setLabel( const OUString& rLabel ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rLabel ); +} + +void VCLXButton::setActionCommand( const OUString& rCommand ) +{ + SolarMutexGuard aGuard; + + maActionCommand = rCommand; +} + +css::awt::Size VCLXButton::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< PushButton > pButton = GetAs< PushButton >(); + if ( pButton ) + aSz = pButton->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXButton::getPreferredSize( ) +{ + css::awt::Size aSz = getMinimumSize(); + aSz.Width += 16; + aSz.Height += 10; + return aSz; +} + +css::awt::Size VCLXButton::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< PushButton > pButton = GetAs< PushButton >(); + if ( pButton ) + { + Size aMinSz = pButton->CalcMinimumSize(); + // no text, thus image + if ( pButton->GetText().isEmpty() ) + { + if ( aSz.Width() < aMinSz.Width() ) + aSz.setWidth( aMinSz.Width() ); + if ( aSz.Height() < aMinSz.Height() ) + aSz.setHeight( aMinSz.Height() ); + } + else + { + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + } + return AWTSize(aSz); +} + +void VCLXButton::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< Button > pButton = GetAs< Button >(); + if ( !pButton ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_FOCUSONCLICK: + ::toolkit::adjustBooleanWindowStyle( Value, pButton, WB_NOPOINTERFOCUS, true ); + break; + + case BASEPROPERTY_TOGGLE: + ::toolkit::adjustBooleanWindowStyle( Value, pButton, WB_TOGGLE, false ); + break; + + case BASEPROPERTY_DEFAULTBUTTON: + { + WinBits nStyle = pButton->GetStyle() | WB_DEFBUTTON; + bool b = bool(); + if ( ( Value >>= b ) && !b ) + nStyle &= ~WB_DEFBUTTON; + pButton->SetStyle( nStyle ); + } + break; + case BASEPROPERTY_STATE: + { + if ( GetWindow()->GetType() == WindowType::PUSHBUTTON ) + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + static_cast<PushButton*>(pButton.get())->SetState( static_cast<TriState>(n) ); + } + } + break; + default: + { + VCLXGraphicControl::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXButton::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< Button > pButton = GetAs< Button >(); + if ( pButton ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_FOCUSONCLICK: + aProp <<= ( ( pButton->GetStyle() & WB_NOPOINTERFOCUS ) == 0 ); + break; + + case BASEPROPERTY_TOGGLE: + aProp <<= ( ( pButton->GetStyle() & WB_TOGGLE ) != 0 ); + break; + + case BASEPROPERTY_DEFAULTBUTTON: + { + aProp <<= ( pButton->GetStyle() & WB_DEFBUTTON ) != 0; + } + break; + case BASEPROPERTY_STATE: + { + if ( GetWindow()->GetType() == WindowType::PUSHBUTTON ) + { + aProp <<= static_cast<sal_Int16>(static_cast<PushButton*>(pButton.get())->GetState()); + } + } + break; + default: + { + aProp = VCLXGraphicControl::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXButton::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ButtonClick: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = maActionCommand; + + Callback aCallback = [ this, aEvent ]() + { this->maActionListeners.actionPerformed( aEvent ); }; + + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + + case VclEventId::PushbuttonToggle: + { + PushButton& rButton = dynamic_cast< PushButton& >( *rVclWindowEvent.GetWindow() ); + + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + if ( maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Selected = ( rButton.GetState() == TRISTATE_TRUE ) ? 1 : 0; + maItemListeners.itemStateChanged( aEvent ); + } + } + break; + + default: + VCLXGraphicControl::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + + +void VCLXImageControl::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_SCALEIMAGE, + BASEPROPERTY_IMAGE_SCALE_MODE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +VCLXImageControl::VCLXImageControl() +{ +} + +VCLXImageControl::~VCLXImageControl() +{ +} + +void VCLXImageControl::ImplSetNewImage() +{ + OSL_PRECOND( GetWindow(), "VCLXImageControl::ImplSetNewImage: window is required to be not-NULL!" ); + VclPtr<ImageControl> pControl = GetAs< ImageControl >(); + pControl->SetImage( GetImage() ); +} + +css::awt::Size VCLXImageControl::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz = GetImage().GetSizePixel(); + aSz = ImplCalcWindowSize( aSz ); + + return AWTSize(aSz); +} + +css::awt::Size VCLXImageControl::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXImageControl::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; + css::awt::Size aMinSz = getMinimumSize(); + if ( aSz.Width < aMinSz.Width ) + aSz.Width = aMinSz.Width; + if ( aSz.Height < aMinSz.Height ) + aSz.Height = aMinSz.Height; + return aSz; +} + +void VCLXImageControl::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ImageControl > pImageControl = GetAs< ImageControl >(); + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + sal_Int16 nScaleMode( ImageScaleMode::ANISOTROPIC ); + if ( pImageControl && ( Value >>= nScaleMode ) ) + { + pImageControl->SetScaleMode( nScaleMode ); + } + } + break; + + case BASEPROPERTY_SCALEIMAGE: + { + // this is for compatibility only, nowadays, the ImageScaleMode property should be used + bool bScaleImage = false; + if ( pImageControl && ( Value >>= bScaleImage ) ) + { + pImageControl->SetScaleMode( bScaleImage ? ImageScaleMode::ANISOTROPIC : ImageScaleMode::NONE ); + } + } + break; + + default: + VCLXGraphicControl::setProperty( PropertyName, Value ); + break; + } +} + +css::uno::Any VCLXImageControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ImageControl > pImageControl = GetAs< ImageControl >(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + + switch ( nPropType ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + aProp <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::ANISOTROPIC ); + break; + + case BASEPROPERTY_SCALEIMAGE: + aProp <<= ( pImageControl && pImageControl->GetScaleMode() != ImageScaleMode::NONE ); + break; + + default: + aProp = VCLXGraphicControl::getProperty( PropertyName ); + break; + } + return aProp; +} + + + + +void VCLXCheckBox::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_STATE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TRISTATE, + BASEPROPERTY_VISUALEFFECT, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_ALIGN, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +VCLXCheckBox::VCLXCheckBox() : maActionListeners( *this ), maItemListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXCheckBox::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXCheckBox::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + VCLXGraphicControl::dispose(); +} + +void VCLXCheckBox::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXCheckBox::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXCheckBox::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXCheckBox::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXCheckBox::setActionCommand( const OUString& rCommand ) +{ + SolarMutexGuard aGuard; + maActionCommand = rCommand; +} + +void VCLXCheckBox::setLabel( const OUString& rLabel ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rLabel ); +} + +void VCLXCheckBox::setState( sal_Int16 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< CheckBox> pCheckBox = GetAs< CheckBox >(); + if ( !pCheckBox) + return; + + TriState eState; + switch ( n ) + { + case 0: eState = TRISTATE_FALSE; break; + case 1: eState = TRISTATE_TRUE; break; + case 2: eState = TRISTATE_INDET; break; + default: eState = TRISTATE_FALSE; + } + pCheckBox->SetState( eState ); + + // #105198# call C++ click listeners (needed for accessibility) + // pCheckBox->GetClickHdl().Call( pCheckBox ); + + // #107218# Call same virtual methods and listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pCheckBox->Toggle(); + pCheckBox->Click(); + SetSynthesizingVCLEvent( false ); +} + +sal_Int16 VCLXCheckBox::getState() +{ + SolarMutexGuard aGuard; + + sal_Int16 nState = -1; + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + switch ( pCheckBox->GetState() ) + { + case TRISTATE_FALSE: nState = 0; break; + case TRISTATE_TRUE: nState = 1; break; + case TRISTATE_INDET: nState = 2; break; + default: OSL_FAIL( "VCLXCheckBox::getState(): unknown TriState!" ); + } + } + + return nState; +} + +void VCLXCheckBox::enableTriState( sal_Bool b ) +{ + SolarMutexGuard aGuard; + + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox) + pCheckBox->EnableTriState( b ); +} + +css::awt::Size VCLXCheckBox::getMinimumSize() +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + aSz = pCheckBox->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXCheckBox::getPreferredSize() +{ + return getMinimumSize(); +} + +css::awt::Size VCLXCheckBox::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + Size aMinSz = pCheckBox->CalcMinimumSize(rNewSize.Width); + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + return AWTSize(aSz); +} + +void VCLXCheckBox::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( !pCheckBox ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + ::toolkit::setVisualEffect( Value, pCheckBox ); + break; + + case BASEPROPERTY_TRISTATE: + { + bool b = bool(); + if ( Value >>= b ) + pCheckBox->EnableTriState( b ); + } + break; + case BASEPROPERTY_STATE: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + setState( n ); + } + break; + default: + { + VCLXGraphicControl::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXCheckBox::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + aProp = ::toolkit::getVisualEffect( pCheckBox ); + break; + case BASEPROPERTY_TRISTATE: + aProp <<= pCheckBox->IsTriStateEnabled(); + break; + case BASEPROPERTY_STATE: + aProp <<= static_cast<sal_Int16>(pCheckBox->GetState()); + break; + default: + { + aProp = VCLXGraphicControl::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXCheckBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::CheckboxToggle: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + if ( maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + aEvent.Selected = pCheckBox->GetState(); + maItemListeners.itemStateChanged( aEvent ); + } + if ( !IsSynthesizingVCLEvent() && maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = maActionCommand; + maActionListeners.actionPerformed( aEvent ); + } + } + } + break; + + default: + VCLXGraphicControl::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + +void VCLXRadioButton::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_STATE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VISUALEFFECT, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_ALIGN, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_GROUPNAME, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + + +VCLXRadioButton::VCLXRadioButton() : maItemListeners( *this ), maActionListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXRadioButton::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXRadioButton::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + VCLXGraphicControl::dispose(); +} + +void VCLXRadioButton::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< RadioButton > pButton = GetAs< RadioButton >(); + if ( !pButton ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + ::toolkit::setVisualEffect( Value, pButton ); + break; + + case BASEPROPERTY_STATE: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + { + bool b = n != 0; + if ( pButton->IsRadioCheckEnabled() ) + pButton->Check( b ); + else + pButton->SetState( b ); + } + } + break; + case BASEPROPERTY_AUTOTOGGLE: + { + bool b = bool(); + if ( Value >>= b ) + pButton->EnableRadioCheck( b ); + } + break; + default: + { + VCLXGraphicControl::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXRadioButton::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< RadioButton > pButton = GetAs< RadioButton >(); + if ( pButton ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + aProp = ::toolkit::getVisualEffect( pButton ); + break; + case BASEPROPERTY_STATE: + aProp <<= static_cast<sal_Int16>( pButton->IsChecked() ? 1 : 0 ); + break; + case BASEPROPERTY_AUTOTOGGLE: + aProp <<= pButton->IsRadioCheckEnabled(); + break; + default: + { + aProp = VCLXGraphicControl::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXRadioButton::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXRadioButton::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXRadioButton::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXRadioButton::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXRadioButton::setLabel( const OUString& rLabel ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rLabel ); +} + +void VCLXRadioButton::setActionCommand( const OUString& rCommand ) +{ + SolarMutexGuard aGuard; + maActionCommand = rCommand; +} + +void VCLXRadioButton::setState( sal_Bool b ) +{ + SolarMutexGuard aGuard; + + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton) + { + pRadioButton->Check( b ); + // #102717# item listeners are called, but not C++ click listeners in StarOffice code => call click hdl + // But this is needed in old code because Accessibility API uses it. + // pRadioButton->GetClickHdl().Call( pRadioButton ); + + // #107218# Call same virtual methods and listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pRadioButton->Click(); + SetSynthesizingVCLEvent( false ); + } +} + +sal_Bool VCLXRadioButton::getState() +{ + SolarMutexGuard aGuard; + + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + return pRadioButton && pRadioButton->IsChecked(); +} + +css::awt::Size VCLXRadioButton::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton ) + aSz = pRadioButton->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXRadioButton::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXRadioButton::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton ) + { + Size aMinSz = pRadioButton->CalcMinimumSize(rNewSize.Width); + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + return AWTSize(aSz); +} + +void VCLXRadioButton::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ButtonClick: + if ( !IsSynthesizingVCLEvent() && maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = maActionCommand; + maActionListeners.actionPerformed( aEvent ); + } + ImplClickedOrToggled( false ); + break; + + case VclEventId::RadiobuttonToggle: + ImplClickedOrToggled( true ); + break; + + default: + VCLXGraphicControl::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +void VCLXRadioButton::ImplClickedOrToggled( bool bToggled ) +{ + // In the forms, RadioChecked is not enabled, call itemStateChanged only for click + // In the dialog editor, RadioChecked is enabled, call itemStateChanged only for bToggled + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton && ( pRadioButton->IsRadioCheckEnabled() == bToggled ) && ( bToggled || pRadioButton->IsStateChanged() ) && maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + aEvent.Selected = pRadioButton->IsChecked() ? 1 : 0; + maItemListeners.itemStateChanged( aEvent ); + } +} + + + +void VCLXSpinField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + 0 ); + VCLXEdit::ImplGetPropertyIds( rIds ); +} + +VCLXSpinField::VCLXSpinField() : maSpinListeners( *this ) +{ +} + +void VCLXSpinField::addSpinListener( const css::uno::Reference< css::awt::XSpinListener > & l ) +{ + SolarMutexGuard aGuard; + maSpinListeners.addInterface( l ); +} + +void VCLXSpinField::removeSpinListener( const css::uno::Reference< css::awt::XSpinListener > & l ) +{ + SolarMutexGuard aGuard; + maSpinListeners.removeInterface( l ); +} + +void VCLXSpinField::up() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->Up(); +} + +void VCLXSpinField::down() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->Down(); +} + +void VCLXSpinField::first() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->First(); +} + +void VCLXSpinField::last() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->Last(); +} + +void VCLXSpinField::enableRepeat( sal_Bool bRepeat ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( bRepeat ) + nStyle |= WB_REPEAT; + else + nStyle &= ~WB_REPEAT; + pWindow->SetStyle( nStyle ); + } +} + +void VCLXSpinField::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::SpinfieldUp: + case VclEventId::SpinfieldDown: + case VclEventId::SpinfieldFirst: + case VclEventId::SpinfieldLast: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( maSpinListeners.getLength() ) + { + css::awt::SpinEvent aEvent; + aEvent.Source = getXWeak(); + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::SpinfieldUp: maSpinListeners.up( aEvent ); + break; + case VclEventId::SpinfieldDown: maSpinListeners.down( aEvent ); + break; + case VclEventId::SpinfieldFirst: maSpinListeners.first( aEvent ); + break; + case VclEventId::SpinfieldLast: maSpinListeners.last( aEvent ); + break; + default: break; + } + + } + } + break; + + default: + VCLXEdit::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + +void VCLXListBox::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_DROPDOWN, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LINECOUNT, + BASEPROPERTY_MULTISELECTION, + BASEPROPERTY_MULTISELECTION_SIMPLEMODE, + BASEPROPERTY_ITEM_SEPARATOR_POS, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_SELECTEDITEMS, + BASEPROPERTY_STRINGITEMLIST, + BASEPROPERTY_TYPEDITEMLIST, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_READONLY, + BASEPROPERTY_ALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + + +VCLXListBox::VCLXListBox() + : maActionListeners( *this ), + maItemListeners( *this ) +{ +} + +void VCLXListBox::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + maActionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +void VCLXListBox::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXListBox::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXListBox::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXListBox::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXListBox::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->InsertEntry( aItem, nPos ); +} + +void VCLXListBox::addItems( const css::uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( !pBox ) + return; + + sal_uInt16 nP = nPos; + for ( auto const & item : aItems ) + { + if ( nP == 0xFFFF ) + { + OSL_FAIL( "VCLXListBox::addItems: too many entries!" ); + // skip remaining entries, list cannot hold them, anyway + break; + } + + pBox->InsertEntry( item, nP++ ); + } +} + +void VCLXListBox::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + for ( sal_Int16 n = nCount; n; ) + pBox->RemoveEntry( nPos + (--n) ); + } +} + +sal_Int16 VCLXListBox::getItemCount() +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + return pBox ? pBox->GetEntryCount() : 0; +} + +OUString VCLXListBox::getItem( sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + OUString aItem; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + aItem = pBox->GetEntry( nPos ); + return aItem; +} + +css::uno::Sequence< OUString> VCLXListBox::getItems() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< OUString> aSeq; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + auto n = pBox->GetEntryCount(); + aSeq = css::uno::Sequence< OUString>( n ); + while (n) + { + --n; + aSeq.getArray()[n] = pBox->GetEntry( n ); + } + } + return aSeq; +} + +sal_Int16 VCLXListBox::getSelectedItemPos() +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + return pBox ? pBox->GetSelectedEntryPos() : 0; +} + +css::uno::Sequence<sal_Int16> VCLXListBox::getSelectedItemsPos() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence<sal_Int16> aSeq; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + const sal_Int32 nSelEntries = pBox->GetSelectedEntryCount(); + aSeq = css::uno::Sequence<sal_Int16>( nSelEntries ); + for ( sal_Int32 n = 0; n < nSelEntries; ++n ) + aSeq.getArray()[n] = pBox->GetSelectedEntryPos( n ); + } + return aSeq; +} + +OUString VCLXListBox::getSelectedItem() +{ + SolarMutexGuard aGuard; + + OUString aItem; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + aItem = pBox->GetSelectedEntry(); + return aItem; +} + +css::uno::Sequence< OUString> VCLXListBox::getSelectedItems() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< OUString> aSeq; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + const sal_Int32 nSelEntries = pBox->GetSelectedEntryCount(); + aSeq = css::uno::Sequence< OUString>( nSelEntries ); + for ( sal_Int32 n = 0; n < nSelEntries; ++n ) + aSeq.getArray()[n] = pBox->GetSelectedEntry( n ); + } + return aSeq; +} + +void VCLXListBox::selectItemPos( sal_Int16 nPos, sal_Bool bSelect ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox && ( pBox->IsEntryPosSelected( nPos ) != bool(bSelect) ) ) + { + pBox->SelectEntryPos( nPos, bSelect ); + + // VCL doesn't call select handler after API call. + // ImplCallItemListeners(); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pBox->Select(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXListBox::selectItemsPos( const css::uno::Sequence<sal_Int16>& aPositions, sal_Bool bSelect ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( !pBox ) + return; + + std::vector<sal_Int32> aPositionVec; + aPositionVec.reserve(aPositions.getLength()); + + bool bChanged = false; + for ( auto n = aPositions.getLength(); n; ) + { + const auto nPos = aPositions.getConstArray()[--n]; + if ( pBox->IsEntryPosSelected( nPos ) != bool(bSelect) ) + { + aPositionVec.push_back(nPos); + bChanged = true; + } + } + + if ( !bChanged ) + return; + + bool bOrigUpdateMode = pBox->IsUpdateMode(); + pBox->SetUpdateMode(false); + + pBox->SelectEntriesPos(aPositionVec, bSelect); + + pBox->SetUpdateMode(bOrigUpdateMode); + + // VCL doesn't call select handler after API call. + // ImplCallItemListeners(); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pBox->Select(); + SetSynthesizingVCLEvent( false ); +} + +void VCLXListBox::selectItem( const OUString& rItemText, sal_Bool bSelect ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + selectItemPos( pBox->GetEntryPos( rItemText ), bSelect ); + } +} + +void VCLXListBox::setDropDownLineCount( sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->SetDropDownLineCount( nLines ); +} + +sal_Int16 VCLXListBox::getDropDownLineCount() +{ + SolarMutexGuard aGuard; + + sal_Int16 nLines = 0; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + nLines = pBox->GetDropDownLineCount(); + return nLines; +} + +sal_Bool VCLXListBox::isMutipleMode() +{ + SolarMutexGuard aGuard; + bool bMulti = false; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + bMulti = pBox->IsMultiSelectionEnabled(); + return bMulti; +} + +void VCLXListBox::setMultipleMode( sal_Bool bMulti ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->EnableMultiSelection( bMulti ); +} + +void VCLXListBox::makeVisible( sal_Int16 nEntry ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->SetTopEntry( nEntry ); +} + +void VCLXListBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ListboxSelect: + { + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if( pListBox ) + { + bool bDropDown = ( pListBox->GetStyle() & WB_DROPDOWN ) != 0; + if ( bDropDown && !IsSynthesizingVCLEvent() && maActionListeners.getLength() ) + { + // Call ActionListener on DropDown event + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = pListBox->GetSelectedEntry(); + maActionListeners.actionPerformed( aEvent ); + } + + if ( maItemListeners.getLength() ) + { + ImplCallItemListeners(); + } + } + } + break; + + case VclEventId::ListboxDoubleClick: + if ( GetWindow() && maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = GetAs<ListBox>()->GetSelectedEntry(); + maActionListeners.actionPerformed( aEvent ); + } + break; + + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXListBox::CreateAccessibleContext() +{ + SolarMutexGuard aGuard; + + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXListBox::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( !pListBox ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_ITEM_SEPARATOR_POS: + { + sal_Int16 nSeparatorPos(0); + if ( Value >>= nSeparatorPos ) + pListBox->SetSeparatorPos( nSeparatorPos ); + } + break; + case BASEPROPERTY_READONLY: + { + bool b = false; + if ( Value >>= b ) + pListBox->SetReadOnly( b); + } + break; + case BASEPROPERTY_MULTISELECTION: + { + bool b = false; + if ( Value >>= b ) + pListBox->EnableMultiSelection( b ); + } + break; + case BASEPROPERTY_MULTISELECTION_SIMPLEMODE: + ::toolkit::adjustBooleanWindowStyle( Value, pListBox, WB_SIMPLEMODE, false ); + break; + case BASEPROPERTY_LINECOUNT: + { + sal_Int16 n = 0; + if ( Value >>= n ) + pListBox->SetDropDownLineCount( n ); + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + css::uno::Sequence< OUString> aItems; + if ( Value >>= aItems ) + { + pListBox->Clear(); + addItems( aItems, 0 ); + } + } + break; + case BASEPROPERTY_SELECTEDITEMS: + { + css::uno::Sequence<sal_Int16> aItems; + if ( Value >>= aItems ) + { + for ( auto n = pListBox->GetEntryCount(); n; ) + pListBox->SelectEntryPos( --n, false ); + + if ( aItems.hasElements() ) + selectItemsPos( aItems, true ); + else + pListBox->SetNoSelection(); + + if ( !pListBox->GetSelectedEntryCount() ) + pListBox->SetTopEntry( 0 ); + } + } + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + { + Color nColor = 0; + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pListBox->SetHighlightColor(nColor); + } + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + { + Color nColor = 0; + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightTextColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pListBox->SetHighlightTextColor(nColor); + } + break; + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXListBox::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + css::uno::Any aProp; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_ITEM_SEPARATOR_POS: + aProp <<= sal_Int16( pListBox->GetSeparatorPos() ); + break; + case BASEPROPERTY_READONLY: + { + aProp <<= pListBox->IsReadOnly(); + } + break; + case BASEPROPERTY_MULTISELECTION: + { + aProp <<= pListBox->IsMultiSelectionEnabled(); + } + break; + case BASEPROPERTY_MULTISELECTION_SIMPLEMODE: + { + aProp <<= ( ( pListBox->GetStyle() & WB_SIMPLEMODE ) == 0 ); + } + break; + case BASEPROPERTY_LINECOUNT: + { + aProp <<= static_cast<sal_Int16>(pListBox->GetDropDownLineCount()); + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + const sal_Int32 nItems = pListBox->GetEntryCount(); + css::uno::Sequence< OUString> aSeq( nItems ); + OUString* pStrings = aSeq.getArray(); + for ( sal_Int32 n = 0; n < nItems; ++n ) + pStrings[n] = pListBox->GetEntry( n ); + aProp <<= aSeq; + + } + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +css::awt::Size VCLXListBox::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + aSz = pListBox->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXListBox::getPreferredSize( ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + { + aSz = pListBox->CalcMinimumSize(); + if ( pListBox->GetStyle() & WB_DROPDOWN ) + aSz.AdjustHeight(4 ); + } + return AWTSize(aSz); +} + +css::awt::Size VCLXListBox::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + Size aSz = VCLSize(rNewSize); + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + aSz = pListBox->CalcAdjustedSize( aSz ); + return AWTSize(aSz); +} + +css::awt::Size VCLXListBox::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + aSz = pListBox->CalcBlockSize( nCols, nLines ); + return AWTSize(aSz); +} + +void VCLXListBox::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + nCols = nLines = 0; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + { + sal_uInt16 nC, nL; + pListBox->GetMaxVisColumnsAndLines( nC, nL ); + nCols = nC; + nLines = nL; + } +} + +void VCLXListBox::ImplCallItemListeners() +{ + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox && maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + + // Set to 0xFFFF on multiple selection, selected entry ID otherwise + aEvent.Selected = (pListBox->GetSelectedEntryCount() == 1 ) ? pListBox->GetSelectedEntryPos() : 0xFFFF; + + maItemListeners.itemStateChanged( aEvent ); + } +} +namespace +{ + Image lcl_getImageFromURL( const OUString& i_rImageURL ) + { + if ( i_rImageURL.isEmpty() ) + return Image(); + + try + { + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XGraphicProvider > xProvider(graphic::GraphicProvider::create(xContext)); + ::comphelper::NamedValueCollection aMediaProperties; + aMediaProperties.put( "URL", i_rImageURL ); + Reference< XGraphic > xGraphic = xProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + return Image( xGraphic ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + return Image(); + } +} +void SAL_CALL VCLXListBox::listItemInserted( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemInserted: no ListBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition <= pListBox->GetEntryCount() ), + "VCLXListBox::listItemInserted: illegal (inconsistent) item position!" ); + pListBox->InsertEntry( + i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : OUString(), + i_rEvent.ItemImageURL.IsPresent ? TkResMgr::getImageFromURL( i_rEvent.ItemImageURL.Value ) : Image(), + i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXListBox::listItemRemoved( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemRemoved: no ListBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pListBox->GetEntryCount() ), + "VCLXListBox::listItemRemoved: illegal (inconsistent) item position!" ); + + pListBox->RemoveEntry( i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXListBox::listItemModified( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemModified: no ListBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pListBox->GetEntryCount() ), + "VCLXListBox::listItemModified: illegal (inconsistent) item position!" ); + + // VCL's ListBox does not support changing an entry's text or image, so remove and re-insert + + const OUString sNewText = i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : pListBox->GetEntry( i_rEvent.ItemPosition ); + const Image aNewImage( i_rEvent.ItemImageURL.IsPresent ? TkResMgr::getImageFromURL( i_rEvent.ItemImageURL.Value ) : pListBox->GetEntryImage( i_rEvent.ItemPosition ) ); + + pListBox->RemoveEntry( i_rEvent.ItemPosition ); + pListBox->InsertEntry( sNewText, aNewImage, i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXListBox::allItemsRemoved( const EventObject& ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemModified: no ListBox?!" ); + + pListBox->Clear(); +} + +void SAL_CALL VCLXListBox::itemListChanged( const EventObject& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemModified: no ListBox?!" ); + + pListBox->Clear(); + + uno::Reference< beans::XPropertySet > xPropSet( i_rEvent.Source, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPSI( xPropSet->getPropertySetInfo(), uno::UNO_SET_THROW ); + uno::Reference< resource::XStringResourceResolver > xStringResourceResolver; + if ( xPSI->hasPropertyByName("ResourceResolver") ) + { + xStringResourceResolver.set( + xPropSet->getPropertyValue("ResourceResolver"), + uno::UNO_QUERY + ); + } + + + Reference< XItemList > xItemList( i_rEvent.Source, uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::Pair< OUString, OUString > > aItems = xItemList->getAllItems(); + for ( const auto& rItem : aItems ) + { + OUString aLocalizationKey( rItem.First ); + if ( xStringResourceResolver.is() && aLocalizationKey.startsWith("&") ) + { + aLocalizationKey = xStringResourceResolver->resolveString(aLocalizationKey.copy( 1 )); + } + pListBox->InsertEntry( aLocalizationKey, lcl_getImageFromURL( rItem.Second ) ); + } +} + +void SAL_CALL VCLXListBox::disposing( const EventObject& i_rEvent ) +{ + // just disambiguate + VCLXWindow::disposing( i_rEvent ); +} + + + + +void VCLXMessageBox::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXTopWindow::ImplGetPropertyIds( rIds ); +} + +VCLXMessageBox::VCLXMessageBox() +{ +} + +VCLXMessageBox::~VCLXMessageBox() +{ +} + +void VCLXMessageBox::setCaptionText( const OUString& rText ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rText ); +} + +OUString VCLXMessageBox::getCaptionText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +void VCLXMessageBox::setMessageText( const OUString& rText ) +{ + SolarMutexGuard aGuard; + VclPtr< MessBox > pBox = GetAs< MessBox >(); + if ( pBox ) + pBox->SetMessText( rText ); +} + +OUString VCLXMessageBox::getMessageText() +{ + SolarMutexGuard aGuard; + OUString aText; + VclPtr< MessBox > pBox = GetAs< MessBox >(); + if ( pBox ) + aText = pBox->GetMessText(); + return aText; +} + +sal_Int16 VCLXMessageBox::execute() +{ + SolarMutexGuard aGuard; + VclPtr< MessBox > pBox = GetAs< MessBox >(); + return pBox ? pBox->Execute() : 0; +} + +css::awt::Size SAL_CALL VCLXMessageBox::getMinimumSize() +{ + return css::awt::Size( 250, 100 ); +} + + + +void VCLXDialog::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXTopWindow::ImplGetPropertyIds( rIds ); +} + +VCLXDialog::VCLXDialog() +{ + SAL_INFO("toolkit", "XDialog created"); +} + +VCLXDialog::~VCLXDialog() +{ + SAL_INFO("toolkit", __FUNCTION__); +} + +void SAL_CALL VCLXDialog::endDialog( ::sal_Int32 i_result ) +{ + SolarMutexGuard aGuard; + VclPtr<Dialog> pDialog = GetAsDynamic< Dialog >(); + if ( pDialog ) + pDialog->EndDialog( i_result ); +} + +void SAL_CALL VCLXDialog::setHelpId( const OUString& rId ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetHelpId( rId ); +} + +void VCLXDialog::setTitle( const OUString& Title ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( Title ); +} + +OUString VCLXDialog::getTitle() +{ + SolarMutexGuard aGuard; + + OUString aTitle; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aTitle = pWindow->GetText(); + return aTitle; +} + +sal_Int16 VCLXDialog::execute() +{ + SolarMutexGuard aGuard; + + sal_Int16 nRet = 0; + if ( GetWindow() ) + { + VclPtr< Dialog > pDlg = GetAs< Dialog >(); + vcl::Window* pParent = pDlg->GetWindow( GetWindowType::ParentOverlap ); + vcl::Window* pOldParent = nullptr; + vcl::Window* pSetParent = nullptr; + if ( pParent && !pParent->IsReallyVisible() ) + { + pOldParent = pDlg->GetParent(); + vcl::Window* pFrame = pDlg->GetWindow( GetWindowType::Frame ); + if( pFrame != pDlg ) + { + pDlg->SetParent( pFrame ); + pSetParent = pFrame; + } + } + + nRet = pDlg->Execute(); + + // set the parent back only in case no new parent was set from outside + // in other words, revert only own changes + if ( pOldParent && pDlg->GetParent() == pSetParent ) + pDlg->SetParent( pOldParent ); + } + return nRet; +} + +void VCLXDialog::endExecute() +{ + endDialog(0); +} + +void SAL_CALL VCLXDialog::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +css::awt::DeviceInfo VCLXDialog::getInfo() +{ + css::awt::DeviceInfo aInfo = VCLXDevice::getInfo(); + + SolarMutexGuard aGuard; + VclPtr< Dialog > pDlg = GetAs< Dialog >(); + if ( pDlg ) + pDlg->GetDrawWindowBorder( aInfo.LeftInset, aInfo.TopInset, aInfo.RightInset, aInfo.BottomInset ); + + return aInfo; +} + +void SAL_CALL VCLXDialog::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + VclPtr< Dialog > pDialog = GetAs< Dialog >(); + if ( !pDialog ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + if (( Value >>= xGraphic ) && xGraphic.is() ) + { + Graphic aImage(xGraphic); + + Wallpaper aWallpaper(aImage.GetBitmapEx()); + aWallpaper.SetStyle( WallpaperStyle::Scale ); + pDialog->SetBackground( aWallpaper ); + } + else if ( bVoid || !xGraphic.is() ) + { + Color aColor = pDialog->GetControlBackground(); + if ( aColor == COL_AUTO ) + aColor = pDialog->GetSettings().GetStyleSettings().GetDialogColor(); + + Wallpaper aWallpaper( aColor ); + pDialog->SetBackground( aWallpaper ); + } + } + break; + + default: + { + VCLXContainer::setProperty( PropertyName, Value ); + } + } +} + + + +VCLXMultiPage::VCLXMultiPage() : maTabListeners( *this ), mTabId( 1 ) +{ + SAL_INFO("toolkit", "VCLXMultiPage::VCLXMultiPage()" ); +} + +void VCLXMultiPage::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_MULTIPAGEVALUE, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEALIGN, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_FOCUSONCLICK, + 0); + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXMultiPage::~VCLXMultiPage() +{ +} +void SAL_CALL VCLXMultiPage::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maTabListeners.disposeAndClear( aObj ); + VCLXContainer::dispose(); +} +// css::awt::XView +void SAL_CALL VCLXMultiPage::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +uno::Any SAL_CALL VCLXMultiPage::getProperty( const OUString& PropertyName ) +{ + SAL_INFO("toolkit", " **** VCLXMultiPage::getProperty " << PropertyName ); + SolarMutexGuard aGuard; + css::uno::Any aProp; + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + + case BASEPROPERTY_MULTIPAGEVALUE: + { + aProp <<= getActiveTabID(); + } + break; + default: + aProp = VCLXContainer::getProperty( PropertyName ); + } + return aProp; +} + +void SAL_CALL VCLXMultiPage::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SAL_INFO("toolkit", " **** VCLXMultiPage::setProperty " << PropertyName ); + SolarMutexGuard aGuard; + + VclPtr< TabControl > pTabControl = GetAs< TabControl >(); + if ( !pTabControl ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_MULTIPAGEVALUE: + { + SAL_INFO("toolkit", "***MULTIPAGE VALUE"); + sal_Int32 nId(0); + Value >>= nId; + // when the multipage is created we attempt to set the activepage + // but no pages created + if ( nId && nId <= getWindows().getLength() ) + activateTab( nId ); + break; + } + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + if (( Value >>= xGraphic ) && xGraphic.is() ) + { + Graphic aImage(xGraphic); + + Wallpaper aWallpaper(aImage.GetBitmapEx()); + aWallpaper.SetStyle( WallpaperStyle::Scale ); + pTabControl->SetBackground( aWallpaper ); + } + else if ( bVoid || !xGraphic.is() ) + { + Color aColor = pTabControl->GetControlBackground(); + if ( aColor == COL_AUTO ) + aColor = pTabControl->GetSettings().GetStyleSettings().GetDialogColor(); + + Wallpaper aWallpaper( aColor ); + pTabControl->SetBackground( aWallpaper ); + } + } + break; + + default: + { + VCLXContainer::setProperty( PropertyName, Value ); + } + } +} + +TabControl *VCLXMultiPage::getTabControl() const +{ + VclPtr<TabControl> pTabControl = GetAsDynamic< TabControl >(); + if ( pTabControl ) + return pTabControl; + throw uno::RuntimeException(); +} +sal_Int32 SAL_CALL VCLXMultiPage::insertTab() +{ + TabControl *pTabControl = getTabControl(); + VclPtrInstance<TabPage> pTab( pTabControl ); + return static_cast< sal_Int32 >( insertTab( pTab, OUString() ) ); +} + +sal_uInt16 VCLXMultiPage::insertTab( TabPage* pPage, OUString const & sTitle ) +{ + TabControl *pTabControl = getTabControl(); + sal_uInt16 id = sal::static_int_cast< sal_uInt16 >( mTabId++ ); + pTabControl->InsertPage( id, sTitle ); + pTabControl->SetTabPage( id, pPage ); + return id; +} + +void SAL_CALL VCLXMultiPage::removeTab( sal_Int32 ID ) +{ + TabControl *pTabControl = getTabControl(); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + pTabControl->RemovePage( sal::static_int_cast< sal_uInt16 >( ID ) ); +} + +void SAL_CALL VCLXMultiPage::activateTab( sal_Int32 ID ) +{ + TabControl *pTabControl = getTabControl(); + SAL_INFO( + "toolkit", + "Attempting to activate tab " << ID << ", active tab is " + << getActiveTabID() << ", numtabs is " << getWindows().getLength()); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + pTabControl->SelectTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ); +} + +sal_Int32 SAL_CALL VCLXMultiPage::getActiveTabID() +{ + return getTabControl()->GetCurPageId( ); +} + +void SAL_CALL VCLXMultiPage::addTabListener( const uno::Reference< awt::XTabListener >& xListener ) +{ + SolarMutexGuard aGuard; + maTabListeners.addInterface( xListener ); +} + +void SAL_CALL VCLXMultiPage::removeTabListener( const uno::Reference< awt::XTabListener >& xListener ) +{ + SolarMutexGuard aGuard; + maTabListeners.addInterface( xListener ); +} + +void SAL_CALL VCLXMultiPage::setTabProps( sal_Int32 ID, const uno::Sequence< beans::NamedValue >& Properties ) +{ + SolarMutexGuard aGuard; + TabControl *pTabControl = getTabControl(); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + + for (const auto& rProp : Properties) + { + const OUString &name = rProp.Name; + const uno::Any &value = rProp.Value; + + if (name == "Title") + { + OUString title = value.get<OUString>(); + pTabControl->SetPageText( sal::static_int_cast< sal_uInt16 >( ID ), title ); + } + } +} + +uno::Sequence< beans::NamedValue > SAL_CALL VCLXMultiPage::getTabProps( sal_Int32 ID ) +{ + SolarMutexGuard aGuard; + TabControl *pTabControl = getTabControl(); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + + uno::Sequence< beans::NamedValue > props + { + { "Title", css::uno::Any(pTabControl->GetPageText( sal::static_int_cast< sal_uInt16 >( ID ) )) }, + { "Position", css::uno::Any(pTabControl->GetPagePos( sal::static_int_cast< sal_uInt16 >( ID ) )) } + }; + return props; +} +void VCLXMultiPage::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::TabpageDeactivate: + { + sal_uLong nPageID = reinterpret_cast<sal_uLong>( rVclWindowEvent.GetData() ); + maTabListeners.deactivated( nPageID ); + break; + + } + case VclEventId::TabpageActivate: + { + sal_uLong nPageID = reinterpret_cast<sal_uLong>( rVclWindowEvent.GetData() ); + maTabListeners.activated( nPageID ); + break; + } + default: + VCLXContainer::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + +VCLXTabPage::VCLXTabPage() +{ +} + +void VCLXTabPage::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEALIGN, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_FOCUSONCLICK, + 0); + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXTabPage::~VCLXTabPage() +{ +} + +// css::awt::XView +void SAL_CALL VCLXTabPage::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +void SAL_CALL VCLXTabPage::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + VclPtr< TabPage > pTabPage = GetAs< TabPage >(); + if ( !pTabPage ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + if (( Value >>= xGraphic ) && xGraphic.is() ) + { + Graphic aImage(xGraphic); + + Wallpaper aWallpaper(aImage.GetBitmapEx()); + aWallpaper.SetStyle( WallpaperStyle::Scale ); + pTabPage->SetBackground( aWallpaper ); + } + else if ( bVoid || !xGraphic.is() ) + { + Color aColor = pTabPage->GetControlBackground(); + if ( aColor == COL_AUTO ) + aColor = pTabPage->GetSettings().GetStyleSettings().GetDialogColor(); + + Wallpaper aWallpaper( aColor ); + pTabPage->SetBackground( aWallpaper ); + } + } + break; + case BASEPROPERTY_TITLE: + { + OUString sTitle; + if ( Value >>= sTitle ) + { + pTabPage->SetText(sTitle); + } + } + break; + + default: + { + VCLXContainer::setProperty( PropertyName, Value ); + } + } +} + +TabPage *VCLXTabPage::getTabPage() const +{ + VclPtr< TabPage > pTabPage = GetAsDynamic< TabPage >(); + if ( pTabPage ) + return pTabPage; + throw uno::RuntimeException(); +} + + + + +VCLXFixedHyperlink::VCLXFixedHyperlink() : + + maActionListeners( *this ) + +{ +} + +VCLXFixedHyperlink::~VCLXFixedHyperlink() +{ +} + +void VCLXFixedHyperlink::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maActionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +void VCLXFixedHyperlink::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ButtonClick: + { + if ( maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + maActionListeners.actionPerformed( aEvent ); + } + [[fallthrough]]; + } + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXFixedHyperlink::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXFixedHyperlink::setText( const OUString& Text ) +{ + SolarMutexGuard aGuard; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if (pBase) + pBase->SetText(Text); +} + +OUString VCLXFixedHyperlink::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +void VCLXFixedHyperlink::setURL( const OUString& URL ) +{ + SolarMutexGuard aGuard; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( pBase ) + pBase->SetURL( URL ); +} + +OUString VCLXFixedHyperlink::getURL( ) +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( pBase ) + aText = pBase->GetURL(); + return aText; +} + +void VCLXFixedHyperlink::setAlignment( sal_Int16 nAlign ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( !pWindow ) + return; + + WinBits nNewBits = 0; + if ( nAlign == css::awt::TextAlign::LEFT ) + nNewBits = WB_LEFT; + else if ( nAlign == css::awt::TextAlign::CENTER ) + nNewBits = WB_CENTER; + else + nNewBits = WB_RIGHT; + + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_LEFT|WB_CENTER|WB_RIGHT); + pWindow->SetStyle( nStyle | nNewBits ); +} + +sal_Int16 VCLXFixedHyperlink::getAlignment() +{ + SolarMutexGuard aGuard; + + sal_Int16 nAlign = 0; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( nStyle & WB_LEFT ) + nAlign = css::awt::TextAlign::LEFT; + else if ( nStyle & WB_CENTER ) + nAlign = css::awt::TextAlign::CENTER; + else + nAlign = css::awt::TextAlign::RIGHT; + } + return nAlign; +} + +void VCLXFixedHyperlink::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXFixedHyperlink::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +css::awt::Size VCLXFixedHyperlink::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if ( pFixedText ) + aSz = pFixedText->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXFixedHyperlink::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXFixedHyperlink::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + Size aSz( VCLUnoHelper::ConvertToVCLSize( rNewSize )); + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if (pFixedText) + { + Size aMinSz = pFixedText->CalcMinimumSize(rNewSize.Width); + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + + return VCLUnoHelper::ConvertToAWTSize(aSz); +} + +void VCLXFixedHyperlink::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( !pBase ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LABEL: + { + OUString sNewLabel; + if ( Value >>= sNewLabel ) + pBase->SetText(sNewLabel); + break; + } + + case BASEPROPERTY_URL: + { + OUString sNewURL; + if ( Value >>= sNewURL ) + pBase->SetURL( sNewURL ); + break; + } + + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXFixedHyperlink::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( pBase ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_URL: + { + aProp <<= pBase->GetURL(); + break; + } + + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXFixedHyperlink::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_NOLABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_URL, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + + + +void VCLXFixedText::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_NOLABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXFixedText::VCLXFixedText() +{ +} + +VCLXFixedText::~VCLXFixedText() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXFixedText::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXFixedText::setText( const OUString& Text ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( Text ); +} + +OUString VCLXFixedText::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +void VCLXFixedText::setAlignment( sal_Int16 nAlign ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( !pWindow ) + return; + + WinBits nNewBits = 0; + if ( nAlign == css::awt::TextAlign::LEFT ) + nNewBits = WB_LEFT; + else if ( nAlign == css::awt::TextAlign::CENTER ) + nNewBits = WB_CENTER; + else + nNewBits = WB_RIGHT; + + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_LEFT|WB_CENTER|WB_RIGHT); + pWindow->SetStyle( nStyle | nNewBits ); +} + +sal_Int16 VCLXFixedText::getAlignment() +{ + SolarMutexGuard aGuard; + + sal_Int16 nAlign = 0; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( nStyle & WB_LEFT ) + nAlign = css::awt::TextAlign::LEFT; + else if ( nStyle & WB_CENTER ) + nAlign = css::awt::TextAlign::CENTER; + else + nAlign = css::awt::TextAlign::RIGHT; + } + return nAlign; +} + +css::awt::Size VCLXFixedText::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if ( pFixedText ) + aSz = pFixedText->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXFixedText::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXFixedText::calcAdjustedSize( const css::awt::Size& rMaxSize ) +{ + SolarMutexGuard aGuard; + + Size aAdjustedSize( VCLUnoHelper::ConvertToVCLSize( rMaxSize ) ); + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if ( pFixedText ) + aAdjustedSize = pFixedText->CalcMinimumSize( rMaxSize.Width ); + return VCLUnoHelper::ConvertToAWTSize( aAdjustedSize ); +} + + + +void VCLXScrollBar::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BLOCKINCREMENT, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LINEINCREMENT, + BASEPROPERTY_LIVE_SCROLL, + BASEPROPERTY_ORIENTATION, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SCROLLVALUE, + BASEPROPERTY_SCROLLVALUE_MAX, + BASEPROPERTY_SCROLLVALUE_MIN, + BASEPROPERTY_SYMBOL_COLOR, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VISIBLESIZE, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXScrollBar::VCLXScrollBar() : maAdjustmentListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXScrollBar::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +// css::lang::XComponent +void VCLXScrollBar::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maAdjustmentListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +// css::awt::XScrollbar +void VCLXScrollBar::addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) +{ + SolarMutexGuard aGuard; + maAdjustmentListeners.addInterface( l ); +} + +void VCLXScrollBar::removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) +{ + SolarMutexGuard aGuard; + maAdjustmentListeners.removeInterface( l ); +} + +void VCLXScrollBar::setValue( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->DoScroll( n ); +} + +void VCLXScrollBar::setValues( sal_Int32 nValue, sal_Int32 nVisible, sal_Int32 nMax ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + { + pScrollBar->SetVisibleSize( nVisible ); + pScrollBar->SetRangeMax( nMax ); + pScrollBar->DoScroll( nValue ); + } +} + +sal_Int32 VCLXScrollBar::getValue() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetThumbPos() : 0; +} + +void VCLXScrollBar::setMaximum( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetRangeMax( n ); +} + +sal_Int32 VCLXScrollBar::getMaximum() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetRangeMax() : 0; +} + +void VCLXScrollBar::setMinimum( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetRangeMin( n ); +} + +sal_Int32 VCLXScrollBar::getMinimum() const +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetRangeMin() : 0; +} + +void VCLXScrollBar::setLineIncrement( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetLineSize( n ); +} + +sal_Int32 VCLXScrollBar::getLineIncrement() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetLineSize() : 0; +} + +void VCLXScrollBar::setBlockIncrement( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetPageSize( n ); +} + +sal_Int32 VCLXScrollBar::getBlockIncrement() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetPageSize() : 0; +} + +void VCLXScrollBar::setVisibleSize( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetVisibleSize( n ); +} + +sal_Int32 VCLXScrollBar::getVisibleSize() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetVisibleSize() : 0; +} + +void VCLXScrollBar::setOrientation( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_HORZ|WB_VERT); + if ( n == css::awt::ScrollBarOrientation::HORIZONTAL ) + nStyle |= WB_HORZ; + else + nStyle |= WB_VERT; + + pWindow->SetStyle( nStyle ); + pWindow->Resize(); + } +} + +sal_Int32 VCLXScrollBar::getOrientation() +{ + SolarMutexGuard aGuard; + + sal_Int32 n = 0; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( nStyle & WB_HORZ ) + n = css::awt::ScrollBarOrientation::HORIZONTAL; + else + n = css::awt::ScrollBarOrientation::VERTICAL; + } + return n; + +} + +// css::awt::VclWindowPeer +void VCLXScrollBar::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( !pScrollBar ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LIVE_SCROLL: + { + bool bDo = false; + if ( !bVoid ) + { + OSL_VERIFY( Value >>= bDo ); + } + AllSettings aSettings( pScrollBar->GetSettings() ); + StyleSettings aStyle( aSettings.GetStyleSettings() ); + DragFullOptions nDragOptions = aStyle.GetDragFullOptions(); + if ( bDo ) + nDragOptions |= DragFullOptions::Scroll; + else + nDragOptions &= ~DragFullOptions::Scroll; + aStyle.SetDragFullOptions( nDragOptions ); + aSettings.SetStyleSettings( aStyle ); + pScrollBar->SetSettings( aSettings ); + } + break; + + case BASEPROPERTY_SCROLLVALUE: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setValue( n ); + } + } + break; + case BASEPROPERTY_SCROLLVALUE_MAX: + case BASEPROPERTY_SCROLLVALUE_MIN: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + { + if ( nPropType == BASEPROPERTY_SCROLLVALUE_MAX ) + setMaximum( n ); + else + setMinimum( n ); + } + } + } + break; + case BASEPROPERTY_LINEINCREMENT: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setLineIncrement( n ); + } + } + break; + case BASEPROPERTY_BLOCKINCREMENT: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setBlockIncrement( n ); + } + } + break; + case BASEPROPERTY_VISIBLESIZE: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setVisibleSize( n ); + } + } + break; + case BASEPROPERTY_ORIENTATION: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setOrientation( n ); + } + } + break; + + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + ::toolkit::setButtonLikeFaceColor( pScrollBar, Value); + } + break; + + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXScrollBar::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + + switch ( nPropType ) + { + case BASEPROPERTY_LIVE_SCROLL: + { + aProp <<= bool( pScrollBar->GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Scroll ); + } + break; + case BASEPROPERTY_SCROLLVALUE: + { + aProp <<= getValue(); + } + break; + case BASEPROPERTY_SCROLLVALUE_MAX: + { + aProp <<= getMaximum(); + } + break; + case BASEPROPERTY_SCROLLVALUE_MIN: + { + aProp <<= getMinimum(); + } + break; + case BASEPROPERTY_LINEINCREMENT: + { + aProp <<= getLineIncrement(); + } + break; + case BASEPROPERTY_BLOCKINCREMENT: + { + aProp <<= getBlockIncrement(); + } + break; + case BASEPROPERTY_VISIBLESIZE: + { + aProp <<= getVisibleSize(); + } + break; + case BASEPROPERTY_ORIENTATION: + { + aProp <<= getOrientation(); + } + break; + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + aProp = ::toolkit::getButtonLikeFaceColor( pScrollBar ); + } + break; + + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXScrollBar::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ScrollbarScroll: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( maAdjustmentListeners.getLength() ) + { + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + + if( pScrollBar ) + { + css::awt::AdjustmentEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Value = pScrollBar->GetThumbPos(); + + // set adjustment type + ScrollType aType = pScrollBar->GetType(); + if ( aType == ScrollType::LineUp || aType == ScrollType::LineDown ) + { + aEvent.Type = css::awt::AdjustmentType_ADJUST_LINE; + } + else if ( aType == ScrollType::PageUp || aType == ScrollType::PageDown ) + { + aEvent.Type = css::awt::AdjustmentType_ADJUST_PAGE; + } + else if ( aType == ScrollType::Drag ) + { + aEvent.Type = css::awt::AdjustmentType_ADJUST_ABS; + } + + maAdjustmentListeners.adjustmentValueChanged( aEvent ); + } + } + } + break; + + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::awt::Size VCLXScrollBar::implGetMinimumSize( vcl::Window const * p ) +{ + tools::Long n = p->GetSettings().GetStyleSettings().GetScrollBarSize(); + return css::awt::Size( n, n ); +} + +css::awt::Size SAL_CALL VCLXScrollBar::getMinimumSize() +{ + SolarMutexGuard aGuard; + return implGetMinimumSize( GetWindow() ); +} + + + + +void VCLXEdit::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ECHOCHAR, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HARDLINEBREAKS, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_HSCROLL, + BASEPROPERTY_LINE_END_FORMAT, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TEXT, + BASEPROPERTY_VSCROLL, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_PAINTTRANSPARENT, + BASEPROPERTY_AUTOHSCROLL, + BASEPROPERTY_AUTOVSCROLL, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXEdit::VCLXEdit() : maTextListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXEdit::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXEdit::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maTextListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +void VCLXEdit::addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + SolarMutexGuard aGuard; + GetTextListeners().addInterface( l ); +} + +void VCLXEdit::removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + SolarMutexGuard aGuard; + GetTextListeners().removeInterface( l ); +} + +void VCLXEdit::setText( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + pEdit->SetText( aText ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXEdit::insertText( const css::awt::Selection& rSel, const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + pEdit->SetSelection( Selection( rSel.Min, rSel.Max ) ); + pEdit->ReplaceSelected( aText ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +OUString VCLXEdit::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +OUString VCLXEdit::getSelectedText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit) + aText = pEdit->GetSelected(); + return aText; + +} + +void VCLXEdit::setSelection( const css::awt::Selection& aSelection ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetSelection( Selection( aSelection.Min, aSelection.Max ) ); +} + +css::awt::Selection VCLXEdit::getSelection() +{ + SolarMutexGuard aGuard; + + Selection aSel; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + aSel = pEdit->GetSelection(); + return css::awt::Selection( aSel.Min(), aSel.Max() ); +} + +sal_Bool VCLXEdit::isEditable() +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + return pEdit && !pEdit->IsReadOnly() && pEdit->IsEnabled(); +} + +void VCLXEdit::setEditable( sal_Bool bEditable ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetReadOnly( !bEditable ); +} + + +void VCLXEdit::setMaxTextLen( sal_Int16 nLen ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetMaxTextLen( nLen ); +} + +sal_Int16 VCLXEdit::getMaxTextLen() +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + return pEdit ? pEdit->GetMaxTextLen() : 0; +} + +void VCLXEdit::setEchoChar( sal_Unicode cEcho ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetEchoChar( cEcho ); +} + +void VCLXEdit::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( !pEdit ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + ::toolkit::adjustBooleanWindowStyle( Value, pEdit, WB_NOHIDESELECTION, true ); + if ( pEdit->GetSubEdit() ) + ::toolkit::adjustBooleanWindowStyle( Value, pEdit->GetSubEdit(), WB_NOHIDESELECTION, true ); + break; + + case BASEPROPERTY_READONLY: + { + bool b = bool(); + if ( Value >>= b ) + pEdit->SetReadOnly( b ); + } + break; + case BASEPROPERTY_ECHOCHAR: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pEdit->SetEchoChar( n ); + } + break; + case BASEPROPERTY_MAXTEXTLEN: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pEdit->SetMaxTextLen( n ); + } + break; + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXEdit::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + aProp <<= ( ( pEdit->GetStyle() & WB_NOHIDESELECTION ) == 0 ); + break; + case BASEPROPERTY_READONLY: + aProp <<= pEdit->IsReadOnly(); + break; + case BASEPROPERTY_ECHOCHAR: + aProp <<= static_cast<sal_Int16>(pEdit->GetEchoChar()); + break; + case BASEPROPERTY_MAXTEXTLEN: + aProp <<= static_cast<sal_Int16>(pEdit->GetMaxTextLen()); + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +css::awt::Size VCLXEdit::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + aSz = pEdit->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXEdit::getPreferredSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + aSz = pEdit->CalcMinimumSize(); + aSz.AdjustHeight(4 ); + } + return AWTSize(aSz); +} + +css::awt::Size VCLXEdit::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; + css::awt::Size aMinSz = getMinimumSize(); + if ( aSz.Height != aMinSz.Height ) + aSz.Height = aMinSz.Height; + + return aSz; +} + +css::awt::Size VCLXEdit::getMinimumSize( sal_Int16 nCols, sal_Int16 ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + if ( nCols ) + aSz = pEdit->CalcSize( nCols ); + else + aSz = pEdit->CalcMinimumSize(); + } + return AWTSize(aSz); +} + +void VCLXEdit::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nLines = 1; + nCols = 0; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + nCols = pEdit->GetMaxVisChars(); +} + +void VCLXEdit::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::EditModify: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( GetTextListeners().getLength() ) + { + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + GetTextListeners().textChanged( aEvent ); + } + } + break; + + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + + +void VCLXComboBox::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_AUTOCOMPLETE, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_DROPDOWN, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LINECOUNT, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_STRINGITEMLIST, + BASEPROPERTY_TYPEDITEMLIST, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_ALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + // no, don't call VCLXEdit here - it has properties which we do *not* want to have at combo box + // #i92690# / 2008-08-12 / frank.schoenheit@sun.com + // VCLXEdit::ImplGetPropertyIds( rIds ); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXComboBox::VCLXComboBox() + : maActionListeners( *this ), maItemListeners( *this ) +{ +} + +VCLXComboBox::~VCLXComboBox() +{ + SAL_INFO("toolkit", __FUNCTION__); +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXComboBox::CreateAccessibleContext() +{ + SolarMutexGuard aGuard; + + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXComboBox::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + maActionListeners.disposeAndClear( aObj ); + VCLXEdit::dispose(); +} + + +void VCLXComboBox::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXComboBox::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXComboBox::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXComboBox::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXComboBox::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + pBox->InsertEntry( aItem, nPos ); +} + +void VCLXComboBox::addItems( const css::uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( !pBox ) + return; + + sal_uInt16 nP = nPos; + for ( const auto& rItem : aItems ) + { + pBox->InsertEntry( rItem, nP ); + if ( nP == 0xFFFF ) + { + OSL_FAIL( "VCLXComboBox::addItems: too many entries!" ); + // skip remaining entries, list cannot hold them, anyway + break; + } + } +} + +void VCLXComboBox::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + { + for ( sal_uInt16 n = nCount; n; ) + pBox->RemoveEntryAt( nPos + (--n) ); + } +} + +sal_Int16 VCLXComboBox::getItemCount() +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + return pBox ? pBox->GetEntryCount() : 0; +} + +OUString VCLXComboBox::getItem( sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + OUString aItem; + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + aItem = pBox->GetEntry( nPos ); + return aItem; +} + +css::uno::Sequence< OUString> VCLXComboBox::getItems() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< OUString> aSeq; + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + { + auto n = pBox->GetEntryCount(); + aSeq = css::uno::Sequence< OUString>( n ); + while ( n ) + { + --n; + aSeq.getArray()[n] = pBox->GetEntry( n ); + } + } + return aSeq; +} + +void VCLXComboBox::setDropDownLineCount( sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + pBox->SetDropDownLineCount( nLines ); +} + +sal_Int16 VCLXComboBox::getDropDownLineCount() +{ + SolarMutexGuard aGuard; + + sal_Int16 nLines = 0; + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + nLines = pBox->GetDropDownLineCount(); + return nLines; +} + +void VCLXComboBox::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( !pComboBox ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINECOUNT: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pComboBox->SetDropDownLineCount( n ); + } + break; + case BASEPROPERTY_AUTOCOMPLETE: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pComboBox->EnableAutocomplete( n != 0 ); + else + { + bool b = bool(); + if ( Value >>= b ) + pComboBox->EnableAutocomplete( b ); + } + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + css::uno::Sequence< OUString> aItems; + if ( Value >>= aItems ) + { + pComboBox->Clear(); + addItems( aItems, 0 ); + } + } + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + { + Color nColor = 0; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pComboBox->SetHighlightColor(nColor); + } + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + { + Color nColor = 0; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightTextColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pComboBox->SetHighlightTextColor(nColor); + } + break; + default: + { + VCLXEdit::setProperty( PropertyName, Value ); + + // #109385# SetBorderStyle is not virtual + if ( nPropType == BASEPROPERTY_BORDER ) + { + sal_uInt16 nBorder = sal_uInt16(); + if ( (Value >>= nBorder) && nBorder != 0 ) + pComboBox->SetBorderStyle( static_cast<WindowBorderStyle>(nBorder) ); + } + } + } +} + +css::uno::Any VCLXComboBox::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINECOUNT: + { + aProp <<= static_cast<sal_Int16>(pComboBox->GetDropDownLineCount()); + } + break; + case BASEPROPERTY_AUTOCOMPLETE: + { + aProp <<= pComboBox->IsAutocompleteEnabled(); + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + const sal_Int32 nItems = pComboBox->GetEntryCount(); + css::uno::Sequence< OUString> aSeq( nItems ); + OUString* pStrings = aSeq.getArray(); + for ( sal_Int32 n = 0; n < nItems; ++n ) + pStrings[n] = pComboBox->GetEntry( n ); + aProp <<= aSeq; + + } + break; + default: + { + aProp = VCLXEdit::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXComboBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ComboboxSelect: + if ( maItemListeners.getLength() ) + { + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if( pComboBox ) + { + if ( !pComboBox->IsTravelSelect() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + + // Set to 0xFFFF on multiple selection, selected entry ID otherwise + aEvent.Selected = pComboBox->GetEntryPos( pComboBox->GetText() ); + + maItemListeners.itemStateChanged( aEvent ); + } + } + } + break; + + case VclEventId::ComboboxDoubleClick: + if ( maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); +// aEvent.ActionCommand = ...; + maActionListeners.actionPerformed( aEvent ); + } + break; + + default: + VCLXEdit::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::awt::Size VCLXComboBox::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + aSz = pComboBox->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXComboBox::getPreferredSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + { + aSz = pComboBox->CalcMinimumSize(); + if ( pComboBox->GetStyle() & WB_DROPDOWN ) + aSz.AdjustHeight(4 ); + } + return AWTSize(aSz); +} + +css::awt::Size VCLXComboBox::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + aSz = pComboBox->CalcAdjustedSize( aSz ); + return AWTSize(aSz); +} + +css::awt::Size VCLXComboBox::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + aSz = pComboBox->CalcBlockSize( nCols, nLines ); + return AWTSize(aSz); +} + +void VCLXComboBox::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nCols = nLines = 0; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + { + sal_uInt16 nC, nL; + pComboBox->GetMaxVisColumnsAndLines( nC, nL ); + nCols = nC; + nLines = nL; + } +} +void SAL_CALL VCLXComboBox::listItemInserted( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemInserted: no ComboBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition <= pComboBox->GetEntryCount() ), + "VCLXComboBox::listItemInserted: illegal (inconsistent) item position!" ); + pComboBox->InsertEntryWithImage( + i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : OUString(), + i_rEvent.ItemImageURL.IsPresent ? lcl_getImageFromURL( i_rEvent.ItemImageURL.Value ) : Image(), + i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXComboBox::listItemRemoved( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemRemoved: no ComboBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pComboBox->GetEntryCount() ), + "VCLXComboBox::listItemRemoved: illegal (inconsistent) item position!" ); + + pComboBox->RemoveEntryAt( i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXComboBox::listItemModified( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemModified: no ComboBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pComboBox->GetEntryCount() ), + "VCLXComboBox::listItemModified: illegal (inconsistent) item position!" ); + + // VCL's ComboBox does not support changing an entry's text or image, so remove and re-insert + + const OUString sNewText = i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : pComboBox->GetEntry( i_rEvent.ItemPosition ); + const Image aNewImage( i_rEvent.ItemImageURL.IsPresent ? lcl_getImageFromURL( i_rEvent.ItemImageURL.Value ) : pComboBox->GetEntryImage( i_rEvent.ItemPosition ) ); + + pComboBox->RemoveEntryAt( i_rEvent.ItemPosition ); + pComboBox->InsertEntryWithImage(sNewText, aNewImage, i_rEvent.ItemPosition); +} + +void SAL_CALL VCLXComboBox::allItemsRemoved( const EventObject& ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemModified: no ComboBox?!" ); + + pComboBox->Clear(); +} + +void SAL_CALL VCLXComboBox::itemListChanged( const EventObject& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemModified: no ComboBox?!" ); + + pComboBox->Clear(); + + uno::Reference< beans::XPropertySet > xPropSet( i_rEvent.Source, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPSI( xPropSet->getPropertySetInfo(), uno::UNO_SET_THROW ); + // bool localize = xPSI->hasPropertyByName("ResourceResolver"); + uno::Reference< resource::XStringResourceResolver > xStringResourceResolver; + if ( xPSI->hasPropertyByName("ResourceResolver") ) + { + xStringResourceResolver.set( + xPropSet->getPropertyValue("ResourceResolver"), + uno::UNO_QUERY + ); + } + + + Reference< XItemList > xItemList( i_rEvent.Source, uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::Pair< OUString, OUString > > aItems = xItemList->getAllItems(); + for ( const auto& rItem : aItems ) + { + OUString aLocalizationKey( rItem.First ); + if ( xStringResourceResolver.is() && !aLocalizationKey.isEmpty() && aLocalizationKey[0] == '&' ) + { + aLocalizationKey = xStringResourceResolver->resolveString(aLocalizationKey.copy( 1 )); + } + pComboBox->InsertEntryWithImage(aLocalizationKey, + lcl_getImageFromURL(rItem.Second)); + } +} +void SAL_CALL VCLXComboBox::disposing( const EventObject& i_rEvent ) +{ + // just disambiguate + VCLXEdit::disposing( i_rEvent ); +} + + + +void VCLXFormattedSpinField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + // Interestingly in the UnoControl API this is + // - not derived from XEdit ultimately, (correct ?) - so cut this here ... +// VCLXSpinField::ImplGetPropertyIds( rIds ); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXFormattedSpinField::VCLXFormattedSpinField() + : mpFormatter(nullptr) +{ +} + +VCLXFormattedSpinField::~VCLXFormattedSpinField() +{ +} + +void VCLXFormattedSpinField::setStrictFormat( bool bStrict ) +{ + SolarMutexGuard aGuard; + + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + pFormatter->SetStrictFormat( bStrict ); +} + +bool VCLXFormattedSpinField::isStrictFormat() const +{ + FormatterBase* pFormatter = GetFormatter(); + return pFormatter && pFormatter->IsStrictFormat(); +} + + +void VCLXFormattedSpinField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + FormatterBase* pFormatter = GetFormatter(); + if ( !pFormatter ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_SPIN: + { + bool b = bool(); + if ( Value >>= b ) + { + WinBits nStyle = GetWindow()->GetStyle() | WB_SPIN; + if ( !b ) + nStyle &= ~WB_SPIN; + GetWindow()->SetStyle( nStyle ); + } + } + break; + case BASEPROPERTY_STRICTFORMAT: + { + bool b = bool(); + if ( Value >>= b ) + { + pFormatter->SetStrictFormat( b ); + } + } + break; + default: + { + VCLXSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXFormattedSpinField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_TABSTOP: + { + aProp <<= ( GetWindow()->GetStyle() & WB_SPIN ) != 0; + } + break; + case BASEPROPERTY_STRICTFORMAT: + { + aProp <<= pFormatter->IsStrictFormat(); + } + break; + default: + { + aProp = VCLXSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + + + +void VCLXDateField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DATE, + BASEPROPERTY_DATEMAX, + BASEPROPERTY_DATEMIN, + BASEPROPERTY_DATESHOWCENTURY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_DROPDOWN, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_EXTDATEFORMAT, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXDateField::VCLXDateField() +{ +} + +VCLXDateField::~VCLXDateField() +{ +} + +//change the window type here to match the role +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXDateField::CreateAccessibleContext() +{ + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetType( WindowType::DATEFIELD ); + } + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXDateField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_DATE: + { + if ( bVoid ) + { + GetAs< DateField >()->EnableEmptyFieldValue( true ); + GetAs< DateField >()->SetEmptyFieldValue(); + } + else + { + util::Date d; + if ((Value >>= d) && d.Year != 0) + setDate( d ); + } + } + break; + case BASEPROPERTY_DATEMIN: + { + util::Date d; + if ((Value >>= d) && d.Year != 0) + setMin( d ); + } + break; + case BASEPROPERTY_DATEMAX: + { + util::Date d; + if ((Value >>= d) && d.Year != 0) + setMax( d ); + } + break; + case BASEPROPERTY_EXTDATEFORMAT: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + GetAs< DateField >()->SetExtDateFormat( static_cast<ExtDateFieldFormat>(n) ); + } + break; + case BASEPROPERTY_DATESHOWCENTURY: + { + bool b = bool(); + if ( Value >>= b ) + GetAs< DateField >()->SetShowDateCentury( b ); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + bool bEnforce( true ); + OSL_VERIFY( Value >>= bEnforce ); + GetAs< DateField >()->EnforceValidValue( bEnforce ); + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXDateField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_DATE: + { + aProp <<= getDate(); + } + break; + case BASEPROPERTY_DATEMIN: + { + aProp <<= getMin(); + } + break; + case BASEPROPERTY_DATEMAX: + { + aProp <<= getMax(); + } + break; + case BASEPROPERTY_DATESHOWCENTURY: + { + aProp <<= GetAs< DateField >()->IsShowDateCentury(); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + aProp <<= GetAs< DateField >()->IsEnforceValidValue( ); + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + +void VCLXDateField::setDate( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + { + pDateField->SetDate( aDate ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pDateField->SetModifyFlag(); + pDateField->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +util::Date VCLXDateField::getDate() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetDate().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setMin( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetMin( aDate ); +} + +util::Date VCLXDateField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetMin().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setMax( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetMax( aDate ); +} + +util::Date VCLXDateField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetMax().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setFirst( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetFirst( aDate ); +} + +util::Date VCLXDateField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetFirst().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setLast( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetLast( aDate ); +} + +util::Date VCLXDateField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetLast().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setLongFormat( sal_Bool bLong ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetLongFormat( bLong ); +} + +sal_Bool VCLXDateField::isLongFormat() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + return pDateField && pDateField->IsLongFormat(); +} + +void VCLXDateField::setEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + { + pDateField->SetEmptyDate(); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pDateField->SetModifyFlag(); + pDateField->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +sal_Bool VCLXDateField::isEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + return pDateField && pDateField->IsEmptyDate(); +} + +void VCLXDateField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXDateField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + + + + +void VCLXTimeField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_EXTTIMEFORMAT, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TIME, + BASEPROPERTY_TIMEMAX, + BASEPROPERTY_TIMEMIN, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXTimeField::VCLXTimeField() +{ +} + +VCLXTimeField::~VCLXTimeField() +{ +} + +//change the window type here to match the role +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXTimeField::CreateAccessibleContext() +{ + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetType( WindowType::TIMEFIELD ); + } + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXTimeField::setTime( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + { + pTimeField->SetTime( aTime ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pTimeField->SetModifyFlag(); + pTimeField->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +util::Time VCLXTimeField::getTime() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetTime().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setMin( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetMin( aTime ); +} + +util::Time VCLXTimeField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetMin().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setMax( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetMax( aTime ); +} + +util::Time VCLXTimeField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetMax().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setFirst( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetFirst( aTime ); +} + +util::Time VCLXTimeField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetFirst().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setLast( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetLast( aTime ); +} + +util::Time VCLXTimeField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetLast().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetEmptyTime(); +} + +sal_Bool VCLXTimeField::isEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + return pTimeField && pTimeField->IsEmptyTime(); +} + +void VCLXTimeField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXTimeField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + + +void VCLXTimeField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_TIME: + { + if ( bVoid ) + { + GetAs< TimeField >()->EnableEmptyFieldValue( true ); + GetAs< TimeField >()->SetEmptyFieldValue(); + } + else + { + util::Time t; + if ( Value >>= t ) + setTime( t ); + } + } + break; + case BASEPROPERTY_TIMEMIN: + { + util::Time t; + if ( Value >>= t ) + setMin( t ); + } + break; + case BASEPROPERTY_TIMEMAX: + { + util::Time t; + if ( Value >>= t ) + setMax( t ); + } + break; + case BASEPROPERTY_EXTTIMEFORMAT: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + GetAs< TimeField >()->SetExtFormat( static_cast<ExtTimeFieldFormat>(n) ); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + bool bEnforce( true ); + OSL_VERIFY( Value >>= bEnforce ); + GetAs< TimeField >()->EnforceValidValue( bEnforce ); + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXTimeField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( GetWindow() ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_TIME: + { + aProp <<= getTime(); + } + break; + case BASEPROPERTY_TIMEMIN: + { + aProp <<= getMin(); + } + break; + case BASEPROPERTY_TIMEMAX: + { + aProp <<= getMax(); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + aProp <<= GetAs< TimeField >()->IsEnforceValidValue( ); + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + + + +void VCLXNumericField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VALUEMAX_DOUBLE, + BASEPROPERTY_VALUEMIN_DOUBLE, + BASEPROPERTY_VALUESTEP_DOUBLE, + BASEPROPERTY_VALUE_DOUBLE, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXNumericField::VCLXNumericField() +{ +} + +VCLXNumericField::~VCLXNumericField() +{ +} + +void VCLXNumericField::setValue( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( !pNumericFormatter ) + return; + + // shift long value using decimal digits + // (e.g., input 105 using 2 digits returns 1,05) + // Thus, to set a value of 1,05, insert 105 and 2 digits + pNumericFormatter->SetValue( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); + + // #107218# Call same listeners like VCL would do after user interaction + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +double VCLXNumericField::getValue() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetValue()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setMin( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetMin( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getMin() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetMin()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setMax( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetMax( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getMax() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetMax()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setFirst( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetFirst( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getFirst() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetFirst()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setLast( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetLast( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getLast() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetLast()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXNumericField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + +void VCLXNumericField::setSpinSize( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetSpinSize( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getSpinSize() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetSpinSize()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + { + double n = getValue(); + pNumericFormatter->SetDecimalDigits( Value ); + setValue( n ); + } +} + +sal_Int16 VCLXNumericField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter ? pNumericFormatter->GetDecimalDigits() : 0; +} + +void VCLXNumericField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VALUE_DOUBLE: + { + if ( bVoid ) + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if (!pNumericFormatter) + return; + pNumericFormatter->EnableEmptyFieldValue( true ); + pNumericFormatter->SetEmptyFieldValue(); + } + else + { + double d = 0; + if ( Value >>= d ) + setValue( d ); + } + } + break; + case BASEPROPERTY_VALUEMIN_DOUBLE: + { + double d = 0; + if ( Value >>= d ) + setMin( d ); + } + break; + case BASEPROPERTY_VALUEMAX_DOUBLE: + { + double d = 0; + if ( Value >>= d ) + setMax( d ); + } + break; + case BASEPROPERTY_VALUESTEP_DOUBLE: + { + double d = 0; + if ( Value >>= d ) + setSpinSize( d ); + } + break; + case BASEPROPERTY_DECIMALACCURACY: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + setDecimalDigits( n ); + } + break; + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + bool b = bool(); + if ( Value >>= b ) + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if (!pNumericFormatter) + return; + pNumericFormatter->SetUseThousandSep( b ); + } + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXNumericField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VALUE_DOUBLE: + { + aProp <<= getValue(); + } + break; + case BASEPROPERTY_VALUEMIN_DOUBLE: + { + aProp <<= getMin(); + } + break; + case BASEPROPERTY_VALUEMAX_DOUBLE: + { + aProp <<= getMax(); + } + break; + case BASEPROPERTY_VALUESTEP_DOUBLE: + { + aProp <<= getSpinSize(); + } + break; + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(pFormatter); + aProp <<= pNumericFormatter->IsUseThousandSep(); + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + +// ---------------------------------------------------- +// ---------------------------------------------------- + +void VCLXMetricField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_UNIT, + BASEPROPERTY_CUSTOMUNITTEXT, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXMetricField::VCLXMetricField() +{ +} + +VCLXMetricField::~VCLXMetricField() +{ +} + +MetricFormatter *VCLXMetricField::GetMetricFormatter() +{ + MetricFormatter *pFormatter = static_cast<MetricFormatter *>(GetFormatter()); + if (!pFormatter) + throw css::uno::RuntimeException(); + return pFormatter; +} + +MetricField *VCLXMetricField::GetMetricField() +{ + VclPtr< MetricField > pField = GetAs< MetricField >(); + if (!pField) + throw css::uno::RuntimeException(); + return pField; +} + +// FIXME: later ... +#define MetricUnitUnoToVcl(a) (static_cast<FieldUnit>(a)) + +#define METRIC_MAP_PAIR(method,parent) \ + sal_Int64 VCLXMetricField::get##method( sal_Int16 nUnit ) \ + { \ + SolarMutexGuard aGuard; \ + return GetMetric##parent()->Get##method( MetricUnitUnoToVcl( nUnit ) ); \ + } \ + void VCLXMetricField::set##method( sal_Int64 nValue, sal_Int16 nUnit ) \ + { \ + SolarMutexGuard aGuard; \ + GetMetric##parent()->Set##method( nValue, MetricUnitUnoToVcl( nUnit ) ); \ + } + +METRIC_MAP_PAIR(Min, Formatter) +METRIC_MAP_PAIR(Max, Formatter) +METRIC_MAP_PAIR(First, Field) +METRIC_MAP_PAIR(Last, Field) + +#undef METRIC_MAP_PAIR + +::sal_Int64 VCLXMetricField::getValue( ::sal_Int16 nUnit ) +{ + SolarMutexGuard aGuard; + return GetMetricFormatter()->GetValue( MetricUnitUnoToVcl( nUnit ) ); +} + +::sal_Int64 VCLXMetricField::getCorrectedValue( ::sal_Int16 nUnit ) +{ + SolarMutexGuard aGuard; + return GetMetricFormatter()->GetCorrectedValue( MetricUnitUnoToVcl( nUnit ) ); +} + +// FIXME: acute cut/paste evilness - move this to the parent Edit class ? +void VCLXMetricField::CallListeners() +{ + // #107218# Call same listeners like VCL would do after user interaction + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXMetricField::setValue( ::sal_Int64 Value, ::sal_Int16 Unit ) +{ + SolarMutexGuard aGuard; + GetMetricFormatter()->SetValue( Value, MetricUnitUnoToVcl( Unit ) ); + CallListeners(); +} + +void VCLXMetricField::setUserValue( ::sal_Int64 Value, ::sal_Int16 Unit ) +{ + SolarMutexGuard aGuard; + GetMetricFormatter()->SetUserValue( Value, MetricUnitUnoToVcl( Unit ) ); + CallListeners(); +} + +void VCLXMetricField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXMetricField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + +void VCLXMetricField::setSpinSize( sal_Int64 Value ) +{ + SolarMutexGuard aGuard; + GetMetricField()->SetSpinSize( Value ); +} + +sal_Int64 VCLXMetricField::getSpinSize() +{ + SolarMutexGuard aGuard; + return GetMetricField()->GetSpinSize(); +} + +void VCLXMetricField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + GetMetricFormatter()->SetDecimalDigits( Value ); +} + +sal_Int16 VCLXMetricField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter ? pNumericFormatter->GetDecimalDigits() : 0; +} + +void VCLXMetricField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_DECIMALACCURACY: + { + sal_Int16 n = 0; + if ( Value >>= n ) + setDecimalDigits( n ); + break; + } + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + bool b = false; + if ( Value >>= b ) + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if (!pNumericFormatter) + return; + pNumericFormatter->SetUseThousandSep( b ); + } + } + break; + case BASEPROPERTY_UNIT: + { + sal_uInt16 nVal = 0; + if ( Value >>= nVal ) + GetAs< MetricField >()->SetUnit( static_cast<FieldUnit>(nVal) ); + break; + } + case BASEPROPERTY_CUSTOMUNITTEXT: + { + OUString aStr; + if ( Value >>= aStr ) + GetAs< MetricField >()->SetCustomUnitText( aStr ); + break; + } + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + break; + } + } +} + +css::uno::Any VCLXMetricField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(pFormatter); + aProp <<= pNumericFormatter->IsUseThousandSep(); + break; + } + case BASEPROPERTY_UNIT: + aProp <<= static_cast<sal_uInt16>(GetAs< MetricField >()->GetUnit()); + break; + case BASEPROPERTY_CUSTOMUNITTEXT: + aProp <<= GetAs< MetricField >()->GetCustomUnitText(); + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + break; + } + } + } + return aProp; +} + +void VCLXPatternField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_EDITMASK, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LITERALMASK, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXPatternField::VCLXPatternField() +{ +} + +VCLXPatternField::~VCLXPatternField() +{ +} + +void VCLXPatternField::setMasks( const OUString& EditMask, const OUString& LiteralMask ) +{ + SolarMutexGuard aGuard; + + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + { + pPatternField->SetMask( OUStringToOString(EditMask, RTL_TEXTENCODING_ASCII_US), LiteralMask ); + } +} + +void VCLXPatternField::getMasks( OUString& EditMask, OUString& LiteralMask ) +{ + SolarMutexGuard aGuard; + + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + { + EditMask = OStringToOUString(pPatternField->GetEditMask(), RTL_TEXTENCODING_ASCII_US); + LiteralMask = pPatternField->GetLiteralMask(); + } +} + +void VCLXPatternField::setString( const OUString& Str ) +{ + SolarMutexGuard aGuard; + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + pPatternField->SetString( Str ); +} + +OUString VCLXPatternField::getString() +{ + SolarMutexGuard aGuard; + + OUString aString; + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + aString = pPatternField->GetString(); + return aString; +} + +void VCLXPatternField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXPatternField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + +void VCLXPatternField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_EDITMASK: + case BASEPROPERTY_LITERALMASK: + { + OUString aString; + if ( Value >>= aString ) + { + OUString aEditMask, aLiteralMask; + getMasks( aEditMask, aLiteralMask ); + if ( nPropType == BASEPROPERTY_EDITMASK ) + aEditMask = aString; + else + aLiteralMask = aString; + setMasks( aEditMask, aLiteralMask ); + } + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXPatternField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( GetWindow() ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_EDITMASK: + case BASEPROPERTY_LITERALMASK: + { + OUString aEditMask, aLiteralMask; + getMasks( aEditMask, aLiteralMask ); + if ( nPropType == BASEPROPERTY_EDITMASK ) + aProp <<= aEditMask; + else + aProp <<= aLiteralMask; + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + + +VCLXToolBox::VCLXToolBox() +{ +} + +VCLXToolBox::~VCLXToolBox() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXToolBox::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +VCLXHeaderBar::VCLXHeaderBar() +{ +} + +VCLXHeaderBar::~VCLXHeaderBar() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXHeaderBar::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + + +VCLXFrame::VCLXFrame() +{ +} + +void VCLXFrame::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_LABEL, + 0); + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXFrame::~VCLXFrame() +{ +} + +// css::awt::XView +void SAL_CALL VCLXFrame::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +void SAL_CALL VCLXFrame::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + + VCLXContainer::setProperty( PropertyName, Value ); +} + +void VCLXFrame::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + VCLXContainer::ProcessWindowEvent( rVclWindowEvent ); +} + +VCLXProgressBar::VCLXProgressBar() + :m_nValue(0) + ,m_nValueMin(0) + ,m_nValueMax(100) +{ +} + +VCLXProgressBar::~VCLXProgressBar() +{ +} + +void VCLXProgressBar::ImplUpdateValue() +{ + VclPtr< ProgressBar > pProgressBar = GetAs< ProgressBar >(); + if ( !pProgressBar ) + return; + + sal_Int32 nVal; + sal_Int32 nValMin; + sal_Int32 nValMax; + + // check min and max + if (m_nValueMin < m_nValueMax) + { + nValMin = m_nValueMin; + nValMax = m_nValueMax; + } + else + { + nValMin = m_nValueMax; + nValMax = m_nValueMin; + } + + // check value + if (m_nValue < nValMin) + { + nVal = nValMin; + } + else if (m_nValue > nValMax) + { + nVal = nValMax; + } + else + { + nVal = m_nValue; + } + + // calculate percent + sal_Int32 nPercent; + if (nValMin != nValMax) + { + nPercent = 100 * (nVal - nValMin) / (nValMax - nValMin); + } + else + { + nPercent = 0; + } + + // set progressbar value + pProgressBar->SetValue( static_cast<sal_uInt16>(nPercent) ); +} + +// css::awt::XProgressBar +void VCLXProgressBar::setForegroundColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetControlForeground( Color(ColorTransparency, nColor) ); + } +} + +void VCLXProgressBar::setBackgroundColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + Color aColor( ColorTransparency, nColor ); + pWindow->SetBackground( aColor ); + pWindow->SetControlBackground( aColor ); + pWindow->Invalidate(); + } +} + +void VCLXProgressBar::setValue( sal_Int32 nValue ) +{ + SolarMutexGuard aGuard; + + m_nValue = nValue; + ImplUpdateValue(); +} + +void VCLXProgressBar::setRange( sal_Int32 nMin, sal_Int32 nMax ) +{ + SolarMutexGuard aGuard; + + if ( nMin < nMax ) + { + // take correct min and max + m_nValueMin = nMin; + m_nValueMax = nMax; + } + else + { + // change min and max + m_nValueMin = nMax; + m_nValueMax = nMin; + } + + ImplUpdateValue(); +} + +sal_Int32 VCLXProgressBar::getValue() +{ + SolarMutexGuard aGuard; + + return m_nValue; +} + +// css::awt::VclWindowPeer +void VCLXProgressBar::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ProgressBar > pProgressBar = GetAs< ProgressBar >(); + if ( !pProgressBar ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_PROGRESSVALUE: + { + if ( Value >>= m_nValue ) + ImplUpdateValue(); + } + break; + case BASEPROPERTY_PROGRESSVALUE_MIN: + { + if ( Value >>= m_nValueMin ) + ImplUpdateValue(); + } + break; + case BASEPROPERTY_PROGRESSVALUE_MAX: + { + if ( Value >>= m_nValueMax ) + ImplUpdateValue(); + } + break; + case BASEPROPERTY_FILLCOLOR: + { + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + if ( bVoid ) + { + pWindow->SetControlForeground(); + } + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->SetControlForeground( nColor ); + } + } + } + break; + default: + VCLXWindow::setProperty( PropertyName, Value ); + break; + } +} + +css::uno::Any VCLXProgressBar::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ProgressBar > pProgressBar = GetAs< ProgressBar >(); + if ( pProgressBar ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_PROGRESSVALUE: + { + aProp <<= m_nValue; + } + break; + case BASEPROPERTY_PROGRESSVALUE_MIN: + { + aProp <<= m_nValueMin; + } + break; + case BASEPROPERTY_PROGRESSVALUE_MAX: + { + aProp <<= m_nValueMax; + } + break; + default: + aProp = VCLXWindow::getProperty( PropertyName ); + break; + } + } + return aProp; +} + +void VCLXProgressBar::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_PROGRESSVALUE, + BASEPROPERTY_PROGRESSVALUE_MIN, + BASEPROPERTY_PROGRESSVALUE_MAX, + BASEPROPERTY_FILLCOLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); +} + +VCLXFileControl::VCLXFileControl() : maTextListeners( *this ) +{ +} + +VCLXFileControl::~VCLXFileControl() +{ + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + pControl->GetEdit().SetModifyHdl( Link<Edit&,void>() ); +} + +namespace +{ + void lcl_setWinBits( vcl::Window* _pWindow, WinBits _nBits, bool _bSet ) + { + WinBits nStyle = _pWindow->GetStyle(); + if ( _bSet ) + nStyle |= _nBits; + else + nStyle &= ~_nBits; + _pWindow->SetStyle( nStyle ); + } +} + +void SAL_CALL VCLXFileControl::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( !pControl ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + { + bool bValue(false); + OSL_VERIFY( Value >>= bValue ); + + lcl_setWinBits( pControl, WB_NOHIDESELECTION, !bValue ); + lcl_setWinBits( &pControl->GetEdit(), WB_NOHIDESELECTION, !bValue ); + } + break; + + default: + VCLXWindow::setProperty( PropertyName, Value ); + break; + } +} + +void VCLXFileControl::SetWindow( const VclPtr< vcl::Window > &pWindow ) +{ + VclPtr< FileControl > pPrevFileControl = GetAsDynamic< FileControl >(); + if ( pPrevFileControl ) + pPrevFileControl->SetEditModifyHdl( Link<Edit&,void>() ); + + FileControl* pNewFileControl = dynamic_cast<FileControl*>( pWindow.get() ); + if ( pNewFileControl ) + pNewFileControl->SetEditModifyHdl( LINK( this, VCLXFileControl, ModifyHdl ) ); + + VCLXWindow::SetWindow( pWindow ); +} + +void VCLXFileControl::addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.addInterface( l ); +} + +void VCLXFileControl::removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.removeInterface( l ); +} + +void VCLXFileControl::setText( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetText( aText ); + + // also in Java a textChanged is triggered, not in VCL. + // css::awt::Toolkit should be JAVA-compliant... + ModifyHdl(); + } +} + +void VCLXFileControl::insertText( const css::awt::Selection& rSel, const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + { + pFileControl->GetEdit().SetSelection( Selection( rSel.Min, rSel.Max ) ); + pFileControl->GetEdit().ReplaceSelected( aText ); + } +} + +OUString VCLXFileControl::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +OUString VCLXFileControl::getSelectedText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl) + aText = pFileControl->GetEdit().GetSelected(); + return aText; + +} + +void VCLXFileControl::setSelection( const css::awt::Selection& aSelection ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + pFileControl->GetEdit().SetSelection( Selection( aSelection.Min, aSelection.Max ) ); +} + +css::awt::Selection VCLXFileControl::getSelection() +{ + SolarMutexGuard aGuard; + + css::awt::Selection aSel; + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + { + aSel.Min = pFileControl->GetEdit().GetSelection().Min(); + aSel.Max = pFileControl->GetEdit().GetSelection().Max(); + } + return aSel; +} + +sal_Bool VCLXFileControl::isEditable() +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + return pFileControl && !pFileControl->GetEdit().IsReadOnly() && pFileControl->GetEdit().IsEnabled(); +} + +void VCLXFileControl::setEditable( sal_Bool bEditable ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + pFileControl->GetEdit().SetReadOnly( !bEditable ); +} + +void VCLXFileControl::setMaxTextLen( sal_Int16 nLen ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + pFileControl->GetEdit().SetMaxTextLen( nLen ); +} + +sal_Int16 VCLXFileControl::getMaxTextLen() +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + return pFileControl ? pFileControl->GetEdit().GetMaxTextLen() : 0; +} + + +IMPL_LINK_NOARG(VCLXFileControl, ModifyHdl, Edit&, void) +{ + ModifyHdl(); +} + +void VCLXFileControl::ModifyHdl() +{ + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + maTextListeners.textChanged( aEvent ); +} + +css::awt::Size VCLXFileControl::getMinimumSize() +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + { + Size aTmpSize = pControl->GetEdit().CalcMinimumSize(); + aTmpSize.AdjustWidth(pControl->GetButton().CalcMinimumSize().Width() ); + aSz = AWTSize(pControl->CalcWindowSize( aTmpSize )); + } + return aSz; +} + +css::awt::Size VCLXFileControl::getPreferredSize() +{ + css::awt::Size aSz = getMinimumSize(); + aSz.Height += 4; + return aSz; +} + +css::awt::Size VCLXFileControl::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz =rNewSize; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + { + css::awt::Size aMinSz = getMinimumSize(); + if ( aSz.Height != aMinSz.Height ) + aSz.Height = aMinSz.Height; + } + return aSz; +} + +css::awt::Size VCLXFileControl::getMinimumSize( sal_Int16 nCols, sal_Int16 ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + { + aSz = AWTSize(pControl->GetEdit().CalcSize( nCols )); + aSz.Width += pControl->GetButton().CalcMinimumSize().Width(); + } + return aSz; +} + +void VCLXFileControl::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nCols = 0; + nLines = 1; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + nCols = pControl->GetEdit().GetMaxVisChars(); +} + +void VCLXFileControl::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + // FIXME: elide duplication ? + BASEPROPERTY_HIDEINACTIVESELECTION, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); +} + +SVTXFormattedField::SVTXFormattedField() + :bIsStandardSupplier(true) + ,nKeyToSetDelayed(-1) +{ +} + +SVTXFormattedField::~SVTXFormattedField() +{ +} + +void SVTXFormattedField::SetWindow( const VclPtr< vcl::Window > &_pWindow ) +{ + VCLXSpinField::SetWindow(_pWindow); + if (GetAs< FormattedField >()) + GetAs< FormattedField >()->GetFormatter().SetAutoColor(true); +} + +void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + { + Formatter& rFormatter = pField->GetFormatter(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_ENFORCE_FORMAT: + { + bool bEnable( true ); + if ( Value >>= bEnable ) + rFormatter.EnableNotANumber( !bEnable ); + } + break; + + case BASEPROPERTY_EFFECTIVE_MIN: + case BASEPROPERTY_VALUEMIN_DOUBLE: + SetMinValue(Value); + break; + + case BASEPROPERTY_EFFECTIVE_MAX: + case BASEPROPERTY_VALUEMAX_DOUBLE: + SetMaxValue(Value); + break; + + case BASEPROPERTY_EFFECTIVE_DEFAULT: + SetDefaultValue(Value); + break; + + case BASEPROPERTY_TREATASNUMBER: + { + bool b; + if ( Value >>= b ) + SetTreatAsNumber(b); + } + break; + + case BASEPROPERTY_FORMATSSUPPLIER: + if (!Value.hasValue()) + setFormatsSupplier(css::uno::Reference< css::util::XNumberFormatsSupplier > (nullptr)); + else + { + css::uno::Reference< css::util::XNumberFormatsSupplier > xNFS; + if ( Value >>= xNFS ) + setFormatsSupplier(xNFS); + } + break; + case BASEPROPERTY_FORMATKEY: + if (!Value.hasValue()) + setFormatKey(0); + else + { + sal_Int32 n = 0; + if ( Value >>= n ) + setFormatKey(n); + } + break; + + case BASEPROPERTY_EFFECTIVE_VALUE: + case BASEPROPERTY_VALUE_DOUBLE: + { + const css::uno::TypeClass rTC = Value.getValueType().getTypeClass(); + if (rTC != css::uno::TypeClass_STRING) + // no string + if (rTC != css::uno::TypeClass_DOUBLE) + // no double + if (Value.hasValue()) + { // but a value + // try if it is something convertible + sal_Int32 nValue = 0; + if (!(Value >>= nValue)) + throw css::lang::IllegalArgumentException(); + SetValue(css::uno::Any(static_cast<double>(nValue))); + break; + } + + SetValue(Value); + } + break; + case BASEPROPERTY_VALUESTEP_DOUBLE: + { + double d = 0.0; + if ( Value >>= d ) + rFormatter.SetSpinSize( d ); + else + { + sal_Int32 n = 0; + if ( Value >>= n ) + rFormatter.SetSpinSize( n ); + } + } + break; + case BASEPROPERTY_DECIMALACCURACY: + { + sal_Int32 n = 0; + if ( Value >>= n ) + rFormatter.SetDecimalDigits( static_cast<sal_uInt16>(n) ); + } + break; + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + bool b; + if ( Value >>= b ) + rFormatter.SetThousandsSep( b ); + } + break; + + default: + VCLXSpinField::setProperty( PropertyName, Value ); + } + + if (BASEPROPERTY_TEXTCOLOR == nPropType) + { // after setting a new text color, think again about the AutoColor flag of the control + // 17.05.2001 - 86859 - frank.schoenheit@germany.sun.com + rFormatter.SetAutoColor(!Value.hasValue()); + } + } + else + VCLXSpinField::setProperty( PropertyName, Value ); +} + +css::uno::Any SVTXFormattedField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aReturn; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + { + Formatter& rFormatter = pField->GetFormatter(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_EFFECTIVE_MIN: + case BASEPROPERTY_VALUEMIN_DOUBLE: + aReturn = GetMinValue(); + break; + + case BASEPROPERTY_EFFECTIVE_MAX: + case BASEPROPERTY_VALUEMAX_DOUBLE: + aReturn = GetMaxValue(); + break; + + case BASEPROPERTY_EFFECTIVE_DEFAULT: + aReturn = GetDefaultValue(); + break; + + case BASEPROPERTY_TREATASNUMBER: + aReturn <<= GetTreatAsNumber(); + break; + + case BASEPROPERTY_EFFECTIVE_VALUE: + case BASEPROPERTY_VALUE_DOUBLE: + aReturn = GetValue(); + break; + + case BASEPROPERTY_VALUESTEP_DOUBLE: + aReturn <<= rFormatter.GetSpinSize(); + break; + + case BASEPROPERTY_DECIMALACCURACY: + aReturn <<= rFormatter.GetDecimalDigits(); + break; + + case BASEPROPERTY_FORMATSSUPPLIER: + { + if (!bIsStandardSupplier) + { // ansonsten void + css::uno::Reference< css::util::XNumberFormatsSupplier > xSupplier = m_xCurrentSupplier; + aReturn <<= xSupplier; + } + } + break; + + case BASEPROPERTY_FORMATKEY: + { + if (!bIsStandardSupplier) + aReturn <<= getFormatKey(); + } + break; + + default: + aReturn = VCLXSpinField::getProperty(PropertyName); + } + } + return aReturn; +} + +css::uno::Any SVTXFormattedField::convertEffectiveValue(const css::uno::Any& rValue) const +{ + css::uno::Any aReturn; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return aReturn; + + Formatter& rFieldFormatter = pField->GetFormatter(); + switch (rValue.getValueType().getTypeClass()) + { + case css::uno::TypeClass_DOUBLE: + if (rFieldFormatter.TreatingAsNumber()) + { + double d = 0.0; + rValue >>= d; + aReturn <<= d; + } + else + { + SvNumberFormatter* pFormatter = rFieldFormatter.GetFormatter(); + if (!pFormatter) + pFormatter = rFieldFormatter.StandardFormatter(); + // should never fail + + const Color* pDum; + double d = 0.0; + rValue >>= d; + OUString sConverted; + pFormatter->GetOutputString(d, 0, sConverted, &pDum); + aReturn <<= sConverted; + } + break; + case css::uno::TypeClass_STRING: + { + OUString aStr; + rValue >>= aStr; + if (rFieldFormatter.TreatingAsNumber()) + { + SvNumberFormatter* pFormatter = rFieldFormatter.GetFormatter(); + if (!pFormatter) + pFormatter = rFieldFormatter.StandardFormatter(); + + double dVal; + sal_uInt32 nTestFormat(0); + if (!pFormatter->IsNumberFormat(aStr, nTestFormat, dVal)) + aReturn.clear(); + aReturn <<= dVal; + } + else + aReturn <<= aStr; + } + break; + default: + aReturn.clear(); + break; + } + return aReturn; +} + +void SVTXFormattedField::SetMinValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + Formatter& rFormatter = pField->GetFormatter(); + switch (rValue.getValueType().getTypeClass()) + + { + case css::uno::TypeClass_DOUBLE: + { + double d = 0.0; + rValue >>= d; + rFormatter.SetMinValue(d); + break; + } + default: + DBG_ASSERT(rValue.getValueType().getTypeClass() == css::uno::TypeClass_VOID, "SVTXFormattedField::SetMinValue : invalid argument (an exception will be thrown) !"); + if ( rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID ) + + { + throw css::lang::IllegalArgumentException(); + } + rFormatter.ClearMinValue(); + break; + } +} + +css::uno::Any SVTXFormattedField::GetMinValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + Formatter& rFormatter = pField->GetFormatter(); + if (!rFormatter.HasMinValue()) + return css::uno::Any(); + + css::uno::Any aReturn; + aReturn <<= rFormatter.GetMinValue(); + return aReturn; +} + +void SVTXFormattedField::SetMaxValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + Formatter& rFormatter = pField->GetFormatter(); + switch (rValue.getValueType().getTypeClass()) + { + case css::uno::TypeClass_DOUBLE: + { + double d = 0.0; + rValue >>= d; + rFormatter.SetMaxValue(d); + break; + } + default: + if (rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID) + + { + throw css::lang::IllegalArgumentException(); + } + rFormatter.ClearMaxValue(); + break; + } +} + +css::uno::Any SVTXFormattedField::GetMaxValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + Formatter& rFormatter = pField->GetFormatter(); + if (!rFormatter.HasMaxValue()) + return css::uno::Any(); + + css::uno::Any aReturn; + aReturn <<= rFormatter.GetMaxValue(); + return aReturn; +} + +void SVTXFormattedField::SetDefaultValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + css::uno::Any aConverted = convertEffectiveValue(rValue); + + Formatter& rFormatter = pField->GetFormatter(); + switch (aConverted.getValueType().getTypeClass()) + { + case css::uno::TypeClass_DOUBLE: + { + double d = 0.0; + aConverted >>= d; + rFormatter.SetDefaultValue(d); + } + break; + case css::uno::TypeClass_STRING: + { + OUString aStr; + aConverted >>= aStr; + rFormatter.SetDefaultText( aStr ); + } + break; + default: + rFormatter.EnableEmptyField(true); + // only void accepted + break; + } +} + +css::uno::Any SVTXFormattedField::GetDefaultValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + Formatter& rFormatter = pField->GetFormatter(); + if (rFormatter.IsEmptyFieldEnabled()) + return css::uno::Any(); + + css::uno::Any aReturn; + if (rFormatter.TreatingAsNumber()) + aReturn <<= rFormatter.GetDefaultValue(); + else + aReturn <<= rFormatter.GetDefaultText(); + return aReturn; +} + +bool SVTXFormattedField::GetTreatAsNumber() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (pField) + return pField->GetFormatter().TreatingAsNumber(); + + return true; +} + +void SVTXFormattedField::SetTreatAsNumber(bool bSet) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (pField) + pField->GetFormatter().TreatAsNumber(bSet); +} + +css::uno::Any SVTXFormattedField::GetValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + + Formatter& rFormatter = pField->GetFormatter(); + css::uno::Any aReturn; + if (!rFormatter.TreatingAsNumber()) + { + OUString sText = rFormatter.GetTextValue(); + aReturn <<= sText; + } + else + { + if (!pField->GetText().isEmpty()) // empty is returned as void by default + aReturn <<= rFormatter.GetValue(); + } + + return aReturn; +} + +void SVTXFormattedField::SetValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + if (!rValue.hasValue()) + { + pField->SetText(""); + } + else + { + Formatter& rFormatter = pField->GetFormatter(); + if (rValue.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE ) + { + double d = 0.0; + rValue >>= d; + rFormatter.SetValue(d); + } + else + { + DBG_ASSERT(rValue.getValueType().getTypeClass() == css::uno::TypeClass_STRING, "SVTXFormattedField::SetValue : invalid argument !"); + + OUString sText; + rValue >>= sText; + if (!rFormatter.TreatingAsNumber()) + rFormatter.SetTextFormatted(sText); + else + rFormatter.SetTextValue(sText); + } + } +// NotifyTextListeners(); +} + +void SVTXFormattedField::setFormatsSupplier(const css::uno::Reference< css::util::XNumberFormatsSupplier > & xSupplier) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + + rtl::Reference<SvNumberFormatsSupplierObj> pNew; + if (!xSupplier.is()) + { + if (pField) + { + Formatter& rFormatter = pField->GetFormatter(); + pNew = new SvNumberFormatsSupplierObj(rFormatter.StandardFormatter()); + bIsStandardSupplier = true; + } + } + else + { + pNew = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(xSupplier); + bIsStandardSupplier = false; + } + + if (!pNew) + return; // TODO : how to process ? + + m_xCurrentSupplier = pNew; + if (!pField) + return; + + // save the actual value + css::uno::Any aCurrent = GetValue(); + Formatter& rFormatter = pField->GetFormatter(); + rFormatter.SetFormatter(m_xCurrentSupplier->GetNumberFormatter(), false); + if (nKeyToSetDelayed != -1) + { + rFormatter.SetFormatKey(nKeyToSetDelayed); + nKeyToSetDelayed = -1; + } + SetValue(aCurrent); + NotifyTextListeners(); +} + +sal_Int32 SVTXFormattedField::getFormatKey() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetFormatKey() : 0; +} + +void SVTXFormattedField::setFormatKey(sal_Int32 nKey) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + Formatter& rFormatter = pField->GetFormatter(); + if (rFormatter.GetFormatter()) + rFormatter.SetFormatKey(nKey); + else + { + // probably I am in a block, in which first the key and next the formatter will be set, + // initially this happens quite certain, as the properties are set in alphabetic sequence, + // and the FormatsSupplier is processed before the FormatKey + nKeyToSetDelayed = nKey; + } + NotifyTextListeners(); +} + +void SVTXFormattedField::NotifyTextListeners() +{ + if ( GetTextListeners().getLength() ) + { + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + GetTextListeners().textChanged( aEvent ); + } +} + +void SVTXFormattedField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + // FIXME: elide duplication ? + BASEPROPERTY_EFFECTIVE_MIN, + BASEPROPERTY_VALUEMIN_DOUBLE, + BASEPROPERTY_EFFECTIVE_MAX, + BASEPROPERTY_VALUEMAX_DOUBLE, + BASEPROPERTY_EFFECTIVE_DEFAULT, + BASEPROPERTY_TREATASNUMBER, + BASEPROPERTY_EFFECTIVE_VALUE, + BASEPROPERTY_VALUE_DOUBLE, + BASEPROPERTY_VALUESTEP_DOUBLE, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_FORMATSSUPPLIER, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_FORMATKEY, + BASEPROPERTY_TREATASNUMBER, + BASEPROPERTY_ENFORCE_FORMAT, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); + VCLXSpinField::ImplGetPropertyIds( rIds ); +} + +SVTXCurrencyField::SVTXCurrencyField() +{ +} + +SVTXCurrencyField::~SVTXCurrencyField() +{ +} + +void SVTXCurrencyField::setValue( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetValue( Value ); +} + +double SVTXCurrencyField::getValue() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetValue() : 0; +} + +void SVTXCurrencyField::setMin( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMinValue( Value ); +} + +double SVTXCurrencyField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMinValue() : 0; +} + +void SVTXCurrencyField::setMax( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMaxValue( Value ); +} + +double SVTXCurrencyField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMaxValue() : 0; +} + +void SVTXCurrencyField::setFirst( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinFirst( Value ); +} + +double SVTXCurrencyField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinFirst() : 0; +} + +void SVTXCurrencyField::setLast( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinLast( Value ); +} + +double SVTXCurrencyField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinLast() : 0; +} + +void SVTXCurrencyField::setSpinSize( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinSize( Value ); +} + +double SVTXCurrencyField::getSpinSize() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinSize() : 0; +} + +void SVTXCurrencyField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetDecimalDigits( Value ); +} + +sal_Int16 SVTXCurrencyField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetDecimalDigits() : 0; +} + +void SVTXCurrencyField::setStrictFormat( sal_Bool bStrict ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetStrictFormat( bStrict ); +} + +sal_Bool SVTXCurrencyField::isStrictFormat() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField && pField->GetFormatter().IsStrictFormat(); +} + +void SVTXCurrencyField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< DoubleCurrencyField > pField = GetAs< DoubleCurrencyField >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_CURRENCYSYMBOL: + { + OUString aStr; + Value >>= aStr; + pField->setCurrencySymbol( aStr ); + } + break; + case BASEPROPERTY_CURSYM_POSITION: + { + bool b = false; + Value >>= b; + pField->setPrependCurrSym(b); + } + break; + + default: + SVTXFormattedField::setProperty(PropertyName, Value); + } + } + else + SVTXFormattedField::setProperty(PropertyName, Value); +} + +css::uno::Any SVTXCurrencyField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aReturn; + + VclPtr< DoubleCurrencyField > pField = GetAs< DoubleCurrencyField >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_CURRENCYSYMBOL: + { + aReturn <<= pField->getCurrencySymbol(); + } + break; + case BASEPROPERTY_CURSYM_POSITION: + { + aReturn <<= pField->getPrependCurrSym(); + } + break; + default: + return SVTXFormattedField::getProperty(PropertyName); + } + } + return SVTXFormattedField::getProperty(PropertyName); +} + +void SVTXCurrencyField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_CURRENCYSYMBOL, + BASEPROPERTY_CURSYM_POSITION, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VALUEMAX_DOUBLE, + BASEPROPERTY_VALUEMIN_DOUBLE, + BASEPROPERTY_VALUESTEP_DOUBLE, + BASEPROPERTY_VALUE_DOUBLE, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +SVTXNumericField::SVTXNumericField() +{ +} + +SVTXNumericField::~SVTXNumericField() +{ +} + + +css::uno::Reference<accessibility::XAccessibleContext> SVTXNumericField::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext(this); +} + + +void SVTXNumericField::setValue( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetValue( Value ); +} + +double SVTXNumericField::getValue() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetValue() : 0; +} + +void SVTXNumericField::setMin( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMinValue( Value ); +} + +double SVTXNumericField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMinValue() : 0; +} + +void SVTXNumericField::setMax( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMaxValue( Value ); +} + +double SVTXNumericField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMaxValue() : 0; +} + +void SVTXNumericField::setFirst( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinFirst( Value ); +} + +double SVTXNumericField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinFirst() : 0; +} + +void SVTXNumericField::setLast( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinLast( Value ); +} + +double SVTXNumericField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinLast() : 0; +} + +void SVTXNumericField::setSpinSize( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinSize( Value ); +} + +double SVTXNumericField::getSpinSize() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinSize() : 0; +} + +void SVTXNumericField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetDecimalDigits( Value ); +} + +sal_Int16 SVTXNumericField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetDecimalDigits() : 0; +} + +void SVTXNumericField::setStrictFormat( sal_Bool bStrict ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetStrictFormat( bStrict ); +} + +sal_Bool SVTXNumericField::isStrictFormat() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField && pField->GetFormatter().IsStrictFormat(); +} + +void SVTXNumericField::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + SVTXFormattedField::ImplGetPropertyIds( rIds ); +} + +SVTXDateField::SVTXDateField() +{ +} + +SVTXDateField::~SVTXDateField() +{ +} + +void SAL_CALL SVTXDateField::setProperty( const OUString& PropertyName, const css::uno::Any& Value ) +{ + VCLXDateField::setProperty( PropertyName, Value ); + + // some properties need to be forwarded to the sub edit, too + SolarMutexGuard g; + VclPtr< Edit > pSubEdit = GetWindow() ? GetAs<Edit>()->GetSubEdit() : nullptr; + if ( !pSubEdit ) + return; + + switch ( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_TEXTLINECOLOR: + if ( !Value.hasValue() ) + pSubEdit->SetTextLineColor(); + else + { + Color nColor; + if ( Value >>= nColor ) + pSubEdit->SetTextLineColor( nColor ); + } + break; + } +} + +void SVTXDateField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_TEXTLINECOLOR, + 0); + VCLXDateField::ImplGetPropertyIds( rIds ); +} + +VCLXMultiLineEdit::VCLXMultiLineEdit() + :maTextListeners( *this ) + ,meLineEndType( LINEEND_LF ) // default behavior before introducing this property: LF (unix-like) +{ +} + +VCLXMultiLineEdit::~VCLXMultiLineEdit() +{ +} + +void VCLXMultiLineEdit::addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.addInterface( l ); +} + +void VCLXMultiLineEdit::removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.removeInterface( l ); +} + +void VCLXMultiLineEdit::setText( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + { + pEdit->SetText( aText ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXMultiLineEdit::insertText( const css::awt::Selection& rSel, const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + { + setSelection( rSel ); + pEdit->ReplaceSelected( aText ); + } +} + +OUString VCLXMultiLineEdit::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aText = pEdit->GetText( meLineEndType ); + return aText; +} + +OUString VCLXMultiLineEdit::getSelectedText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit) + aText = pMultiLineEdit->GetSelected( meLineEndType ); + return aText; + +} + +void VCLXMultiLineEdit::setSelection( const css::awt::Selection& aSelection ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + { + pMultiLineEdit->SetSelection( Selection( aSelection.Min, aSelection.Max ) ); + } +} + +css::awt::Selection VCLXMultiLineEdit::getSelection() +{ + SolarMutexGuard aGuard; + + css::awt::Selection aSel; + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + { + aSel.Min = pMultiLineEdit->GetSelection().Min(); + aSel.Max = pMultiLineEdit->GetSelection().Max(); + } + return aSel; +} + +sal_Bool VCLXMultiLineEdit::isEditable() +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + return pMultiLineEdit && !pMultiLineEdit->IsReadOnly() && pMultiLineEdit->IsEnabled(); +} + +void VCLXMultiLineEdit::setEditable( sal_Bool bEditable ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + pMultiLineEdit->SetReadOnly( !bEditable ); +} + +void VCLXMultiLineEdit::setMaxTextLen( sal_Int16 nLen ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + pMultiLineEdit->SetMaxTextLen( nLen ); +} + +sal_Int16 VCLXMultiLineEdit::getMaxTextLen() +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + return pMultiLineEdit ? static_cast<sal_Int16>(pMultiLineEdit->GetMaxTextLen()) : sal_Int16(0); +} + +OUString VCLXMultiLineEdit::getTextLines() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aText = pEdit->GetTextLines( meLineEndType ); + return aText; +} + +css::awt::Size VCLXMultiLineEdit::getMinimumSize() +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcMinimumSize()); + return aSz; +} + +css::awt::Size VCLXMultiLineEdit::getPreferredSize() +{ + return getMinimumSize(); +} + +css::awt::Size VCLXMultiLineEdit::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcAdjustedSize( VCLSize(rNewSize ))); + return aSz; +} + +css::awt::Size VCLXMultiLineEdit::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcBlockSize( nCols, nLines )); + return aSz; +} + +void VCLXMultiLineEdit::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nCols = nLines = 0; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + { + sal_uInt16 nC, nL; + pEdit->GetMaxVisColumnsAndLines( nC, nL ); + nCols = nC; + nLines = nL; + } +} + +void VCLXMultiLineEdit::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::EditModify: + { + if ( maTextListeners.getLength() ) + { + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + maTextListeners.textChanged( aEvent ); + } + } + break; + default: + { + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + } + break; + } +} + +void VCLXMultiLineEdit::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( !pMultiLineEdit ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINE_END_FORMAT: + { + sal_Int16 nLineEndType = css::awt::LineEndFormat::LINE_FEED; + OSL_VERIFY( Value >>= nLineEndType ); + switch ( nLineEndType ) + { + case css::awt::LineEndFormat::CARRIAGE_RETURN: meLineEndType = LINEEND_CR; break; + case css::awt::LineEndFormat::LINE_FEED: meLineEndType = LINEEND_LF; break; + case css::awt::LineEndFormat::CARRIAGE_RETURN_LINE_FEED: meLineEndType = LINEEND_CRLF; break; + default: OSL_FAIL( "VCLXMultiLineEdit::setProperty: invalid line end value!" ); break; + } + } + break; + + case BASEPROPERTY_READONLY: + { + bool b; + if ( Value >>= b ) + pMultiLineEdit->SetReadOnly( b ); + } + break; + case BASEPROPERTY_MAXTEXTLEN: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pMultiLineEdit->SetMaxTextLen( n ); + } + break; + case BASEPROPERTY_HIDEINACTIVESELECTION: + { + bool b; + if ( Value >>= b ) + { + pMultiLineEdit->EnableFocusSelectionHide( b ); + lcl_setWinBits( pMultiLineEdit, WB_NOHIDESELECTION, !b ); + } + } + break; + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXMultiLineEdit::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINE_END_FORMAT: + { + sal_Int16 nLineEndType = css::awt::LineEndFormat::LINE_FEED; + switch ( meLineEndType ) + { + case LINEEND_CR: nLineEndType = css::awt::LineEndFormat::CARRIAGE_RETURN; break; + case LINEEND_LF: nLineEndType = css::awt::LineEndFormat::LINE_FEED; break; + case LINEEND_CRLF: nLineEndType = css::awt::LineEndFormat::CARRIAGE_RETURN_LINE_FEED; break; + default: OSL_FAIL( "VCLXMultiLineEdit::getProperty: invalid line end value!" ); break; + } + aProp <<= nLineEndType; + } + break; + + case BASEPROPERTY_READONLY: + { + aProp <<= pMultiLineEdit->IsReadOnly(); + } + break; + case BASEPROPERTY_MAXTEXTLEN: + { + aProp <<= static_cast<sal_Int16>(pMultiLineEdit->GetMaxTextLen()); + } + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void SAL_CALL VCLXMultiLineEdit::setFocus( ) +{ + SolarMutexGuard aGuard; + + // don't grab the focus if we already have it. Reason is that the only thing which the edit + // does is forwarding the focus to its text window. This text window then does a "select all". + // So if the text window already has the focus, and we give the focus to the multi line + // edit, then all which happens is that everything is selected. + // #i27072# + if ( GetWindow() && !GetWindow()->HasChildPathFocus() ) + GetWindow()->GrabFocus(); +} + +void VCLXMultiLineEdit::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + // FIXME: elide duplication ? + BASEPROPERTY_LINE_END_FORMAT, + BASEPROPERTY_READONLY, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_HIDEINACTIVESELECTION, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); +} + +css::uno::Reference<css::accessibility::XAccessibleContext> VCLXMultiLineEdit::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext(this); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindows_internal.hxx b/toolkit/source/awt/vclxwindows_internal.hxx new file mode 100644 index 0000000000..0425225865 --- /dev/null +++ b/toolkit/source/awt/vclxwindows_internal.hxx @@ -0,0 +1,33 @@ +/* -*- 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_AWT_VCLXWINDOWS_INTERNAL_HXX +#define INCLUDED_TOOLKIT_SOURCE_AWT_VCLXWINDOWS_INTERNAL_HXX + +#include <vcl/window.hxx> + +namespace toolkit +{ +void setButtonLikeFaceColor(vcl::Window* _pWindow, const css::uno::Any& _rColorValue); +css::uno::Any getButtonLikeFaceColor(const vcl::Window* _pWindow); +} + +#endif // INCLUDED_TOOLKIT_SOURCE_AWT_VCLXWINDOWS_INTERNAL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/toolkit/source/hatchwindow/documentcloser.cxx b/toolkit/source/hatchwindow/documentcloser.cxx new file mode 100644 index 0000000000..70171d7308 --- /dev/null +++ b/toolkit/source/hatchwindow/documentcloser.cxx @@ -0,0 +1,229 @@ +/* -*- 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 <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/dialoghelper.hxx> +#include <vcl/window.hxx> +#include <tools/link.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +using namespace ::com::sun::star; + +namespace { + +// the service is implemented as a wrapper to be able to die by refcount +// the disposing mechanics is required for java related scenarios +class ODocumentCloser : public ::cppu::WeakImplHelper< css::lang::XComponent, + css::lang::XServiceInfo > +{ + std::mutex m_aMutex; + css::uno::Reference< css::frame::XFrame > m_xFrame; + ::comphelper::OInterfaceContainerHelper4<lang::XEventListener> m_aListenersContainer; // list of listeners + + bool m_bDisposed; + +public: + explicit ODocumentCloser(const css::uno::Sequence< css::uno::Any >& aArguments); + +// XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) 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; +}; + +class MainThreadFrameCloserRequest +{ + uno::Reference< frame::XFrame > m_xFrame; + + public: + explicit MainThreadFrameCloserRequest( uno::Reference< frame::XFrame > xFrame ) + : m_xFrame(std::move( xFrame )) + {} + + DECL_STATIC_LINK( MainThreadFrameCloserRequest, worker, void*, void ); + + static void Start( MainThreadFrameCloserRequest* pRequest ); +}; + + +void MainThreadFrameCloserRequest::Start( MainThreadFrameCloserRequest* pMTRequest ) +{ + if ( pMTRequest ) + { + if ( Application::IsMainThread() ) + { + // this is the main thread + worker( nullptr, pMTRequest ); + } + else + Application::PostUserEvent( LINK( nullptr, MainThreadFrameCloserRequest, worker ), pMTRequest ); + } +} + + +IMPL_STATIC_LINK( MainThreadFrameCloserRequest, worker, void*, p, void ) +{ + MainThreadFrameCloserRequest* pMTRequest = static_cast<MainThreadFrameCloserRequest*>(p); + if ( !pMTRequest ) + return; + + if ( pMTRequest->m_xFrame.is() ) + { + // this is the main thread, the solar mutex must be locked + SolarMutexGuard aGuard; + + try + { + uno::Reference< awt::XWindow > xWindow = pMTRequest->m_xFrame->getContainerWindow(); + uno::Reference< awt::XVclWindowPeer > xWinPeer( xWindow, uno::UNO_QUERY_THROW ); + + xWindow->setVisible( false ); + + // reparent the window + xWinPeer->setProperty( "PluginParent", uno::Any( sal_Int64(0) ) ); + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if (pWindow) + vcl::EndAllDialogs(pWindow); + } + catch( uno::Exception& ) + { + // ignore all the errors + } + + try + { + uno::Reference< util::XCloseable > xCloseable( pMTRequest->m_xFrame, uno::UNO_QUERY_THROW ); + xCloseable->close( true ); + } + catch( uno::Exception& ) + { + // ignore all the errors + } + } + + delete pMTRequest; +} + +ODocumentCloser::ODocumentCloser(const css::uno::Sequence< css::uno::Any >& aArguments) +: m_bDisposed( false ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( !m_refCount ) + throw uno::RuntimeException(); // the object must be refcounted already! + + sal_Int32 nLen = aArguments.getLength(); + if ( nLen != 1 ) + throw lang::IllegalArgumentException( + "Wrong count of parameters!", + uno::Reference< uno::XInterface >(), + 0 ); + + if ( !( aArguments[0] >>= m_xFrame ) || !m_xFrame.is() ) + throw lang::IllegalArgumentException( + "Nonempty reference is expected as the first argument!", + uno::Reference< uno::XInterface >(), + 0 ); +} + + +// XComponent + +void SAL_CALL ODocumentCloser::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( m_bDisposed ) + return; + + lang::EventObject aSource( getXWeak() ); + m_aListenersContainer.disposeAndClear( aGuard, aSource ); + + // TODO: trigger a main thread execution to close the frame + if ( m_xFrame.is() ) + { + // the created object will be deleted after thread execution + MainThreadFrameCloserRequest* pCloser = new MainThreadFrameCloserRequest( m_xFrame ); + MainThreadFrameCloserRequest::Start( pCloser ); + } + + m_bDisposed = true; +} + + +void SAL_CALL ODocumentCloser::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aListenersContainer.addInterface( aGuard, xListener ); +} + + +void SAL_CALL ODocumentCloser::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aListenersContainer.removeInterface( aGuard, xListener ); +} + +// XServiceInfo +OUString SAL_CALL ODocumentCloser::getImplementationName( ) +{ + return "com.sun.star.comp.embed.DocumentCloser"; +} + +sal_Bool SAL_CALL ODocumentCloser::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL ODocumentCloser::getSupportedServiceNames() +{ + return { "com.sun.star.embed.DocumentCloser" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_embed_DocumentCloser_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &arguments) +{ + return cppu::acquire(new ODocumentCloser(arguments)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/hatchwindow.cxx b/toolkit/source/hatchwindow/hatchwindow.cxx new file mode 100644 index 0000000000..4685126b20 --- /dev/null +++ b/toolkit/source/hatchwindow/hatchwindow.cxx @@ -0,0 +1,159 @@ +/* -*- 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/embed/XHatchWindowController.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include "hatchwindow.hxx" +#include "ipwin.hxx" + +#include <toolkit/helper/convert.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +VCLXHatchWindow::VCLXHatchWindow() +: pHatchWindow(nullptr) +{ +} + +VCLXHatchWindow::~VCLXHatchWindow() +{ +} + +void VCLXHatchWindow::initializeWindow( const uno::Reference< awt::XWindowPeer >& xParent, + const awt::Rectangle& aBounds, + const awt::Size& aSize ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pParent; + VCLXWindow* pParentComponent = dynamic_cast<VCLXWindow*>( xParent.get() ); + + if ( pParentComponent ) + pParent = pParentComponent->GetWindow(); + + OSL_ENSURE( pParent, "No parent window is provided!" ); + if ( !pParent ) + throw lang::IllegalArgumentException(); // TODO + + pHatchWindow = VclPtr<SvResizeWindow>::Create( pParent, this ); + pHatchWindow->setPosSizePixel( aBounds.X, aBounds.Y, aBounds.Width, aBounds.Height ); + aHatchBorderSize = aSize; + pHatchWindow->SetHatchBorderPixel( Size( aSize.Width, aSize.Height ) ); + + SetWindow( pHatchWindow ); + pHatchWindow->SetComponentInterface( this ); + + //pHatchWindow->Show(); +} + +void VCLXHatchWindow::QueryObjAreaPixel( tools::Rectangle & aRect ) +{ + if ( !m_xController.is() ) + return; + + awt::Rectangle aUnoRequestRect = AWTRectangle( aRect ); + + try { + awt::Rectangle aUnoResultRect = m_xController->calcAdjustedRectangle( aUnoRequestRect ); + aRect = VCLRectangle( aUnoResultRect ); + } + catch( uno::Exception& ) + { + OSL_FAIL( "Can't adjust rectangle size!" ); + } +} + +void VCLXHatchWindow::RequestObjAreaPixel( const tools::Rectangle & aRect ) +{ + if ( m_xController.is() ) + { + awt::Rectangle aUnoRequestRect = AWTRectangle( aRect ); + + try { + m_xController->requestPositioning( aUnoRequestRect ); + } + catch( uno::Exception& ) + { + OSL_FAIL( "Can't request resizing!" ); + } + } +} + +void VCLXHatchWindow::InplaceDeactivate() +{ + if ( m_xController.is() ) + { + // TODO: communicate with controller + } +} + + +css::awt::Size SAL_CALL VCLXHatchWindow::getHatchBorderSize() +{ + return aHatchBorderSize; +} + +void SAL_CALL VCLXHatchWindow::setHatchBorderSize( const css::awt::Size& _hatchbordersize ) +{ + if ( pHatchWindow ) + { + aHatchBorderSize = _hatchbordersize; + pHatchWindow->SetHatchBorderPixel( Size( aHatchBorderSize.Width, aHatchBorderSize.Height ) ); + } +} + +void SAL_CALL VCLXHatchWindow::setController( const uno::Reference< embed::XHatchWindowController >& xController ) +{ + m_xController = xController; +} + +void SAL_CALL VCLXHatchWindow::dispose() +{ + pHatchWindow.clear(); + VCLXWindow::dispose(); +} + +void SAL_CALL VCLXHatchWindow::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + VCLXWindow::addEventListener( xListener ); +} + +void SAL_CALL VCLXHatchWindow::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + VCLXWindow::removeEventListener( xListener ); +} + +void VCLXHatchWindow::Activated() +{ + if ( m_xController.is() ) + m_xController->activated(); +} + +void VCLXHatchWindow::Deactivated() +{ + if ( m_xController.is() ) + m_xController->deactivated(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/hatchwindow.hxx b/toolkit/source/hatchwindow/hatchwindow.hxx new file mode 100644 index 0000000000..128a5198f9 --- /dev/null +++ b/toolkit/source/hatchwindow/hatchwindow.hxx @@ -0,0 +1,60 @@ +/* -*- 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/embed/XHatchWindow.hpp> + +#include <toolkit/awt/vclxwindow.hxx> + +class SvResizeWindow; +typedef cppu::ImplInheritanceHelper< VCLXWindow, css::embed::XHatchWindow> VCLXHatchWindow_Base; +class VCLXHatchWindow : public VCLXHatchWindow_Base +{ + css::uno::Reference< css::embed::XHatchWindowController > m_xController; + css::awt::Size aHatchBorderSize; + VclPtr<SvResizeWindow> pHatchWindow; + +public: + VCLXHatchWindow(); + virtual ~VCLXHatchWindow() override; + + void initializeWindow( const css::uno::Reference< css::awt::XWindowPeer >& xParent, + const css::awt::Rectangle& aBounds, + const css::awt::Size& aSize ); + + void QueryObjAreaPixel( tools::Rectangle & ); + void RequestObjAreaPixel( const tools::Rectangle & ); + void InplaceDeactivate(); + void Activated(); + void Deactivated(); + + // XHatchWindow + virtual void SAL_CALL setController( const css::uno::Reference< css::embed::XHatchWindowController >& xController ) override; + virtual css::awt::Size SAL_CALL getHatchBorderSize() override; + virtual void SAL_CALL setHatchBorderSize( const css::awt::Size& _hatchbordersize ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/hatchwindowfactory.cxx b/toolkit/source/hatchwindow/hatchwindowfactory.cxx new file mode 100644 index 0000000000..3e50db64ee --- /dev/null +++ b/toolkit/source/hatchwindow/hatchwindowfactory.cxx @@ -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 . + */ + +#include <com/sun/star/embed/XHatchWindowFactory.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> + +#include "hatchwindow.hxx" + +using namespace ::com::sun::star; + +namespace { + +class OHatchWindowFactory : public ::cppu::WeakImplHelper< + embed::XHatchWindowFactory, + lang::XServiceInfo > +{ +public: + OHatchWindowFactory() {} + + // XHatchWindowFactory + virtual uno::Reference< embed::XHatchWindow > SAL_CALL createHatchWindowInstance( const uno::Reference< awt::XWindowPeer >& xParent, const awt::Rectangle& aBounds, const awt::Size& aSize ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +uno::Reference< embed::XHatchWindow > SAL_CALL OHatchWindowFactory::createHatchWindowInstance( + const uno::Reference< awt::XWindowPeer >& xParent, + const awt::Rectangle& aBounds, + const awt::Size& aHandlerSize ) +{ + if ( !xParent.is() ) + throw lang::IllegalArgumentException(); // TODO + + SolarMutexGuard aGuard; + rtl::Reference<VCLXHatchWindow> pResult = new VCLXHatchWindow(); + pResult->initializeWindow( xParent, aBounds, aHandlerSize ); + return pResult; +} + +OUString SAL_CALL OHatchWindowFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.HatchWindowFactory"; +} + +sal_Bool SAL_CALL OHatchWindowFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OHatchWindowFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.HatchWindowFactory", "com.sun.star.comp.embed.HatchWindowFactory" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_embed_HatchWindowFactory_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OHatchWindowFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/ipwin.cxx b/toolkit/source/hatchwindow/ipwin.cxx new file mode 100644 index 0000000000..52d3668b7f --- /dev/null +++ b/toolkit/source/hatchwindow/ipwin.cxx @@ -0,0 +1,621 @@ +/* -*- 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/accessibility/AccessibleRole.hpp> + +#include <osl/diagnose.h> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> + +#include "ipwin.hxx" +#include "hatchwindow.hxx" + +/************************************************************************/ +/************************************************************************* +|* SvResizeHelper::SvResizeHelper() +|* +|* Description +*************************************************************************/ +SvResizeHelper::SvResizeHelper() + : aBorder( 5, 5 ) + , nGrab( -1 ) +{ +} + +/************************************************************************* +|* SvResizeHelper::FillHandleRects() +|* +|* Description: the eight handles to magnify +*************************************************************************/ +std::array<tools::Rectangle,8> SvResizeHelper::FillHandleRectsPixel() const +{ + std::array<tools::Rectangle,8> aRects; + + // only because of EMPTY_RECT + Point aBottomRight = aOuter.BottomRight(); + + // upper left + aRects[ 0 ] = tools::Rectangle( aOuter.TopLeft(), aBorder ); + // upper middle + aRects[ 1 ] = tools::Rectangle( Point( aOuter.Center().X() - aBorder.Width() / 2, + aOuter.Top() ), + aBorder ); + // upper right + aRects[ 2 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1, + aOuter.Top() ), + aBorder ); + // middle right + aRects[ 3 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1, + aOuter.Center().Y() - aBorder.Height() / 2 ), + aBorder ); + // lower right + aRects[ 4 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1, + aBottomRight.Y() - aBorder.Height() +1 ), + aBorder ); + // lower middle + aRects[ 5 ] = tools::Rectangle( Point( aOuter.Center().X() - aBorder.Width() / 2, + aBottomRight.Y() - aBorder.Height() +1), + aBorder ); + // lower left + aRects[ 6 ] = tools::Rectangle( Point( aOuter.Left(), + aBottomRight.Y() - aBorder.Height() +1), + aBorder ); + // middle left + aRects[ 7 ] = tools::Rectangle( Point( aOuter.Left(), + aOuter.Center().Y() - aBorder.Height() / 2 ), + aBorder ); + return aRects; +} + +/************************************************************************* +|* SvResizeHelper::FillMoveRectsPixel() +|* +|* Description: the four edges are calculated +*************************************************************************/ +std::array<tools::Rectangle,4> SvResizeHelper::FillMoveRectsPixel() const +{ + std::array<tools::Rectangle,4> aRects; + + // upper + aRects[ 0 ] = aOuter; + aRects[ 0 ].SetBottom( aRects[ 0 ].Top() + aBorder.Height() -1 ); + // right + aRects[ 1 ] = aOuter; + if (!aOuter.IsWidthEmpty()) + aRects[ 1 ].SetLeft( aRects[ 1 ].Right() - aBorder.Width() -1 ); + // lower + aRects[ 2 ] = aOuter; + if (!aOuter.IsHeightEmpty()) + aRects[ 2 ].SetTop( aRects[ 2 ].Bottom() - aBorder.Height() -1 ); + // left + aRects[ 3 ] = aOuter; + aRects[ 3 ].SetRight( aRects[ 3 ].Left() + aBorder.Width() -1 ); + + return aRects; +} + +/************************************************************************* +|* SvResizeHelper::Draw() +|* +|* Description +*************************************************************************/ +void SvResizeHelper::Draw(vcl::RenderContext& rRenderContext) +{ + rRenderContext.Push(); + rRenderContext.SetMapMode( MapMode() ); + + rRenderContext.SetFillColor( COL_LIGHTGRAY ); + rRenderContext.SetLineColor(); + + std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel(); + sal_uInt16 i; + for (i = 0; i < 4; i++) + rRenderContext.DrawRect(aMoveRects[i]); + // draw handles + rRenderContext.SetFillColor(Color()); // black + std::array<tools::Rectangle,8> aRects = FillHandleRectsPixel(); + for (i = 0; i < 8; i++) + rRenderContext.DrawRect( aRects[ i ] ); + rRenderContext.Pop(); +} + +/************************************************************************* +|* SvResizeHelper::InvalidateBorder() +|* +|* Description +*************************************************************************/ +void SvResizeHelper::InvalidateBorder( vcl::Window * pWin ) +{ + std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel(); + for(const auto & rMoveRect : aMoveRects) + pWin->Invalidate( rMoveRect ); +} + +/************************************************************************* +|* SvResizeHelper::SelectBegin() +|* +|* Description +*************************************************************************/ +bool SvResizeHelper::SelectBegin( vcl::Window * pWin, const Point & rPos ) +{ + if( -1 == nGrab ) + { + nGrab = SelectMove( pWin, rPos ); + if( -1 != nGrab ) + { + aSelPos = rPos; // store start position + pWin->CaptureMouse(); + return true; + } + } + return false; +} + +/************************************************************************* +|* SvResizeHelper::SelectMove() +|* +|* Description +*************************************************************************/ +short SvResizeHelper::SelectMove( vcl::Window * pWin, const Point & rPos ) +{ + if( -1 == nGrab ) + { + std::array<tools::Rectangle,8> aRects = FillHandleRectsPixel(); + for( sal_uInt16 i = 0; i < 8; i++ ) + if( aRects[ i ].Contains( rPos ) ) + return i; + // Move-Rect overlaps Handles + std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel(); + for(const auto & rMoveRect : aMoveRects) + if( rMoveRect.Contains( rPos ) ) + return 8; + } + else + { + tools::Rectangle aRect = pWin->PixelToLogic(GetTrackRectPixel( rPos )); + pWin->ShowTracking( aRect ); + } + return nGrab; +} + +Point SvResizeHelper::GetTrackPosPixel( const tools::Rectangle & rRect ) const +{ + // not important how the rectangle is returned, it is important + // which handle has been touched + Point aPos; + tools::Rectangle aRect( rRect ); + aRect.Normalize(); + // only because of EMPTY_RECT + Point aBR = aOuter.BottomRight(); + Point aTR = aOuter.TopRight(); + Point aBL = aOuter.BottomLeft(); + bool bRTL = AllSettings::GetLayoutRTL(); + switch( nGrab ) + { + case 0: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.TopLeft() - aOuter.TopLeft(); + break; + case 1: + aPos.setY( aRect.Top() - aOuter.Top() ); + break; + case 2: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.TopRight() - aTR; + break; + case 3: + if( bRTL ) + aPos.setX( aRect.Left() - aTR.X() ); + else + aPos.setX( aRect.Right() - aTR.X() ); + break; + case 4: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.BottomRight() - aBR; + break; + case 5: + aPos.setY( aRect.Bottom() - aBR.Y() ); + break; + case 6: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.BottomLeft() - aBL; + break; + case 7: + if( bRTL ) + aPos.setX( aRect.Right() + aOuter.Right() - aOuter.Right() ); + else + aPos.setX( aRect.Left() - aOuter.Left() ); + break; + case 8: + aPos = aRect.TopLeft() - aOuter.TopLeft(); + break; + } + return aPos + aSelPos; +} + +/************************************************************************* +|* SvResizeHelper::GetTrackRectPixel() +|* +|* Description +*************************************************************************/ +tools::Rectangle SvResizeHelper::GetTrackRectPixel( const Point & rTrackPos ) const +{ + tools::Rectangle aTrackRect; + if( -1 != nGrab ) + { + Point aDiff = rTrackPos - aSelPos; + aTrackRect = aOuter; + Point aBR = aOuter.BottomRight(); + bool bRTL = AllSettings::GetLayoutRTL(); + switch( nGrab ) + { + case 0: + aTrackRect.AdjustTop(aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 1: + aTrackRect.AdjustTop(aDiff.Y() ); + break; + case 2: + aTrackRect.AdjustTop(aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 3: + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 4: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 5: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + break; + case 6: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 7: + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 8: + if( bRTL ) + aDiff.setX( -aDiff.X() ); // workaround for move in RTL mode + aTrackRect.SetPos( aTrackRect.TopLeft() + aDiff ); + break; + } + } + return aTrackRect; +} + +void SvResizeHelper::ValidateRect( tools::Rectangle & rValidate ) const +{ + switch( nGrab ) + { + case 0: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetTop( rValidate.Bottom() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetLeft( rValidate.Right() ); + break; + case 1: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetTop( rValidate.Bottom() ); + break; + case 2: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetTop( rValidate.Bottom() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() ); + break; + case 3: + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() ); + break; + case 4: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() ); + break; + case 5: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() ); + break; + case 6: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetLeft( rValidate.Right() ); + break; + case 7: + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetLeft( rValidate.Right() ); + break; + } + + // Mindestgr"osse 5 x 5 + if( rValidate.Left() + 5 > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() + 5 ); + if( rValidate.Top() + 5 > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() + 5 ); +} + +/************************************************************************* +|* SvResizeHelper::SelectRelease() +|* +|* Description +*************************************************************************/ +bool SvResizeHelper::SelectRelease( vcl::Window * pWin, const Point & rPos, + tools::Rectangle & rOutPosSize ) +{ + if( -1 != nGrab ) + { + rOutPosSize = GetTrackRectPixel( rPos ); + rOutPosSize.Normalize(); + nGrab = -1; + pWin->ReleaseMouse(); + pWin->HideTracking(); + return true; + } + return false; +} + +/************************************************************************* +|* SvResizeHelper::Release() +|* +|* Description +*************************************************************************/ +void SvResizeHelper::Release( vcl::Window * pWin ) +{ + if( nGrab != -1 ) + { + pWin->ReleaseMouse(); + pWin->HideTracking(); + nGrab = -1; + } +} + +/************************************************************************* +|* SvResizeWindow::SvResizeWindow() +|* +|* Description +*************************************************************************/ +SvResizeWindow::SvResizeWindow +( + vcl::Window * pParent, + VCLXHatchWindow* pWrapper +) + : Window( pParent, WB_CLIPCHILDREN ) + , m_aOldPointer(PointerStyle::Arrow) + , m_nMoveGrab( -1 ) + , m_bActive( false ) + , m_pWrapper( pWrapper ) +{ + OSL_ENSURE( pParent != nullptr && pWrapper != nullptr, "Wrong initialization of hatch window!" ); + SetBackground(); + SetAccessibleRole( css::accessibility::AccessibleRole::EMBEDDED_OBJECT ); + m_aResizer.SetOuterRectPixel( tools::Rectangle( Point(), GetOutputSizePixel() ) ); +} + +/************************************************************************* +|* SvResizeWindow::SetHatchBorderPixel() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::SetHatchBorderPixel( const Size & rSize ) +{ + m_aResizer.SetBorderPixel( rSize ); +} + +/************************************************************************* +|* SvResizeWindow::SelectMouse() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::SelectMouse( const Point & rPos ) +{ + short nGrab = m_aResizer.SelectMove( this, rPos ); + if( nGrab >= 4 ) + nGrab -= 4; + if( m_nMoveGrab == nGrab ) + return; + + // Pointer did change + if( -1 == nGrab ) + SetPointer( m_aOldPointer ); + else + { + PointerStyle aStyle = PointerStyle::Move; + if( nGrab == 3 ) + aStyle = PointerStyle::ESize; + else if( nGrab == 2 ) + aStyle = PointerStyle::NESize; + else if( nGrab == 1 ) + aStyle = PointerStyle::SSize; + else if( nGrab == 0 ) + aStyle = PointerStyle::SESize; + if( m_nMoveGrab == -1 ) // the first time + { + m_aOldPointer = GetPointer(); + SetPointer( aStyle ); + } + else + SetPointer( aStyle ); + } + m_nMoveGrab = nGrab; +} + +/************************************************************************* +|* SvResizeWindow::MouseButtonDown() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::MouseButtonDown( const MouseEvent & rEvt ) +{ + if( m_aResizer.SelectBegin( this, rEvt.GetPosPixel() ) ) + SelectMouse( rEvt.GetPosPixel() ); +} + +/************************************************************************* +|* SvResizeWindow::MouseMove() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::MouseMove( const MouseEvent & rEvt ) +{ + if( m_aResizer.GetGrab() == -1 ) + SelectMouse( rEvt.GetPosPixel() ); + else + { + tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) ); + Point aDiff = GetPosPixel(); + aRect.SetPos( aRect.TopLeft() + aDiff ); + m_aResizer.ValidateRect( aRect ); + + m_pWrapper->QueryObjAreaPixel( aRect ); + aRect.SetPos( aRect.TopLeft() - aDiff ); + Point aPos = m_aResizer.GetTrackPosPixel( aRect ); + + SelectMouse( aPos ); + } +} + +/************************************************************************* +|* SvResizeWindow::MouseButtonUp() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::MouseButtonUp( const MouseEvent & rEvt ) +{ + if( m_aResizer.GetGrab() == -1 ) + return; + + tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) ); + Point aDiff = GetPosPixel(); + aRect.SetPos( aRect.TopLeft() + aDiff ); + // aRect -= GetAllBorderPixel(); + m_aResizer.ValidateRect( aRect ); + + m_pWrapper->QueryObjAreaPixel( aRect ); + + tools::Rectangle aOutRect; + if( m_aResizer.SelectRelease( this, rEvt.GetPosPixel(), aOutRect ) ) + { + m_nMoveGrab = -1; + SetPointer( m_aOldPointer ); + m_pWrapper->RequestObjAreaPixel( aRect ); + } +} + +/************************************************************************* +|* SvResizeWindow::KeyEvent() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::KeyInput( const KeyEvent & rEvt ) +{ + if( rEvt.GetKeyCode().GetCode() == KEY_ESCAPE ) + { + m_aResizer.Release( this ); + m_pWrapper->InplaceDeactivate(); + } +} + +/************************************************************************* +|* SvResizeWindow::Resize() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::Resize() +{ + m_aResizer.InvalidateBorder( this ); // old area + m_aResizer.SetOuterRectPixel( tools::Rectangle( Point(), GetOutputSizePixel() ) ); + m_aResizer.InvalidateBorder( this ); // new area +} + +/************************************************************************* +|* SvResizeWindow::Paint() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle & /*rRect*/ ) +{ + m_aResizer.Draw(rRenderContext); +} + +bool SvResizeWindow::PreNotify( NotifyEvent& rEvt ) +{ + if ( rEvt.GetType() == NotifyEventType::GETFOCUS && !m_bActive ) + { + m_bActive = true; + m_pWrapper->Activated(); + } + + return Window::PreNotify(rEvt); +} + +bool SvResizeWindow::EventNotify( NotifyEvent& rEvt ) +{ + if ( rEvt.GetType() == NotifyEventType::LOSEFOCUS && m_bActive ) + { + bool bHasFocus = HasChildPathFocus(true); + if ( !bHasFocus ) + { + m_bActive = false; + m_pWrapper->Deactivated(); + } + } + + return Window::EventNotify(rEvt); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/ipwin.hxx b/toolkit/source/hatchwindow/ipwin.hxx new file mode 100644 index 0000000000..72567ce268 --- /dev/null +++ b/toolkit/source/hatchwindow/ipwin.hxx @@ -0,0 +1,92 @@ +/* -*- 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 <tools/gen.hxx> +#include <vcl/window.hxx> +#include <array> + +/********************** SvResizeHelper *********************************** +*************************************************************************/ +class SvResizeHelper +{ + Size aBorder; + tools::Rectangle aOuter; + short nGrab; // -1 no Grab, 0 - 7, 8 = Move, see FillHandle... + Point aSelPos; +public: + SvResizeHelper(); + + short GetGrab() const + { + return nGrab; + } + void SetBorderPixel(const Size & rBorderP) + { + aBorder = rBorderP; + } + void SetOuterRectPixel(const tools::Rectangle& rRect) + { + aOuter = rRect; + } + // Clockwise, start at upper left + + std::array<tools::Rectangle,8> FillHandleRectsPixel() const; + std::array<tools::Rectangle,4> FillMoveRectsPixel() const; + void Draw(vcl::RenderContext& rRenderContext); + void InvalidateBorder( vcl::Window * ); + bool SelectBegin( vcl::Window *, const Point & rPos ); + short SelectMove( vcl::Window * pWin, const Point & rPos ); + Point GetTrackPosPixel( const tools::Rectangle & rRect ) const; + tools::Rectangle GetTrackRectPixel( const Point & rTrackPos ) const; + void ValidateRect( tools::Rectangle & rValidate ) const; + bool SelectRelease( vcl::Window *, const Point & rPos, tools::Rectangle & rOutPosSize ); + void Release( vcl::Window * pWin ); +}; + +/********************** SvResizeWindow *********************************** +*************************************************************************/ +class VCLXHatchWindow; +class SvResizeWindow : public vcl::Window +{ + PointerStyle m_aOldPointer; + short m_nMoveGrab; // last pointer type + SvResizeHelper m_aResizer; + bool m_bActive; + + VCLXHatchWindow* m_pWrapper; +public: + SvResizeWindow( vcl::Window* pParent, VCLXHatchWindow* pWrapper ); + + void SetHatchBorderPixel( const Size & rSize ); + + void SelectMouse( const Point & rPos ); + virtual void MouseButtonUp( const MouseEvent & rEvt ) override; + virtual void MouseMove( const MouseEvent & rEvt ) override; + virtual void MouseButtonDown( const MouseEvent & rEvt ) override; + virtual void KeyInput( const KeyEvent & rEvt ) override; + virtual void Resize() override; + virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle & ) override; + virtual bool EventNotify( NotifyEvent& rNEvt ) override; + virtual bool PreNotify( NotifyEvent& rNEvt ) override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/accessibilityclient.cxx b/toolkit/source/helper/accessibilityclient.cxx new file mode 100644 index 0000000000..cad81b3a91 --- /dev/null +++ b/toolkit/source/helper/accessibilityclient.cxx @@ -0,0 +1,235 @@ +/* -*- 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 <config_feature_desktop.h> +#include <config_wasm_strip.h> + +#include <sal/config.h> + +#include <toolkit/helper/accessiblefactory.hxx> +#include <osl/module.h> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <tools/svlibrary.h> + +#include <helper/accessibilityclient.hxx> + +namespace toolkit +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::accessibility; + + namespace + { +#ifndef DISABLE_DYNLOADING + oslModule s_hAccessibleImplementationModule = nullptr; +#endif +#if HAVE_FEATURE_DESKTOP +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + GetStandardAccComponentFactory s_pAccessibleFactoryFunc = nullptr; +#endif +#endif + ::rtl::Reference< IAccessibleFactory > s_pFactory; + } + + + //= AccessibleDummyFactory + + namespace { + + class AccessibleDummyFactory: + public IAccessibleFactory + { + public: + AccessibleDummyFactory(); + AccessibleDummyFactory(const AccessibleDummyFactory&) = delete; + AccessibleDummyFactory& operator=(const AccessibleDummyFactory&) = delete; + + protected: + virtual ~AccessibleDummyFactory() override; + + public: + // IAccessibleFactory + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXButton* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXCheckBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXRadioButton* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXListBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXFixedHyperlink* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXFixedText* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXScrollBar* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXEdit* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXMultiLineEdit* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXComboBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXToolBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXHeaderBar* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( SVTXNumericField* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXWindow* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessible > + createAccessible( Menu* /*_pMenu*/, bool /*_bIsMenuBar*/ ) override + { + return nullptr; + } + }; + + } + + AccessibleDummyFactory::AccessibleDummyFactory() + { + } + + + AccessibleDummyFactory::~AccessibleDummyFactory() + { + } + + + //= AccessibilityClient + + + AccessibilityClient::AccessibilityClient() + :m_bInitialized( false ) + { + } + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +#if HAVE_FEATURE_DESKTOP +#ifndef DISABLE_DYNLOADING + extern "C" { static void thisModule() {} } +#else + extern "C" void *getStandardAccessibleFactory(); +#endif +#endif // HAVE_FEATURE_DESKTOP +#endif // ENABLE_WASM_STRIP_ACCESSIBILITY + + void AccessibilityClient::ensureInitialized() + { + if ( m_bInitialized ) + return; + + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +#if HAVE_FEATURE_DESKTOP + // load the library implementing the factory + if (!s_pFactory) + { +#ifndef DISABLE_DYNLOADING + s_hAccessibleImplementationModule = osl_loadModuleRelative( &thisModule, u"" SVLIBRARY( "acc" ) ""_ustr.pData, 0 ); + if ( s_hAccessibleImplementationModule != nullptr ) + { + s_pAccessibleFactoryFunc = reinterpret_cast<GetStandardAccComponentFactory>( + osl_getFunctionSymbol( s_hAccessibleImplementationModule, u"getStandardAccessibleFactory"_ustr.pData )); + + } + OSL_ENSURE( s_pAccessibleFactoryFunc, "AccessibilityClient::ensureInitialized: could not load the library, or not retrieve the needed symbol!" ); +#else + s_pAccessibleFactoryFunc = getStandardAccessibleFactory; +#endif // DISABLE_DYNLOADING + + // get a factory instance + if ( s_pAccessibleFactoryFunc ) + { + IAccessibleFactory* pFactory = static_cast< IAccessibleFactory* >( (*s_pAccessibleFactoryFunc)() ); + OSL_ENSURE( pFactory, "AccessibilityClient::ensureInitialized: no factory provided by the A11Y lib!" ); + if ( pFactory ) + { + s_pFactory = pFactory; + pFactory->release(); + } + } + } +#endif // HAVE_FEATURE_DESKTOP +#endif // ENABLE_WASM_STRIP_ACCESSIBILITY + + if (!s_pFactory) + // the attempt to load the lib, or to create the factory, failed + // -> fall back to a dummy factory + s_pFactory = new AccessibleDummyFactory; + + m_bInitialized = true; + } + + IAccessibleFactory& AccessibilityClient::getFactory() + { + ensureInitialized(); + OSL_ENSURE( s_pFactory.is(), "AccessibilityClient::getFactory: at least a dummy factory should have been created!" ); + return *s_pFactory; + } + + +} // namespace toolkit + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/btndlg.cxx b/toolkit/source/helper/btndlg.cxx new file mode 100644 index 0000000000..a2471c3f92 --- /dev/null +++ b/toolkit/source/helper/btndlg.cxx @@ -0,0 +1,304 @@ +/* -*- 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 <vcl/toolkit/button.hxx> +#include <vcl/stdtext.hxx> +#include <helper/btndlg.hxx> +#include <sal/log.hxx> +#include <map> +#include <memory> + +struct ImplBtnDlgItem +{ + sal_uInt16 mnId; + bool mbOwnButton; + tools::Long mnSepSize; + VclPtr<PushButton> mpPushButton; + + ImplBtnDlgItem() : mnId(0), mbOwnButton(false), mnSepSize(0) {} +}; + +void ButtonDialog::ImplInitButtonDialogData() +{ + mnButtonSize = 0; + mnCurButtonId = 0; + mnFocusButtonId = BUTTONDIALOG_BUTTON_NOTFOUND; + mbFormat = true; +} + +ButtonDialog::ButtonDialog( WindowType nType ) : + Dialog( nType ) +{ + ImplInitButtonDialogData(); +} + +ButtonDialog::~ButtonDialog() +{ + disposeOnce(); +} + +void ButtonDialog::dispose() +{ + for (auto & it : m_ItemList) + { + if ( it->mbOwnButton ) + it->mpPushButton.disposeAndClear(); + } + m_ItemList.clear(); + Dialog::dispose(); +} + +VclPtr<PushButton> ButtonDialog::ImplCreatePushButton( ButtonDialogFlags nBtnFlags ) +{ + VclPtr<PushButton> pBtn; + WinBits nStyle = 0; + + if ( nBtnFlags & ButtonDialogFlags::Default ) + nStyle |= WB_DEFBUTTON; + if ( nBtnFlags & ButtonDialogFlags::Cancel ) + pBtn = VclPtr<CancelButton>::Create( this, nStyle ); + else if ( nBtnFlags & ButtonDialogFlags::OK ) + pBtn = VclPtr<OKButton>::Create( this, nStyle ); + else if ( nBtnFlags & ButtonDialogFlags::Help ) + pBtn = VclPtr<HelpButton>::Create( this, nStyle ); + else + pBtn = VclPtr<PushButton>::Create( this, nStyle ); + + if ( !(nBtnFlags & ButtonDialogFlags::Help) ) + pBtn->SetClickHdl( LINK( this, ButtonDialog, ImplClickHdl ) ); + + return pBtn; +} + +tools::Long ButtonDialog::ImplGetButtonSize() +{ + if ( !mbFormat ) + return mnButtonSize; + + // Calculate ButtonSize + tools::Long nLastSepSize = 0; + tools::Long nSepSize = 0; + maCtrlSize = Size( IMPL_MINSIZE_BUTTON_WIDTH, IMPL_MINSIZE_BUTTON_HEIGHT ); + + for (const auto & it : m_ItemList) + { + nSepSize += nLastSepSize; + + tools::Long nTxtWidth = it->mpPushButton->GetOutDev()->GetCtrlTextWidth(it->mpPushButton->GetText()); + nTxtWidth += IMPL_EXTRA_BUTTON_WIDTH; + + if ( nTxtWidth > maCtrlSize.Width() ) + maCtrlSize.setWidth( nTxtWidth ); + + tools::Long nTxtHeight = it->mpPushButton->GetTextHeight(); + nTxtHeight += IMPL_EXTRA_BUTTON_HEIGHT; + + if ( nTxtHeight > maCtrlSize.Height() ) + maCtrlSize.setHeight( nTxtHeight ); + + nSepSize += it->mnSepSize; + + if ( GetStyle() & WB_HORZ ) + nLastSepSize = IMPL_SEP_BUTTON_X; + else + nLastSepSize = IMPL_SEP_BUTTON_Y; + } + + size_t const nButtonCount = m_ItemList.size(); + + if ( GetStyle() & WB_HORZ ) + mnButtonSize = nSepSize + (nButtonCount*maCtrlSize.Width()); + else + mnButtonSize = nSepSize + (nButtonCount*maCtrlSize.Height()); + + return mnButtonSize; +} + +void ButtonDialog::ImplPosControls() +{ + if ( !mbFormat ) + return; + + // Create PushButtons and determine Sizes + ImplGetButtonSize(); + + // determine dialog size + Size aDlgSize = maPageSize; + tools::Long nX; + tools::Long nY; + if ( GetStyle() & WB_HORZ ) + { + if ( mnButtonSize+(IMPL_DIALOG_OFFSET*2) > aDlgSize.Width() ) + aDlgSize.setWidth( mnButtonSize+(IMPL_DIALOG_OFFSET*2) ); + if ( GetStyle() & WB_LEFT ) + nX = IMPL_DIALOG_OFFSET; + else if ( GetStyle() & WB_RIGHT ) + nX = aDlgSize.Width()-mnButtonSize-IMPL_DIALOG_OFFSET; + else + nX = (aDlgSize.Width()-mnButtonSize)/2; + + aDlgSize.AdjustHeight(IMPL_DIALOG_OFFSET+maCtrlSize.Height() ); + nY = aDlgSize.Height()-maCtrlSize.Height()-IMPL_DIALOG_OFFSET; + } + else + { + if ( mnButtonSize+(IMPL_DIALOG_OFFSET*2) > aDlgSize.Height() ) + aDlgSize.setHeight( mnButtonSize+(IMPL_DIALOG_OFFSET*2) ); + if ( GetStyle() & WB_BOTTOM ) + nY = aDlgSize.Height()-mnButtonSize-IMPL_DIALOG_OFFSET; + else if ( GetStyle() & WB_VCENTER ) + nY = (aDlgSize.Height()-mnButtonSize)/2; + else + nY = IMPL_DIALOG_OFFSET; + + aDlgSize.AdjustWidth(IMPL_DIALOG_OFFSET+maCtrlSize.Width() ); + nX = aDlgSize.Width()-maCtrlSize.Width()-IMPL_DIALOG_OFFSET; + } + + // Arrange PushButtons + for (auto & it : m_ItemList) + { + if ( GetStyle() & WB_HORZ ) + nX += it->mnSepSize; + else + nY += it->mnSepSize; + + it->mpPushButton->SetPosSizePixel( Point( nX, nY ), maCtrlSize ); + it->mpPushButton->Show(); + + if ( GetStyle() & WB_HORZ ) + nX += maCtrlSize.Width()+IMPL_SEP_BUTTON_X; + else + nY += maCtrlSize.Height()+IMPL_SEP_BUTTON_Y; + } + + SetOutputSizePixel(aDlgSize); + SetMinOutputSizePixel(aDlgSize); + + mbFormat = false; +} + +IMPL_LINK( ButtonDialog, ImplClickHdl, Button*, pBtn, void ) +{ + for (const auto & it : m_ItemList) + { + if ( it->mpPushButton == pBtn ) + { + mnCurButtonId = it->mnId; + if ( IsInExecute() ) + EndDialog( mnCurButtonId ); + break; + } + } +} + +void ButtonDialog::Resize() +{ +} + +void ButtonDialog::StateChanged( StateChangedType nType ) +{ + if ( nType == StateChangedType::InitShow ) + { + ImplPosControls(); + for (auto & it : m_ItemList) + { + if ( it->mpPushButton && it->mbOwnButton ) + it->mpPushButton->SetZOrder(nullptr, ZOrderFlags::Last); + } + + // Set focus on default button. + if ( mnFocusButtonId != BUTTONDIALOG_BUTTON_NOTFOUND ) + { + for (auto & it : m_ItemList) + { + if (it->mnId == mnFocusButtonId ) + { + if (it->mpPushButton->IsVisible()) + it->mpPushButton->GrabFocus(); + + break; + } + } + } + } + + Dialog::StateChanged( nType ); +} + +void ButtonDialog::AddButton( StandardButtonType eType, sal_uInt16 nId, + ButtonDialogFlags nBtnFlags, tools::Long nSepPixel ) +{ + // PageItem anlegen + std::unique_ptr<ImplBtnDlgItem> pItem(new ImplBtnDlgItem); + pItem->mnId = nId; + pItem->mbOwnButton = true; + pItem->mnSepSize = nSepPixel; + + if ( eType == StandardButtonType::OK ) + nBtnFlags |= ButtonDialogFlags::OK; + else if ( eType == StandardButtonType::Help ) + nBtnFlags |= ButtonDialogFlags::Help; + else if ( (eType == StandardButtonType::Cancel) || (eType == StandardButtonType::Close) ) + nBtnFlags |= ButtonDialogFlags::Cancel; + pItem->mpPushButton = ImplCreatePushButton( nBtnFlags ); + + // Standard-Buttons have the right text already + if ( !((eType == StandardButtonType::OK && pItem->mpPushButton->GetType() == WindowType::OKBUTTON) || + (eType == StandardButtonType::Cancel && pItem->mpPushButton->GetType() == WindowType::CANCELBUTTON) || + (eType == StandardButtonType::Help && pItem->mpPushButton->GetType() == WindowType::HELPBUTTON)) ) + { + std::map<StandardButtonType, OUString> mapButtonTypeToID = {{StandardButtonType::Yes, "yes"}, + {StandardButtonType::No, "no"}, {StandardButtonType::Retry, "retry"}, + {StandardButtonType::Close, "close"}, {StandardButtonType::More, "more"}, + {StandardButtonType::Ignore, "ignore"}, {StandardButtonType::Abort, "abort"}, + {StandardButtonType::Less, "less"}, {StandardButtonType::Count, "count"}}; + auto itr = mapButtonTypeToID.find(eType); + if (itr != mapButtonTypeToID.end()) + pItem->mpPushButton->set_id(itr->second); + pItem->mpPushButton->SetText( GetStandardText( eType ) ); + } + + if ( nBtnFlags & ButtonDialogFlags::Focus ) + mnFocusButtonId = nId; + + m_ItemList.push_back(std::move(pItem)); + + mbFormat = true; +} + +void ButtonDialog::RemoveButton( sal_uInt16 nId ) +{ + auto it = std::find_if(m_ItemList.begin(), m_ItemList.end(), + [&nId](const std::unique_ptr<ImplBtnDlgItem>& rItem) { return rItem->mnId == nId; }); + if (it != m_ItemList.end()) + { + (*it)->mpPushButton->Hide(); + if ((*it)->mbOwnButton) + (*it)->mpPushButton.disposeAndClear(); + else + (*it)->mpPushButton.clear(); + m_ItemList.erase(it); + return; + } + + SAL_WARN( "vcl.window", "ButtonDialog::RemoveButton(): ButtonId invalid" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/imagealign.cxx b/toolkit/source/helper/imagealign.cxx new file mode 100644 index 0000000000..43e7cb012c --- /dev/null +++ b/toolkit/source/helper/imagealign.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/awt/ImagePosition.hpp> +#include <com/sun/star/awt/ImageAlign.hpp> + +#include <helper/imagealign.hxx> +#include <osl/diagnose.h> + +namespace toolkit +{ + + + using namespace ::com::sun::star::awt::ImagePosition; + using namespace ::com::sun::star::awt::ImageAlign; + + sal_Int16 translateImagePosition( ImageAlign _eVCLAlign ) + { + sal_Int16 nReturn = AboveCenter; + switch ( _eVCLAlign ) + { + case ImageAlign::Left: nReturn = LeftCenter; break; + case ImageAlign::Top: nReturn = AboveCenter; break; + case ImageAlign::Right: nReturn = RightCenter; break; + case ImageAlign::Bottom: nReturn = BelowCenter; break; + case ImageAlign::LeftTop: nReturn = LeftTop; break; + case ImageAlign::LeftBottom: nReturn = LeftBottom; break; + case ImageAlign::TopLeft: nReturn = AboveLeft; break; + case ImageAlign::TopRight: nReturn = AboveRight; break; + case ImageAlign::RightTop: nReturn = RightTop; break; + case ImageAlign::RightBottom: nReturn = RightBottom; break; + case ImageAlign::BottomLeft: nReturn = BelowLeft; break; + case ImageAlign::BottomRight: nReturn = BelowRight; break; + case ImageAlign::Center: nReturn = Centered; break; + default: + OSL_FAIL( "translateImagePosition: unknown IMAGEALIGN value!" ); + } + return nReturn; + } + + ImageAlign translateImagePosition( sal_Int16 _eUNOAlign ) + { + ImageAlign nReturn = ImageAlign::Top; + switch ( _eUNOAlign ) + { + case LeftCenter: nReturn = ImageAlign::Left; break; + case AboveCenter: nReturn = ImageAlign::Top; break; + case RightCenter: nReturn = ImageAlign::Right; break; + case BelowCenter: nReturn = ImageAlign::Bottom; break; + case LeftTop: nReturn = ImageAlign::LeftTop; break; + case LeftBottom: nReturn = ImageAlign::LeftBottom; break; + case AboveLeft: nReturn = ImageAlign::TopLeft; break; + case AboveRight: nReturn = ImageAlign::TopRight; break; + case RightTop: nReturn = ImageAlign::RightTop; break; + case RightBottom: nReturn = ImageAlign::RightBottom; break; + case BelowLeft: nReturn = ImageAlign::BottomLeft; break; + case BelowRight: nReturn = ImageAlign::BottomRight; break; + case Centered: nReturn = ImageAlign::Center; break; + default: + OSL_FAIL( "translateImagePosition: unknown css.awt.ImagePosition value!" ); + } + return nReturn; + } + + sal_Int16 getCompatibleImageAlign( ImageAlign _eAlign ) + { + sal_Int16 nReturn = TOP; + switch ( _eAlign ) + { + case ImageAlign::LeftTop: + case ImageAlign::Left: + case ImageAlign::LeftBottom: nReturn = LEFT; break; + + case ImageAlign::TopLeft: + case ImageAlign::Top: + case ImageAlign::TopRight: nReturn = TOP; break; + + case ImageAlign::RightTop: + case ImageAlign::Right: + case ImageAlign::RightBottom: nReturn = RIGHT; break; + + case ImageAlign::BottomLeft: + case ImageAlign::Bottom: + case ImageAlign::BottomRight: nReturn = BOTTOM; break; + + case ImageAlign::Center: nReturn = TOP; break; + default: + OSL_FAIL( "getCompatibleImageAlign: unknown IMAGEALIGN value!" ); + } + return nReturn; + } + + sal_Int16 getExtendedImagePosition( sal_Int16 _nImageAlign ) + { + sal_Int16 nReturn = AboveCenter; + switch ( _nImageAlign ) + { + case LEFT: nReturn = LeftCenter; break; + case TOP: nReturn = AboveCenter; break; + case RIGHT: nReturn = RightCenter; break; + case BOTTOM: nReturn = BelowCenter; break; + default: + OSL_FAIL( "getExtendedImagePosition: unknown ImageAlign value!" ); + } + return nReturn; + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/listenermultiplexer.cxx b/toolkit/source/helper/listenermultiplexer.cxx new file mode 100644 index 0000000000..1c3ac1de52 --- /dev/null +++ b/toolkit/source/helper/listenermultiplexer.cxx @@ -0,0 +1,247 @@ +/* -*- 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 <toolkit/helper/listenermultiplexer.hxx> +#include <toolkit/helper/macros.hxx> +#include <com/sun/star/lang/DisposedException.hpp> + +// class EventListenerMultiplexer + +EventListenerMultiplexer::EventListenerMultiplexer( ::cppu::OWeakObject& rSource ) + : ListenerMultiplexerBase( rSource ) +{ +} + +void SAL_CALL EventListenerMultiplexer::acquire() noexcept +{ + return ListenerMultiplexerBase::acquire(); +} + +void SAL_CALL EventListenerMultiplexer::release() noexcept +{ + return ListenerMultiplexerBase::release(); +} + +// css::uno::XInterface +css::uno::Any EventListenerMultiplexer::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< css::lang::XEventListener* >(this) ); + return (aRet.hasValue() ? aRet : ListenerMultiplexerBase::queryInterface( rType )); +} + +// css::lang::XEventListener +void EventListenerMultiplexer::disposing( const css::lang::EventObject& ) +{ +} + + +// class FocusListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( FocusListenerMultiplexer, css::awt::XFocusListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( FocusListenerMultiplexer, css::awt::XFocusListener, focusGained, css::awt::FocusEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( FocusListenerMultiplexer, css::awt::XFocusListener, focusLost, css::awt::FocusEvent ) + + +// class WindowListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( WindowListenerMultiplexer, css::awt::XWindowListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowResized, css::awt::WindowEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowMoved, css::awt::WindowEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowShown, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowHidden, css::lang::EventObject ) + + +// class VclContainerListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( VclContainerListenerMultiplexer, css::awt::XVclContainerListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( VclContainerListenerMultiplexer, css::awt::XVclContainerListener, windowAdded, css::awt::VclContainerEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( VclContainerListenerMultiplexer, css::awt::XVclContainerListener, windowRemoved, css::awt::VclContainerEvent ) + + +// class KeyListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( KeyListenerMultiplexer, css::awt::XKeyListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( KeyListenerMultiplexer, css::awt::XKeyListener, keyPressed, css::awt::KeyEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( KeyListenerMultiplexer, css::awt::XKeyListener, keyReleased, css::awt::KeyEvent ) + + +// class MouseListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( MouseListenerMultiplexer, css::awt::XMouseListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mousePressed, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mouseReleased, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mouseEntered, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mouseExited, css::awt::MouseEvent ) + + +// class MouseMotionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( MouseMotionListenerMultiplexer, css::awt::XMouseMotionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseMotionListenerMultiplexer, css::awt::XMouseMotionListener, mouseDragged, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseMotionListenerMultiplexer, css::awt::XMouseMotionListener, mouseMoved, css::awt::MouseEvent ) + + +// class PaintListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( PaintListenerMultiplexer, css::awt::XPaintListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( PaintListenerMultiplexer, css::awt::XPaintListener, windowPaint, css::awt::PaintEvent ) + + +// class TopWindowListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TopWindowListenerMultiplexer, css::awt::XTopWindowListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowOpened, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowClosing, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowClosed, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowMinimized, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowNormalized, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowActivated, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowDeactivated, css::lang::EventObject ) + + +// class TextListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TextListenerMultiplexer, css::awt::XTextListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TextListenerMultiplexer, css::awt::XTextListener, textChanged, css::awt::TextEvent ) + + +// class ActionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( ActionListenerMultiplexer, css::awt::XActionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ActionListenerMultiplexer, css::awt::XActionListener, actionPerformed, css::awt::ActionEvent ) + + +// class ItemListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( ItemListenerMultiplexer, css::awt::XItemListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ItemListenerMultiplexer, css::awt::XItemListener, itemStateChanged, css::awt::ItemEvent ) + + +// class TabListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TabListenerMultiplexer, css::awt::XTabListener ) + +void TabListenerMultiplexer::inserted( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, inserted, ::sal_Int32 ) + +void TabListenerMultiplexer::removed( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, removed, ::sal_Int32 ) + +void TabListenerMultiplexer::changed( sal_Int32 evt, const css::uno::Sequence< css::beans::NamedValue >& evt2 ) +{ + sal_Int32 aMulti( evt ); + std::unique_lock g(m_aMutex); + ::comphelper::OInterfaceIteratorHelper4 aIt(g, maListeners); + g.unlock(); + while( aIt.hasMoreElements() ) + { + css::uno::Reference<css::awt::XTabListener> xListener(aIt.next()); + try + { + xListener->changed( aMulti, evt2 ); + } + catch(const css::lang::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 css::uno::RuntimeException&) + { + DISPLAY_EXCEPTION( TabListenerMultiplexer, changed ) + } + } +} + + +void TabListenerMultiplexer::activated( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, activated, ::sal_Int32 ) + +void TabListenerMultiplexer::deactivated( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, deactivated, ::sal_Int32 ) + + +// class ContainerListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( ContainerListenerMultiplexer, css::container::XContainerListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ContainerListenerMultiplexer, css::container::XContainerListener, elementInserted, css::container::ContainerEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ContainerListenerMultiplexer, css::container::XContainerListener, elementRemoved, css::container::ContainerEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ContainerListenerMultiplexer, css::container::XContainerListener, elementReplaced, css::container::ContainerEvent ) + + +// class SpinListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( SpinListenerMultiplexer, css::awt::XSpinListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, up, css::awt::SpinEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, down, css::awt::SpinEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, first, css::awt::SpinEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, last, css::awt::SpinEvent ) + + +// class AdjustmentListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( AdjustmentListenerMultiplexer, css::awt::XAdjustmentListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( AdjustmentListenerMultiplexer, css::awt::XAdjustmentListener, adjustmentValueChanged, css::awt::AdjustmentEvent ) + + +// class MenuListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( MenuListenerMultiplexer, css::awt::XMenuListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemHighlighted, css::awt::MenuEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemSelected, css::awt::MenuEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemActivated, css::awt::MenuEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemDeactivated, css::awt::MenuEvent ) + + +// class TreeSelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TreeSelectionListenerMultiplexer, css::view::XSelectionChangeListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeSelectionListenerMultiplexer, css::view::XSelectionChangeListener, selectionChanged, css::lang::EventObject ) + + +// class TreeSelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, requestChildNodes, css::awt::tree::TreeExpansionEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD_EXCEPTION( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeExpanding, css::awt::tree::TreeExpansionEvent, css::awt::tree::ExpandVetoException ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD_EXCEPTION( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeCollapsing, css::awt::tree::TreeExpansionEvent, css::awt::tree::ExpandVetoException ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeExpanded, css::awt::tree::TreeExpansionEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeCollapsed, css::awt::tree::TreeExpansionEvent ) + + +// class TreeEditListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TreeEditListenerMultiplexer, css::awt::tree::XTreeEditListener ) + + +// class SelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( SelectionListenerMultiplexer, css::awt::grid::XGridSelectionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SelectionListenerMultiplexer, css::awt::grid::XGridSelectionListener, selectionChanged, css::awt::grid::GridSelectionEvent ) + + +// class SelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TabPageListenerMultiplexer, css::awt::tab::XTabPageContainerListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TabPageListenerMultiplexer, css::awt::tab::XTabPageContainerListener, tabPageActivated, css::awt::tab::TabPageActivatedEvent ) +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/property.cxx b/toolkit/source/helper/property.cxx new file mode 100644 index 0000000000..945c4b016a --- /dev/null +++ b/toolkit/source/helper/property.cxx @@ -0,0 +1,335 @@ +/* -*- 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 <helper/property.hxx> + +#include <tools/debug.hxx> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/tree/XTreeDataModel.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <algorithm> +#include <string_view> +#include <utility> +#include <unordered_map> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::FontDescriptor; +using ::com::sun::star::style::VerticalAlignment; +using ::com::sun::star::graphic::XGraphic; + +using namespace com::sun::star; + +namespace { + +struct ImplPropertyInfo +{ + css::uno::Type aType; + sal_uInt16 nPropId; + sal_Int16 nAttribs; + bool bDependsOnOthers; // eg. VALUE depends on MIN/MAX and must be set after MIN/MAX. + + ImplPropertyInfo( sal_uInt16 nId, const css::uno::Type& rType, + sal_Int16 nAttrs, bool bDepends = false ) + : aType(rType) + , nPropId(nId) + , nAttribs(nAttrs) + , bDependsOnOthers(bDepends) + { + } + +}; + +} + +#define DECL_PROP_1( asciiname, id, type, attrib1 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 ) } +#define DECL_PROP_2( asciiname, id, type, attrib1, attrib2 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2 ) } +#define DECL_PROP_3( asciiname, id, type, attrib1, attrib2, attrib3 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2 | css::beans::PropertyAttribute::attrib3 ) } + +#define DECL_DEP_PROP_2( asciiname, id, type, attrib1, attrib2 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2, true ) } +#define DECL_DEP_PROP_3( asciiname, id, type, attrib1, attrib2, attrib3 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2 | css::beans::PropertyAttribute::attrib3, true ) } + +typedef std::unordered_map<OUString, ImplPropertyInfo> ImpPropertyInfoMap; +static const ImpPropertyInfoMap & ImplGetPropertyInfos() +{ + static const ImpPropertyInfoMap aImplPropertyInfos { + DECL_PROP_2 ( "AccessibleName", ACCESSIBLENAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "Align", ALIGN, sal_Int16, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Autocomplete", AUTOCOMPLETE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "AutoHScroll", AUTOHSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_1 ( "AutoMnemonics", AUTOMNEMONICS, bool, BOUND ), + DECL_PROP_2 ( "AutoToggle", AUTOTOGGLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "AutoVScroll", AUTOVSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "BackgroundColor", BACKGROUNDCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_2 ( "BlockIncrement", BLOCKINCREMENT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "Border", BORDER, sal_Int16, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "BorderColor", BORDERCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Closeable", CLOSEABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "CurrencySymbol", CURRENCYSYMBOL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "CustomUnitText", CUSTOMUNITTEXT, OUString, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "Date", DATE, util::Date, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "DateFormat", EXTDATEFORMAT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DateMax", DATEMAX, util::Date, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DateMin", DATEMIN, util::Date, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "DateShowCentury", DATESHOWCENTURY, bool, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "DecimalAccuracy", DECIMALACCURACY, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DefaultButton", DEFAULTBUTTON, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DefaultControl", DEFAULTCONTROL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DesktopAsParent", DESKTOP_AS_PARENT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DisplayBackgroundColor", DISPLAYBACKGROUNDCOLOR, sal_Int32, BOUND, MAYBEVOID ), + DECL_PROP_2 ( "Dropdown", DROPDOWN, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "EchoChar", ECHOCHAR, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "EditMask", EDITMASK, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "EffectiveDefault", EFFECTIVE_DEFAULT, Any, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "EffectiveMax", EFFECTIVE_MAX, double, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "EffectiveMin", EFFECTIVE_MIN, double, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "EffectiveValue", EFFECTIVE_VALUE, Any, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Enabled", ENABLED, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "EnforceFormat", ENFORCE_FORMAT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "FillColor", FILLCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "FocusOnClick", FOCUSONCLICK, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontRelief", FONTRELIEF, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontEmphasisMark", FONTEMPHASISMARK, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontDescriptor", FONTDESCRIPTOR, FontDescriptor, BOUND, MAYBEDEFAULT ), + + // parts of css::awt::FontDescriptor + DECL_PROP_2 ( "FontName", FONTDESCRIPTORPART_NAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontStyleName", FONTDESCRIPTORPART_STYLENAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontFamily", FONTDESCRIPTORPART_FAMILY, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontCharset", FONTDESCRIPTORPART_CHARSET, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontHeight", FONTDESCRIPTORPART_HEIGHT, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontWidth", FONTDESCRIPTORPART_WIDTH, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontPitch", FONTDESCRIPTORPART_PITCH, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontWeight", FONTDESCRIPTORPART_WEIGHT, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontCharWidth", FONTDESCRIPTORPART_CHARWIDTH, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontOrientation", FONTDESCRIPTORPART_ORIENTATION, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontSlant", FONTDESCRIPTORPART_SLANT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontUnderline", FONTDESCRIPTORPART_UNDERLINE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontStrikeout", FONTDESCRIPTORPART_STRIKEOUT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontKerning", FONTDESCRIPTORPART_KERNING, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontWordLineMode", FONTDESCRIPTORPART_WORDLINEMODE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontType", FONTDESCRIPTORPART_TYPE, sal_Int16, BOUND, MAYBEDEFAULT ), + + DECL_PROP_3 ( "FormatKey", FORMATKEY, sal_Int32, BOUND, MAYBEVOID, TRANSIENT ), + DECL_PROP_3 ( "FormatsSupplier", FORMATSSUPPLIER, Reference< css::util::XNumberFormatsSupplier >, BOUND, MAYBEVOID, TRANSIENT ), + + DECL_PROP_2 ( "Graphic", GRAPHIC, Reference< XGraphic >, BOUND, TRANSIENT ), + DECL_PROP_2 ( "GroupName", GROUPNAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HelpText", HELPTEXT, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HelpURL", HELPURL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HideInactiveSelection", HIDEINACTIVESELECTION, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HighContrastMode", HIGHCONTRASTMODE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HScroll", HSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HardLineBreaks", HARDLINEBREAKS, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "HighlightColor", HIGHLIGHT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID), + DECL_PROP_3 ( "HighlightTextColor", HIGHLIGHT_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID), + DECL_PROP_2 ( "ImageAlign", IMAGEALIGN, sal_Int16, BOUND, MAYBEDEFAULT), + DECL_PROP_2 ( "ImagePosition", IMAGEPOSITION, sal_Int16, BOUND, MAYBEDEFAULT), + DECL_PROP_2 ( "ImageURL", IMAGEURL, css::uno::Any, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ItemSeparatorPos", ITEM_SEPARATOR_POS, sal_Int16, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Label", LABEL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "LineColor", LINECOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "LineCount", LINECOUNT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "LineEndFormat", LINE_END_FORMAT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "LineIncrement", LINEINCREMENT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "LiteralMask", LITERALMASK, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "LiveScroll", LIVE_SCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "MaxTextLen", MAXTEXTLEN, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Moveable", MOVEABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_1 ( "MouseTransparent", MOUSETRANSPARENT, bool, BOUND ), + DECL_PROP_2 ( "MultiLine", MULTILINE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "MultiSelection", MULTISELECTION, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "MultiSelectionSimpleMode", MULTISELECTION_SIMPLEMODE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "NativeWidgetLook", NATIVE_WIDGET_LOOK, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "NoLabel", NOLABEL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Orientation", ORIENTATION, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PaintTransparent", PAINTTRANSPARENT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PluginParent", PLUGINPARENT, sal_Int64, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PrependCurrencySymbol", CURSYM_POSITION, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Printable", PRINTABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "ProgressValue", PROGRESSVALUE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "ProgressValueMax", PROGRESSVALUE_MAX, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ProgressValueMin", PROGRESSVALUE_MIN, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PushButtonType", PUSHBUTTONTYPE, sal_Int16, BOUND, MAYBEDEFAULT), + DECL_PROP_2 ( "ReadOnly", READONLY, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Repeat", REPEAT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "AutoRepeat", AUTO_REPEAT, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "RepeatDelay", REPEAT_DELAY, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScaleImage", SCALEIMAGE, bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "ScaleMode", IMAGE_SCALE_MODE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "ScrollValue", SCROLLVALUE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "ScrollValueMax", SCROLLVALUE_MAX, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollValueMin", SCROLLVALUE_MIN, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollWidth", SCROLLWIDTH, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollHeight", SCROLLHEIGHT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollTop", SCROLLTOP, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollLeft", SCROLLLEFT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "SelectedItems", SELECTEDITEMS, Sequence<sal_Int16>, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowThousandsSeparator", NUMSHOWTHOUSANDSEP, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Sizeable", SIZEABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Spin", SPIN, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "SpinIncrement", SPININCREMENT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "SpinValue", SPINVALUE, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "SpinValueMax", SPINVALUE_MAX, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "SpinValueMin", SPINVALUE_MIN, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "State", STATE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "StrictFormat", STRICTFORMAT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "StringItemList", STRINGITEMLIST, Sequence< OUString >, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "TypedItemList", TYPEDITEMLIST, Sequence< Any >, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "VisualEffect", VISUALEFFECT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "SymbolColor", SYMBOL_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "Tabstop", TABSTOP, bool, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Text", TEXT, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "TextColor", TEXTCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "TextLineColor", TEXTLINECOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "Time", TIME, util::Time, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "TimeFormat", EXTTIMEFORMAT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "TimeMax", TIMEMAX, util::Time, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "TimeMin", TIMEMIN, util::Time, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Title", TITLE, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Toggle", TOGGLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "TreatAsNumber", TREATASNUMBER, bool, BOUND, MAYBEDEFAULT,TRANSIENT ), + DECL_PROP_2 ( "TriState", TRISTATE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Unit", UNIT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "VScroll", VSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "Value", VALUE_DOUBLE, double, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "ValueMax", VALUEMAX_DOUBLE, double, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ValueMin", VALUEMIN_DOUBLE, double, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ValueStep", VALUESTEP_DOUBLE, double, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "VerticalAlign", VERTICALALIGN, VerticalAlignment, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "VisibleSize", VISIBLESIZE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Activated", ACTIVATED, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Complete", COMPLETE, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "CurrentItemID", CURRENTITEMID, sal_Int16, BOUND, MAYBEDEFAULT ), + + DECL_PROP_2 ( "MouseWheelBehavior", MOUSE_WHEEL_BEHAVIOUR, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "StepTime", STEP_TIME, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Decoration", DECORATION, sal_Bool, BOUND, MAYBEDEFAULT ), + + DECL_PROP_2 ( "SelectionType", TREE_SELECTIONTYPE, css::view::SelectionType, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Editable", TREE_EDITABLE, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "DataModel", TREE_DATAMODEL, Reference< css::awt::tree::XTreeDataModel >,BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "RootDisplayed", TREE_ROOTDISPLAYED, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowsHandles", TREE_SHOWSHANDLES, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowsRootHandles", TREE_SHOWSROOTHANDLES, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "RowHeight", ROW_HEIGHT, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "InvokesStopNodeEditing", TREE_INVOKESSTOPNODEEDITING, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DialogSourceURL", DIALOGSOURCEURL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "URL", URL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "WritingMode", WRITING_MODE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ContextWritingMode", CONTEXT_WRITING_MODE, sal_Int16, BOUND, MAYBEDEFAULT, TRANSIENT ), + DECL_PROP_2 ( "ShowRowHeader", GRID_SHOWROWHEADER, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "RowHeaderWidth", ROW_HEADER_WIDTH, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowColumnHeader", GRID_SHOWCOLUMNHEADER, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ColumnHeaderHeight", COLUMN_HEADER_HEIGHT, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_1 ( "GridDataModel", GRID_DATAMODEL, Reference< css::awt::grid::XGridDataModel >, BOUND ), + DECL_PROP_1 ( "ColumnModel", GRID_COLUMNMODEL, Reference< css::awt::grid::XGridColumnModel >, BOUND ), + DECL_PROP_3 ( "SelectionModel", GRID_SELECTIONMODE, css::view::SelectionType, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "EnableVisible", ENABLEVISIBLE, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ReferenceDevice", REFERENCE_DEVICE, Reference< XDevice >,BOUND, MAYBEDEFAULT, TRANSIENT ), + DECL_PROP_3 ( "HeaderBackgroundColor", GRID_HEADER_BACKGROUND, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "HeaderTextColor", GRID_HEADER_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "GridLineColor", GRID_LINE_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "RowBackgroundColors", GRID_ROW_BACKGROUND_COLORS, Sequence< sal_Int32 >, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "UseGridLines", USE_GRID_LINES, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "MultiPageValue", MULTIPAGEVALUE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "AllDialogChildren", USERFORMCONTAINEES, Reference< css::container::XNameContainer >, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "ActiveSelectionBackgroundColor", ACTIVE_SEL_BACKGROUND_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "InactiveSelectionBackgroundColor", INACTIVE_SEL_BACKGROUND_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "ActiveSelectionTextColor", ACTIVE_SEL_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "InactiveSelectionTextColor", INACTIVE_SEL_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + }; + return aImplPropertyInfos; +} + +sal_uInt16 GetPropertyId( const OUString& rPropertyName ) +{ + const ImpPropertyInfoMap & rMap = ImplGetPropertyInfos(); + auto it = rMap.find(rPropertyName); + return it != rMap.end() ? it->second.nPropId : 0; +} + +static const ImplPropertyInfo* ImplGetImplPropertyInfo( sal_uInt16 nPropertyId ) +{ + const ImpPropertyInfoMap & rMap = ImplGetPropertyInfos(); + + for (auto const & rPair : rMap) + if (rPair.second.nPropId == nPropertyId) + return &rPair.second; + return nullptr; +} + +const OUString& GetPropertyName( sal_uInt16 nPropertyId ) +{ + const ImpPropertyInfoMap & rMap = ImplGetPropertyInfos(); + + for (auto const & rPair : rMap) + if (rPair.second.nPropId == nPropertyId) + return rPair.first; + + assert(false && "Invalid PropertyId!"); + static const OUString EMPTY; + return EMPTY; +} + +const css::uno::Type* GetPropertyType( sal_uInt16 nPropertyId ) +{ + const ImplPropertyInfo* pImplPropertyInfo = ImplGetImplPropertyInfo( nPropertyId ); + DBG_ASSERT( pImplPropertyInfo, "Invalid PropertyId!" ); + return pImplPropertyInfo ? &pImplPropertyInfo->aType : nullptr; +} + +sal_Int16 GetPropertyAttribs( sal_uInt16 nPropertyId ) +{ + const ImplPropertyInfo* pImplPropertyInfo = ImplGetImplPropertyInfo( nPropertyId ); + DBG_ASSERT( pImplPropertyInfo, "Invalid PropertyId!" ); + return pImplPropertyInfo ? pImplPropertyInfo->nAttribs : 0; +} + +bool DoesDependOnOthers( sal_uInt16 nPropertyId ) +{ + const ImplPropertyInfo* pImplPropertyInfo = ImplGetImplPropertyInfo( nPropertyId ); + DBG_ASSERT( pImplPropertyInfo, "Invalid PropertyId!" ); + return pImplPropertyInfo && pImplPropertyInfo->bDependsOnOthers; +} + +bool CompareProperties( const css::uno::Any& r1, const css::uno::Any& r2 ) +{ + return r1 == r2; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/servicenames.cxx b/toolkit/source/helper/servicenames.cxx new file mode 100644 index 0000000000..825672fd20 --- /dev/null +++ b/toolkit/source/helper/servicenames.cxx @@ -0,0 +1,24 @@ +/* -*- 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 <helper/servicenames.hxx> + +const char szServiceName_UnoControlDialog[] = "stardiv.vcl.control.Dialog"; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/tkresmgr.cxx b/toolkit/source/helper/tkresmgr.cxx new file mode 100644 index 0000000000..3a687f5236 --- /dev/null +++ b/toolkit/source/helper/tkresmgr.cxx @@ -0,0 +1,57 @@ +/* -*- 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 <comphelper/processfactory.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <comphelper/diagnose_ex.hxx> + +#include <vcl/image.hxx> + +#include <helper/tkresmgr.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::graphic::XGraphicProvider; +using namespace ::com::sun::star; + +Image TkResMgr::getImageFromURL(const OUString& i_rImageURL) +{ + if (i_rImageURL.isEmpty()) + return Image(); + + try + { + Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + Reference<XGraphicProvider> xProvider(graphic::GraphicProvider::create(xContext)); + ::comphelper::NamedValueCollection aMediaProperties; + aMediaProperties.put("URL", i_rImageURL); + Reference<XGraphic> xGraphic + = xProvider->queryGraphic(aMediaProperties.getPropertyValues()); + return Image(xGraphic); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + return Image(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/unopropertyarrayhelper.cxx b/toolkit/source/helper/unopropertyarrayhelper.cxx new file mode 100644 index 0000000000..bc0f996d3d --- /dev/null +++ b/toolkit/source/helper/unopropertyarrayhelper.cxx @@ -0,0 +1,148 @@ +/* -*- 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 <helper/property.hxx> +#include <map> + +#include <helper/unopropertyarrayhelper.hxx> + + + +UnoPropertyArrayHelper::UnoPropertyArrayHelper( const css::uno::Sequence<sal_Int32>& rIDs ) +{ + for ( const sal_Int32 nID : rIDs ) + maIDs.insert( nID ); +} + +UnoPropertyArrayHelper::UnoPropertyArrayHelper( const std::vector< sal_uInt16 > &rIDs ) +{ + for (const auto& rId : rIDs) + maIDs.insert( rId ); +} + +bool UnoPropertyArrayHelper::ImplHasProperty( sal_uInt16 nPropId ) const +{ + if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + nPropId = BASEPROPERTY_FONTDESCRIPTOR; + + return maIDs.find( nPropId ) != maIDs.end(); +} + +// ::cppu::IPropertyArrayHelper +sal_Bool UnoPropertyArrayHelper::fillPropertyMembersByHandle( OUString * pPropName, sal_Int16 * pAttributes, sal_Int32 nPropId ) +{ + sal_uInt16 id = sal::static_int_cast< sal_uInt16 >(nPropId); + bool bValid = ImplHasProperty( id ); + if ( bValid ) + { + if ( pPropName ) + *pPropName = GetPropertyName( id ); + if ( pAttributes ) + *pAttributes = GetPropertyAttribs( id ); + } + return bValid; +} + +css::uno::Sequence< css::beans::Property > UnoPropertyArrayHelper::getProperties() +{ + // Sort by names ... + + std::map<OUString, sal_uInt16> aSortedPropsIds; + for (const auto& rId : maIDs) + { + sal_uInt16 nId = sal::static_int_cast< sal_uInt16 >(rId); + aSortedPropsIds.emplace(GetPropertyName( nId ), nId); + + if ( nId == BASEPROPERTY_FONTDESCRIPTOR ) + { + // single properties ... + for ( sal_uInt16 i = BASEPROPERTY_FONTDESCRIPTORPART_START; i <= BASEPROPERTY_FONTDESCRIPTORPART_END; i++ ) + aSortedPropsIds.emplace(GetPropertyName( i ), i); + } + } + + sal_uInt32 nProps = aSortedPropsIds.size(); // could be more now + css::uno::Sequence< css::beans::Property> aProps( nProps ); + css::beans::Property* pProps = aProps.getArray(); + + sal_uInt32 n = 0; + for ( const auto& rPropIds : aSortedPropsIds ) + { + sal_uInt16 nId = rPropIds.second; + pProps[n].Name = rPropIds.first; + pProps[n].Handle = nId; + pProps[n].Type = *GetPropertyType( nId ); + pProps[n].Attributes = GetPropertyAttribs( nId ); + ++n; + } + + return aProps; +} + +css::beans::Property UnoPropertyArrayHelper::getPropertyByName(const OUString& rPropertyName) +{ + css::beans::Property aProp; + sal_uInt16 nId = GetPropertyId( rPropertyName ); + if ( ImplHasProperty( nId ) ) + { + aProp.Name = rPropertyName; + aProp.Handle = -1; + aProp.Type = *GetPropertyType( nId ); + aProp.Attributes = GetPropertyAttribs( nId ); + } + + return aProp; +} + +sal_Bool UnoPropertyArrayHelper::hasPropertyByName(const OUString& rPropertyName) +{ + return ImplHasProperty( GetPropertyId( rPropertyName ) ); +} + +sal_Int32 UnoPropertyArrayHelper::getHandleByName( const OUString & rPropertyName ) +{ + sal_Int32 nId = static_cast<sal_Int32>(GetPropertyId( rPropertyName )); + return nId ? nId : -1; +} + +sal_Int32 UnoPropertyArrayHelper::fillHandles( sal_Int32* pHandles, const css::uno::Sequence< OUString > & rPropNames ) +{ + const OUString* pNames = rPropNames.getConstArray(); + sal_Int32 nValues = rPropNames.getLength(); + sal_Int32 nValidHandles = 0; + + for ( sal_Int32 n = 0; n < nValues; n++ ) + { + sal_uInt16 nPropId = GetPropertyId( pNames[n] ); + if ( nPropId && ImplHasProperty( nPropId ) ) + { + pHandles[n] = nPropId; + nValidHandles++; + } + else + { + pHandles[n] = -1; + } + } + return nValidHandles; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/unowrapper.cxx b/toolkit/source/helper/unowrapper.cxx new file mode 100644 index 0000000000..82b4dd1733 --- /dev/null +++ b/toolkit/source/helper/unowrapper.cxx @@ -0,0 +1,321 @@ +/* -*- 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 <toolkit/helper/vclunohelper.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/awt/vclxwindows.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <awt/vclxcontainer.hxx> +#include <awt/vclxgraphics.hxx> +#include <awt/vclxtopwindow.hxx> +#include <awt/vclxwindows.hxx> + +#include <toolkit/dllapi.h> +#include <vcl/menu.hxx> + +#include <helper/unowrapper.hxx> + +using namespace ::com::sun::star; + +static rtl::Reference<VCLXWindow> CreateXWindow( vcl::Window const * pWindow ) +{ + switch ( pWindow->GetType() ) + { + case WindowType::IMAGEBUTTON: + case WindowType::SPINBUTTON: + case WindowType::MENUBUTTON: + case WindowType::MOREBUTTON: + case WindowType::PUSHBUTTON: + case WindowType::HELPBUTTON: + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: return new VCLXButton; + case WindowType::CHECKBOX: return new VCLXCheckBox; + // #i95042# + // A Window of type <MetricBox> is inherited from type <ComboBox>. + // Thus, it does make more sense to return a <VCLXComboBox> instance + // instead of only a <VCLXWindow> instance, especially regarding its + // corresponding accessibility API. + case WindowType::METRICBOX: + case WindowType::COMBOBOX: return new VCLXComboBox; + case WindowType::FORMATTEDFIELD: return new SVTXNumericField; + case WindowType::SPINFIELD: + case WindowType::CURRENCYFIELD: return new VCLXNumericField; + case WindowType::DATEFIELD: return new VCLXDateField; + case WindowType::MULTILINEEDIT: + case WindowType::EDIT: return new VCLXEdit; + case WindowType::METRICFIELD: return new VCLXSpinField; + case WindowType::MESSBOX: + case WindowType::INFOBOX: + case WindowType::WARNINGBOX: + case WindowType::QUERYBOX: + case WindowType::ERRORBOX: return new VCLXMessageBox; + case WindowType::FIXEDIMAGE: return new VCLXImageControl; + case WindowType::FIXEDTEXT: return new VCLXFixedText; + case WindowType::MULTILISTBOX: + case WindowType::LISTBOX: return new VCLXListBox; + case WindowType::DIALOG: + case WindowType::TABDIALOG: + case WindowType::BUTTONDIALOG: + case WindowType::MODELESSDIALOG: return new VCLXDialog; + case WindowType::PATTERNFIELD: return new VCLXPatternField; + case WindowType::RADIOBUTTON: return new VCLXRadioButton; + case WindowType::SCROLLBAR: return new VCLXScrollBar; + case WindowType::TIMEFIELD: return new VCLXTimeField; + + case WindowType::WORKWINDOW: + case WindowType::DOCKINGWINDOW: + case WindowType::FLOATINGWINDOW: + case WindowType::HELPTEXTWINDOW: return new VCLXTopWindow; + + case WindowType::WINDOW: + case WindowType::TABPAGE: return new VCLXContainer; + + case WindowType::TOOLBOX: return new VCLXToolBox; + case WindowType::TABCONTROL: return new VCLXMultiPage; + + case WindowType::HEADERBAR: return new VCLXHeaderBar; + + case WindowType::BORDERWINDOW: + { + if (pWindow->IsNativeFrame()) + return new VCLXTopWindow; + return new VCLXWindow(true); + } + + // case WindowType::FIXEDLINE: + // case WindowType::FIXEDBITMAP: + // case WindowType::DATEBOX: + // case WindowType::GROUPBOX: + // case WindowType::LONGCURRENCYBOX: + // case WindowType::SPLITTER: + // case WindowType::STATUSBAR: + // case WindowType::TABCONTROL: + // case WindowType::NUMERICBOX: + // case WindowType::TRISTATEBOX: + // case WindowType::TIMEBOX: + // case WindowType::SPLITWINDOW: + // case WindowType::SCROLLBARBOX: + // case WindowType::PATTERNBOX: + // case WindowType::CURRENCYBOX: + default: return new VCLXWindow( true ); + } +} + + + + +extern "C" { + +TOOLKIT_DLLPUBLIC UnoWrapperBase* CreateUnoWrapper() +{ + return new UnoWrapper( nullptr ); +} + +} // extern "C" + + +UnoWrapper::UnoWrapper( const css::uno::Reference< css::awt::XToolkit>& rxToolkit ) +{ + mxToolkit = rxToolkit; +} + +void UnoWrapper::Destroy() +{ + delete this; +} + +UnoWrapper::~UnoWrapper() +{ +} + +css::uno::Reference< css::awt::XToolkit> UnoWrapper::GetVCLToolkit() +{ + if ( !mxToolkit.is() ) + mxToolkit = VCLUnoHelper::CreateToolkit(); + return mxToolkit; +} + +css::uno::Reference< css::awt::XVclWindowPeer> UnoWrapper::GetWindowInterface( vcl::Window* pWindow ) +{ + css::uno::Reference< css::awt::XVclWindowPeer> xPeer = pWindow->GetWindowPeer(); + if ( xPeer ) + return xPeer; + + rtl::Reference<VCLXWindow> xVCLXWindow = CreateXWindow( pWindow ); + xVCLXWindow->SetWindow( pWindow ); + pWindow->SetWindowPeer( xVCLXWindow, xVCLXWindow.get() ); + return xVCLXWindow; +} + +VclPtr<vcl::Window> UnoWrapper::GetWindow(const css::uno::Reference<css::awt::XWindow>& rWindow) +{ + return VCLUnoHelper::GetWindow(rWindow); +} + +void UnoWrapper::SetWindowInterface( vcl::Window* pWindow, const css::uno::Reference< css::awt::XVclWindowPeer> & xIFace ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( xIFace.get() ); + + assert( pVCLXWindow && "must be a VCLXWindow subclass" ); + if ( !pVCLXWindow ) + return; + + if (!pWindow) + { + // we are disconnecting a peer from a window + pVCLXWindow->SetWindow( nullptr ); + } + else + { + css::uno::Reference< css::awt::XVclWindowPeer> xPeer = pWindow->GetWindowPeer(); + if( xPeer.is() ) + { + bool bSameInstance( pVCLXWindow == dynamic_cast< VCLXWindow* >( xPeer.get() )); + SAL_WARN_IF( !bSameInstance, "toolkit.helper", "UnoWrapper::SetWindowInterface: there is already a WindowPeer/ComponentInterface for this VCL window" ); + if ( bSameInstance ) + return; + } + pVCLXWindow->SetWindow( pWindow ); + pWindow->SetWindowPeer( xIFace, pVCLXWindow ); + } +} + +css::uno::Reference<css::awt::XPopupMenu> UnoWrapper::CreateMenuInterface( PopupMenu* pPopupMenu ) +{ + return new VCLXPopupMenu(pPopupMenu); +} + +css::uno::Reference< css::awt::XGraphics> UnoWrapper::CreateGraphics( OutputDevice* pOutDev ) +{ + rtl::Reference<VCLXGraphics> pGrf = new VCLXGraphics; + pGrf->Init( pOutDev ); + return pGrf; +} + +void UnoWrapper::ReleaseAllGraphics( OutputDevice* pOutDev ) +{ + std::vector< VCLXGraphics* > *pLst = pOutDev->GetUnoGraphicsList(); + if ( pLst ) + { + for (VCLXGraphics* pGrf : *pLst) + { + pGrf->SetOutputDevice( nullptr ); + } + } + +} + +static bool lcl_ImplIsParent( vcl::Window const * pParentWindow, vcl::Window* pPossibleChild ) +{ + vcl::Window* pWindow = ( pPossibleChild != pParentWindow ) ? pPossibleChild : nullptr; + while ( pWindow && ( pWindow != pParentWindow ) ) + pWindow = pWindow->GetParent(); + + return pWindow != nullptr; +} + +void UnoWrapper::WindowDestroyed( vcl::Window* pWindow ) +{ + // their still might be some children created with css::loader::Java + // that would otherwise not be destroyed until the garbage collector cleans up + VclPtr< vcl::Window > pChild = pWindow->GetWindow( GetWindowType::FirstChild ); + while ( pChild ) + { + VclPtr< vcl::Window > pNextChild = pChild->GetWindow( GetWindowType::Next ); + + VclPtr< vcl::Window > pClient = pChild->GetWindow( GetWindowType::Client ); + if ( pClient && pClient->GetWindowPeer() ) + { + css::uno::Reference< css::lang::XComponent > xComp = pClient->GetComponentInterface( false ); + xComp->dispose(); + } + else + { + // We need it to dispose the child windows properly (even without window peer), + // otherwise the vcl::Window will be leaked. + pClient.disposeAndClear(); + } + + pChild = pNextChild; + } + + // find system windows... + VclPtr< vcl::Window > pOverlap = pWindow->GetWindow( GetWindowType::Overlap ); + if ( pOverlap ) + { + pOverlap = pOverlap->GetWindow( GetWindowType::FirstOverlap ); + while ( pOverlap ) + { + VclPtr< vcl::Window > pNextOverlap = pOverlap->GetWindow( GetWindowType::Next ); + VclPtr< vcl::Window > pClient = pOverlap->GetWindow( GetWindowType::Client ); + + if ( pClient && pClient->GetWindowPeer() && lcl_ImplIsParent( pWindow, pClient ) ) + { + css::uno::Reference< css::lang::XComponent > xComp = pClient->GetComponentInterface( false ); + xComp->dispose(); + } + + pOverlap = pNextOverlap; + } + } + + { + VclPtr< vcl::Window > pParent = pWindow->GetParent(); + if ( pParent && pParent->GetWindowPeer() ) + pParent->GetWindowPeer()->notifyWindowRemoved( *pWindow ); + } + + VCLXWindow* pWindowPeer = pWindow->GetWindowPeer(); + uno::Reference< lang::XComponent > xWindowPeerComp = pWindow->GetComponentInterface( false ); + OSL_ENSURE( ( pWindowPeer != nullptr ) == xWindowPeerComp.is(), + "UnoWrapper::WindowDestroyed: inconsistency in the window's peers!" ); + if ( pWindowPeer ) + { + pWindowPeer->SetWindow( nullptr ); + pWindow->SetWindowPeer( nullptr, nullptr ); + } + if ( xWindowPeerComp.is() ) + xWindowPeerComp->dispose(); + + // #102132# Iterate over frames after setting Window peer to NULL, + // because while destroying other frames, we get into the method again and try + // to destroy this window again... + // #i42462#/#116855# no, don't loop: Instead, just ensure that all our top-window-children + // are disposed, too (which should also be a valid fix for #102132#, but doesn't have the extreme + // performance penalties) + VclPtr< vcl::Window > pTopWindowChild = pWindow->GetWindow( GetWindowType::FirstTopWindowChild ); + while ( pTopWindowChild ) + { + OSL_ENSURE( pTopWindowChild->GetParent() == pWindow, + "UnoWrapper::WindowDestroyed: inconsistency in the SystemWindow relationship!" ); + + VclPtr< vcl::Window > pNextTopChild = pTopWindowChild->GetWindow( GetWindowType::NextTopWindowSibling ); + + pTopWindowChild.disposeAndClear(); + pTopWindowChild = pNextTopChild; + } +} + + +css::uno::Reference< css::accessibility::XAccessible > UnoWrapper::CreateAccessible( Menu* pMenu, bool bIsMenuBar ) +{ + return maAccessibleFactoryAccess.getFactory().createAccessible( pMenu, bIsMenuBar ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/vclunohelper.cxx b/toolkit/source/helper/vclunohelper.cxx new file mode 100644 index 0000000000..9f05ae7070 --- /dev/null +++ b/toolkit/source/helper/vclunohelper.cxx @@ -0,0 +1,604 @@ +/* -*- 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 <tools/stream.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/event.hxx> +#include <vcl/graph.hxx> +#include <vcl/metric.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/unohelp.hxx> +#include <vcl/window.hxx> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/SimpleFontMetric.hpp> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/awt/MouseButton.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> +#include <awt/vclxbitmap.hxx> +#include <awt/vclxregion.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <awt/vclxgraphics.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <controls/unocontrolcontainer.hxx> +#include <controls/unocontrolcontainermodel.hxx> +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Point.hpp> + +using namespace ::com::sun::star; + + +uno::Reference< css::awt::XToolkit> VCLUnoHelper::CreateToolkit() +{ + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< awt::XToolkit> xToolkit( awt::Toolkit::create(xContext), uno::UNO_QUERY_THROW ); + return xToolkit; +} + +BitmapEx VCLUnoHelper::GetBitmap( const css::uno::Reference< css::awt::XBitmap>& rxBitmap ) +{ + BitmapEx aBmp; + + css::uno::Reference< css::graphic::XGraphic > xGraphic( rxBitmap, css::uno::UNO_QUERY ); + if( xGraphic.is() ) + { + Graphic aGraphic( xGraphic ); + aBmp = aGraphic.GetBitmapEx(); + } + else if ( rxBitmap.is() ) + { + VCLXBitmap* pVCLBitmap = dynamic_cast<VCLXBitmap*>( rxBitmap.get() ); + if ( pVCLBitmap ) + aBmp = pVCLBitmap->GetBitmap(); + else + { + Bitmap aDIB, aMask; + { + css::uno::Sequence<sal_Int8> aBytes = rxBitmap->getDIB(); + SvMemoryStream aMem( aBytes.getArray(), aBytes.getLength(), StreamMode::READ ); + ReadDIB(aDIB, aMem, true); + } + { + css::uno::Sequence<sal_Int8> aBytes = rxBitmap->getMaskDIB(); + SvMemoryStream aMem( aBytes.getArray(), aBytes.getLength(), StreamMode::READ ); + ReadDIB(aMask, aMem, true); + } + aMask.Invert(); // Convert from transparency to alpha + aBmp = BitmapEx( aDIB, aMask ); + } + } + return aBmp; +} + +css::uno::Reference< css::awt::XBitmap> VCLUnoHelper::CreateBitmap( const BitmapEx& rBitmap ) +{ + Graphic aGraphic( rBitmap ); + css::uno::Reference< css::awt::XBitmap> xBmp( aGraphic.GetXGraphic(), css::uno::UNO_QUERY ); + return xBmp; +} + +vcl::Window* VCLUnoHelper::GetWindow( const css::uno::Reference< css::awt::XWindow>& rxWindow ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( rxWindow.get() ); + return pVCLXWindow ? pVCLXWindow->GetWindow() : nullptr; +} + +vcl::Window* VCLUnoHelper::GetWindow( const css::uno::Reference< css::awt::XWindow2>& rxWindow ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( rxWindow.get() ); + return pVCLXWindow ? pVCLXWindow->GetWindow() : nullptr; +} + +vcl::Window* VCLUnoHelper::GetWindow( const css::uno::Reference< css::awt::XWindowPeer>& rxWindow ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( rxWindow.get() ); + return pVCLXWindow ? pVCLXWindow->GetWindow() : nullptr; +} + +vcl::Region VCLUnoHelper::GetRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + vcl::Region aRegion; + VCLXRegion* pVCLRegion = dynamic_cast<VCLXRegion*>( rxRegion.get() ); + if ( pVCLRegion ) + aRegion = pVCLRegion->GetRegion(); + else + { + const css::uno::Sequence< css::awt::Rectangle > aRects = rxRegion->getRectangles(); + for ( const auto& rRect : aRects ) + aRegion.Union( VCLRectangle( rRect ) ); + } + return aRegion; +} + +css::uno::Reference< css::awt::XWindow> VCLUnoHelper::GetInterface( vcl::Window* pWindow ) +{ + css::uno::Reference< css::awt::XWindow > xWin; + if ( pWindow ) + { + css::uno::Reference< css::awt::XWindowPeer> xPeer = pWindow->GetComponentInterface(); + xWin.set(xPeer, css::uno::UNO_QUERY); + } + return xWin; +} + +OutputDevice* VCLUnoHelper::GetOutputDevice( const css::uno::Reference< css::awt::XDevice>& rxDevice ) +{ + VclPtr<OutputDevice> pOutDev; + VCLXDevice* pDev = dynamic_cast<VCLXDevice*>( rxDevice.get() ); + if ( pDev ) + pOutDev = pDev->GetOutputDevice(); + return pOutDev; +} + +OutputDevice* VCLUnoHelper::GetOutputDevice( const css::uno::Reference< css::awt::XGraphics>& rxGraphics ) +{ + OutputDevice* pOutDev = nullptr; + VCLXGraphics* pGrf = dynamic_cast<VCLXGraphics*>( rxGraphics.get() ); + if ( pGrf ) + pOutDev = pGrf->GetOutputDevice(); + return pOutDev; +} + +tools::Polygon VCLUnoHelper::CreatePolygon( const css::uno::Sequence< sal_Int32 >& DataX, + const css::uno::Sequence< sal_Int32 >& DataY ) +{ + sal_Int32 nLen = DataX.getLength(); + const sal_Int32* pDataX = DataX.getConstArray(); + const sal_Int32* pDataY = DataY.getConstArray(); + tools::Polygon aPoly( static_cast<sal_uInt16>(nLen) ); + for ( sal_Int32 n = 0; n < nLen; n++ ) + { + Point aPnt; + aPnt.setX( pDataX[n] ); + aPnt.setY( pDataY[n] ); + aPoly[n] = aPnt; + } + return aPoly; +} + +css::uno::Reference< css::awt::XControlContainer> VCLUnoHelper::CreateControlContainer( vcl::Window* pWindow ) +{ + rtl::Reference<UnoControlContainer> pContainer = new UnoControlContainer( pWindow->GetComponentInterface() ); + + rtl::Reference<UnoControlModel> pContainerModel = new UnoControlContainerModel( ::comphelper::getProcessComponentContext() ); + pContainer->setModel( pContainerModel ); + + return pContainer; +} + +css::awt::FontDescriptor VCLUnoHelper::CreateFontDescriptor( const vcl::Font& rFont ) +{ + css::awt::FontDescriptor aFD; + aFD.Name = rFont.GetFamilyName(); + aFD.StyleName = rFont.GetStyleName(); + aFD.Height = static_cast<sal_Int16>(rFont.GetFontSize().Height()); + aFD.Width = static_cast<sal_Int16>(rFont.GetFontSize().Width()); + aFD.Family = sal::static_int_cast< sal_Int16 >(rFont.GetFamilyType()); + aFD.CharSet = rFont.GetCharSet(); + aFD.Pitch = sal::static_int_cast< sal_Int16 >(rFont.GetPitch()); + aFD.CharacterWidth = vcl::unohelper::ConvertFontWidth(rFont.GetWidthType()); + aFD.Weight = vcl::unohelper::ConvertFontWeight(rFont.GetWeight()); + aFD.Slant = vcl::unohelper::ConvertFontSlant(rFont.GetItalic()); + aFD.Underline = sal::static_int_cast< sal_Int16 >(rFont.GetUnderline()); + aFD.Strikeout = sal::static_int_cast< sal_Int16 >(rFont.GetStrikeout()); + aFD.Orientation = rFont.GetOrientation().get() / 10.0; + aFD.Kerning = rFont.IsKerning(); + aFD.WordLineMode = rFont.IsWordLineMode(); + aFD.Type = 0; // ??? => Only in Metric... + return aFD; +} + +vcl::Font VCLUnoHelper::CreateFont( const css::awt::FontDescriptor& rDescr, const vcl::Font& rInitFont ) +{ + vcl::Font aFont( rInitFont ); + if ( !rDescr.Name.isEmpty() ) + aFont.SetFamilyName( rDescr.Name ); + if ( !rDescr.StyleName.isEmpty() ) + aFont.SetStyleName( rDescr.StyleName ); + if ( rDescr.Height ) + aFont.SetFontSize( Size( rDescr.Width, rDescr.Height ) ); + if ( static_cast<FontFamily>(rDescr.Family) != FAMILY_DONTKNOW ) + aFont.SetFamily( static_cast<FontFamily>(rDescr.Family) ); + if ( static_cast<rtl_TextEncoding>(rDescr.CharSet) != RTL_TEXTENCODING_DONTKNOW ) + aFont.SetCharSet( static_cast<rtl_TextEncoding>(rDescr.CharSet) ); + if ( static_cast<FontPitch>(rDescr.Pitch) != PITCH_DONTKNOW ) + aFont.SetPitch( static_cast<FontPitch>(rDescr.Pitch) ); + if ( rDescr.CharacterWidth ) + aFont.SetWidthType(vcl::unohelper::ConvertFontWidth(rDescr.CharacterWidth)); + if ( rDescr.Weight ) + aFont.SetWeight(vcl::unohelper::ConvertFontWeight(rDescr.Weight)); + if ( rDescr.Slant != css::awt::FontSlant_DONTKNOW ) + aFont.SetItalic(vcl::unohelper::ConvertFontSlant(rDescr.Slant)); + if ( static_cast<FontLineStyle>(rDescr.Underline) != LINESTYLE_DONTKNOW ) + aFont.SetUnderline( static_cast<FontLineStyle>(rDescr.Underline) ); + if ( static_cast<FontStrikeout>(rDescr.Strikeout) != STRIKEOUT_DONTKNOW ) + aFont.SetStrikeout( static_cast<FontStrikeout>(rDescr.Strikeout) ); + + // Not DONTKNOW + aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rDescr.Orientation * 10)) ); + aFont.SetKerning( static_cast<FontKerning>(rDescr.Kerning) ); + aFont.SetWordLineMode( rDescr.WordLineMode ); + + return aFont; +} + +vcl::Font VCLUnoHelper::CreateFont( const css::uno::Reference< css::awt::XFont >& rxFont ) +{ + vcl::Font aFont; + VCLXFont* pVCLXFont = dynamic_cast<VCLXFont*>( rxFont.get() ); + if ( pVCLXFont ) + aFont = pVCLXFont->GetFont(); + return aFont; +} + + +css::awt::SimpleFontMetric VCLUnoHelper::CreateFontMetric( const FontMetric& rFontMetric ) +{ + css::awt::SimpleFontMetric aFM; + aFM.Ascent = static_cast<sal_Int16>(rFontMetric.GetAscent()); + aFM.Descent = static_cast<sal_Int16>(rFontMetric.GetDescent()); + aFM.Leading = static_cast<sal_Int16>(rFontMetric.GetInternalLeading()); + aFM.Slant = static_cast<sal_Int16>(rFontMetric.GetSlant()); + aFM.FirstChar = 0x0020; + aFM.LastChar = 0xFFFD; + return aFM; +} + +bool VCLUnoHelper::IsZero(const css::awt::Rectangle& rRect) +{ + return ( !rRect.X && !rRect.Y && !rRect.Width && !rRect.Height ); +} + +MapUnit VCLUnoHelper::UnoEmbed2VCLMapUnit( sal_Int32 nUnoEmbedMapUnit ) +{ + switch( nUnoEmbedMapUnit ) + { + case css::embed::EmbedMapUnits::ONE_100TH_MM: + return MapUnit::Map100thMM; + case css::embed::EmbedMapUnits::ONE_10TH_MM: + return MapUnit::Map10thMM; + case css::embed::EmbedMapUnits::ONE_MM: + return MapUnit::MapMM; + case css::embed::EmbedMapUnits::ONE_CM: + return MapUnit::MapCM; + case css::embed::EmbedMapUnits::ONE_1000TH_INCH: + return MapUnit::Map1000thInch; + case css::embed::EmbedMapUnits::ONE_100TH_INCH: + return MapUnit::Map100thInch; + case css::embed::EmbedMapUnits::ONE_10TH_INCH: + return MapUnit::Map10thInch; + case css::embed::EmbedMapUnits::ONE_INCH: + return MapUnit::MapInch; + case css::embed::EmbedMapUnits::POINT: + return MapUnit::MapPoint; + case css::embed::EmbedMapUnits::TWIP: + return MapUnit::MapTwip; + case css::embed::EmbedMapUnits::PIXEL: + return MapUnit::MapPixel; + } + + OSL_FAIL( "Unexpected UNO map mode is provided!" ); + return MapUnit::LASTENUMDUMMY; +} + +sal_Int32 VCLUnoHelper::VCL2UnoEmbedMapUnit( MapUnit nVCLMapUnit ) +{ + switch( nVCLMapUnit ) + { + case MapUnit::Map100thMM: + return css::embed::EmbedMapUnits::ONE_100TH_MM; + case MapUnit::Map10thMM: + return css::embed::EmbedMapUnits::ONE_10TH_MM; + case MapUnit::MapMM: + return css::embed::EmbedMapUnits::ONE_MM; + case MapUnit::MapCM: + return css::embed::EmbedMapUnits::ONE_CM; + case MapUnit::Map1000thInch: + return css::embed::EmbedMapUnits::ONE_1000TH_INCH; + case MapUnit::Map100thInch: + return css::embed::EmbedMapUnits::ONE_100TH_INCH; + case MapUnit::Map10thInch: + return css::embed::EmbedMapUnits::ONE_10TH_INCH; + case MapUnit::MapInch: + return css::embed::EmbedMapUnits::ONE_INCH; + case MapUnit::MapPoint: + return css::embed::EmbedMapUnits::POINT; + case MapUnit::MapTwip: + return css::embed::EmbedMapUnits::TWIP; + case MapUnit::MapPixel: + return css::embed::EmbedMapUnits::PIXEL; + default: ; // avoid compiler warning + } + + OSL_FAIL( "Unexpected VCL map mode is provided!" ); + return -1; +} + +using namespace ::com::sun::star::util; + + +namespace +{ + enum UnitConversionDirection + { + FieldUnitToMeasurementUnit, + MeasurementUnitToFieldUnit + }; + + sal_Int16 convertMeasurementUnit( sal_Int16 _nUnit, UnitConversionDirection eDirection, sal_Int16& _rFieldToUNOValueFactor ) + { + static struct _unit_table + { + FieldUnit eFieldUnit; + sal_Int16 nMeasurementUnit; + sal_Int16 nFieldToMeasureFactor; + } const aUnits[] = { + { FieldUnit::NONE, -1 , -1}, + { FieldUnit::MM, MeasureUnit::MM, 1 }, // must precede MM_10TH + { FieldUnit::MM, MeasureUnit::MM_10TH, 10 }, + { FieldUnit::MM_100TH, MeasureUnit::MM_100TH, 1 }, + { FieldUnit::CM, MeasureUnit::CM, 1 }, + { FieldUnit::M, MeasureUnit::M, 1 }, + { FieldUnit::KM, MeasureUnit::KM, 1 }, + { FieldUnit::TWIP, MeasureUnit::TWIP, 1 }, + { FieldUnit::POINT, MeasureUnit::POINT, 1 }, + { FieldUnit::PICA, MeasureUnit::PICA, 1 }, + { FieldUnit::INCH, MeasureUnit::INCH, 1 }, // must precede INCH_*TH + { FieldUnit::INCH, MeasureUnit::INCH_10TH, 10 }, + { FieldUnit::INCH, MeasureUnit::INCH_100TH, 100 }, + { FieldUnit::INCH, MeasureUnit::INCH_1000TH, 1000 }, + { FieldUnit::FOOT, MeasureUnit::FOOT, 1 }, + { FieldUnit::MILE, MeasureUnit::MILE, 1 }, + }; + for (auto & aUnit : aUnits) + { + if ( eDirection == FieldUnitToMeasurementUnit ) + { + if ( ( aUnit.eFieldUnit == static_cast<FieldUnit>(_nUnit) ) && ( aUnit.nFieldToMeasureFactor == _rFieldToUNOValueFactor ) ) + return aUnit.nMeasurementUnit; + } + else + { + if ( aUnit.nMeasurementUnit == _nUnit ) + { + _rFieldToUNOValueFactor = aUnit.nFieldToMeasureFactor; + return static_cast<sal_Int16>(aUnit.eFieldUnit); + } + } + } + if ( eDirection == FieldUnitToMeasurementUnit ) + return -1; + + _rFieldToUNOValueFactor = 1; + return sal_Int16(FieldUnit::NONE); + } +} + +//= MeasurementUnitConversion + + +sal_Int16 VCLUnoHelper::ConvertToMeasurementUnit( FieldUnit _nFieldUnit, sal_Int16 _nUNOToFieldValueFactor ) +{ + return convertMeasurementUnit( static_cast<sal_Int16>(_nFieldUnit), FieldUnitToMeasurementUnit, _nUNOToFieldValueFactor ); +} + + +FieldUnit VCLUnoHelper::ConvertToFieldUnit( sal_Int16 _nMeasurementUnit, sal_Int16& _rFieldToUNOValueFactor ) +{ + return static_cast<FieldUnit>(convertMeasurementUnit( _nMeasurementUnit, MeasurementUnitToFieldUnit, _rFieldToUNOValueFactor )); +} + + +MapUnit /* MapModeUnit */ VCLUnoHelper::ConvertToMapModeUnit(sal_Int16 /* com.sun.star.util.MeasureUnit.* */ _nMeasureUnit) +{ + MapUnit eMode; + switch(_nMeasureUnit) + { + case css::util::MeasureUnit::MM_100TH: + eMode = MapUnit::Map100thMM; + break; + + case css::util::MeasureUnit::MM_10TH: + eMode = MapUnit::Map10thMM; + break; + + case css::util::MeasureUnit::MM: + eMode = MapUnit::MapMM; + break; + + case css::util::MeasureUnit::CM: + eMode = MapUnit::MapCM; + break; + + case css::util::MeasureUnit::INCH_1000TH: + eMode = MapUnit::Map1000thInch; + break; + + case css::util::MeasureUnit::INCH_100TH: + eMode = MapUnit::Map100thInch; + break; + + case css::util::MeasureUnit::INCH_10TH: + eMode = MapUnit::Map10thInch; + break; + + case css::util::MeasureUnit::INCH: + eMode = MapUnit::MapInch; + break; + + case css::util::MeasureUnit::POINT: + eMode = MapUnit::MapPoint; + break; + + case css::util::MeasureUnit::TWIP: + eMode = MapUnit::MapTwip; + break; + + case css::util::MeasureUnit::PIXEL: + eMode = MapUnit::MapPixel; + break; + + case css::util::MeasureUnit::APPFONT: + eMode = MapUnit::MapAppFont; + break; + + case css::util::MeasureUnit::SYSFONT: + eMode = MapUnit::MapSysFont; + break; + + default: + throw css::lang::IllegalArgumentException("Unsupported measure unit.", nullptr, 1 ); + } + return eMode; +} + +::Size VCLUnoHelper::ConvertToVCLSize(css::awt::Size const& _aSize) +{ + ::Size aVCLSize(_aSize.Width, _aSize.Height); + return aVCLSize; +} + +css::awt::Size VCLUnoHelper::ConvertToAWTSize(::Size /* VCLSize */ const& _aSize) +{ + css::awt::Size aAWTSize(_aSize.Width(), _aSize.Height()); + return aAWTSize; +} + + +::Point VCLUnoHelper::ConvertToVCLPoint(css::awt::Point const& _aPoint) +{ + ::Point aVCLPoint(_aPoint.X, _aPoint.Y); + return aVCLPoint; +} + +css::awt::Point VCLUnoHelper::ConvertToAWTPoint(::Point /* VCLPoint */ const& _aPoint) +{ + css::awt::Point aAWTPoint(_aPoint.X(), _aPoint.Y()); + return aAWTPoint; +} + +::tools::Rectangle VCLUnoHelper::ConvertToVCLRect( css::awt::Rectangle const & _rRect ) +{ + return ::tools::Rectangle( _rRect.X, _rRect.Y, _rRect.X + _rRect.Width - 1, _rRect.Y + _rRect.Height - 1 ); +} + +css::awt::Rectangle VCLUnoHelper::ConvertToAWTRect( ::tools::Rectangle const & _rRect ) +{ + return css::awt::Rectangle( _rRect.Left(), _rRect.Top(), _rRect.GetWidth(), _rRect.GetHeight() ); +} + +awt::MouseEvent VCLUnoHelper::createMouseEvent( const ::MouseEvent& _rVclEvent, const uno::Reference< uno::XInterface >& _rxContext ) +{ + awt::MouseEvent aMouseEvent; + aMouseEvent.Source = _rxContext; + + aMouseEvent.Modifiers = 0; + if ( _rVclEvent.IsShift() ) + aMouseEvent.Modifiers |= css::awt::KeyModifier::SHIFT; + if ( _rVclEvent.IsMod1() ) + aMouseEvent.Modifiers |= css::awt::KeyModifier::MOD1; + if ( _rVclEvent.IsMod2() ) + aMouseEvent.Modifiers |= css::awt::KeyModifier::MOD2; + + aMouseEvent.Buttons = 0; + if ( _rVclEvent.IsLeft() ) + aMouseEvent.Buttons |= css::awt::MouseButton::LEFT; + if ( _rVclEvent.IsRight() ) + aMouseEvent.Buttons |= css::awt::MouseButton::RIGHT; + if ( _rVclEvent.IsMiddle() ) + aMouseEvent.Buttons |= css::awt::MouseButton::MIDDLE; + + aMouseEvent.X = _rVclEvent.GetPosPixel().X(); + aMouseEvent.Y = _rVclEvent.GetPosPixel().Y(); + aMouseEvent.ClickCount = _rVclEvent.GetClicks(); + aMouseEvent.PopupTrigger = false; + + return aMouseEvent; +} + +::MouseEvent VCLUnoHelper::createVCLMouseEvent( const awt::MouseEvent& _rAwtEvent ) +{ + ::MouseEvent aMouseEvent( Point( _rAwtEvent.X, _rAwtEvent.Y ), _rAwtEvent.ClickCount, + ::MouseEventModifiers::NONE, _rAwtEvent.Buttons, _rAwtEvent.Modifiers ); + + return aMouseEvent; +} + +awt::KeyEvent VCLUnoHelper::createKeyEvent( const ::KeyEvent& _rVclEvent, const uno::Reference< uno::XInterface >& _rxContext ) +{ + awt::KeyEvent aKeyEvent; + aKeyEvent.Source = _rxContext; + + aKeyEvent.Modifiers = 0; + if ( _rVclEvent.GetKeyCode().IsShift() ) + aKeyEvent.Modifiers |= awt::KeyModifier::SHIFT; + if ( _rVclEvent.GetKeyCode().IsMod1() ) + aKeyEvent.Modifiers |= awt::KeyModifier::MOD1; + if ( _rVclEvent.GetKeyCode().IsMod2() ) + aKeyEvent.Modifiers |= awt::KeyModifier::MOD2; + if ( _rVclEvent.GetKeyCode().IsMod3() ) + aKeyEvent.Modifiers |= awt::KeyModifier::MOD3; + + aKeyEvent.KeyCode = _rVclEvent.GetKeyCode().GetCode(); + aKeyEvent.KeyChar = _rVclEvent.GetCharCode(); + aKeyEvent.KeyFunc = ::sal::static_int_cast< sal_Int16 >( _rVclEvent.GetKeyCode().GetFunction()); + + return aKeyEvent; +} + +::KeyEvent VCLUnoHelper::createVCLKeyEvent( const awt::KeyEvent& _rAwtEvent ) +{ + sal_Unicode nChar = _rAwtEvent.KeyChar; + vcl::KeyCode aKeyCode( _rAwtEvent.KeyCode, _rAwtEvent.Modifiers & awt::KeyModifier::SHIFT, + _rAwtEvent.Modifiers & awt::KeyModifier::MOD1, + _rAwtEvent.Modifiers & awt::KeyModifier::MOD2, + _rAwtEvent.Modifiers & awt::KeyModifier::MOD3 ); + + return ::KeyEvent (nChar, aKeyCode); + +} + +::PointerStyle VCLUnoHelper::getMousePointer(const css::uno::Reference<css::awt::XWindowPeer>& rWindowPeer) +{ + ::PointerStyle eType = ::PointerStyle::Arrow; // default ? + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rWindowPeer); + if (pWindow) + eType = pWindow->GetPointer(); + return eType; +} + +void VCLUnoHelper::setMousePointer(const css::uno::Reference<css::awt::XWindowPeer>& rWindowPeer, ::PointerStyle ePointer) +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rWindowPeer); + if (!pWindow) + return; + pWindow->SetPointer(ePointer); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |