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