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