Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/window/menuitemlist.cxx
Line
Count
Source (jump to first uncovered line)
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 "menuitemlist.hxx"
21
22
#include <salframe.hxx>
23
#include <salinst.hxx>
24
#include <salmenu.hxx>
25
#include <svdata.hxx>
26
27
#include <vcl/i18nhelp.hxx>
28
#include <vcl/mnemonic.hxx>
29
#include <vcl/settings.hxx>
30
#include <vcl/vcllayout.hxx>
31
#include <vcl/window.hxx>
32
33
using namespace css;
34
using namespace vcl;
35
36
MenuItemData::~MenuItemData()
37
0
{
38
0
    if (aUserValueReleaseFunc)
39
0
        aUserValueReleaseFunc(nUserValue);
40
0
    pSalMenuItem.reset();
41
0
    pSubMenu.disposeAndClear();
42
0
}
43
44
SalLayoutGlyphs* MenuItemData::GetTextGlyphs(const OutputDevice* pOutputDevice)
45
0
{
46
0
    if (aTextGlyphs.IsValid())
47
        // Use pre-calculated result.
48
0
        return &aTextGlyphs;
49
50
0
    OUString aNonMnemonicString = removeMnemonicFromString(aText);
51
0
    std::unique_ptr<SalLayout> pLayout
52
0
        = pOutputDevice->ImplLayout(aNonMnemonicString, 0, aNonMnemonicString.getLength(),
53
0
                                    Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
54
0
    if (!pLayout)
55
0
        return nullptr;
56
57
    // Remember the calculation result.
58
0
    aTextGlyphs = pLayout->GetGlyphs();
59
60
0
    return &aTextGlyphs;
61
0
}
62
63
MenuItemList::~MenuItemList()
64
0
{
65
0
}
66
67
MenuItemData* MenuItemList::Insert(
68
    sal_uInt16 nId,
69
    MenuItemType eType,
70
    MenuItemBits nBits,
71
    const OUString& rStr,
72
    Menu* pMenu,
73
    size_t nPos,
74
    const OUString &rIdent
75
)
76
0
{
77
0
    MenuItemData* pData     = new MenuItemData( rStr );
78
0
    pData->nId              = nId;
79
0
    pData->sIdent           = rIdent;
80
0
    pData->eType            = eType;
81
0
    pData->nBits            = nBits;
82
0
    pData->pSubMenu         = nullptr;
83
0
    pData->nUserValue       = nullptr;
84
0
    pData->bChecked         = false;
85
0
    pData->bEnabled         = true;
86
0
    pData->bVisible         = true;
87
0
    pData->bIsTemporary     = false;
88
89
0
    SalItemParams aSalMIData;
90
0
    aSalMIData.nId = nId;
91
0
    aSalMIData.eType = eType;
92
0
    aSalMIData.nBits = nBits;
93
0
    aSalMIData.pMenu = pMenu;
94
0
    aSalMIData.aText = rStr;
95
96
    // Native-support: returns NULL if not supported
97
0
    pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( aSalMIData );
98
99
0
    if( nPos < maItemList.size() ) {
100
0
        maItemList.insert( maItemList.begin() + nPos, std::unique_ptr<MenuItemData>(pData) );
101
0
    } else {
102
0
        maItemList.emplace_back( pData );
103
0
    }
104
0
    return pData;
105
0
}
106
107
void MenuItemList::InsertSeparator(const OUString &rIdent, size_t nPos)
108
0
{
109
0
    MenuItemData* pData     = new MenuItemData;
110
0
    pData->nId              = 0;
111
0
    pData->sIdent           = rIdent;
112
0
    pData->eType            = MenuItemType::SEPARATOR;
113
0
    pData->nBits            = MenuItemBits::NONE;
114
0
    pData->pSubMenu         = nullptr;
115
0
    pData->nUserValue       = nullptr;
116
0
    pData->bChecked         = false;
117
0
    pData->bEnabled         = true;
118
0
    pData->bVisible         = true;
119
0
    pData->bIsTemporary     = false;
120
121
0
    SalItemParams aSalMIData;
122
0
    aSalMIData.nId = 0;
123
0
    aSalMIData.eType = MenuItemType::SEPARATOR;
124
0
    aSalMIData.nBits = MenuItemBits::NONE;
125
0
    aSalMIData.pMenu = nullptr;
126
0
    aSalMIData.aText.clear();
127
0
    aSalMIData.aImage = Image();
128
129
    // Native-support: returns NULL if not supported
130
0
    pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( aSalMIData );
131
132
0
    if( nPos < maItemList.size() ) {
133
0
        maItemList.insert( maItemList.begin() + nPos, std::unique_ptr<MenuItemData>(pData) );
134
0
    } else {
135
0
        maItemList.emplace_back( pData );
136
0
    }
137
0
}
138
139
void MenuItemList::Remove( size_t nPos )
140
0
{
141
0
    maItemList.erase( maItemList.begin() + nPos );
142
0
}
143
144
void MenuItemList::Clear()
145
0
{
146
0
    maItemList.clear();
147
0
}
148
149
MenuItemData* MenuItemList::GetData( sal_uInt16 nSVId, size_t& rPos ) const
150
0
{
151
0
    for( size_t i = 0, n = maItemList.size(); i < n; ++i )
152
0
    {
153
0
        if ( maItemList[ i ]->nId == nSVId )
154
0
        {
155
0
            rPos = i;
156
0
            return maItemList[ i ].get();
157
0
        }
158
0
    }
159
0
    return nullptr;
160
0
}
161
162
MenuItemData* MenuItemList::SearchItem(
163
    sal_Unicode cSelectChar,
164
    KeyCode aKeyCode,
165
    size_t& rPos,
166
    size_t& nDuplicates,
167
    size_t nCurrentPos
168
) const
169
0
{
170
0
    const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
171
172
0
    size_t nListCount = maItemList.size();
173
174
    // try character code first
175
0
    nDuplicates = GetItemCount( cSelectChar );  // return number of duplicates
176
0
    if( nDuplicates )
177
0
    {
178
0
        MenuItemData* pFirstMatch = nullptr;
179
0
        size_t nFirstPos(0);
180
0
        for ( rPos = 0; rPos < nListCount; rPos++)
181
0
        {
182
0
            MenuItemData* pData = maItemList[ rPos ].get();
183
0
            if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) )
184
0
            {
185
0
                if (nDuplicates == 1)
186
0
                    return pData;
187
0
                if (rPos > nCurrentPos)
188
0
                    return pData;   // select next entry with the same mnemonic
189
0
                if (!pFirstMatch)   // stash the first match for use if nothing follows nCurrentPos
190
0
                {
191
0
                    pFirstMatch = pData;
192
0
                    nFirstPos = rPos;
193
0
                }
194
0
            }
195
0
        }
196
0
        if (pFirstMatch)
197
0
        {
198
0
            rPos = nFirstPos;
199
0
            return pFirstMatch;
200
0
        }
201
0
    }
202
203
    // nothing found, try keycode instead
204
0
    nDuplicates = GetItemCount( aKeyCode ); // return number of duplicates
205
206
0
    if( nDuplicates )
207
0
    {
208
0
        char ascii = 0;
209
0
        if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z )
210
0
            ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A));
211
212
0
        MenuItemData* pFirstMatch = nullptr;
213
0
        size_t nFirstPos(0);
214
0
        for ( rPos = 0; rPos < nListCount; rPos++)
215
0
        {
216
0
            MenuItemData* pData = maItemList[ rPos ].get();
217
0
            if ( pData->bEnabled )
218
0
            {
219
0
                sal_Int32 n = pData->aText.indexOf('~');
220
0
                if ( n != -1 )
221
0
                {
222
0
                    KeyCode nKeyCode;
223
0
                    sal_Unicode nUnicode = pData->aText[n+1];
224
0
                    vcl::Window* pDefWindow = ImplGetDefaultWindow();
225
0
                    if(  (  pDefWindow
226
0
                         && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( nUnicode,
227
0
                             Application::GetSettings().GetUILanguageTag().getLanguageType(), nKeyCode )
228
0
                         && aKeyCode.GetCode() == nKeyCode.GetCode()
229
0
                         )
230
0
                      || (  ascii
231
0
                         && rI18nHelper.MatchMnemonic( pData->aText, ascii )
232
0
                         )
233
0
                      )
234
0
                    {
235
0
                        if (nDuplicates == 1)
236
0
                            return pData;
237
0
                        if (rPos > nCurrentPos)
238
0
                            return pData;   // select next entry with the same mnemonic
239
0
                        if (!pFirstMatch)   // stash the first match for use if nothing follows nCurrentPos
240
0
                        {
241
0
                            pFirstMatch = pData;
242
0
                            nFirstPos = rPos;
243
0
                        }
244
0
                    }
245
0
                }
246
0
            }
247
0
        }
248
0
        if (pFirstMatch)
249
0
        {
250
0
            rPos = nFirstPos;
251
0
            return pFirstMatch;
252
0
        }
253
0
    }
254
255
0
    return nullptr;
256
0
}
257
258
size_t MenuItemList::GetItemCount( sal_Unicode cSelectChar ) const
259
0
{
260
    // returns number of entries with same mnemonic
261
0
    const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
262
263
0
    size_t nItems = 0;
264
0
    for ( size_t nPos = maItemList.size(); nPos; )
265
0
    {
266
0
        MenuItemData* pData = maItemList[ --nPos ].get();
267
0
        if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) )
268
0
            nItems++;
269
0
    }
270
271
0
    return nItems;
272
0
}
273
274
size_t MenuItemList::GetItemCount( KeyCode aKeyCode ) const
275
0
{
276
    // returns number of entries with same mnemonic
277
    // uses key codes instead of character codes
278
0
    const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
279
0
    char ascii = 0;
280
0
    if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z )
281
0
        ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A));
282
283
0
    size_t nItems = 0;
284
0
    for ( size_t nPos = maItemList.size(); nPos; )
285
0
    {
286
0
        MenuItemData* pData = maItemList[ --nPos ].get();
287
0
        if ( pData->bEnabled )
288
0
        {
289
0
            sal_Int32 n = pData->aText.indexOf('~');
290
0
            if (n != -1)
291
0
            {
292
0
                KeyCode nKeyCode;
293
                // if MapUnicodeToKeyCode fails or is unsupported we try the pure ascii mapping of the keycodes
294
                // so we have working shortcuts when ascii mnemonics are used
295
0
                vcl::Window* pDefWindow = ImplGetDefaultWindow();
296
0
                if(  (  pDefWindow
297
0
                     && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( pData->aText[n+1],
298
0
                         Application::GetSettings().GetUILanguageTag().getLanguageType(), nKeyCode )
299
0
                     && aKeyCode.GetCode() == nKeyCode.GetCode()
300
0
                     )
301
0
                  || (  ascii
302
0
                     && rI18nHelper.MatchMnemonic( pData->aText, ascii )
303
0
                     )
304
0
                  )
305
0
                    nItems++;
306
0
            }
307
0
        }
308
0
    }
309
310
0
    return nItems;
311
0
}
312
313
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */