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