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