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     WindowAlign eAlign( pToolBox->GetAlign() );
160     sal_uInt16 nId = m_xPopupMenu->execute(
161         css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ),
162         VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ),
163         ( eAlign == WINDOWALIGN_TOP || eAlign == WINDOWALIGN_BOTTOM ) ?
164             css::awt::PopupMenuDirection::EXECUTE_DOWN :
165             css::awt::PopupMenuDirection::EXECUTE_RIGHT );
166     pToolBox->SetItemDown( m_nToolBoxId, sal_False );
167 
168     if ( nId )
169         functionExecuted( m_xPopupMenu->getCommand( nId ) );
170 
171     return xRet;
172 }
173 
174 void PopupMenuToolbarController::functionExecuted( const OUString &/*rCommand*/)
175 {
176 }
177 
178 sal_uInt16 PopupMenuToolbarController::getDropDownStyle() const
179 {
180     return TIB_DROPDOWN;
181 }
182 
183 void PopupMenuToolbarController::createPopupMenuController()
184 {
185     if( !m_bHasController )
186         return;
187 
188     if ( !m_xPopupMenuController.is() )
189     {
190         css::uno::Sequence< css::uno::Any > aArgs( 2 );
191         css::beans::PropertyValue aProp;
192 
193         aProp.Name = DECLARE_ASCII( "Frame" );
194         aProp.Value <<= m_xFrame;
195         aArgs[0] <<= aProp;
196 
197         aProp.Name = DECLARE_ASCII( "ModuleIdentifier" );
198         aProp.Value <<= getModuleName();
199         aArgs[1] <<= aProp;
200         try
201         {
202             m_xPopupMenu.set(
203                 m_xContext->getServiceManager()->createInstanceWithContext(
204                     DECLARE_ASCII( "com.sun.star.awt.PopupMenu" ), m_xContext ),
205                         css::uno::UNO_QUERY_THROW );
206             m_xPopupMenuController.set(
207                 m_xPopupMenuFactory->createInstanceWithArgumentsAndContext(
208                     m_aPopupCommand, aArgs, m_xContext), css::uno::UNO_QUERY_THROW );
209 
210             m_xPopupMenuController->setPopupMenu( m_xPopupMenu );
211         }
212         catch ( const css::uno::Exception &e )
213         {
214             m_xPopupMenu.clear();
215             OSL_TRACE( "PopupMenuToolbarController - caught an exception! %s",
216                        rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
217             (void) e;
218         }
219     }
220 }
221 
222 DEFINE_XSERVICEINFO_MULTISERVICE_2( WizardsToolbarController,
223                                     ::cppu::OWeakObject,
224                                     DECLARE_ASCII("com.sun.star.frame.ToolbarController"),
225                                     DECLARE_ASCII("org.apache.openoffice.comp.framework.WizardsToolbarController")
226                                    )
227 
228 DEFINE_INIT_SERVICE( WizardsToolbarController, {} )
229 
230 WizardsToolbarController::WizardsToolbarController(
231     const css::uno::Reference< css::uno::XComponentContext >& xContext )
232     : PopupMenuToolbarController( xContext )
233 {
234 }
235 
236 sal_uInt16 WizardsToolbarController::getDropDownStyle() const
237 {
238     return TIB_DROPDOWNONLY;
239 }
240 
241 DEFINE_XSERVICEINFO_MULTISERVICE_2( OpenToolbarController,
242                                     ::cppu::OWeakObject,
243                                     DECLARE_ASCII("com.sun.star.frame.ToolbarController"),
244                                     DECLARE_ASCII("org.apache.openoffice.comp.framework.OpenToolbarController")
245                                    )
246 
247 DEFINE_INIT_SERVICE( OpenToolbarController, {} )
248 
249 OpenToolbarController::OpenToolbarController(
250     const css::uno::Reference< css::uno::XComponentContext >& xContext )
251     : PopupMenuToolbarController( xContext, DECLARE_ASCII( UNO_COMMAND_RECENT_FILE_LIST ) )
252 {
253 }
254 
255 
256 DEFINE_XSERVICEINFO_MULTISERVICE_2( NewToolbarController,
257                                     ::cppu::OWeakObject,
258                                     DECLARE_ASCII("com.sun.star.frame.ToolbarController"),
259                                     DECLARE_ASCII("org.apache.openoffice.comp.framework.NewToolbarController")
260                                    )
261 
262 DEFINE_INIT_SERVICE( NewToolbarController, {} )
263 
264 NewToolbarController::NewToolbarController(
265     const css::uno::Reference< css::uno::XComponentContext >& xContext )
266     : PopupMenuToolbarController( xContext )
267 {
268 }
269 
270 void SAL_CALL
271 NewToolbarController::initialize(
272     const css::uno::Sequence< css::uno::Any >& aArguments )
273 throw ( css::uno::Exception, css::uno::RuntimeException )
274 {
275     PopupMenuToolbarController::initialize( aArguments );
276 
277     osl::MutexGuard aGuard( m_aMutex );
278     createPopupMenuController();
279 }
280 
281 void SAL_CALL
282 NewToolbarController::statusChanged(
283     const css::frame::FeatureStateEvent& rEvent )
284     throw ( css::uno::RuntimeException )
285 {
286     if ( rEvent.IsEnabled )
287     {
288         OUString aState;
289         rEvent.State >>= aState;
290         // set the image even if the state is not a string
291         // this will set the image of the default module
292         setItemImage( aState );
293     }
294 
295     enable( rEvent.IsEnabled );
296 }
297 
298 void SAL_CALL
299 NewToolbarController::execute( sal_Int16 /*KeyModifier*/ )
300     throw ( css::uno::RuntimeException )
301 {
302     osl::MutexGuard aGuard( m_aMutex );
303     if ( !m_aLastURL.getLength() )
304         return;
305 
306     OUString aTarget( RTL_CONSTASCII_USTRINGPARAM( "_default" ) );
307     if ( m_xPopupMenu.is() )
308     {
309         // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu
310         MenuConfiguration::Attributes* pMenuAttributes( 0 );
311         VCLXPopupMenu*  pTkPopupMenu =
312             ( VCLXPopupMenu * ) VCLXMenu::GetImplementation( m_xPopupMenu );
313 
314         vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
315         PopupMenu* pVCLPopupMenu = dynamic_cast< PopupMenu * >( pTkPopupMenu->GetMenu() );
316         if ( pVCLPopupMenu )
317             pMenuAttributes = reinterpret_cast< MenuConfiguration::Attributes* >(
318                 pVCLPopupMenu->GetUserValue( pVCLPopupMenu->GetCurItemId() ) );
319 
320         if ( pMenuAttributes )
321             aTarget = pMenuAttributes->aTargetFrame;
322     }
323 
324     css::uno::Sequence< css::beans::PropertyValue > aArgs( 1 );
325     aArgs[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer"  ));
326     aArgs[0].Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM( SFX_REFERER_USER ) );
327 
328     dispatchCommand( m_aLastURL, aArgs, aTarget );
329 }
330 
331 void NewToolbarController::functionExecuted( const OUString &rCommand )
332 {
333     setItemImage( rCommand );
334 }
335 
336 /**
337     it return the existing state of the given URL in the popupmenu of this toolbox control.
338 
339     If the given URL can be located as an action command of one menu item of the
340     popup menu of this control, we return sal_True. Otherwise we return sal_False.
341     Further we return a fallback URL, in case we have to return sal_False. Because
342     the outside code must select a valid item of the popup menu every time ...
343     and we define it here. By the way this method was written to handle
344     error situations gracefully. E.g. it can be called during creation time
345     but then we have no valid menu. For this case we know another fallback URL.
346     Then we return the private:factory/ URL of the default factory.
347 
348     @param  rPopupMenu
349                 pounts to the popup menu, on which item we try to locate the given URL
350                 Can be NULL! Search will be suppressed then.
351 
352     @param  sURL
353                 the URL for searching
354 
355     @param  sFallback
356                 contains the fallback URL in case we return FALSE
357                 Must point to valid memory!
358 
359     @param  aImage
360                 contains the image of the menu for the URL.
361 
362     @return sal_True - if URL could be located as an item of the popup menu.
363             sal_False - otherwise.
364 */
365 static sal_Bool Impl_ExistURLInMenu(
366     const css::uno::Reference< css::awt::XPopupMenu > &rPopupMenu,
367     OUString &sURL,
368     OUString &sFallback,
369     Image &aImage )
370 {
371     sal_Bool bValidFallback( sal_False );
372     sal_uInt16 nCount( 0 );
373     if ( rPopupMenu.is() && ( nCount = rPopupMenu->getItemCount() ) != 0 && sURL.getLength() )
374     {
375         for ( sal_uInt16 n = 0; n < nCount; ++n )
376         {
377             sal_uInt16 nId = rPopupMenu->getItemId( n );
378             OUString aCmd( rPopupMenu->getCommand( nId ) );
379 
380             if ( !bValidFallback && aCmd.getLength() )
381             {
382                 sFallback = aCmd;
383                 bValidFallback = sal_True;
384             }
385 
386             // match even if the menu command is more detailed
387             // (maybe an additional query) #i28667#
388             if ( aCmd.match( sURL ) )
389             {
390                 sURL = aCmd;
391                 const css::uno::Reference< css::graphic::XGraphic > xGraphic(
392                     rPopupMenu->getItemImage( nId ) );
393                 if ( xGraphic.is() )
394                     aImage = Image( xGraphic );
395                 return sal_True;
396             }
397         }
398     }
399 
400     if ( !bValidFallback )
401     {
402         rtl::OUStringBuffer aBuffer;
403         aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "private:factory/" ) );
404         aBuffer.append( SvtModuleOptions().GetDefaultModuleName() );
405         sFallback = aBuffer.makeStringAndClear();
406     }
407 
408     return sal_False;
409 }
410 
411 /** We accept URL's here only, which exist as items of our internal popup menu.
412     All other ones will be ignored and a fallback is used.
413  */
414 void NewToolbarController::setItemImage( const OUString &rCommand )
415 {
416     vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
417     ToolBox* pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
418     if ( !pToolBox )
419         return;
420 
421     OUString aURL = rCommand;
422     OUString sFallback;
423     Image aMenuImage;
424 
425     sal_Bool bValid( Impl_ExistURLInMenu( m_xPopupMenu, aURL, sFallback, aMenuImage ) );
426     if ( !bValid )
427         aURL = sFallback;
428 
429     sal_Bool bBig = SvtMiscOptions().AreCurrentSymbolsLarge();
430     sal_Bool bHC = pToolBox->GetSettings().GetStyleSettings().GetHighContrastMode();
431 
432     INetURLObject aURLObj( aURL );
433     Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj, bBig, bHC );
434     if ( !aImage )
435         aImage = !!aMenuImage ?
436             aMenuImage :
437             SvFileInformationManager::GetImage( aURLObj, bBig, bHC );
438 
439     // if everything failed, just use the image associated with the toolbar item command
440     if ( !aImage )
441         return;
442 
443     Size aBigSize( pToolBox->GetDefaultImageSize() );
444     if ( bBig && aImage.GetSizePixel() != aBigSize )
445     {
446         BitmapEx aScaleBmpEx( aImage.GetBitmapEx() );
447         aScaleBmpEx.Scale( aBigSize, BMP_SCALE_INTERPOLATE );
448         pToolBox->SetItemImage( m_nToolBoxId, Image( aScaleBmpEx ) );
449     }
450     else
451         pToolBox->SetItemImage( m_nToolBoxId, aImage );
452 
453     m_aLastURL = aURL;
454 }
455 
456 
457 }
458