Avoid potential negative array index access to cached text.
[LibreOffice.git] / framework / source / fwe / helper / actiontriggerhelper.cxx
blob88edd70beb795d5e385428b710fc47c216a578f3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <framework/actiontriggerhelper.hxx>
21 #include <classes/actiontriggerseparatorpropertyset.hxx>
22 #include <classes/rootactiontriggercontainer.hxx>
23 #include <framework/addonsoptions.hxx>
24 #include <com/sun/star/awt/XBitmap.hpp>
25 #include <com/sun/star/awt/XPopupMenu.hpp>
26 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
28 #include <com/sun/star/lang/XServiceInfo.hpp>
29 #include <toolkit/awt/vclxmenu.hxx>
30 #include <tools/stream.hxx>
31 #include <vcl/dibtools.hxx>
32 #include <vcl/graph.hxx>
33 #include <vcl/svapp.hxx>
34 #include <o3tl/string_view.hxx>
36 const sal_uInt16 START_ITEMID = 1000;
38 using namespace com::sun::star::awt;
39 using namespace com::sun::star::uno;
40 using namespace com::sun::star::lang;
41 using namespace com::sun::star::beans;
42 using namespace com::sun::star::container;
44 namespace framework
47 // implementation helper ( menu => ActionTrigger )
49 static bool IsSeparator( const Reference< XPropertySet >& xPropertySet )
51 Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
52 try
54 return xServiceInfo->supportsService( SERVICENAME_ACTIONTRIGGERSEPARATOR );
56 catch (const Exception&)
60 return false;
63 static void GetMenuItemAttributes( const Reference< XPropertySet >& xActionTriggerPropertySet,
64 OUString& aMenuLabel,
65 OUString& aCommandURL,
66 OUString& aHelpURL,
67 Reference< XBitmap >& xBitmap,
68 Reference< XIndexContainer >& xSubContainer )
70 Any a;
72 try
74 // mandatory properties
75 a = xActionTriggerPropertySet->getPropertyValue("Text");
76 a >>= aMenuLabel;
77 a = xActionTriggerPropertySet->getPropertyValue("CommandURL");
78 a >>= aCommandURL;
79 a = xActionTriggerPropertySet->getPropertyValue("Image");
80 a >>= xBitmap;
81 a = xActionTriggerPropertySet->getPropertyValue("SubContainer");
82 a >>= xSubContainer;
84 catch (const Exception&)
88 // optional properties
89 try
91 a = xActionTriggerPropertySet->getPropertyValue("HelpURL");
92 a >>= aHelpURL;
94 catch (const Exception&)
99 static void InsertSubMenuItems(const Reference<XPopupMenu>& rSubMenu, sal_uInt16& nItemId,
100 const Reference<XIndexContainer>& xActionTriggerContainer)
102 if ( !xActionTriggerContainer.is() )
103 return;
105 AddonsOptions aAddonOptions;
106 OUString aSlotURL( "slot:" );
108 for ( sal_Int32 i = 0; i < xActionTriggerContainer->getCount(); i++ )
112 Reference< XPropertySet > xPropSet;
113 if (( xActionTriggerContainer->getByIndex( i ) >>= xPropSet ) && ( xPropSet.is() ))
115 if ( IsSeparator( xPropSet ))
117 // Separator
118 SolarMutexGuard aGuard;
119 rSubMenu->insertSeparator(i);
121 else
123 // Menu item
124 OUString aLabel;
125 OUString aCommandURL;
126 OUString aHelpURL;
127 Reference< XBitmap > xBitmap;
128 Reference< XIndexContainer > xSubContainer;
130 sal_uInt16 nNewItemId = nItemId++;
131 GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer );
133 SolarMutexGuard aGuard;
135 // insert new menu item
136 sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
137 if ( nIndex >= 0 )
139 // Special code for our menu implementation: some menu items don't have a
140 // command url but uses the item id as a unique identifier. These entries
141 // got a special url during conversion from menu=>actiontriggercontainer.
142 // Now we have to extract this special url and set the correct item id!!!
143 nNewItemId = static_cast<sal_uInt16>(o3tl::toInt32(aCommandURL.subView( nIndex+aSlotURL.getLength() )));
144 rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
146 else
148 rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
149 rSubMenu->setCommand(nNewItemId, aCommandURL);
152 // handle bitmap
153 if ( xBitmap.is() )
155 bool bImageSet = false;
157 Reference<css::graphic::XGraphic> xGraphic(xBitmap, UNO_QUERY);
158 if (xGraphic.is())
160 // we can take the optimized route if XGraphic is supported
161 rSubMenu->setItemImage(nNewItemId, xGraphic, false);
162 bImageSet = true;
165 if ( !bImageSet )
167 // This is an unknown implementation of a XBitmap interface. We have to
168 // use a more time consuming way to build an Image!
169 BitmapEx aBitmap;
171 Sequence< sal_Int8 > aDIBSeq;
173 aDIBSeq = xBitmap->getDIB();
174 SvMemoryStream aMem( const_cast<sal_Int8 *>(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ );
175 ReadDIBBitmapEx(aBitmap, aMem);
178 aDIBSeq = xBitmap->getMaskDIB();
179 if ( aDIBSeq.hasElements() )
181 Bitmap aMaskBitmap;
182 SvMemoryStream aMem( const_cast<sal_Int8 *>(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ );
183 ReadDIB(aMaskBitmap, aMem, true);
184 aBitmap = BitmapEx(aBitmap.GetBitmap(), aMaskBitmap);
187 if (!aBitmap.IsEmpty())
188 rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
191 else
193 // Support add-on images for context menu interceptors
194 BitmapEx aBitmap(aAddonOptions.GetImageFromURL(aCommandURL, false, true));
195 if (!aBitmap.IsEmpty())
196 rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
199 if ( xSubContainer.is() )
201 rtl::Reference xNewSubMenu(new VCLXPopupMenu);
203 // Sub menu (recursive call CreateSubMenu )
204 InsertSubMenuItems(xNewSubMenu, nItemId, xSubContainer);
205 rSubMenu->setPopupMenu(nNewItemId, xNewSubMenu);
211 catch (const IndexOutOfBoundsException&)
213 return;
215 catch (const WrappedTargetException&)
217 return;
219 catch (const RuntimeException&)
221 return;
226 // implementation helper ( ActionTrigger => menu )
228 /// @throws RuntimeException
229 static Reference< XPropertySet > CreateActionTrigger(sal_uInt16 nItemId,
230 const Reference<XPopupMenu>& rMenu,
231 const Reference<XIndexContainer>& rActionTriggerContainer)
233 Reference< XPropertySet > xPropSet;
235 Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
236 if ( xMultiServiceFactory.is() )
238 xPropSet.set( xMultiServiceFactory->createInstance( "com.sun.star.ui.ActionTrigger" ),
239 UNO_QUERY );
241 Any a;
245 // Retrieve the menu attributes and set them in our PropertySet
246 OUString aLabel = rMenu->getItemText(nItemId);
247 a <<= aLabel;
248 xPropSet->setPropertyValue("Text", a );
250 OUString aCommandURL = rMenu->getCommand(nItemId);
252 if ( aCommandURL.isEmpty() )
254 aCommandURL = "slot:" + OUString::number( nItemId );
257 a <<= aCommandURL;
258 xPropSet->setPropertyValue("CommandURL", a );
260 Reference<XBitmap> xBitmap(rMenu->getItemImage(nItemId), UNO_QUERY);
261 if (xBitmap.is())
263 a <<= xBitmap;
264 xPropSet->setPropertyValue("Image", a );
267 catch (const Exception&)
272 return xPropSet;
275 /// @throws RuntimeException
276 static Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer )
278 Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
279 if ( xMultiServiceFactory.is() )
281 return Reference< XPropertySet >( xMultiServiceFactory->createInstance(
282 "com.sun.star.ui.ActionTriggerSeparator" ),
283 UNO_QUERY );
286 return Reference< XPropertySet >();
289 /// @throws RuntimeException
290 static Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer )
292 Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
293 if ( xMultiServiceFactory.is() )
295 return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
296 "com.sun.star.ui.ActionTriggerContainer" ),
297 UNO_QUERY );
300 return Reference< XIndexContainer >();
303 static void FillActionTriggerContainerWithMenu(const Reference<XPopupMenu>& rMenu,
304 const Reference<XIndexContainer>& rActionTriggerContainer)
306 SolarMutexGuard aGuard;
308 for (sal_uInt16 nPos = 0, nCount = rMenu->getItemCount(); nPos < nCount; ++nPos)
310 sal_uInt16 nItemId = rMenu->getItemId(nPos);
311 css::awt::MenuItemType nType = rMenu->getItemType(nPos);
315 Any a;
316 Reference< XPropertySet > xPropSet;
318 if (nType == css::awt::MenuItemType_SEPARATOR)
320 xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );
322 a <<= xPropSet;
323 rActionTriggerContainer->insertByIndex( nPos, a );
325 else
327 xPropSet = CreateActionTrigger(nItemId, rMenu, rActionTriggerContainer);
329 a <<= xPropSet;
330 rActionTriggerContainer->insertByIndex( nPos, a );
332 css::uno::Reference<XPopupMenu> xPopupMenu = rMenu->getPopupMenu(nItemId);
333 if (xPopupMenu.is())
335 // recursive call to build next sub menu
336 Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );
338 a <<= xSubContainer;
339 xPropSet->setPropertyValue("SubContainer", a );
340 FillActionTriggerContainerWithMenu(xPopupMenu, xSubContainer);
344 catch (const Exception&)
350 void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
351 const Reference<XPopupMenu>& rNewMenu,
352 const Reference<XIndexContainer>& rActionTriggerContainer)
354 sal_uInt16 nItemId = START_ITEMID;
356 if ( rActionTriggerContainer.is() )
357 InsertSubMenuItems(rNewMenu, nItemId, rActionTriggerContainer);
360 void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
361 Reference< XIndexContainer > const & xActionTriggerContainer,
362 const css::uno::Reference<XPopupMenu>& rMenu)
364 FillActionTriggerContainerWithMenu(rMenu, xActionTriggerContainer);
367 Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
368 const css::uno::Reference<XPopupMenu>& rMenu,
369 const OUString* pMenuIdentifier )
371 return new RootActionTriggerContainer(rMenu, pMenuIdentifier);
376 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */