Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/fwe/helper/actiontriggerhelper.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
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/bitmap.hxx>
32
#include <vcl/dibtools.hxx>
33
#include <vcl/graph.hxx>
34
#include <vcl/graphic/BitmapHelper.hxx>
35
#include <vcl/svapp.hxx>
36
#include <o3tl/string_view.hxx>
37
38
const sal_uInt16 START_ITEMID = 1000;
39
40
using namespace com::sun::star::awt;
41
using namespace com::sun::star::uno;
42
using namespace com::sun::star::lang;
43
using namespace com::sun::star::beans;
44
using namespace com::sun::star::container;
45
46
namespace framework
47
{
48
49
// implementation helper ( menu => ActionTrigger )
50
51
static bool IsSeparator( const Reference< XPropertySet >& xPropertySet )
52
0
{
53
0
    Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
54
0
    try
55
0
    {
56
0
        return xServiceInfo->supportsService( SERVICENAME_ACTIONTRIGGERSEPARATOR );
57
0
    }
58
0
    catch (const Exception&)
59
0
    {
60
0
    }
61
62
0
    return false;
63
0
}
64
65
static void GetMenuItemAttributes( const Reference< XPropertySet >& xActionTriggerPropertySet,
66
                            OUString& aMenuLabel,
67
                            OUString& aCommandURL,
68
                            OUString& aHelpURL,
69
                            Reference<css::graphic::XGraphic>& xGraphic,
70
                            Reference< XIndexContainer >& xSubContainer )
71
0
{
72
0
    try
73
0
    {
74
        // mandatory properties
75
0
        xActionTriggerPropertySet->getPropertyValue(u"Text"_ustr) >>= aMenuLabel;
76
0
        xActionTriggerPropertySet->getPropertyValue(u"CommandURL"_ustr) >>= aCommandURL;
77
0
        xGraphic = vcl::GetGraphic(xActionTriggerPropertySet->getPropertyValue(u"Image"_ustr));
78
0
        xActionTriggerPropertySet->getPropertyValue(u"SubContainer"_ustr) >>= xSubContainer;
79
0
    }
80
0
    catch (const Exception&)
81
0
    {
82
0
    }
83
84
    // optional properties
85
0
    try
86
0
    {
87
0
        xActionTriggerPropertySet->getPropertyValue(u"HelpURL"_ustr) >>= aHelpURL;
88
0
    }
89
0
    catch (const Exception&)
90
0
    {
91
0
    }
92
0
}
93
94
static void InsertSubMenuItems(const Reference<XPopupMenu>& rSubMenu, sal_uInt16& nItemId,
95
                               const Reference<XIndexContainer>& xActionTriggerContainer)
96
0
{
97
0
    if ( !xActionTriggerContainer.is() )
98
0
        return;
99
100
0
    AddonsOptions aAddonOptions;
101
0
    OUString aSlotURL( u"slot:"_ustr );
102
103
0
    for ( sal_Int32 i = 0; i < xActionTriggerContainer->getCount(); i++ )
104
0
    {
105
0
        try
106
0
        {
107
0
            Reference< XPropertySet > xPropSet;
108
0
            if (( xActionTriggerContainer->getByIndex( i ) >>= xPropSet ) && ( xPropSet.is() ))
109
0
            {
110
0
                if ( IsSeparator( xPropSet ))
111
0
                {
112
                    // Separator
113
0
                    rSubMenu->insertSeparator(i);
114
0
                }
115
0
                else
116
0
                {
117
                    // Menu item
118
0
                    OUString aLabel;
119
0
                    OUString aCommandURL;
120
0
                    OUString aHelpURL;
121
0
                    Reference<css::graphic::XGraphic> xGraphic;
122
0
                    Reference< XIndexContainer > xSubContainer;
123
124
0
                    sal_uInt16 nNewItemId = nItemId++;
125
0
                    GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xGraphic, xSubContainer );
126
127
0
                    {
128
                        // insert new menu item
129
0
                        sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
130
0
                        if ( nIndex >= 0 )
131
0
                        {
132
                            // Special code for our menu implementation: some menu items don't have a
133
                            // command url but uses the item id as a unique identifier. These entries
134
                            // got a special url during conversion from menu=>actiontriggercontainer.
135
                            // Now we have to extract this special url and set the correct item id!!!
136
0
                            nNewItemId = static_cast<sal_uInt16>(o3tl::toInt32(aCommandURL.subView( nIndex+aSlotURL.getLength() )));
137
0
                            rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
138
0
                        }
139
0
                        else
140
0
                        {
141
0
                            rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
142
0
                            rSubMenu->setCommand(nNewItemId, aCommandURL);
143
0
                        }
144
145
                        // handle bitmap
146
0
                        if (xGraphic)
147
0
                        {
148
0
                            rSubMenu->setItemImage(nNewItemId, xGraphic, false);
149
0
                        }
150
0
                        else
151
0
                        {
152
                            // Support add-on images for context menu interceptors
153
0
                            Bitmap aBitmap(aAddonOptions.GetImageFromURL(aCommandURL, false, true));
154
0
                            if (!aBitmap.IsEmpty())
155
0
                                rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
156
0
                        }
157
158
0
                        if ( xSubContainer.is() )
159
0
                        {
160
0
                            rtl::Reference xNewSubMenu(new VCLXPopupMenu);
161
162
                            // Sub menu (recursive call CreateSubMenu )
163
0
                            InsertSubMenuItems(xNewSubMenu, nItemId, xSubContainer);
164
0
                            rSubMenu->setPopupMenu(nNewItemId, xNewSubMenu);
165
0
                        }
166
0
                    }
167
0
                }
168
0
            }
169
0
        }
170
0
        catch (const IndexOutOfBoundsException&)
171
0
        {
172
0
            return;
173
0
        }
174
0
        catch (const WrappedTargetException&)
175
0
        {
176
0
            return;
177
0
        }
178
0
        catch (const RuntimeException&)
179
0
        {
180
0
            return;
181
0
        }
182
0
    }
183
0
}
184
185
// implementation helper ( ActionTrigger => menu )
186
187
/// @throws RuntimeException
188
static Reference< XPropertySet > CreateActionTrigger(sal_uInt16 nItemId,
189
                                                     const Reference<XPopupMenu>& rMenu,
190
                                                     const Reference<XIndexContainer>& rActionTriggerContainer)
191
0
{
192
0
    Reference< XPropertySet > xPropSet;
193
194
0
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
195
0
    if ( xMultiServiceFactory.is() )
196
0
    {
197
0
        xPropSet.set( xMultiServiceFactory->createInstance( u"com.sun.star.ui.ActionTrigger"_ustr ),
198
0
                      UNO_QUERY );
199
200
0
        try
201
0
        {
202
            // Retrieve the menu attributes and set them in our PropertySet
203
0
            OUString aLabel = rMenu->getItemText(nItemId);
204
0
            xPropSet->setPropertyValue(u"Text"_ustr, Any(aLabel));
205
206
0
            OUString aCommandURL = rMenu->getCommand(nItemId);
207
208
0
            if ( aCommandURL.isEmpty() )
209
0
            {
210
0
                aCommandURL = "slot:" + OUString::number( nItemId );
211
0
            }
212
213
0
            xPropSet->setPropertyValue(u"CommandURL"_ustr, Any(aCommandURL));
214
215
0
            Reference<XBitmap> xBitmap(rMenu->getItemImage(nItemId), UNO_QUERY);
216
0
            if (xBitmap.is())
217
0
            {
218
0
                xPropSet->setPropertyValue(u"Image"_ustr, Any(xBitmap));
219
0
            }
220
0
        }
221
0
        catch (const Exception&)
222
0
        {
223
0
        }
224
0
    }
225
226
0
    return xPropSet;
227
0
}
228
229
/// @throws RuntimeException
230
static Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer )
231
0
{
232
0
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
233
0
    if ( xMultiServiceFactory.is() )
234
0
    {
235
0
        return Reference< XPropertySet >(   xMultiServiceFactory->createInstance(
236
0
                                                u"com.sun.star.ui.ActionTriggerSeparator"_ustr ),
237
0
                                            UNO_QUERY );
238
0
    }
239
240
0
    return Reference< XPropertySet >();
241
0
}
242
243
/// @throws RuntimeException
244
static Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer )
245
0
{
246
0
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
247
0
    if ( xMultiServiceFactory.is() )
248
0
    {
249
0
        return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
250
0
                                                u"com.sun.star.ui.ActionTriggerContainer"_ustr ),
251
0
                                             UNO_QUERY );
252
0
    }
253
254
0
    return Reference< XIndexContainer >();
255
0
}
256
257
static void FillActionTriggerContainerWithMenu(const Reference<XPopupMenu>& rMenu,
258
                                               const Reference<XIndexContainer>& rActionTriggerContainer)
259
0
{
260
0
    for (sal_uInt16 nPos = 0, nCount = rMenu->getItemCount(); nPos < nCount; ++nPos)
261
0
    {
262
0
        sal_uInt16 nItemId = rMenu->getItemId(nPos);
263
0
        css::awt::MenuItemType nType = rMenu->getItemType(nPos);
264
265
0
        try
266
0
        {
267
0
            if (nType == css::awt::MenuItemType_SEPARATOR)
268
0
            {
269
0
                Reference< XPropertySet > xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );
270
271
0
                rActionTriggerContainer->insertByIndex(nPos, Any(xPropSet));
272
0
            }
273
0
            else
274
0
            {
275
0
                Reference< XPropertySet > xPropSet = CreateActionTrigger(nItemId, rMenu, rActionTriggerContainer);
276
277
0
                rActionTriggerContainer->insertByIndex(nPos, Any(xPropSet));
278
279
0
                css::uno::Reference<XPopupMenu> xPopupMenu = rMenu->getPopupMenu(nItemId);
280
0
                if (xPopupMenu.is())
281
0
                {
282
                    // recursive call to build next sub menu
283
0
                    Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );
284
285
0
                    xPropSet->setPropertyValue(u"SubContainer"_ustr, Any(xSubContainer));
286
0
                    FillActionTriggerContainerWithMenu(xPopupMenu, xSubContainer);
287
0
                }
288
0
            }
289
0
        }
290
0
        catch (const Exception&)
291
0
        {
292
0
        }
293
0
    }
294
0
}
295
296
void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
297
    const Reference<XPopupMenu>& rNewMenu,
298
    const Reference<XIndexContainer>& rActionTriggerContainer)
299
0
{
300
0
    sal_uInt16 nItemId = START_ITEMID;
301
302
0
    if ( rActionTriggerContainer.is() )
303
0
        InsertSubMenuItems(rNewMenu, nItemId, rActionTriggerContainer);
304
0
}
305
306
void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
307
    Reference< XIndexContainer > const & xActionTriggerContainer,
308
    const css::uno::Reference<XPopupMenu>& rMenu)
309
0
{
310
0
    FillActionTriggerContainerWithMenu(rMenu, xActionTriggerContainer);
311
0
}
312
313
Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
314
    const css::uno::Reference<XPopupMenu>& rMenu,
315
    const OUString* pMenuIdentifier )
316
0
{
317
0
    return new RootActionTriggerContainer(rMenu, pMenuIdentifier);
318
0
}
319
320
}
321
322
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */