sdk-examples/trunk/cpp/components/StatusbarController/WordCountStatusbarController/WordCountDispatch.cxx (399 lines of code) (raw):

/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include "WordCountDispatch.hxx" #include "defines.hxx" #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/beans/Pair.hpp> #include <com/sun/star/beans/NamedValue.hpp> #include <com/sun/star/container/XIndexAccess.hpp> #include <com/sun/star/frame/XFrame.hpp> #include <com/sun/star/frame/XDispatchProvider.hpp> #include <com/sun/star/lang/XMultiComponentFactory.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/text/XTextRangeCompare.hpp> #include <com/sun/star/util/XRefreshable.hpp> #include <com/sun/star/view/XSelectionSupplier.hpp> #include <com/sun/star/i18n/WordType.hpp> #include <rtl/ustrbuf.hxx> #include <rtl/instance.hxx> #include <rtl/ref.hxx> #include <cppuhelper/implbase1.hxx> #include <vector> #include <map> #include <algorithm> #include <set> using namespace framework::statusbar_controller_wordcount; using namespace com::sun::star::awt; using namespace com::sun::star::i18n; using namespace com::sun::star::container; using namespace com::sun::star::lang; using namespace com::sun::star::beans; using namespace com::sun::star::uno; using namespace com::sun::star::text; using namespace com::sun::star::util; using namespace com::sun::star::view; using namespace com::sun::star::frame; using rtl::OUString; using rtl::OUStringBuffer; namespace { static OUString st_OwnCommand = OUSTR_COMMAND_STATUSWORDCOUNT; class AsyncDispatcher : public cppu::WeakImplHelper1< XCallback > { public: AsyncDispatcher() {} ~AsyncDispatcher() {} virtual void SAL_CALL notify( const Any &aData ) throw ( RuntimeException ) { Pair< URL, Reference< XDispatch > > aDispatchInfo; if ( ( aData >>= aDispatchInfo ) && aDispatchInfo.Second.is() ) { try { aDispatchInfo.Second->dispatch( aDispatchInfo.First, Sequence<PropertyValue>( 0 ) ); } catch ( ... ) {} } } }; struct StaticAsyncDispatcher : public rtl::StaticWithInit< rtl::Reference< AsyncDispatcher >, StaticAsyncDispatcher> { rtl::Reference< AsyncDispatcher > operator()() { OSL_TRACE( "sbctlwc: Initializing static AsyncDispatcher" ); return rtl::Reference< AsyncDispatcher >( new AsyncDispatcher() ); } }; template < class REF > void lcl_DisposeAndClear( REF &rUnoRef ) { Reference< XComponent > xComp( rUnoRef, UNO_QUERY ); if ( xComp.is() ) { try { xComp->dispose(); } catch ( ... ) { } } rUnoRef.clear(); } struct DefaultFeatureState : public rtl::StaticWithInit< FeatureStateEvent, DefaultFeatureState > { FeatureStateEvent operator()() { FeatureStateEvent aFeatureState; aFeatureState.IsEnabled = sal_False; aFeatureState.Requery = sal_False; URL aURL; aURL.Complete = st_OwnCommand; aFeatureState.FeatureURL = aURL; Any aVal; aVal <<= sal_uInt32( 0 ); Sequence< NamedValue > aArgs( 2 ); aArgs[0].Name = C2U( "WordCount" ); aArgs[0].Value = aVal; aArgs[1].Name = C2U( "Selection" ); aArgs[1].Value = aVal; aFeatureState.State <<= aArgs; return aFeatureState; } }; typedef std::set< OUString > SupportedModulesSet; struct SupportedModulesInit { SupportedModulesSet * operator()() { static SupportedModulesSet aSupportedModules; lcl_InitSet( aSupportedModules ); return &aSupportedModules; } private: void lcl_InitSet( SupportedModulesSet &aSet ) { // TODO add all text document services aSet.insert( C2U( STR_MODULE_TEXTDOCUMENT ) ); } }; struct SupportedModules : public rtl::StaticAggregate< SupportedModulesSet, SupportedModulesInit > {}; } WordCountDispatch::WordCountDispatch( const Reference< XComponentContext > &rxContext, const Reference< XFrame > &rxFrame, const OUString &rModuleIdentifier ) : BaseDispatch( rxContext, rxFrame, rModuleIdentifier ) , m_aCommand( st_OwnCommand ) , m_bIsModified( false ) , m_bInGetStatus( false ) , m_xBreakIterator() , m_xRequestCallback() { OSL_TRACE( "sbctlwc::WordCountDispatch::WordCountDispatch" ); try { m_xURLTransformer.set( m_xContext->getServiceManager()->createInstanceWithContext( OUSTR_SERVICENAME_URLTRANSFORMER, m_xContext ), UNO_QUERY_THROW ); m_xBreakIterator.set( m_xContext->getServiceManager()->createInstanceWithContext( OUSTR_SERVICENAME_BREAKITERATOR, m_xContext ), UNO_QUERY_THROW ); m_xRequestCallback.set( m_xContext->getServiceManager()->createInstanceWithContext( OUSTR_SERVICENAME_CALLBACK, m_xContext ), UNO_QUERY_THROW ); Reference< XController > xController( m_xFrame->getController() ); m_xSelectionSupplier.set( xController, UNO_QUERY_THROW ); if ( m_xSelectionSupplier.is() ) m_xSelectionSupplier->addSelectionChangeListener( this ); m_xModifiable.set( xController->getModel(), UNO_QUERY_THROW ); Reference< XModifyBroadcaster > xBroadcaster( m_xModifiable, UNO_QUERY_THROW ); if ( xBroadcaster.is() ) xBroadcaster->addModifyListener( this ); static bool bURLParsed = false; if ( !bURLParsed ) { FeatureStateEvent &aFeatureState = DefaultFeatureState::get(); m_xURLTransformer->parseStrict( aFeatureState.FeatureURL ); bURLParsed = true; } } catch ( ... ) { } } WordCountDispatch::~WordCountDispatch( ) { OSL_TRACE( "sbctlwc::WordCountDispatch::~WordCountDispatch" ); } Reference< XDispatch > WordCountDispatch::Create( const Reference< XComponentContext > &rxContext, const Reference< XFrame > &rxFrame, const OUString &rModuleIdentifier ) { OSL_TRACE( "sbctlwc::WordCountDispatch::Create" ); return Reference< XDispatch > ( static_cast< cppu::OWeakObject * >( new WordCountDispatch( rxContext, rxFrame, rModuleIdentifier ) ), UNO_QUERY ); } bool WordCountDispatch::SupportsURL( const URL &aURL, const OUString &rModuleIdentifier ) { const SupportedModulesSet &aModules = *SupportedModules::get(); return aModules.find( rModuleIdentifier ) != aModules.end() && aURL.Complete.equals( st_OwnCommand ); } void SAL_CALL WordCountDispatch::disposing( ) { OSL_TRACE( "sbctlwc::WordCountDispatch::disposing" ); osl::ClearableMutexGuard aGuard( m_aMutex ); // When the dispatch pool disposes us, stop listening // otherwise the model will keep us alive if ( m_xModifiable.is() ) { m_xModifiable->removeModifyListener( this ); m_xModifiable.clear(); } if ( m_xSelectionSupplier.is() ) { m_xSelectionSupplier->removeSelectionChangeListener( this ); m_xModifiable.clear(); } m_xBreakIterator.clear(); m_xFrame.clear(); lcl_DisposeAndClear( m_xRequestCallback ); lcl_DisposeAndClear( m_xURLTransformer ); aGuard.clear(); BaseDispatch::disposing( ); } void SAL_CALL WordCountDispatch::modified( const EventObject &aEvent ) throw ( RuntimeException ) { OSL_TRACE( "sbctlwc::WordCountDispatch::modified" ); // prevent endless loop: // getting the document word count triggers a document modified event if ( m_bInGetStatus ) return; Reference< XModifiable > xModifiable( aEvent.Source, UNO_QUERY ); if ( xModifiable.is() ) { bool bModified; osl::ClearableMutexGuard aLock( m_aMutex ); m_bIsModified = bModified = xModifiable->isModified(); aLock.clear( ); if ( bModified ) BaseDispatch::modified( aEvent ); } } void SAL_CALL WordCountDispatch::selectionChanged( const EventObject &aEvent ) throw ( RuntimeException ) { OSL_TRACE( "sbctlwc::WordCountDispatch::selectionChanged" ); if ( m_bInGetStatus ) return; BaseDispatch::selectionChanged( aEvent ); } void SAL_CALL WordCountDispatch::disposing( const EventObject &aEvent ) throw ( RuntimeException ) { OSL_TRACE( "sbctlwc::WordCountDispatch::disposing(aEvent)" ); Reference< XModifiable > xModifiable( aEvent.Source, UNO_QUERY ); if ( xModifiable.is() && xModifiable == m_xModifiable ) { OSL_TRACE( "disposing XModel" ); m_xModifiable->removeModifyListener( this ); m_xModifiable.clear(); return; } Reference< XSelectionSupplier > xSelectionSupplier( aEvent.Source, UNO_QUERY ); if ( xSelectionSupplier.is() && xSelectionSupplier == m_xSelectionSupplier ) { OSL_TRACE( "disposing XController" ); m_xSelectionSupplier->removeSelectionChangeListener( this ); m_xSelectionSupplier.clear(); } } void WordCountDispatch::ExecuteCommand( const URL &rURL, const Sequence< PropertyValue > &/*lArguments*/ ) { OSL_TRACE( "sbctlwc::WordCountDispatch::ExecuteCommand" ); if ( rURL.Complete.equals( st_OwnCommand ) ) ShowWordCountDialog(); } FeatureStateEvent WordCountDispatch::GetState( const URL &rURL ) { OSL_TRACE( "sbctlwc::WordCountDispatch::GetState" ); FeatureStateEvent aEvent = DefaultFeatureState::get(); aEvent.Source = Reference<XDispatch > ( this ); osl::MutexGuard aGuard( m_aMutex ); // prevent endless loop: // getting the document word count triggers a document modified event m_bInGetStatus = true; try { if ( rURL.Complete.equals( GetCommand() ) ) { sal_Int32 nWordCount( 0 ); sal_Int32 nSelWords( 0 ); Reference< XController> xController = m_xFrame->getController( ); Reference<XSelectionSupplier> xSelSuppl( xController, UNO_QUERY ); Any aSelection; const bool bHasSelection = xSelSuppl.is() && ( aSelection = xSelSuppl->getSelection() ).hasValue(); Reference< XModel > xModel( xController->getModel( ) ); Reference< XPropertySet> xDocPropSet( xModel, UNO_QUERY ); Reference< XModifiable > xModifiable( xModel, UNO_QUERY ); // WARNING: this will trigger a document modified event xDocPropSet->getPropertyValue( C2U( "WordCount" ) ) >>= nWordCount; if ( bHasSelection ) { Reference< XServiceInfo > xServiceInfo( aSelection, UNO_QUERY ); if ( xServiceInfo.is( ) && xServiceInfo->supportsService( C2U( "com.sun.star.text.TextRanges" ) ) ) { Reference< XIndexAccess > xIndexAccess( xServiceInfo, UNO_QUERY ); for ( sal_Int32 n = 0; n < xIndexAccess->getCount( ); n++ ) { try { OUString sText; Reference< XTextRange > xTextRange( xIndexAccess->getByIndex( n ), UNO_QUERY ); Reference< XTextRangeCompare > xRangeCompare( xTextRange->getText(), UNO_QUERY ); if ( xRangeCompare->compareRegionStarts( xTextRange->getStart(), xTextRange->getEnd() ) == 0 || ( sText = xTextRange->getString() ).getLength() == 0 ) continue; // TODO the text range has a Locale in CharLocale // *if* all the text range has the same Locale, of course // Use this Locale with the BreakIterator nSelWords += CountWords( sText ); } catch ( ... ) {} } } } Sequence< NamedValue > aArgs; aEvent.State >>= aArgs; aArgs[0].Value <<= sal_uInt32( nWordCount ); aArgs[1].Value <<= sal_uInt32( nSelWords ); aEvent.State <<= aArgs; aEvent.IsEnabled = sal_True; // needed because the controller may die // for example, switching to Preview creates a new view/controller if ( !m_xSelectionSupplier.is( ) ) { m_xSelectionSupplier.set( xSelSuppl ); if ( m_xSelectionSupplier.is( ) ) m_xSelectionSupplier->addSelectionChangeListener( this ); } // this seems not really needed: we live as the model lives if ( !m_xModifiable.is( ) ) { m_xModifiable.set( xModifiable ); Reference< XModifyBroadcaster > xBroadcaster( m_xModifiable, UNO_QUERY ); if ( xBroadcaster.is( ) ) xBroadcaster->addModifyListener( this ); } } } catch ( ... ) { } m_bInGetStatus = false; return aEvent; } sal_Int32 WordCountDispatch::CountWords( const OUString &sText ) { sal_Int32 nWords( 1 ); sal_Int32 nStartPos( 1 ); Locale aLocale; Boundary aNext = m_xBreakIterator->nextWord( sText, nStartPos, aLocale, WordType::WORD_COUNT ); while ( aNext.startPos != aNext.endPos ) { nWords++; nStartPos = aNext.startPos; aNext = m_xBreakIterator->nextWord( sText, nStartPos, aLocale, WordType::WORD_COUNT ); } return nWords; } void WordCountDispatch::ShowWordCountDialog() const { Reference< XURLTransformer > xURLParser; Reference< XFrame > xFrame; Reference< XRequestCallback > xRequestCallback; osl::ClearableMutexGuard aLock( m_aMutex ); xURLParser = m_xURLTransformer; xFrame = m_xFrame; xRequestCallback = m_xRequestCallback; aLock.clear( ); try { URL aCommand; aCommand.Complete = OUSTR_COMMAND_UNO_WORDCOUNTDLG; xURLParser->parseStrict( aCommand ); Reference< XDispatchProvider > xProvider( xFrame, UNO_QUERY ); Reference< XDispatch > xDispatch = xProvider->queryDispatch( aCommand, C2U( "_self" ), 0 ); if ( xRequestCallback.is() && xDispatch.is() ) { xRequestCallback->addCallback( StaticAsyncDispatcher::get().get(), makeAny( Pair< URL, Reference< XDispatch > >( aCommand, xDispatch ) ) ); } } catch ( ... ) { } }