1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_framework.hxx"
24 
25 #include <uielement/popuptoolbarcontroller.hxx>
26 #include <framework/menuconfiguration.hxx>
27 #include <toolkit/awt/vclxmenu.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <svtools/imagemgr.hxx>
30 #include <svtools/miscopt.hxx>
31 #include <toolkit/helper/vclunohelper.hxx>
32 #include <tools/urlobj.hxx>
33 #include <unotools/moduleoptions.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/toolbox.hxx>
36 #include <vos/mutex.hxx>
37 
38 #include <com/sun/star/awt/PopupMenuDirection.hpp>
39 #include <com/sun/star/frame/PopupMenuControllerFactory.hpp>
40 #include <com/sun/star/frame/XDispatchProvider.hpp>
41 
42 #define UNO_COMMAND_RECENT_FILE_LIST    ".uno:RecentFileList"
43 #define SFX_REFERER_USER                "private:user"
44 
45 using rtl::OUString;
46 namespace css = ::com::sun::star;
47 
48 namespace framework
49 {
50 
51 PopupMenuToolbarController::PopupMenuToolbarController(
52     const css::uno::Reference< css::uno::XComponentContext >& xContext,
53     const OUString &rPopupCommand )
54     : svt::ToolboxController()
55     , m_xContext( xContext )
56     , m_bHasController( sal_False )
57     , m_aPopupCommand( rPopupCommand )
58 {
59 }
60 
61 PopupMenuToolbarController::~PopupMenuToolbarController()
62 {
63 }
64 
65 void SAL_CALL PopupMenuToolbarController::dispose()
66 throw ( css::uno::RuntimeException )
67 {
68     svt::ToolboxController::dispose();
69 
70     osl::MutexGuard aGuard( m_aMutex );
71     if( m_xPopupMenuController.is() )
72     {
73         css::uno::Reference< css::lang::XComponent > xComponent(
74             m_xPopupMenuController, css::uno::UNO_QUERY );
75         if( xComponent.is() )
76         {
77             try
78             {
79                 xComponent->dispose();
80             }
81             catch (...)
82             {}
83         }
84         m_xPopupMenuController.clear();
85     }
86 
87     m_xContext.clear();
88     m_xPopupMenuFactory.clear();
89     m_xPopupMenu.clear();
90 }
91 
92 void SAL_CALL PopupMenuToolbarController::initialize(
93     const css::uno::Sequence< css::uno::Any >& aArguments )
94 throw ( css::uno::Exception, css::uno::RuntimeException )
95 {
96     ToolboxController::initialize( aArguments );
97 
98     osl::MutexGuard aGuard( m_aMutex );
99     if ( !m_aPopupCommand.getLength() )
100         m_aPopupCommand = m_aCommandURL;
101 
102     try
103     {
104         m_xPopupMenuFactory.set(
105             css::frame::PopupMenuControllerFactory::create( m_xContext ) );
106         m_bHasController = m_xPopupMenuFactory->hasController(
107             m_aPopupCommand, getModuleName() );
108     }
109     catch (const css::uno::Exception& e)
110     {
111         OSL_TRACE( "PopupMenuToolbarController - caught an exception! %s",
112                    rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
113         (void) e;
114     }
115 
116     vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
117     ToolBox* pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
118     if ( pToolBox )
119     {
120         ToolBoxItemBits nCurStyle( pToolBox->GetItemBits( m_nToolBoxId ) );
121         ToolBoxItemBits nSetStyle( getDropDownStyle() );
122         pToolBox->SetItemBits( m_nToolBoxId,
123                                m_bHasController ?
124                                     nCurStyle | nSetStyle :
125                                     nCurStyle & ~nSetStyle );
126     }
127 
128 }
129 
130 void SAL_CALL
131 PopupMenuToolbarController::statusChanged(
132     const css::frame::FeatureStateEvent& rEvent )
133     throw ( css::uno::RuntimeException )
134 {
135     // TODO move to base class
136 
137     svt::ToolboxController::statusChanged( rEvent );
138     enable( rEvent.IsEnabled );
139 }
140 
141 css::uno::Reference< css::awt::XWindow > SAL_CALL
142 PopupMenuToolbarController::createPopupWindow()
143     throw ( css::uno::RuntimeException )
144 {
145     css::uno::Reference< css::awt::XWindow > xRet;
146 
147     osl::MutexGuard aGuard( m_aMutex );
148     if ( !m_bHasController )
149         return xRet;
150 
151     createPopupMenuController();
152 
153     vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
154     ToolBox* pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
155     if ( !pToolBox )
156         return xRet;
157 
158     pToolBox->SetItemDown( m_nToolBoxId, sal_True );
159     sal_uInt16 nId = m_xPopupMenu->execute(
160         css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ),
161         VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ),
162         css::awt::PopupMenuDirection::EXECUTE_DEFAULT );
163     pToolBox->SetItemDown( m_nToolBoxId, sal_False );
164 
165     if ( nId )
166         functionExecuted( m_xPopupMenu->getCommand( nId ) );
167 
168     return xRet;
169 }
170 
171 void PopupMenuToolbarController::functionExecuted( const OUString &/*rCommand*/)
172 {
173 }
174 
175 sal_uInt16 PopupMenuToolbarController::getDropDownStyle() const
176 {
177     return TIB_DROPDOWN;
178 }
179 
180 void PopupMenuToolbarController::createPopupMenuController()
181 {
182     if( !m_bHasController )
183         return;
184 
185     if ( !m_xPopupMenuController.is() )
186     {
187         css::uno::Sequence< css::uno::Any > aArgs( 2 );
188         css::beans::PropertyValue aProp;
189 
190         aProp.Name = DECLARE_ASCII( "Frame" );
191         aProp.Value <<= m_xFrame;
192         aArgs[0] <<= aProp;
193 
194         aProp.Name = DECLARE_ASCII( "ModuleIdentifier" );
195         aProp.Value <<= getModuleName();
196         aArgs[1] <<= aProp;
197         try
198         {
199             m_xPopupMenu.set(
200                 m_xContext->getServiceManager()->createInstanceWithContext(
201                     DECLARE_ASCII( "com.sun.star.awt.PopupMenu" ), m_xContext ),
202                         css::uno::UNO_QUERY_THROW );
203             m_xPopupMenuController.set(
204                 m_xPopupMenuFactory->createInstanceWithArgumentsAndContext(
205                     m_aPopupCommand, aArgs, m_xContext), css::uno::UNO_QUERY_THROW );
206 
207             m_xPopupMenuController->setPopupMenu( m_xPopupMenu );
208         }
209         catch ( const css::uno::Exception &e )
210         {
211             m_xPopupMenu.clear();
212             OSL_TRACE( "PopupMenuToolbarController - caught an exception! %s",
213                        rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
214             (void) e;
215         }
216     }
217 }
218 
219 DEFINE_XSERVICEINFO_MULTISERVICE_2( WizardsToolbarController,
220                                     ::cppu::OWeakObject,
221                                     DECLARE_ASCII("com.sun.star.frame.ToolbarController"),
222                                     DECLARE_ASCII("org.apache.openoffice.comp.framework.WizardsToolbarController")
223                                    )
224 
225 DEFINE_INIT_SERVICE( WizardsToolbarController, {} )
226 
227 WizardsToolbarController::WizardsToolbarController(
228     const css::uno::Reference< css::uno::XComponentContext >& xContext )
229     : PopupMenuToolbarController( xContext )
230 {
231 }
232 
233 sal_uInt16 WizardsToolbarController::getDropDownStyle() const
234 {
235     return TIB_DROPDOWNONLY;
236 }
237 
238 DEFINE_XSERVICEINFO_MULTISERVICE_2( OpenToolbarController,
239                                     ::cppu::OWeakObject,
240                                     DECLARE_ASCII("com.sun.star.frame.ToolbarController"),
241                                     DECLARE_ASCII("org.apache.openoffice.comp.framework.OpenToolbarController")
242                                    )
243 
244 DEFINE_INIT_SERVICE( OpenToolbarController, {} )
245 
246 OpenToolbarController::OpenToolbarController(
247     const css::uno::Reference< css::uno::XComponentContext >& xContext )
248     : PopupMenuToolbarController( xContext, DECLARE_ASCII( UNO_COMMAND_RECENT_FILE_LIST ) )
249 {
250 }
251 
252 
253 DEFINE_XSERVICEINFO_MULTISERVICE_2( NewToolbarController,
254                                     ::cppu::OWeakObject,
255                                     DECLARE_ASCII("com.sun.star.frame.ToolbarController"),
256                                     DECLARE_ASCII("org.apache.openoffice.comp.framework.NewToolbarController")
257                                    )
258 
259 DEFINE_INIT_SERVICE( NewToolbarController, {} )
260 
261 NewToolbarController::NewToolbarController(
262     const css::uno::Reference< css::uno::XComponentContext >& xContext )
263     : PopupMenuToolbarController( xContext )
264 {
265 }
266 
267 void SAL_CALL
268 NewToolbarController::statusChanged(
269     const css::frame::FeatureStateEvent& rEvent )
270     throw ( css::uno::RuntimeException )
271 {
272     if ( rEvent.IsEnabled )
273     {
274         OUString aState;
275         rEvent.State >>= aState;
276         // set the image even if the state is not a string
277         // this will set the image of the default module
278         setItemImage( aState );
279     }
280 
281     enable( rEvent.IsEnabled );
282 }
283 
284 void SAL_CALL
285 NewToolbarController::execute( sal_Int16 /*KeyModifier*/ )
286     throw ( css::uno::RuntimeException )
287 {
288     osl::MutexGuard aGuard( m_aMutex );
289     if ( !m_aLastURL.getLength() )
290         return;
291 
292     OUString aTarget( RTL_CONSTASCII_USTRINGPARAM( "_default" ) );
293     if ( m_xPopupMenu.is() )
294     {
295         // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu
296         MenuConfiguration::Attributes* pMenuAttributes( 0 );
297         VCLXPopupMenu*  pTkPopupMenu = dynamic_cast< VCLXPopupMenu * >(
298             VCLXMenu::GetImplementation( m_xPopupMenu ) );
299 
300         vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
301         PopupMenu* pVCLPopupMenu = dynamic_cast< PopupMenu * >( pTkPopupMenu->GetMenu() );
302         if ( pVCLPopupMenu )
303             pMenuAttributes = reinterpret_cast< MenuConfiguration::Attributes* >(
304                 pVCLPopupMenu->GetUserValue( pVCLPopupMenu->GetCurItemId() ) );
305 
306         if ( pMenuAttributes )
307             aTarget = pMenuAttributes->aTargetFrame;
308     }
309 
310     css::uno::Sequence< css::beans::PropertyValue > aArgs( 1 );
311     aArgs[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer"  ));
312     aArgs[0].Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM( SFX_REFERER_USER ) );
313 
314     dispatchCommand( m_aLastURL, aArgs, aTarget );
315 }
316 
317 void NewToolbarController::functionExecuted( const OUString &rCommand )
318 {
319     setItemImage( rCommand );
320 }
321 
322 /**
323     it return the existing state of the given URL in the popupmenu of this toolbox control.
324 
325     If the given URL can be located as an action command of one menu item of the
326     popup menu of this control, we return sal_True. Otherwhise we return sal_False.
327     Further we return a fallback URL, in case we have to return sal_False. Because
328     the outside code must select a valid item of the popup menu everytime ...
329     and we define it here. By the way this m ethod was written to handle
330     error situations gracefully. E.g. it can be called during creation time
331     but then we have no valid menu. For this case we know another fallback URL.
332     Then we return the private:factory/ URL of the default factory.
333 
334     @param  rPopupMenu
335                 pounts to the popup menu, on which item we try to locate the given URL
336                 Can be NULL! Search will be supressed then.
337 
338     @param  sURL
339                 the URL for searching
340 
341     @param  sFallback
342                 contains the fallback URL in case we return FALSE
343                 Must point to valid memory!
344 
345     @param  aImage
346                 contains the image of the menu for the URL.
347 
348     @return sal_True - if URL could be located as an item of the popup menu.
349             sal_False - otherwhise.
350 */
351 static sal_Bool Impl_ExistURLInMenu(
352     const css::uno::Reference< css::awt::XPopupMenu > &rPopupMenu,
353     OUString &sURL,
354     OUString &sFallback,
355     Image &aImage )
356 {
357     sal_Bool bValidFallback( sal_False );
358     sal_uInt16 nCount( 0 );
359     if ( rPopupMenu.is() && ( nCount = rPopupMenu->getItemCount() ) != 0 && sURL.getLength() )
360     {
361         for ( sal_uInt16 n = 0; n < nCount; ++n )
362         {
363             sal_uInt16 nId = rPopupMenu->getItemId( n );
364             OUString aCmd( rPopupMenu->getCommand( nId ) );
365 
366             if ( !bValidFallback && aCmd.getLength() )
367             {
368                 sFallback = aCmd;
369                 bValidFallback = sal_True;
370             }
371 
372             // match even if the menu command is more detailed
373             // (maybe an additional query) #i28667#
374             if ( aCmd.match( sURL ) )
375             {
376                 sURL = aCmd;
377                 const css::uno::Reference< css::graphic::XGraphic > xGraphic(
378                     rPopupMenu->getItemImage( nId ) );
379                 if ( xGraphic.is() )
380                     aImage = Image( xGraphic );
381                 return sal_True;
382             }
383         }
384     }
385 
386     if ( !bValidFallback )
387     {
388         rtl::OUStringBuffer aBuffer;
389         aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "private:factory/" ) );
390         aBuffer.append( SvtModuleOptions().GetDefaultModuleName() );
391         sFallback = aBuffer.makeStringAndClear();
392     }
393 
394     return sal_False;
395 }
396 
397 /** We accept URL's here only, which exist as items of our internal popup menu.
398     All other ones will be ignored and a fallback is used.
399  */
400 void NewToolbarController::setItemImage( const OUString &rCommand )
401 {
402     vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
403     ToolBox* pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
404     if ( !pToolBox )
405         return;
406 
407     OUString aURL = rCommand;
408     OUString sFallback;
409     Image aMenuImage;
410 
411     sal_Bool bValid( Impl_ExistURLInMenu( m_xPopupMenu, aURL, sFallback, aMenuImage ) );
412     // do not change aURL if Impl_ExistURLInMenu returned sal_False
413     // this allows later initialization of the PopupMenuController on createPopupWindow()
414     // and works even if SvFileInformationManager does not know the module
415     if ( !aURL.getLength() )
416         aURL = sFallback;
417 
418     sal_Bool bBig = SvtMiscOptions().AreCurrentSymbolsLarge();
419     sal_Bool bHC = pToolBox->GetSettings().GetStyleSettings().GetHighContrastMode();
420 
421     INetURLObject aURLObj( aURL );
422     Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj, bBig, bHC );
423     if ( !aImage )
424     {
425         if ( !!aMenuImage )
426             aImage =  aMenuImage;
427         else if ( !bValid )
428             // If SvFileInformationManager didn't know the module, try with the default
429             aImage = SvFileInformationManager::GetImageNoDefault( INetURLObject( sFallback ), bBig, bHC );
430 
431         if ( !aImage )
432             aImage = SvFileInformationManager::GetImage( aURLObj, bBig, bHC );
433     }
434 
435     // if everything failed, just use the image associated with the toolbar item command
436     if ( !aImage )
437         return;
438 
439     Size aBigSize( pToolBox->GetDefaultImageSize() );
440     if ( bBig && aImage.GetSizePixel() != aBigSize )
441     {
442         BitmapEx aScaleBmpEx( aImage.GetBitmapEx() );
443         aScaleBmpEx.Scale( aBigSize, BMP_SCALE_INTERPOLATE );
444         pToolBox->SetItemImage( m_nToolBoxId, Image( aScaleBmpEx ) );
445     }
446     else
447         pToolBox->SetItemImage( m_nToolBoxId, aImage );
448 
449     m_aLastURL = aURL;
450 }
451 
452 
453 }
454