1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
47 // implementation helper ( menu => ActionTrigger )
49 static bool IsSeparator( const Reference
< XPropertySet
>& xPropertySet
)
51 Reference
< XServiceInfo
> xServiceInfo( xPropertySet
, UNO_QUERY
);
54 return xServiceInfo
->supportsService( SERVICENAME_ACTIONTRIGGERSEPARATOR
);
56 catch (const Exception
&)
63 static void GetMenuItemAttributes( const Reference
< XPropertySet
>& xActionTriggerPropertySet
,
65 OUString
& aCommandURL
,
67 Reference
< XBitmap
>& xBitmap
,
68 Reference
< XIndexContainer
>& xSubContainer
)
74 // mandatory properties
75 a
= xActionTriggerPropertySet
->getPropertyValue("Text");
77 a
= xActionTriggerPropertySet
->getPropertyValue("CommandURL");
79 a
= xActionTriggerPropertySet
->getPropertyValue("Image");
81 a
= xActionTriggerPropertySet
->getPropertyValue("SubContainer");
84 catch (const Exception
&)
88 // optional properties
91 a
= xActionTriggerPropertySet
->getPropertyValue("HelpURL");
94 catch (const Exception
&)
99 static void InsertSubMenuItems(const Reference
<XPopupMenu
>& rSubMenu
, sal_uInt16
& nItemId
,
100 const Reference
<XIndexContainer
>& xActionTriggerContainer
)
102 if ( !xActionTriggerContainer
.is() )
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
))
118 SolarMutexGuard aGuard
;
119 rSubMenu
->insertSeparator(i
);
125 OUString aCommandURL
;
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
);
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
);
148 rSubMenu
->insertItem(nNewItemId
, aLabel
, 0, i
);
149 rSubMenu
->setCommand(nNewItemId
, aCommandURL
);
155 bool bImageSet
= false;
157 Reference
<css::graphic::XGraphic
> xGraphic(xBitmap
, UNO_QUERY
);
160 // we can take the optimized route if XGraphic is supported
161 rSubMenu
->setItemImage(nNewItemId
, xGraphic
, false);
167 // This is an unknown implementation of a XBitmap interface. We have to
168 // use a more time consuming way to build an Image!
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() )
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);
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
&)
215 catch (const WrappedTargetException
&)
219 catch (const RuntimeException
&)
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" ),
245 // Retrieve the menu attributes and set them in our PropertySet
246 OUString aLabel
= rMenu
->getItemText(nItemId
);
248 xPropSet
->setPropertyValue("Text", a
);
250 OUString aCommandURL
= rMenu
->getCommand(nItemId
);
252 if ( aCommandURL
.isEmpty() )
254 aCommandURL
= "slot:" + OUString::number( nItemId
);
258 xPropSet
->setPropertyValue("CommandURL", a
);
260 Reference
<XBitmap
> xBitmap(rMenu
->getItemImage(nItemId
), UNO_QUERY
);
264 xPropSet
->setPropertyValue("Image", a
);
267 catch (const Exception
&)
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" ),
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" ),
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
);
316 Reference
< XPropertySet
> xPropSet
;
318 if (nType
== css::awt::MenuItemType_SEPARATOR
)
320 xPropSet
= CreateActionTriggerSeparator( rActionTriggerContainer
);
323 rActionTriggerContainer
->insertByIndex( nPos
, a
);
327 xPropSet
= CreateActionTrigger(nItemId
, rMenu
, rActionTriggerContainer
);
330 rActionTriggerContainer
->insertByIndex( nPos
, a
);
332 css::uno::Reference
<XPopupMenu
> xPopupMenu
= rMenu
->getPopupMenu(nItemId
);
335 // recursive call to build next sub menu
336 Reference
< XIndexContainer
> xSubContainer
= CreateActionTriggerContainer( rActionTriggerContainer
);
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: */