1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_framework.hxx"
30 #include <framework/actiontriggerhelper.hxx>
31 #include <classes/actiontriggerseparatorpropertyset.hxx>
32 #include <classes/rootactiontriggercontainer.hxx>
33 #include <classes/imagewrapper.hxx>
34 #include <framework/addonsoptions.hxx>
35 #include <com/sun/star/lang/XServiceInfo.hpp>
36 #include <com/sun/star/beans/XPropertySet.hpp>
37 #include <com/sun/star/awt/XBitmap.hpp>
38 #include <vcl/svapp.hxx>
39 #include <vos/mutex.hxx>
40 #include <tools/stream.hxx>
41 #include <cppuhelper/weak.hxx>
42 #include <comphelper/processfactory.hxx>
43 
44 
45 const sal_uInt16 START_ITEMID = 1000;
46 
47 using namespace rtl;
48 using namespace vos;
49 using namespace com::sun::star::awt;
50 using namespace com::sun::star::uno;
51 using namespace com::sun::star::lang;
52 using namespace com::sun::star::beans;
53 using namespace com::sun::star::container;
54 
55 namespace framework
56 {
57 
58 // ----------------------------------------------------------------------------
59 // implementation helper ( menu => ActionTrigger )
60 // ----------------------------------------------------------------------------
61 
62 sal_Bool IsSeparator( Reference< XPropertySet > xPropertySet )
63 {
64 	Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
65 	try
66 	{
67 		return xServiceInfo->supportsService( OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICENAME_ACTIONTRIGGERSEPARATOR )) );
68 	}
69 	catch ( Exception& )
70 	{
71 	}
72 
73 	return sal_False;
74 }
75 
76 void GetMenuItemAttributes( Reference< XPropertySet > xActionTriggerPropertySet,
77 							OUString& aMenuLabel,
78 							OUString& aCommandURL,
79 							OUString& aHelpURL,
80 							Reference< XBitmap >& xBitmap,
81 							Reference< XIndexContainer >& xSubContainer )
82 {
83 	Any a;
84 
85 	try
86 	{
87 		// mandatory properties
88 		a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" )) );
89 		a >>= aMenuLabel;
90 		a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CommandURL" )) );
91 		a >>= aCommandURL;
92 		a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Image" )) );
93 		a >>= xBitmap;
94 		a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SubContainer" )) );
95 		a >>= xSubContainer;
96 	}
97 	catch ( Exception& )
98 	{
99 	}
100 
101 	// optional properties
102 	try
103 	{
104 		a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpURL" )) );
105 		a >>= aHelpURL;
106 	}
107 	catch ( Exception& )
108 	{
109 	}
110 }
111 
112 void InsertSubMenuItems( Menu* pSubMenu, sal_uInt16& nItemId, Reference< XIndexContainer > xActionTriggerContainer )
113 {
114 	Reference< XIndexAccess > xIndexAccess( xActionTriggerContainer, UNO_QUERY );
115 	if ( xIndexAccess.is() )
116 	{
117         AddonsOptions aAddonOptions;
118 	    const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
119 	    sal_Bool bHiContrast = rSettings.GetHighContrastMode();
120 
121         OUString aSlotURL( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));
122 
123 		for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); i++ )
124 		{
125 			try
126 			{
127 				Reference< XPropertySet > xPropSet;
128 				if (( xIndexAccess->getByIndex( i ) >>= xPropSet ) && ( xPropSet.is() ))
129 				{
130 					if ( IsSeparator( xPropSet ))
131 					{
132 						// Separator
133 						OGuard aGuard( Application::GetSolarMutex() );
134 						pSubMenu->InsertSeparator();
135 					}
136 					else
137 					{
138 						// Menu item
139 						OUString aLabel;
140 						OUString aCommandURL;
141 						OUString aHelpURL;
142 						Reference< XBitmap > xBitmap;
143 						Reference< XIndexContainer > xSubContainer;
144 						sal_Bool bSpecialItemId = sal_False;
145 
146 						sal_uInt16 nNewItemId = nItemId++;
147 						GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer );
148 
149 						OGuard aGuard( Application::GetSolarMutex() );
150 						{
151 							// insert new menu item
152 							sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
153 							if ( nIndex >= 0 )
154 							{
155 								// Special code for our menu implementation: some menu items don't have a
156 								// command url but uses the item id as a unqiue identifier. These entries
157 								// got a special url during conversion from menu=>actiontriggercontainer.
158 								// Now we have to extract this special url and set the correct item id!!!
159 								bSpecialItemId = sal_True;
160 								nNewItemId = (sal_uInt16)aCommandURL.copy( nIndex+aSlotURL.getLength() ).toInt32();
161 								pSubMenu->InsertItem( nNewItemId, aLabel );
162 							}
163 							else
164 							{
165 								pSubMenu->InsertItem( nNewItemId, aLabel );
166 								pSubMenu->SetItemCommand( nNewItemId, aCommandURL );
167 							}
168 
169 							// handle bitmap
170 							if ( xBitmap.is() )
171 							{
172 								sal_Bool bImageSet = sal_False;
173 
174 								Reference< XUnoTunnel > xUnoTunnel( xBitmap, UNO_QUERY );
175 								if ( xUnoTunnel.is() )
176 								{
177 									// Try to get implementation pointer through XUnoTunnel
178 									sal_Int64 nPointer = xUnoTunnel->getSomething( ImageWrapper::GetUnoTunnelId() );
179 									if ( nPointer )
180 									{
181 										// This is our own optimized implementation of menu images!
182 										ImageWrapper* pImageWrapper = reinterpret_cast< ImageWrapper * >( nPointer );
183 										Image aMenuImage = pImageWrapper->GetImage();
184 
185 										if ( !!aMenuImage )
186 											pSubMenu->SetItemImage( nNewItemId, aMenuImage );
187 
188 										bImageSet = sal_True;
189 									}
190 								}
191 
192 								if ( !bImageSet )
193 								{
194 									// This is an unknown implementation of a XBitmap interface. We have to
195 									// use a more time consuming way to build an Image!
196 									Image	aImage;
197 									Bitmap	aBitmap;
198 
199 									Sequence< sal_Int8 > aDIBSeq;
200 									{
201 										aDIBSeq = xBitmap->getDIB();
202 										SvMemoryStream aMem( (void *)aDIBSeq.getConstArray(), aDIBSeq.getLength(), STREAM_READ );
203 										aMem >> aBitmap;
204 									}
205 
206 									aDIBSeq = xBitmap->getMaskDIB();
207 									if ( aDIBSeq.getLength() > 0 )
208 									{
209 										Bitmap aMaskBitmap;
210 										SvMemoryStream aMem( (void *)aDIBSeq.getConstArray(), aDIBSeq.getLength(), STREAM_READ );
211 										aMem >> aMaskBitmap;
212 										aImage = Image( aBitmap, aMaskBitmap );
213 									}
214 									else
215 										aImage = Image( aBitmap );
216 
217                                     if ( !!aImage )
218 										pSubMenu->SetItemImage( nNewItemId, aImage );
219 								}
220 							}
221                             else
222                             {
223                                 // Support add-on images for context menu interceptors
224                                 Image aImage = aAddonOptions.GetImageFromURL( aCommandURL, sal_False, bHiContrast, sal_True );
225                                 if ( !!aImage )
226                                     pSubMenu->SetItemImage( nNewItemId, aImage );
227                             }
228 
229 							if ( xSubContainer.is() )
230 							{
231 								PopupMenu* pNewSubMenu = new PopupMenu;
232 
233 								// Sub menu (recursive call CreateSubMenu )
234 								InsertSubMenuItems( pNewSubMenu, nItemId, xSubContainer );
235 								pSubMenu->SetPopupMenu( nNewItemId, pNewSubMenu );
236 							}
237 						}
238 					}
239 				}
240 			}
241 			catch ( IndexOutOfBoundsException )
242 			{
243 				return;
244 			}
245 			catch ( WrappedTargetException )
246 			{
247 				return;
248 			}
249 			catch ( RuntimeException )
250 			{
251 				return;
252 			}
253 		}
254 	}
255 }
256 
257 
258 // ----------------------------------------------------------------------------
259 // implementation helper ( ActionTrigger => menu )
260 // ----------------------------------------------------------------------------
261 
262 Reference< XPropertySet > CreateActionTrigger( sal_uInt16 nItemId, const Menu* pMenu, const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
263 {
264 	Reference< XPropertySet > xPropSet;
265 
266 	Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
267 	if ( xMultiServiceFactory.is() )
268 	{
269 		xPropSet = Reference< XPropertySet >(	xMultiServiceFactory->createInstance(
270 													OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTrigger" )) ),
271 												UNO_QUERY );
272 
273 		Any a;
274 
275 		try
276 		{
277 			// Retrieve the menu attributes and set them in our PropertySet
278 			OUString aLabel = pMenu->GetItemText( nItemId );
279 			a <<= aLabel;
280 			xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" )), a );
281 
282 			OUString aCommandURL = pMenu->GetItemCommand( nItemId );
283 
284 			if ( aCommandURL.getLength() == 0 )
285 			{
286 				aCommandURL = OUString( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));
287 				aCommandURL += OUString::valueOf( (sal_Int32)nItemId );
288 			}
289 
290 			a <<= aCommandURL;
291 			xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CommandURL" )), a );
292 
293 			Image aImage = pMenu->GetItemImage( nItemId );
294 			if ( !!aImage )
295 			{
296 				// We use our own optimized XBitmap implementation
297 				Reference< XBitmap > xBitmap( static_cast< cppu::OWeakObject* >( new ImageWrapper( aImage )), UNO_QUERY );
298 				a <<= xBitmap;
299 				xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Image" )), a );
300 			}
301 		}
302 		catch ( Exception& )
303 		{
304 		}
305 	}
306 
307 	return xPropSet;
308 }
309 
310 Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
311 {
312 	Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
313 	if ( xMultiServiceFactory.is() )
314 	{
315 		return Reference< XPropertySet >(	xMultiServiceFactory->createInstance(
316 												OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTriggerSeparator" )) ),
317 											UNO_QUERY );
318 	}
319 
320 	return Reference< XPropertySet >();
321 }
322 
323 Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
324 {
325 	Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
326 	if ( xMultiServiceFactory.is() )
327 	{
328 		return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
329 												OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTriggerContainer" )) ),
330 											 UNO_QUERY );
331 	}
332 
333 	return Reference< XIndexContainer >();
334 }
335 
336 void FillActionTriggerContainerWithMenu( const Menu* pMenu, Reference< XIndexContainer >& rActionTriggerContainer )
337 {
338 	OGuard aGuard( Application::GetSolarMutex() );
339 
340 	for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
341 	{
342 		sal_uInt16			nItemId = pMenu->GetItemId( nPos );
343 		MenuItemType	nType	= pMenu->GetItemType( nPos );
344 
345 		try
346 		{
347 			Any a;
348 			Reference< XPropertySet > xPropSet;
349 
350 			if ( nType == MENUITEM_SEPARATOR )
351 			{
352 				xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );
353 
354 				a <<= xPropSet;
355 				rActionTriggerContainer->insertByIndex( nPos, a );
356 			}
357 			else
358 			{
359 				xPropSet = CreateActionTrigger( nItemId, pMenu, rActionTriggerContainer );
360 
361 				a <<= xPropSet;
362 				rActionTriggerContainer->insertByIndex( nPos, a );
363 
364 				PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nItemId );
365 				if ( pPopupMenu )
366 				{
367 					// recursive call to build next sub menu
368 					Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );
369 
370 					a <<= xSubContainer;
371 					xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SubContainer" )), a );
372 					FillActionTriggerContainerWithMenu( pPopupMenu, xSubContainer );
373 				}
374 			}
375 		}
376 		catch ( Exception& )
377 		{
378 		}
379 	}
380 }
381 
382 void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
383 	Menu* pNewMenu,
384 	const Reference< XIndexContainer >& rActionTriggerContainer )
385 {
386 	sal_uInt16 nItemId = START_ITEMID;
387 
388 	if ( rActionTriggerContainer.is() )
389 		InsertSubMenuItems( pNewMenu, nItemId, rActionTriggerContainer );
390 }
391 
392 void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
393 	Reference< XIndexContainer >& xActionTriggerContainer,
394 	const Menu* pMenu )
395 {
396 	FillActionTriggerContainerWithMenu( pMenu, xActionTriggerContainer );
397 }
398 
399 Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
400 	// #110897#
401 	const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory,
402     const Menu* pMenu,
403     const ::rtl::OUString* pMenuIdentifier )
404 {
405     return new RootActionTriggerContainer( pMenu, pMenuIdentifier, xServiceFactory );
406 }
407 
408 }
409