Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/uielement/menubarmerger.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 <uielement/menubarmerger.hxx>
21
#include <framework/addonsoptions.hxx>
22
#include <com/sun/star/uno/Sequence.hxx>
23
#include <o3tl/string_view.hxx>
24
25
using namespace ::com::sun::star;
26
27
const char SEPARATOR_STRING[]               = "private:separator";
28
29
const char16_t MERGECOMMAND_ADDAFTER[]      = u"AddAfter";
30
const char16_t MERGECOMMAND_ADDBEFORE[]     = u"AddBefore";
31
const char16_t MERGECOMMAND_REPLACE[]       = u"Replace";
32
const char16_t MERGECOMMAND_REMOVE[]        = u"Remove";
33
34
const char16_t MERGEFALLBACK_ADDPATH[]       = u"AddPath";
35
const char16_t MERGEFALLBACK_IGNORE[]        = u"Ignore";
36
37
namespace framework
38
{
39
40
/**
41
 Check whether a module identifier is part of a context
42
 defined by a colon separated list of module identifier.
43
44
 @param
45
     rContext
46
47
     Describes a context string list where all contexts
48
     are delimited by a colon. For more information about
49
     the module identifier used as context strings see the
50
     IDL description of css::frame::XModuleManager
51
52
  @param
53
     rModuleIdentifier
54
55
     A string describing a module identifier. See IDL
56
     description of css::frame::XModuleManager.
57
58
*/
59
bool MenuBarMerger::IsCorrectContext(
60
    std::u16string_view rContext, std::u16string_view rModuleIdentifier )
61
0
{
62
0
    return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos ));
63
0
}
64
65
void MenuBarMerger::RetrieveReferencePath(
66
    std::u16string_view rReferencePathString,
67
    ::std::vector< OUString >& rReferencePath )
68
0
{
69
0
    const char aDelimiter = '\\';
70
71
0
    rReferencePath.clear();
72
0
    sal_Int32 nIndex( 0 );
73
0
    do
74
0
    {
75
0
        OUString aToken( o3tl::getToken(rReferencePathString, 0, aDelimiter, nIndex ) );
76
0
        if ( !aToken.isEmpty() )
77
0
            rReferencePath.push_back( aToken );
78
0
    }
79
0
    while ( nIndex >= 0 );
80
0
}
81
82
ReferencePathInfo MenuBarMerger::FindReferencePath(
83
    const ::std::vector< OUString >& rReferencePath,
84
    Menu* pMenu )
85
0
{
86
0
    sal_uInt32       i( 0 );
87
0
    const sal_uInt32 nCount( rReferencePath.size() );
88
89
0
    ReferencePathInfo aResult;
90
0
    if ( !nCount )
91
0
    {
92
0
        aResult.pPopupMenu = nullptr;
93
0
        aResult.nPos = 0;
94
0
        aResult.nLevel = -1;
95
0
        aResult.eResult = RP_MENUITEM_NOT_FOUND;
96
0
        return aResult;
97
0
    }
98
99
0
    Menu*            pCurrMenu( pMenu );
100
0
    RPResultInfo     eResult( RP_OK );
101
102
0
    sal_Int32  nLevel( - 1 );
103
0
    sal_uInt16 nPos( MENU_ITEM_NOTFOUND );
104
0
    do
105
0
    {
106
0
        ++nLevel;
107
0
        const OUString& aCmd( rReferencePath[i] );
108
109
0
        if ( i == nCount-1 )
110
0
        {
111
            // Check last reference path element. Must be a leave (menu item).
112
0
            sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu );
113
0
            if ( nTmpPos != MENU_ITEM_NOTFOUND )
114
0
                nPos = nTmpPos;
115
0
            eResult = ( nTmpPos != MENU_ITEM_NOTFOUND ) ? RP_OK : RP_MENUITEM_NOT_FOUND;
116
0
        }
117
0
        else
118
0
        {
119
            // Check reference path element. Must be a node (popup menu)!
120
0
            sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu );
121
0
            if ( nTmpPos != MENU_ITEM_NOTFOUND )
122
0
            {
123
0
                sal_uInt16 nItemId = pCurrMenu->GetItemId( nTmpPos );
124
0
                Menu* pTmpMenu     = pCurrMenu->GetPopupMenu( nItemId );
125
0
                if ( pTmpMenu != nullptr )
126
0
                    pCurrMenu = pTmpMenu;
127
0
                else
128
0
                {
129
0
                    nPos    = nTmpPos;
130
0
                    eResult = RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND;
131
0
                }
132
0
            }
133
0
            else
134
0
                eResult = RP_POPUPMENU_NOT_FOUND;
135
0
        }
136
0
        i++;
137
0
    }
138
0
    while ((i < nCount) && (eResult == RP_OK));
139
140
0
    aResult.pPopupMenu = pCurrMenu;
141
0
    aResult.nPos       = nPos;
142
0
    aResult.nLevel     = nLevel;
143
0
    aResult.eResult    = eResult;
144
145
0
    return aResult;
146
0
}
147
148
sal_uInt16 MenuBarMerger::FindMenuItem( std::u16string_view rCmd, Menu const * pCurrMenu )
149
0
{
150
0
    for ( sal_uInt16 i = 0; i < pCurrMenu->GetItemCount(); i++ )
151
0
    {
152
0
        const sal_uInt16 nItemId = pCurrMenu->GetItemId( i );
153
0
        if ( nItemId > 0 )
154
0
        {
155
0
            if ( rCmd == pCurrMenu->GetItemCommand( nItemId ) )
156
0
                return i;
157
0
        }
158
0
    }
159
160
0
    return MENU_ITEM_NOTFOUND;
161
0
}
162
163
bool MenuBarMerger::CreateSubMenu(
164
    Menu*                     pSubMenu,
165
    sal_uInt16&               nItemId,
166
    const OUString&    rModuleIdentifier,
167
    const AddonMenuContainer& rAddonSubMenu )
168
0
{
169
0
    const sal_uInt32 nSize = rAddonSubMenu.size();
170
0
    for ( sal_uInt32 i = 0; i < nSize; i++ )
171
0
    {
172
0
        const AddonMenuItem& rMenuItem = rAddonSubMenu[i];
173
174
0
        if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
175
0
        {
176
0
            if ( rMenuItem.aURL == SEPARATOR_STRING )
177
0
            {
178
0
                pSubMenu->InsertSeparator();
179
0
            }
180
0
            else
181
0
            {
182
0
                pSubMenu->InsertItem(nItemId, rMenuItem.aTitle);
183
0
                pSubMenu->SetItemCommand( nItemId, rMenuItem.aURL );
184
0
                if ( !rMenuItem.aSubMenu.empty() )
185
0
                {
186
0
                    VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create();
187
0
                    pSubMenu->SetPopupMenu( nItemId, pPopupMenu );
188
0
                    ++nItemId;
189
190
0
                    CreateSubMenu( pPopupMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu );
191
0
                }
192
0
                else
193
0
                    ++nItemId;
194
0
            }
195
0
        }
196
0
    }
197
198
0
    return true;
199
0
}
200
201
bool MenuBarMerger::MergeMenuItems(
202
    Menu*                     pMenu,
203
    sal_uInt16                nPos,
204
    sal_uInt16                nModIndex,
205
    sal_uInt16&               nItemId,
206
    const OUString&    rModuleIdentifier,
207
    const AddonMenuContainer& rAddonMenuItems )
208
0
{
209
0
    sal_uInt16       nIndex( 0 );
210
0
    const sal_uInt32 nSize = rAddonMenuItems.size();
211
0
    for ( sal_uInt32 i = 0; i < nSize; i++ )
212
0
    {
213
0
        const AddonMenuItem& rMenuItem = rAddonMenuItems[i];
214
215
0
        if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
216
0
        {
217
0
            if ( rMenuItem.aURL == SEPARATOR_STRING )
218
0
            {
219
0
                pMenu->InsertSeparator({}, nPos + nModIndex + nIndex);
220
0
            }
221
0
            else
222
0
            {
223
0
                pMenu->InsertItem(nItemId, rMenuItem.aTitle, MenuItemBits::NONE, {}, nPos + nModIndex + nIndex);
224
0
                pMenu->SetItemCommand( nItemId, rMenuItem.aURL );
225
0
                if ( !rMenuItem.aSubMenu.empty() )
226
0
                {
227
0
                    VclPtr<PopupMenu> pSubMenu = VclPtr<PopupMenu>::Create();
228
0
                    pMenu->SetPopupMenu( nItemId, pSubMenu );
229
0
                    ++nItemId;
230
231
0
                    CreateSubMenu( pSubMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu );
232
0
                }
233
0
                else
234
0
                    ++nItemId;
235
0
            }
236
0
            ++nIndex;
237
0
        }
238
0
    }
239
240
0
    return true;
241
0
}
242
243
bool MenuBarMerger::ReplaceMenuItem(
244
    Menu*                     pMenu,
245
    sal_uInt16                nPos,
246
    sal_uInt16&               rItemId,
247
    const OUString&    rModuleIdentifier,
248
    const AddonMenuContainer& rAddonMenuItems )
249
0
{
250
    // There is no replace available. Therefore we first have to
251
    // remove the old menu entry,
252
0
    pMenu->RemoveItem( nPos );
253
254
0
    return MergeMenuItems( pMenu, nPos, 0, rItemId, rModuleIdentifier, rAddonMenuItems );
255
0
}
256
257
bool MenuBarMerger::RemoveMenuItems(
258
    Menu*                     pMenu,
259
    sal_uInt16                nPos,
260
    std::u16string_view    rMergeCommandParameter )
261
0
{
262
0
    const sal_uInt16 nParam( sal_uInt16( o3tl::toInt32(rMergeCommandParameter) ));
263
0
    sal_uInt16       nCount = std::max( nParam, sal_uInt16(1) );
264
265
0
    sal_uInt16 i = 0;
266
0
    while (( nPos < pMenu->GetItemCount() ) && ( i < nCount ))
267
0
    {
268
0
        pMenu->RemoveItem( nPos );
269
0
        ++i;
270
0
    }
271
272
0
    return true;
273
0
}
274
275
bool MenuBarMerger::ProcessMergeOperation(
276
    Menu*                     pMenu,
277
    sal_uInt16                nPos,
278
    sal_uInt16&               nItemId,
279
    std::u16string_view rMergeCommand,
280
    std::u16string_view rMergeCommandParameter,
281
    const OUString&    rModuleIdentifier,
282
    const AddonMenuContainer& rAddonMenuItems )
283
0
{
284
0
    sal_uInt16 nModIndex( 0 );
285
286
0
    if ( rMergeCommand == MERGECOMMAND_ADDBEFORE )
287
0
    {
288
0
        nModIndex = 0;
289
0
        return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems );
290
0
    }
291
0
    else if ( rMergeCommand == MERGECOMMAND_ADDAFTER )
292
0
    {
293
0
        nModIndex = 1;
294
0
        return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems );
295
0
    }
296
0
    else if ( rMergeCommand == MERGECOMMAND_REPLACE )
297
0
    {
298
0
        return ReplaceMenuItem( pMenu, nPos, nItemId, rModuleIdentifier, rAddonMenuItems );
299
0
    }
300
0
    else if ( rMergeCommand == MERGECOMMAND_REMOVE )
301
0
    {
302
0
        return RemoveMenuItems( pMenu, nPos, rMergeCommandParameter );
303
0
    }
304
305
0
    return false;
306
0
}
307
308
bool MenuBarMerger::ProcessFallbackOperation(
309
    const ReferencePathInfo&                aRefPathInfo,
310
    sal_uInt16&                             rItemId,
311
    std::u16string_view              rMergeCommand,
312
    std::u16string_view              rMergeFallback,
313
    const ::std::vector< OUString >& rReferencePath,
314
    const std::u16string_view        rModuleIdentifier,
315
    const AddonMenuContainer&               rAddonMenuItems )
316
0
{
317
0
    if (( rMergeFallback == MERGEFALLBACK_IGNORE ) ||
318
0
        ( rMergeCommand  == MERGECOMMAND_REPLACE ) ||
319
0
        ( rMergeCommand  == MERGECOMMAND_REMOVE  ) )
320
0
    {
321
0
        return true;
322
0
    }
323
0
    else if ( rMergeFallback == MERGEFALLBACK_ADDPATH )
324
0
    {
325
0
        Menu*            pCurrMenu( aRefPathInfo.pPopupMenu );
326
0
        sal_Int32        nLevel( aRefPathInfo.nLevel );
327
0
        const sal_Int32  nSize( rReferencePath.size() );
328
0
        bool             bFirstLevel( true );
329
330
0
        while ( nLevel < nSize )
331
0
        {
332
0
            if ( nLevel == nSize-1 )
333
0
            {
334
0
                const sal_uInt32 nCount = rAddonMenuItems.size();
335
0
                for ( sal_uInt32 i = 0; i < nCount; ++i )
336
0
                {
337
0
                    const AddonMenuItem& rMenuItem = rAddonMenuItems[i];
338
0
                    if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
339
0
                    {
340
0
                        if ( rMenuItem.aURL == SEPARATOR_STRING )
341
0
                            pCurrMenu->InsertSeparator();
342
0
                        else
343
0
                        {
344
0
                            pCurrMenu->InsertItem(rItemId, rMenuItem.aTitle);
345
0
                            pCurrMenu->SetItemCommand( rItemId, rMenuItem.aURL );
346
0
                            ++rItemId;
347
0
                        }
348
0
                    }
349
0
                }
350
0
            }
351
0
            else
352
0
            {
353
0
                const OUString& aCmd( rReferencePath[nLevel] );
354
355
0
                VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create();
356
357
0
                if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND ))
358
0
                {
359
                    // special case: menu item without popup
360
0
                    sal_uInt16 nInsPos = aRefPathInfo.nPos;
361
0
                    sal_uInt16 nSetItemId = pCurrMenu->GetItemId( nInsPos );
362
0
                    pCurrMenu->SetItemCommand( nSetItemId, aCmd );
363
0
                    pCurrMenu->SetPopupMenu( nSetItemId, pPopupMenu );
364
0
                }
365
0
                else
366
0
                {
367
                    // normal case: insert a new item with popup
368
0
                    pCurrMenu->InsertItem(rItemId, OUString());
369
0
                    pCurrMenu->SetItemCommand( rItemId, aCmd );
370
0
                    pCurrMenu->SetPopupMenu( rItemId, pPopupMenu );
371
0
                }
372
373
0
                pCurrMenu = pPopupMenu;
374
0
                ++rItemId;
375
0
                bFirstLevel = false;
376
0
            }
377
0
            ++nLevel;
378
0
        }
379
0
        return true;
380
0
    }
381
382
0
    return false;
383
0
}
384
385
void MenuBarMerger::GetMenuEntry(
386
    const uno::Sequence< beans::PropertyValue >& rAddonMenuEntry,
387
    AddonMenuItem&                               rAddonMenuItem )
388
0
{
389
    // Reset submenu member
390
0
    rAddonMenuItem.aSubMenu.clear();
391
392
0
    for ( const beans::PropertyValue& rProp : rAddonMenuEntry )
393
0
    {
394
0
        OUString aMenuEntryPropName = rProp.Name;
395
0
        if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL )
396
0
            rProp.Value >>= rAddonMenuItem.aURL;
397
0
        else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE )
398
0
            rProp.Value >>= rAddonMenuItem.aTitle;
399
0
        else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU )
400
0
        {
401
0
            uno::Sequence< uno::Sequence< beans::PropertyValue > > aSubMenu;
402
0
            rProp.Value >>= aSubMenu;
403
0
            GetSubMenu( aSubMenu, rAddonMenuItem.aSubMenu );
404
0
        }
405
0
        else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT )
406
0
            rProp.Value >>= rAddonMenuItem.aContext;
407
0
    }
408
0
}
409
410
void MenuBarMerger::GetSubMenu(
411
    const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSubMenuEntries,
412
    AddonMenuContainer&                                           rSubMenu )
413
0
{
414
0
    rSubMenu.clear();
415
416
0
    const sal_Int32 nCount = rSubMenuEntries.getLength();
417
0
    rSubMenu.reserve(rSubMenu.size() + nCount);
418
0
    for ( sal_Int32 i = 0; i < nCount; i++ )
419
0
    {
420
0
        const uno::Sequence< beans::PropertyValue >& rMenuEntry = rSubMenuEntries[ i ];
421
422
0
        AddonMenuItem aMenuItem;
423
0
        GetMenuEntry( rMenuEntry, aMenuItem );
424
0
        rSubMenu.push_back(std::move(aMenuItem));
425
0
    }
426
0
}
427
428
} // namespace framework
429
430
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */