1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svtools/popupmenucontrollerbase.hxx>
22 #include <com/sun/star/beans/PropertyValue.hpp>
23 #include <com/sun/star/frame/XDispatchProvider.hpp>
24 #include <com/sun/star/frame/XFrame.hpp>
25 #include <com/sun/star/lang/DisposedException.hpp>
26 #include <com/sun/star/util/URLTransformer.hpp>
29 #include <vcl/svapp.hxx>
30 #include <osl/mutex.hxx>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <toolkit/awt/vclxmenu.hxx>
34 using namespace com::sun::star
;
35 using namespace css::uno
;
36 using namespace css::lang
;
37 using namespace css::frame
;
38 using namespace css::beans
;
39 using namespace css::util
;
46 struct PopupMenuControllerBaseDispatchInfo
48 Reference
< XDispatch
> mxDispatch
;
50 const Sequence
< PropertyValue
> maArgs
;
52 PopupMenuControllerBaseDispatchInfo( const Reference
< XDispatch
>& xDispatch
, URL aURL
, const Sequence
< PropertyValue
>& rArgs
)
53 : mxDispatch( xDispatch
), maURL(std::move( aURL
)), maArgs( rArgs
) {}
58 PopupMenuControllerBase::PopupMenuControllerBase( const Reference
< XComponentContext
>& xContext
) :
59 m_bInitialized( false )
62 m_xURLTransformer
.set( util::URLTransformer::create( xContext
) );
65 PopupMenuControllerBase::~PopupMenuControllerBase()
70 void PopupMenuControllerBase::resetPopupMenu( css::uno::Reference
< css::awt::XPopupMenu
> const & rPopupMenu
)
72 if ( rPopupMenu
.is() && rPopupMenu
->getItemCount() > 0 )
78 void PopupMenuControllerBase::disposing(std::unique_lock
<std::mutex
>& /*rGuard*/)
80 // Reset our members and set disposed flag
87 sal_Bool SAL_CALL
PopupMenuControllerBase::supportsService( const OUString
& ServiceName
)
89 return cppu::supportsService(this, ServiceName
);
93 void SAL_CALL
PopupMenuControllerBase::disposing( const EventObject
& )
95 std::unique_lock
aLock( m_aMutex
);
102 void SAL_CALL
PopupMenuControllerBase::itemHighlighted( const awt::MenuEvent
& )
106 void SAL_CALL
PopupMenuControllerBase::itemSelected( const awt::MenuEvent
& rEvent
)
108 std::unique_lock
aLock( m_aMutex
);
109 throwIfDisposed(aLock
);
111 if( m_xPopupMenu
.is() )
113 Sequence
<PropertyValue
> aArgs
;
114 dispatchCommandImpl( aLock
, m_xPopupMenu
->getCommand( rEvent
.MenuId
), aArgs
, OUString() );
118 void PopupMenuControllerBase::dispatchCommand( const OUString
& sCommandURL
,
119 const css::uno::Sequence
< css::beans::PropertyValue
>& rArgs
,
120 const OUString
& sTarget
)
122 std::unique_lock
aLock( m_aMutex
);
123 throwIfDisposed(aLock
);
124 dispatchCommandImpl(aLock
, sCommandURL
, rArgs
, sTarget
);
127 void PopupMenuControllerBase::dispatchCommandImpl( std::unique_lock
<std::mutex
>& /*rGuard*/,
128 const OUString
& sCommandURL
,
129 const css::uno::Sequence
< css::beans::PropertyValue
>& rArgs
,
130 const OUString
& sTarget
)
135 Reference
< XDispatchProvider
> xDispatchProvider( m_xFrame
, UNO_QUERY_THROW
);
137 aURL
.Complete
= sCommandURL
;
138 m_xURLTransformer
->parseStrict( aURL
);
140 Reference
< XDispatch
> xDispatch( xDispatchProvider
->queryDispatch( aURL
, sTarget
, 0 ), UNO_SET_THROW
);
142 Application::PostUserEvent( LINK(nullptr, PopupMenuControllerBase
, ExecuteHdl_Impl
), new PopupMenuControllerBaseDispatchInfo( xDispatch
, std::move(aURL
), rArgs
) );
151 IMPL_STATIC_LINK( PopupMenuControllerBase
, ExecuteHdl_Impl
, void*, p
, void )
153 PopupMenuControllerBaseDispatchInfo
* pDispatchInfo
= static_cast<PopupMenuControllerBaseDispatchInfo
*>(p
);
154 pDispatchInfo
->mxDispatch
->dispatch( pDispatchInfo
->maURL
, pDispatchInfo
->maArgs
);
155 delete pDispatchInfo
;
158 void SAL_CALL
PopupMenuControllerBase::itemActivated( const awt::MenuEvent
& )
162 void SAL_CALL
PopupMenuControllerBase::itemDeactivated( const awt::MenuEvent
& )
166 void SAL_CALL
PopupMenuControllerBase::updatePopupMenu()
169 std::unique_lock
aLock(m_aMutex
);
170 throwIfDisposed(aLock
);
173 updateCommand( m_aCommandURL
);
176 void PopupMenuControllerBase::updateCommand( const OUString
& rCommandURL
)
178 std::unique_lock
aLock( m_aMutex
);
179 Reference
< XStatusListener
> xStatusListener(this);
180 Reference
< XDispatch
> xDispatch( m_xDispatch
);
182 aTargetURL
.Complete
= rCommandURL
;
183 m_xURLTransformer
->parseStrict( aTargetURL
);
186 // Add/remove status listener to get a status update once
187 if ( xDispatch
.is() )
189 xDispatch
->addStatusListener( xStatusListener
, aTargetURL
);
190 xDispatch
->removeStatusListener( xStatusListener
, aTargetURL
);
196 Reference
< XDispatch
> SAL_CALL
197 PopupMenuControllerBase::queryDispatch(
199 const OUString
& /*sTarget*/,
200 sal_Int32
/*nFlags*/ )
202 // must be implemented by subclass
203 std::unique_lock
aLock( m_aMutex
);
204 throwIfDisposed(aLock
);
206 return Reference
< XDispatch
>();
209 Sequence
< Reference
< XDispatch
> > SAL_CALL
PopupMenuControllerBase::queryDispatches( const Sequence
< DispatchDescriptor
>& lDescriptor
)
211 // Create return list - which must have same size then the given descriptor
212 // It's not allowed to pack it!
214 std::unique_lock
aLock(m_aMutex
);
215 throwIfDisposed(aLock
);
218 sal_Int32 nCount
= lDescriptor
.getLength();
219 uno::Sequence
< uno::Reference
< frame::XDispatch
> > lDispatcher( nCount
);
221 // Step over all descriptors and try to get any dispatcher for it.
222 std::transform(lDescriptor
.begin(), lDescriptor
.end(), lDispatcher
.getArray(),
223 [this](const DispatchDescriptor
& rDesc
) -> uno::Reference
< frame::XDispatch
> {
224 return queryDispatch(rDesc
.FeatureURL
, rDesc
.FrameName
, rDesc
.SearchFlags
); });
231 PopupMenuControllerBase::dispatch(
233 const Sequence
< PropertyValue
>& /*seqProperties*/ )
235 // must be implemented by subclass
236 std::unique_lock
aLock( m_aMutex
);
237 throwIfDisposed(aLock
);
241 PopupMenuControllerBase::addStatusListener(
242 const Reference
< XStatusListener
>& xControl
,
245 std::unique_lock
aLock( m_aMutex
);
246 throwIfDisposed(aLock
);
248 bool bStatusUpdate( false );
249 maStatusListeners
.addInterface( aLock
, xControl
);
251 if ( aURL
.Complete
.startsWith( m_aBaseURL
) )
252 bStatusUpdate
= true;
257 // Dummy update for popup menu controllers
258 FeatureStateEvent aEvent
;
259 aEvent
.FeatureURL
= aURL
;
260 aEvent
.IsEnabled
= true;
261 aEvent
.Requery
= false;
262 aEvent
.State
= Any();
263 xControl
->statusChanged( aEvent
);
267 void SAL_CALL
PopupMenuControllerBase::removeStatusListener(
268 const Reference
< XStatusListener
>& xControl
,
269 const URL
& /*aURL*/ )
271 std::unique_lock
aLock( m_aMutex
);
272 maStatusListeners
.removeInterface( aLock
, xControl
);
275 OUString
PopupMenuControllerBase::determineBaseURL( std::u16string_view aURL
)
277 // Just use the main part of the URL for popup menu controllers
278 OUString
aMainURL( u
"vnd.sun.star.popup:"_ustr
);
280 size_t nSchemePart
= aURL
.find( ':' );
281 if (( nSchemePart
!= std::u16string_view::npos
&& nSchemePart
> 0 ) &&
282 ( aURL
.size() > ( nSchemePart
+1 )))
284 size_t nQueryPart
= aURL
.find( '?', nSchemePart
);
285 if ( nQueryPart
!= std::u16string_view::npos
&& nQueryPart
> 0 )
286 aMainURL
+= aURL
.substr( nSchemePart
, nQueryPart
-nSchemePart
);
287 else if ( nQueryPart
== std::u16string_view::npos
)
288 aMainURL
+= aURL
.substr( nSchemePart
+1 );
295 void SAL_CALL
PopupMenuControllerBase::initialize( const Sequence
< Any
>& aArguments
)
297 std::unique_lock
aLock( m_aMutex
);
298 initializeImpl(aLock
, aArguments
);
302 void PopupMenuControllerBase::initializeImpl( std::unique_lock
<std::mutex
>& /*rGuard*/, const Sequence
< Any
>& aArguments
)
304 bool bInitialized( m_bInitialized
);
308 PropertyValue aPropValue
;
309 OUString aCommandURL
;
310 Reference
< XFrame
> xFrame
;
312 for ( const auto& rArgument
: aArguments
)
314 if ( rArgument
>>= aPropValue
)
316 if ( aPropValue
.Name
== "Frame" )
317 aPropValue
.Value
>>= xFrame
;
318 else if ( aPropValue
.Name
== "CommandURL" )
319 aPropValue
.Value
>>= aCommandURL
;
320 else if ( aPropValue
.Name
== "ModuleIdentifier" )
321 aPropValue
.Value
>>= m_aModuleName
;
325 if ( xFrame
.is() && !aCommandURL
.isEmpty() )
327 m_xFrame
= std::move(xFrame
);
328 m_aCommandURL
= aCommandURL
;
329 m_aBaseURL
= determineBaseURL( aCommandURL
);
330 m_bInitialized
= true;
333 // XPopupMenuController
334 void SAL_CALL
PopupMenuControllerBase::setPopupMenu( const Reference
< awt::XPopupMenu
>& xPopupMenu
)
337 std::unique_lock
aLock( m_aMutex
);
338 throwIfDisposed(aLock
);
340 if ( !m_xFrame
.is() || m_xPopupMenu
.is() )
343 // Create popup menu on demand
344 SolarMutexGuard aSolarMutexGuard
;
346 m_xPopupMenu
= dynamic_cast<VCLXPopupMenu
*>(xPopupMenu
.get());
347 assert(bool(xPopupMenu
) == bool(m_xPopupMenu
) && "we only support VCLXPopupMenu");
348 m_xPopupMenu
->addMenuListener( Reference
< awt::XMenuListener
>(this) );
350 Reference
< XDispatchProvider
> xDispatchProvider( m_xFrame
, UNO_QUERY
);
353 aTargetURL
.Complete
= m_aCommandURL
;
354 m_xURLTransformer
->parseStrict( aTargetURL
);
355 m_xDispatch
= xDispatchProvider
->queryDispatch( aTargetURL
, OUString(), 0 );
357 impl_setPopupMenu(aLock
);
362 void PopupMenuControllerBase::impl_setPopupMenu(std::unique_lock
<std::mutex
>& /*rGuard*/)
368 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */