Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/window/menu.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 <accessibility/accessiblemenubasecomponent.hxx>
21
#include <accessibility/vclxaccessiblemenubar.hxx>
22
#include <accessibility/vclxaccessiblepopupmenu.hxx>
23
24
#include <comphelper/diagnose_ex.hxx>
25
#include <sal/log.hxx>
26
#include <tools/json_writer.hxx>
27
28
#include <comphelper/types.hxx>
29
#include <comphelper/lok.hxx>
30
#include <vcl/dialoghelper.hxx>
31
#include <vcl/salnativewidgets.hxx>
32
#include <vcl/svapp.hxx>
33
#include <vcl/mnemonic.hxx>
34
#include <vcl/image.hxx>
35
#include <vcl/event.hxx>
36
#include <vcl/help.hxx>
37
#include <vcl/toolkit/floatwin.hxx>
38
#include <vcl/decoview.hxx>
39
#include <vcl/menu.hxx>
40
#include <vcl/taskpanelist.hxx>
41
#include <vcl/settings.hxx>
42
#include <vcl/commandinfoprovider.hxx>
43
44
#include <salinst.hxx>
45
#include <svdata.hxx>
46
#include <strings.hrc>
47
#include <window.h>
48
#include <salmenu.hxx>
49
#include <salframe.hxx>
50
51
#include "menubarwindow.hxx"
52
#include "menufloatingwindow.hxx"
53
#include "menuitemlist.hxx"
54
55
#include <com/sun/star/uno/Reference.h>
56
#include <com/sun/star/lang/XComponent.hpp>
57
#include <com/sun/star/accessibility/XAccessible.hpp>
58
#include <vcl/toolkit/unowrap.hxx>
59
#include <rtl/ustrbuf.hxx>
60
61
#include <map>
62
#include <string_view>
63
#include <vector>
64
65
#include <officecfg/Office/Common.hxx>
66
#include <officecfg/VCL.hxx>
67
68
namespace vcl
69
{
70
71
struct MenuLayoutData : public ControlLayoutData
72
{
73
    std::vector< sal_uInt16 >               m_aLineItemIds;
74
    std::map< sal_uInt16, tools::Rectangle >       m_aVisibleItemBoundRects;
75
};
76
77
}
78
79
using namespace vcl;
80
81
constexpr auto EXTRAITEMHEIGHT = 4;
82
constexpr auto SPACE_AROUND_TITLE = 4;
83
84
static void ImplSetMenuItemData( MenuItemData* pData )
85
0
{
86
    // convert data
87
0
    if ( !pData->aImage )
88
0
        pData->eType = MenuItemType::STRING;
89
0
    else if ( pData->aText.isEmpty() )
90
0
        pData->eType = MenuItemType::IMAGE;
91
0
    else
92
0
        pData->eType = MenuItemType::STRINGIMAGE;
93
0
}
94
95
namespace {
96
97
void ImplClosePopupToolBox( const VclPtr<vcl::Window>& pWin )
98
0
{
99
0
    if ( pWin->GetType() == WindowType::TOOLBOX && ImplGetDockingManager()->IsInPopupMode( pWin ) )
100
0
    {
101
0
        SystemWindow* pFloatingWindow = ImplGetDockingManager()->GetFloatingWindow(pWin);
102
0
        if (pFloatingWindow)
103
0
            static_cast<FloatingWindow*>(pFloatingWindow)->EndPopupMode( FloatWinPopupEndFlags::CloseAll );
104
0
    }
105
0
}
106
107
// TODO: Move to common code with the same function in toolbox
108
// Draw the ">>" - more indicator at the coordinates
109
void lclDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
110
0
{
111
0
    auto popIt = rRenderContext.ScopedPush(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
112
0
    rRenderContext.SetLineColor();
113
114
0
    if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
115
0
        rRenderContext.SetFillColor(COL_WHITE);
116
0
    else
117
0
        rRenderContext.SetFillColor(COL_BLACK);
118
0
    float fScaleFactor = rRenderContext.GetDPIScaleFactor();
119
120
0
    int linewidth = 1 * fScaleFactor;
121
0
    int space = 4 * fScaleFactor;
122
123
0
    tools::Long width = 8 * fScaleFactor;
124
0
    tools::Long height = 5 * fScaleFactor;
125
126
    //Keep odd b/c drawing code works better
127
0
    if ( height % 2 == 0 )
128
0
        height--;
129
130
0
    tools::Long heightOrig = height;
131
132
0
    tools::Long x = rRect.Left() + (rRect.getOpenWidth() - width)/2 + 1;
133
0
    tools::Long y = rRect.Top() + (rRect.getOpenHeight() - height)/2 + 1;
134
0
    while( height >= 1)
135
0
    {
136
0
        rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
137
0
        x += space;
138
0
        rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
139
0
        x -= space;
140
0
        y++;
141
0
        if( height <= heightOrig / 2 + 1) x--;
142
0
        else            x++;
143
0
        height--;
144
0
    }
145
0
}
146
147
} // end anonymous namespace
148
149
Menu::Menu()
150
0
    : m_pFirstDel(nullptr)
151
0
    , m_pItemList(new MenuItemList)
152
0
    , m_pStartedFrom(nullptr)
153
0
    , m_pWindow(nullptr)
154
0
    , m_nTitleHeight(0)
155
0
    , m_nEventId(nullptr)
156
0
    , m_nHighlightedItemPos(ITEMPOS_INVALID)
157
0
    , m_nMenuFlags(MenuFlags::NONE)
158
0
    , m_nSelectedId(0)
159
0
    , m_nImgOrChkPos(0)
160
0
    , m_nTextPos(0)
161
0
    , m_bCanceled(false)
162
0
    , m_bInCallback(false)
163
0
    , m_bKilled(false)
164
0
{
165
0
}
166
167
Menu::~Menu()
168
0
{
169
0
    disposeOnce();
170
0
}
171
172
void Menu::dispose()
173
0
{
174
0
    ImplCallEventListeners( VclEventId::ObjectDying, ITEMPOS_INVALID );
175
176
0
    m_pWindow.disposeAndClear();
177
178
0
    if (m_pAccessible.is())
179
0
        m_pAccessible->dispose();
180
0
    m_pAccessible.clear();
181
182
0
    if (m_nEventId)
183
0
        Application::RemoveUserEvent(m_nEventId);
184
185
    // Notify deletion of this menu
186
0
    ImplMenuDelData* pDelData = m_pFirstDel;
187
0
    while ( pDelData )
188
0
    {
189
0
        pDelData->mpMenu = nullptr;
190
0
        pDelData = pDelData->mpNext;
191
0
    }
192
193
0
    m_bKilled = true;
194
195
    // tdf#140225 when clearing pItemList, keep SalMenu in sync with
196
    // their removal during menu teardown
197
0
    for (size_t n = m_pItemList->size(); n;)
198
0
    {
199
0
        --n;
200
0
        if (m_pSalMenu)
201
0
            m_pSalMenu->RemoveItem(n);
202
0
        m_pItemList->Remove(n);
203
0
    }
204
205
0
    assert(!m_pItemList->size());
206
207
0
    m_pLayoutData.reset();
208
209
    // Native-support: destroy SalMenu
210
0
    m_pSalMenu.reset();
211
212
0
    m_pStartedFrom.reset();
213
0
    m_pWindow.reset();
214
0
    VclReferenceBase::dispose();
215
0
}
216
217
void Menu::CreateAutoMnemonics()
218
0
{
219
0
    MnemonicGenerator aMnemonicGenerator;
220
0
    size_t n;
221
0
    for (n = 0; n < m_pItemList->size(); n++)
222
0
    {
223
0
        MenuItemData* pData = m_pItemList->GetDataFromPos(n);
224
0
        if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
225
0
            aMnemonicGenerator.RegisterMnemonic( pData->aText );
226
0
    }
227
0
    for (n = 0; n < m_pItemList->size(); n++)
228
0
    {
229
0
        MenuItemData* pData = m_pItemList->GetDataFromPos(n);
230
0
        if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
231
0
            pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText );
232
0
    }
233
0
}
234
235
void Menu::Activate()
236
0
{
237
0
    m_bInCallback = true;
238
239
0
    ImplMenuDelData aDelData( this );
240
241
0
    ImplCallEventListeners( VclEventId::MenuActivate, ITEMPOS_INVALID );
242
243
0
    if( !aDelData.isDeleted() )
244
0
    {
245
0
        if (!m_aActivateHdl.Call(this))
246
0
        {
247
0
            if( !aDelData.isDeleted() )
248
0
            {
249
0
                Menu* pStartMenu = ImplGetStartMenu();
250
0
                if ( pStartMenu && ( pStartMenu != this ) )
251
0
                {
252
0
                    pStartMenu->m_bInCallback = true;
253
                    // MT 11/01: Call EventListener here? I don't know...
254
0
                    pStartMenu->m_aActivateHdl.Call(this);
255
0
                    pStartMenu->m_bInCallback = false;
256
0
                }
257
0
            }
258
0
        }
259
0
        m_bInCallback = false;
260
0
    }
261
262
0
    if (!aDelData.isDeleted() && !(m_nMenuFlags & MenuFlags::NoAutoMnemonics))
263
0
        CreateAutoMnemonics();
264
0
}
265
266
void Menu::Deactivate()
267
0
{
268
0
    for (size_t n = m_pItemList->size(); n;)
269
0
    {
270
0
        MenuItemData* pData = m_pItemList->GetDataFromPos(--n);
271
0
        if ( pData->bIsTemporary )
272
0
        {
273
0
            if ( ImplGetSalMenu() )
274
0
                ImplGetSalMenu()->RemoveItem( n );
275
276
0
            m_pItemList->Remove(n);
277
0
        }
278
0
    }
279
280
0
    m_bInCallback = true;
281
282
0
    ImplMenuDelData aDelData( this );
283
284
0
    Menu* pStartMenu = ImplGetStartMenu();
285
0
    ImplCallEventListeners( VclEventId::MenuDeactivate, ITEMPOS_INVALID );
286
287
0
    if( !aDelData.isDeleted() )
288
0
    {
289
0
        if (!m_aDeactivateHdl.Call(this))
290
0
        {
291
0
            if( !aDelData.isDeleted() )
292
0
            {
293
0
                if ( pStartMenu && ( pStartMenu != this ) )
294
0
                {
295
0
                    pStartMenu->m_bInCallback = true;
296
0
                    pStartMenu->m_aDeactivateHdl.Call(this);
297
0
                    pStartMenu->m_bInCallback = false;
298
0
                }
299
0
            }
300
0
        }
301
0
    }
302
303
0
    if( !aDelData.isDeleted() )
304
0
    {
305
0
        m_bInCallback = false;
306
0
    }
307
0
}
308
309
void Menu::ImplSelect()
310
0
{
311
0
    MenuItemData* pData = GetItemList()->GetData(m_nSelectedId);
312
0
    if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) )
313
0
    {
314
0
        bool bChecked = IsItemChecked(m_nSelectedId);
315
0
        if ( pData->nBits & MenuItemBits::RADIOCHECK )
316
0
        {
317
0
            if ( !bChecked )
318
0
                CheckItem(m_nSelectedId);
319
0
        }
320
0
        else
321
0
            CheckItem(m_nSelectedId, !bChecked);
322
0
    }
323
324
    // call select
325
0
    ImplSVData* pSVData = ImplGetSVData();
326
0
    pSVData->maAppData.mpActivePopupMenu = nullptr;        // if new execute in select()
327
0
    m_nEventId = Application::PostUserEvent(LINK(this, Menu, ImplCallSelect));
328
0
}
329
330
void Menu::Select()
331
0
{
332
0
    ImplMenuDelData aDelData( this );
333
334
0
    ImplCallEventListeners( VclEventId::MenuSelect, GetItemPos( GetCurItemId() ) );
335
0
    if (aDelData.isDeleted())
336
0
        return;
337
0
    if (m_aSelectHdl.Call(this))
338
0
        return;
339
0
    if (aDelData.isDeleted())
340
0
        return;
341
0
    Menu* pStartMenu = ImplGetStartMenu();
342
0
    if (!pStartMenu || (pStartMenu == this))
343
0
        return;
344
0
    pStartMenu->m_nSelectedId = m_nSelectedId;
345
0
    pStartMenu->m_sSelectedIdent = m_sSelectedIdent;
346
0
    pStartMenu->m_aSelectHdl.Call(this);
347
0
}
348
349
#if defined(MACOSX)
350
void Menu::ImplSelectWithStart( Menu* pSMenu )
351
{
352
    auto pOldStartedFrom = m_pStartedFrom;
353
    m_pStartedFrom = pSMenu;
354
    auto pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->m_pStartedFrom : VclPtr<Menu>();
355
    Select();
356
    if( pOldStartedFrom )
357
        pOldStartedFrom->m_pStartedFrom = pOldStartedStarted;
358
    m_pStartedFrom = pOldStartedFrom;
359
}
360
#endif
361
362
void Menu::ImplCallEventListeners( VclEventId nEvent, sal_uInt16 nPos )
363
0
{
364
0
    ImplMenuDelData aDelData( this );
365
366
0
    VclMenuEvent aEvent( this, nEvent, nPos );
367
368
    // This is needed by atk accessibility bridge
369
0
    if ( nEvent == VclEventId::MenuHighlight )
370
0
    {
371
0
        Application::ImplCallEventListeners( aEvent );
372
0
    }
373
374
0
    if ( !aDelData.isDeleted() )
375
0
    {
376
        // Copy the list, because this can be destroyed when calling a Link...
377
0
        std::list<Link<VclMenuEvent&, void>> aCopy(m_aEventListeners);
378
0
        for ( const auto& rLink : aCopy )
379
0
        {
380
0
            if (std::find(m_aEventListeners.begin(), m_aEventListeners.end(), rLink)
381
0
                != m_aEventListeners.end())
382
0
                rLink.Call( aEvent );
383
0
        }
384
0
    }
385
0
}
386
387
void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener )
388
0
{
389
0
    m_aEventListeners.push_back(rEventListener);
390
0
}
391
392
void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener )
393
0
{
394
0
    m_aEventListeners.remove(rEventListener);
395
0
}
396
397
MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits,
398
                                  const OUString& rStr, Menu* pMenu,
399
                                  size_t nPos, const OUString &rIdent)
400
0
{
401
    // put Item in MenuItemList
402
0
    MenuItemData* pData
403
0
        = m_pItemList->Insert(nId, MenuItemType::STRING, nBits, rStr, pMenu, nPos, rIdent);
404
405
    // update native menu
406
0
    if (ImplGetSalMenu() && pData->pSalMenuItem)
407
0
        ImplGetSalMenu()->InsertItem(pData->pSalMenuItem.get(), nPos);
408
409
0
    return pData;
410
0
}
411
412
void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits,
413
    const OUString &rIdent, sal_uInt16 nPos)
414
0
{
415
0
    SAL_WARN_IF( !nItemId, "vcl", "Menu::InsertItem(): ItemId == 0" );
416
0
    SAL_WARN_IF( GetItemPos( nItemId ) != MENU_ITEM_NOTFOUND, "vcl",
417
0
                "Menu::InsertItem(): ItemId already exists" );
418
419
    // if Position > ItemCount, append
420
0
    if (nPos >= m_pItemList->size())
421
0
        nPos = MENU_APPEND;
422
423
    // put Item in MenuItemList
424
0
    NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent);
425
426
0
    vcl::Window* pWin = GetWindow();
427
0
    m_pLayoutData.reset();
428
0
    if ( pWin )
429
0
    {
430
0
        ImplCalcSize(*pWin);
431
0
        if ( pWin->IsVisible() )
432
0
            pWin->Invalidate();
433
0
    }
434
0
    ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
435
0
}
436
437
void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage,
438
    MenuItemBits nItemBits, const OUString &rIdent, sal_uInt16 nPos)
439
0
{
440
0
    InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos);
441
0
    SetItemImage( nItemId, rImage );
442
0
}
443
444
void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr,
445
    const Image& rImage, MenuItemBits nItemBits,
446
    const OUString &rIdent, sal_uInt16 nPos)
447
0
{
448
0
    InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
449
0
    SetItemImage( nItemId, rImage );
450
0
}
451
452
void Menu::InsertSeparator(const OUString &rIdent, sal_uInt16 nPos)
453
0
{
454
    // do nothing if it's a menu bar
455
0
    if (IsMenuBar())
456
0
        return;
457
458
    // if position > ItemCount, append
459
0
    if (nPos >= m_pItemList->size())
460
0
        nPos = MENU_APPEND;
461
462
    // put separator in item list
463
0
    m_pItemList->InsertSeparator(rIdent, nPos);
464
465
    // update native menu
466
0
    size_t itemPos = (nPos != MENU_APPEND) ? nPos : m_pItemList->size() - 1;
467
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(itemPos);
468
0
    if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
469
0
        ImplGetSalMenu()->InsertItem( pData->pSalMenuItem.get(), nPos );
470
471
0
    m_pLayoutData.reset();
472
473
0
    ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
474
0
}
475
476
void Menu::RemoveItem( sal_uInt16 nPos )
477
0
{
478
0
    bool bRemove = false;
479
480
0
    if ( nPos < GetItemCount() )
481
0
    {
482
        // update native menu
483
0
        if( ImplGetSalMenu() )
484
0
            ImplGetSalMenu()->RemoveItem( nPos );
485
486
0
        m_pItemList->Remove(nPos);
487
0
        bRemove = true;
488
0
    }
489
490
0
    vcl::Window* pWin = GetWindow();
491
0
    if ( pWin )
492
0
    {
493
0
        ImplCalcSize(*pWin);
494
0
        if ( pWin->IsVisible() )
495
0
            pWin->Invalidate();
496
0
    }
497
0
    m_pLayoutData.reset();
498
499
0
    if ( bRemove )
500
0
        ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos );
501
0
}
502
503
static void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
504
0
{
505
0
    MenuItemType eType = rMenu.GetItemType( nPos );
506
507
0
    if ( eType == MenuItemType::DONTKNOW )
508
0
        return;
509
510
0
    if ( eType == MenuItemType::SEPARATOR )
511
0
        pThis->InsertSeparator( {}, nNewPos );
512
0
    else
513
0
    {
514
0
        sal_uInt16 nId = rMenu.GetItemId( nPos );
515
516
0
        SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
517
0
                    "Menu::CopyItem(): ItemId already exists" );
518
519
0
        MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
520
521
0
        if (!pData)
522
0
            return;
523
524
0
        if ( eType == MenuItemType::STRINGIMAGE )
525
0
            pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
526
0
        else if ( eType == MenuItemType::STRING )
527
0
            pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos );
528
0
        else
529
0
            pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
530
531
0
        if ( rMenu.IsItemChecked( nId ) )
532
0
            pThis->CheckItem( nId );
533
0
        if ( !rMenu.IsItemEnabled( nId ) )
534
0
            pThis->EnableItem( nId, false );
535
0
        pThis->SetHelpId( nId, pData->aHelpId );
536
0
        pThis->SetHelpText( nId, pData->aHelpText );
537
0
        pThis->SetAccelKey( nId, pData->aAccelKey );
538
0
        pThis->SetItemCommand( nId, pData->aCommandStr );
539
0
        pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
540
541
0
        PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
542
0
        if ( pSubMenu )
543
0
        {
544
            // create auto-copy
545
0
            VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
546
0
            pThis->SetPopupMenu( nId, pNewMenu );
547
0
        }
548
0
    }
549
0
}
550
551
void Menu::Clear()
552
0
{
553
0
    for ( sal_uInt16 i = GetItemCount(); i; i-- )
554
0
        RemoveItem( 0 );
555
0
}
556
557
0
sal_uInt16 Menu::GetItemCount() const { return static_cast<sal_uInt16>(m_pItemList->size()); }
558
559
bool Menu::HasValidEntries(bool bCheckPopups) const
560
0
{
561
0
    bool bValidEntries = false;
562
0
    sal_uInt16 nCount = GetItemCount();
563
0
    for (sal_uInt16 n = 0; !bValidEntries && (n < nCount); n++)
564
0
    {
565
0
        MenuItemData* pItem = m_pItemList->GetDataFromPos(n);
566
0
        if (pItem->bEnabled && (pItem->eType != MenuItemType::SEPARATOR))
567
0
        {
568
0
            if (bCheckPopups && pItem->pSubMenu)
569
0
                bValidEntries = pItem->pSubMenu->HasValidEntries(true);
570
0
            else
571
0
                bValidEntries = true;
572
0
        }
573
0
    }
574
0
    return bValidEntries;
575
0
}
576
577
sal_uInt16 Menu::ImplGetVisibleItemCount() const
578
0
{
579
0
    sal_uInt16 nItems = 0;
580
0
    for (size_t n = m_pItemList->size(); n;)
581
0
    {
582
0
        if ( ImplIsVisible( --n ) )
583
0
            nItems++;
584
0
    }
585
0
    return nItems;
586
0
}
587
588
sal_uInt16 Menu::ImplGetFirstVisible() const
589
0
{
590
0
    for (size_t n = 0; n < m_pItemList->size(); n++)
591
0
    {
592
0
        if ( ImplIsVisible( n ) )
593
0
            return n;
594
0
    }
595
0
    return ITEMPOS_INVALID;
596
0
}
597
598
sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
599
0
{
600
0
    for ( size_t n = nPos; n; )
601
0
    {
602
0
        if (ImplIsVisible(--n))
603
0
            return n;
604
0
    }
605
0
    return ITEMPOS_INVALID;
606
0
}
607
608
sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
609
0
{
610
0
    for (size_t n = nPos + 1; n < m_pItemList->size(); n++)
611
0
    {
612
0
        if ( ImplIsVisible( n ) )
613
0
            return n;
614
0
    }
615
0
    return ITEMPOS_INVALID;
616
0
}
617
618
sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
619
0
{
620
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(nPos);
621
622
0
    if ( pData )
623
0
        return pData->nId;
624
0
    else
625
0
        return 0;
626
0
}
627
628
sal_uInt16 Menu::GetItemId(std::u16string_view rIdent) const
629
0
{
630
0
    for (size_t n = 0; n < m_pItemList->size(); ++n)
631
0
    {
632
0
        MenuItemData* pData = m_pItemList->GetDataFromPos(n);
633
0
        if (pData && pData->sIdent == rIdent)
634
0
            return pData->nId;
635
0
    }
636
0
    return MENU_ITEM_NOTFOUND;
637
0
}
638
639
sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
640
0
{
641
0
    size_t          nPos;
642
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
643
644
0
    if ( pData )
645
0
        return static_cast<sal_uInt16>(nPos);
646
0
    else
647
0
        return MENU_ITEM_NOTFOUND;
648
0
}
649
650
MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
651
0
{
652
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(nPos);
653
654
0
    if ( pData )
655
0
        return pData->eType;
656
0
    else
657
0
        return MenuItemType::DONTKNOW;
658
0
}
659
660
OUString Menu::GetItemIdent(sal_uInt16 nId) const
661
0
{
662
0
    const MenuItemData* pData = m_pItemList->GetData(nId);
663
0
    return pData ? pData->sIdent : OUString();
664
0
}
665
666
void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
667
0
{
668
0
    size_t        nPos;
669
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
670
671
0
    if (pData && (pData->nBits != nBits))
672
0
    {
673
        // these two menu item bits are relevant for (accessible) role
674
0
        const MenuItemBits nRoleMask = MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK;
675
0
        const bool bRoleBitsChanged = (pData->nBits & nRoleMask) != (nBits & nRoleMask);
676
677
0
        pData->nBits = nBits;
678
679
        // update native menu
680
0
        if (ImplGetSalMenu())
681
0
            ImplGetSalMenu()->SetItemBits(nPos, nBits);
682
683
0
        if (bRoleBitsChanged)
684
0
            ImplCallEventListeners(VclEventId::MenuItemRoleChanged, nPos);
685
0
    }
686
0
}
687
688
MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
689
0
{
690
0
    MenuItemBits nBits = MenuItemBits::NONE;
691
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
692
0
    if ( pData )
693
0
        nBits = pData->nBits;
694
0
    return nBits;
695
0
}
696
697
void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc)
698
0
{
699
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
700
0
    if (pData)
701
0
    {
702
0
        if (pData->aUserValueReleaseFunc)
703
0
            pData->aUserValueReleaseFunc(pData->nUserValue);
704
0
        pData->aUserValueReleaseFunc = aFunc;
705
0
        pData->nUserValue = nUserValue;
706
0
    }
707
0
}
708
709
void* Menu::GetUserValue( sal_uInt16 nItemId ) const
710
0
{
711
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
712
0
    return pData ? pData->nUserValue : nullptr;
713
0
}
714
715
void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
716
0
{
717
0
    size_t          nPos;
718
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
719
720
    // Item does not exist -> return NULL
721
0
    if ( !pData )
722
0
        return;
723
724
    // same menu, nothing to do
725
0
    if ( pData->pSubMenu.get() == pMenu )
726
0
        return;
727
728
    // remove old menu
729
0
    auto oldSubMenu = pData->pSubMenu;
730
731
    // data exchange
732
0
    pData->pSubMenu = pMenu;
733
734
    // #112023# Make sure pStartedFrom does not point to invalid (old) data
735
0
    if ( pData->pSubMenu )
736
0
        pData->pSubMenu->m_pStartedFrom = nullptr;
737
738
    // set native submenu
739
0
    if( ImplGetSalMenu() && pData->pSalMenuItem )
740
0
    {
741
0
        if( pMenu )
742
0
            ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), pMenu->ImplGetSalMenu(), nPos );
743
0
        else
744
0
            ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), nullptr, nPos );
745
0
    }
746
747
0
    oldSubMenu.disposeAndClear();
748
749
0
    ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos );
750
0
}
751
752
PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
753
0
{
754
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
755
756
0
    if ( pData )
757
0
        return pData->pSubMenu.get();
758
0
    else
759
0
        return nullptr;
760
0
}
761
762
void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
763
0
{
764
0
    size_t          nPos;
765
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
766
767
0
    if ( !pData )
768
0
        return;
769
770
0
    if ( pData->aAccelKey == rKeyCode )
771
0
        return;
772
773
0
    pData->aAccelKey = rKeyCode;
774
775
    // update native menu
776
0
    if( ImplGetSalMenu() && pData->pSalMenuItem )
777
0
        ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem.get(), rKeyCode, rKeyCode.GetName() );
778
0
}
779
780
KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
781
0
{
782
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
783
784
0
    if ( pData )
785
0
        return pData->aAccelKey;
786
0
    else
787
0
        return KeyCode();
788
0
}
789
790
KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
791
0
{
792
0
    KeyEvent aRet;
793
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
794
0
    if( pData )
795
0
    {
796
0
        sal_Int32 nPos = pData->aText.indexOf( '~' );
797
0
        if( nPos != -1 && nPos < pData->aText.getLength()-1 )
798
0
        {
799
0
            sal_uInt16 nCode = 0;
800
0
            sal_Unicode cAccel = pData->aText[nPos+1];
801
0
            if( cAccel >= 'a' && cAccel <= 'z' )
802
0
                nCode = KEY_A + (cAccel-'a');
803
0
            else if( cAccel >= 'A' && cAccel <= 'Z' )
804
0
                nCode = KEY_A + (cAccel-'A');
805
0
            else if( cAccel >= '0' && cAccel <= '9' )
806
0
                nCode = KEY_0 + (cAccel-'0');
807
808
0
            aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
809
0
        }
810
811
0
    }
812
0
    return aRet;
813
0
}
814
815
void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
816
0
{
817
0
    size_t          nPos;
818
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
819
820
0
    if ( !pData || pData->bChecked == bCheck )
821
0
        return;
822
823
    // if radio-check, then uncheck previous
824
0
    if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
825
0
         (pData->nBits & MenuItemBits::RADIOCHECK) )
826
0
    {
827
0
        MenuItemData*   pGroupData;
828
0
        sal_uInt16          nGroupPos;
829
0
        sal_uInt16          nItemCount = GetItemCount();
830
0
        bool            bFound = false;
831
832
0
        nGroupPos = nPos;
833
0
        while ( nGroupPos )
834
0
        {
835
0
            pGroupData = m_pItemList->GetDataFromPos(nGroupPos - 1);
836
0
            if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
837
0
            {
838
0
                if ( IsItemChecked( pGroupData->nId ) )
839
0
                {
840
0
                    CheckItem( pGroupData->nId, false );
841
0
                    bFound = true;
842
0
                    break;
843
0
                }
844
0
            }
845
0
            else
846
0
                break;
847
0
            nGroupPos--;
848
0
        }
849
850
0
        if ( !bFound )
851
0
        {
852
0
            nGroupPos = nPos+1;
853
0
            while ( nGroupPos < nItemCount )
854
0
            {
855
0
                pGroupData = m_pItemList->GetDataFromPos(nGroupPos);
856
0
                if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
857
0
                {
858
0
                    if ( IsItemChecked( pGroupData->nId ) )
859
0
                    {
860
0
                        CheckItem( pGroupData->nId, false );
861
0
                        break;
862
0
                    }
863
0
                }
864
0
                else
865
0
                    break;
866
0
                nGroupPos++;
867
0
            }
868
0
        }
869
0
    }
870
871
0
    pData->bChecked = bCheck;
872
873
    // update native menu
874
0
    if( ImplGetSalMenu() )
875
0
        ImplGetSalMenu()->CheckItem( nPos, bCheck );
876
877
0
    ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos );
878
0
}
879
880
void Menu::CheckItem( std::u16string_view rIdent , bool bCheck )
881
0
{
882
0
    CheckItem( GetItemId( rIdent ), bCheck );
883
0
}
884
885
bool Menu::IsItemCheckable(sal_uInt16 nItemId) const
886
0
{
887
0
    size_t nPos;
888
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
889
890
0
    if (!pData)
891
0
        return false;
892
893
0
    return pData->HasCheck();
894
0
}
895
896
bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
897
0
{
898
0
    size_t          nPos;
899
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
900
901
0
    if ( !pData )
902
0
        return false;
903
904
0
    return pData->bChecked;
905
0
}
906
907
void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
908
0
{
909
0
    size_t          nPos;
910
0
    MenuItemData* pItemData = m_pItemList->GetData(nItemId, nPos);
911
912
0
    if ( !(pItemData && ( pItemData->bEnabled != bEnable )) )
913
0
        return;
914
915
0
    pItemData->bEnabled = bEnable;
916
917
0
    vcl::Window* pWin = GetWindow();
918
0
    if ( pWin && pWin->IsVisible() )
919
0
    {
920
0
        SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
921
0
        tools::Long nX = 0;
922
0
        size_t nCount = m_pItemList->size();
923
0
        for ( size_t n = 0; n < nCount; n++ )
924
0
        {
925
0
            MenuItemData* pData = m_pItemList->GetDataFromPos(n);
926
0
            if ( n == nPos )
927
0
            {
928
0
                pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
929
0
                break;
930
0
            }
931
0
            nX += pData->aSz.Width();
932
0
        }
933
0
    }
934
    // update native menu
935
0
    if( ImplGetSalMenu() )
936
0
        ImplGetSalMenu()->EnableItem( nPos, bEnable );
937
938
0
    ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos );
939
0
}
940
941
bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
942
0
{
943
0
    size_t          nPos;
944
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
945
946
0
    if ( !pData )
947
0
        return false;
948
949
0
    return pData->bEnabled;
950
0
}
951
952
void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
953
0
{
954
0
    size_t          nPos;
955
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
956
957
0
    SAL_WARN_IF(IsMenuBar() && !bVisible , "vcl", "Menu::ShowItem - ignored for menu bar entries!");
958
0
    if (IsMenuBar() || !pData || (pData->bVisible == bVisible))
959
0
        return;
960
961
0
    vcl::Window* pWin = GetWindow();
962
0
    if ( pWin && pWin->IsVisible() )
963
0
    {
964
0
        SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
965
0
        return;
966
0
    }
967
0
    pData->bVisible = bVisible;
968
969
    // update native menu
970
0
    if( ImplGetSalMenu() )
971
0
        ImplGetSalMenu()->ShowItem( nPos, bVisible );
972
0
}
973
974
void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
975
0
{
976
0
    size_t          nPos;
977
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
978
979
0
    if ( !pData )
980
0
        return;
981
982
0
    if ( rStr == pData->aText )
983
0
        return;
984
985
0
    pData->aText = rStr;
986
    // Clear layout for aText.
987
0
    pData->aTextGlyphs.Invalidate();
988
0
    ImplSetMenuItemData( pData );
989
    // update native menu
990
0
    if( ImplGetSalMenu() && pData->pSalMenuItem )
991
0
        ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem.get(), rStr );
992
993
0
    vcl::Window* pWin = GetWindow();
994
0
    m_pLayoutData.reset();
995
0
    if (pWin && IsMenuBar())
996
0
    {
997
0
        ImplCalcSize(*pWin);
998
0
        if ( pWin->IsVisible() )
999
0
            pWin->Invalidate();
1000
0
    }
1001
1002
0
    ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos );
1003
0
}
1004
1005
OUString Menu::GetItemText( sal_uInt16 nItemId ) const
1006
0
{
1007
0
    size_t          nPos;
1008
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
1009
1010
0
    if ( pData )
1011
0
        return pData->aText;
1012
1013
0
    return OUString();
1014
0
}
1015
1016
void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1017
0
{
1018
0
    size_t          nPos;
1019
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
1020
1021
0
    if ( !pData )
1022
0
        return;
1023
1024
0
    pData->aImage = rImage;
1025
0
    ImplSetMenuItemData( pData );
1026
1027
    // update native menu
1028
0
    if( ImplGetSalMenu() && pData->pSalMenuItem )
1029
0
        ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem.get(), rImage );
1030
0
}
1031
1032
Image Menu::GetItemImage( sal_uInt16 nItemId ) const
1033
0
{
1034
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1035
1036
0
    if ( pData )
1037
0
        return pData->aImage;
1038
0
    else
1039
0
        return Image();
1040
0
}
1041
1042
void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1043
0
{
1044
0
    size_t        nPos;
1045
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
1046
1047
0
    if ( pData )
1048
0
        pData->aCommandStr = rCommand;
1049
0
}
1050
1051
OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
1052
0
{
1053
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1054
1055
0
    if (pData)
1056
0
        return pData->aCommandStr;
1057
1058
0
    return OUString();
1059
0
}
1060
1061
void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
1062
0
{
1063
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1064
1065
0
    if ( pData )
1066
0
        pData->aHelpCommandStr = rStr;
1067
0
}
1068
1069
OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
1070
0
{
1071
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1072
1073
0
    if ( pData )
1074
0
        return pData->aHelpCommandStr;
1075
1076
0
    return OUString();
1077
0
}
1078
1079
void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
1080
0
{
1081
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1082
1083
0
    if ( pData )
1084
0
        pData->aHelpText = rStr;
1085
0
}
1086
1087
OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
1088
0
{
1089
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1090
1091
0
    if (!pData)
1092
0
        return OUString();
1093
1094
0
    if (!pData->aHelpText.isEmpty())
1095
0
        return pData->aHelpText;
1096
1097
0
    if (!pData->aHelpId.isEmpty() || !pData->aCommandStr.isEmpty())
1098
0
    {
1099
0
        Help* pHelp = Application::GetHelp();
1100
0
        if ( pHelp )
1101
0
        {
1102
0
            if (!pData->aCommandStr.isEmpty())
1103
0
                pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr );
1104
0
            if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty())
1105
0
                pData->aHelpText = pHelp->GetHelpText( pData->aHelpId );
1106
0
        }
1107
0
    }
1108
1109
    //Fallback to Menu::GetAccessibleDescription without reentry to GetHelpText()
1110
0
    return pData->aAccessibleDescription;
1111
0
}
1112
1113
void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
1114
0
{
1115
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1116
1117
0
    if ( pData )
1118
0
    {
1119
0
        pData->aTipHelpText = rStr;
1120
1121
0
        if (ImplGetSalMenu() && pData->pSalMenuItem)
1122
0
            ImplGetSalMenu()->SetItemTooltip(pData->pSalMenuItem.get(), rStr);
1123
0
    }
1124
0
}
1125
1126
OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
1127
0
{
1128
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1129
1130
0
    if ( pData )
1131
0
        return pData->aTipHelpText;
1132
1133
0
    return OUString();
1134
0
}
1135
1136
void Menu::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
1137
0
{
1138
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1139
1140
0
    if ( pData )
1141
0
        pData->aHelpId = rHelpId;
1142
0
}
1143
1144
OUString Menu::GetHelpId( sal_uInt16 nItemId ) const
1145
0
{
1146
0
    OUString aRet;
1147
1148
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
1149
1150
0
    if ( pData )
1151
0
    {
1152
0
        if ( !pData->aHelpId.isEmpty() )
1153
0
            aRet = pData->aHelpId;
1154
0
        else
1155
0
            aRet = pData->aCommandStr;
1156
0
    }
1157
1158
0
    return aRet;
1159
0
}
1160
1161
Menu& Menu::operator=( const Menu& rMenu )
1162
0
{
1163
0
    if(this == &rMenu)
1164
0
        return *this;
1165
1166
    // clean up
1167
0
    Clear();
1168
1169
    // copy items
1170
0
    sal_uInt16 nCount = rMenu.GetItemCount();
1171
0
    for ( sal_uInt16 i = 0; i < nCount; i++ )
1172
0
        ImplCopyItem( this, rMenu, i, MENU_APPEND );
1173
1174
0
    m_aActivateHdl = rMenu.m_aActivateHdl;
1175
0
    m_aDeactivateHdl = rMenu.m_aDeactivateHdl;
1176
0
    m_aSelectHdl = rMenu.m_aSelectHdl;
1177
0
    m_aTitleText = rMenu.m_aTitleText;
1178
0
    m_nTitleHeight = rMenu.m_nTitleHeight;
1179
1180
0
    return *this;
1181
0
}
1182
1183
// Returns true if the item is completely hidden on the GUI and shouldn't
1184
// be possible to interact with
1185
bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const
1186
0
{
1187
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(nPos);
1188
0
    if (pData)
1189
0
    {
1190
0
        MenuItemData* pPreviousData = m_pItemList->GetDataFromPos(nPos - 1);
1191
0
        if (pPreviousData && pPreviousData->bHiddenOnGUI)
1192
0
        {
1193
0
            return true;
1194
0
        }
1195
0
    }
1196
0
    return false;
1197
0
}
1198
1199
bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
1200
0
{
1201
0
    bool bVisible = true;
1202
1203
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(nPos);
1204
    // check general visibility first
1205
0
    if( pData && !pData->bVisible )
1206
0
        bVisible = false;
1207
1208
0
    if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
1209
0
    {
1210
0
        if( nPos == 0 ) // no separator should be shown at the very beginning
1211
0
            bVisible = false;
1212
0
        else
1213
0
        {
1214
            // always avoid adjacent separators
1215
0
            size_t nCount = m_pItemList->size();
1216
0
            size_t n;
1217
0
            MenuItemData* pNextData = nullptr;
1218
            // search next visible item
1219
0
            for( n = nPos + 1; n < nCount; n++ )
1220
0
            {
1221
0
                pNextData = m_pItemList->GetDataFromPos(n);
1222
0
                if( pNextData && pNextData->bVisible )
1223
0
                {
1224
0
                    if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
1225
0
                        break;
1226
0
                }
1227
0
            }
1228
0
            if( n == nCount ) // no next visible item
1229
0
                bVisible = false;
1230
            // check for separator
1231
0
            if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
1232
0
                bVisible = false;
1233
1234
0
            if( bVisible )
1235
0
            {
1236
0
                for( n = nPos; n > 0; n-- )
1237
0
                {
1238
0
                    pNextData = m_pItemList->GetDataFromPos(n - 1);
1239
0
                    if( pNextData && pNextData->bVisible )
1240
0
                    {
1241
0
                        if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
1242
0
                            break;
1243
0
                    }
1244
0
                }
1245
0
                if( n == 0 ) // no previous visible item
1246
0
                    bVisible = false;
1247
0
            }
1248
0
        }
1249
0
    }
1250
1251
    // not allowed for menubar, as I do not know
1252
    // whether a menu-entry will disappear or will appear
1253
0
    if (bVisible && !IsMenuBar() && (m_nMenuFlags & MenuFlags::HideDisabledEntries)
1254
0
        && !(m_nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
1255
0
    {
1256
0
        if( !pData ) // e.g. nPos == ITEMPOS_INVALID
1257
0
            bVisible = false;
1258
0
        else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
1259
0
        {
1260
            // tdf#86850 Always display clipboard functions
1261
0
            if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" ||
1262
0
                 pData->sIdent == ".uno:Cut" || pData->sIdent == ".uno:Copy" || pData->sIdent == ".uno:Paste" )
1263
0
                bVisible = true;
1264
0
            else
1265
                // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1266
0
                bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
1267
0
        }
1268
0
    }
1269
1270
0
    return bVisible;
1271
0
}
1272
1273
bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
1274
0
{
1275
0
    return IsMenuVisible() && ImplIsVisible( nItemPos );
1276
0
}
1277
1278
bool Menu::IsMenuVisible() const
1279
0
{
1280
0
    return m_pWindow && m_pWindow->IsReallyVisible();
1281
0
}
1282
1283
bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
1284
0
{
1285
0
    bool bSelectable = true;
1286
1287
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(nPos);
1288
    // check general visibility first
1289
0
    if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
1290
0
        bSelectable = false;
1291
1292
0
    return bSelectable;
1293
0
}
1294
1295
rtl::Reference<comphelper::OAccessible> Menu::CreateAccessible()
1296
0
{
1297
0
    rtl::Reference<OAccessibleMenuBaseComponent> xAccessible;
1298
0
    if (IsMenuBar())
1299
0
        xAccessible = new VCLXAccessibleMenuBar(this);
1300
0
    else
1301
0
        xAccessible = new VCLXAccessiblePopupMenu(this);
1302
0
    xAccessible->SetStates();
1303
0
    return xAccessible;
1304
0
}
1305
1306
rtl::Reference<comphelper::OAccessible> Menu::GetAccessible()
1307
0
{
1308
    // Since PopupMenu are sometimes shared by different instances of MenuBar, the mpAccessible member gets
1309
    // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1310
    // mpAccessible member only for sub menus.
1311
0
    if (m_pStartedFrom && m_pStartedFrom != this)
1312
0
    {
1313
0
        for (sal_uInt16 i = 0, nCount = m_pStartedFrom->GetItemCount(); i < nCount; ++i)
1314
0
        {
1315
0
            sal_uInt16 nItemId = m_pStartedFrom->GetItemId(i);
1316
0
            if (static_cast<Menu*>(m_pStartedFrom->GetPopupMenu(nItemId)) == this)
1317
0
            {
1318
0
                rtl::Reference<comphelper::OAccessible> pParent = m_pStartedFrom->GetAccessible();
1319
0
                if (pParent.is())
1320
0
                {
1321
0
                    css::uno::Reference<css::accessibility::XAccessible> xAcc = pParent->getAccessibleChild(i);
1322
0
                    if (!xAcc)
1323
0
                        return {};
1324
1325
0
                    rtl::Reference<comphelper::OAccessible> pAccessible = dynamic_cast<comphelper::OAccessible*>(xAcc.get());
1326
0
                    assert(pAccessible.is() && "Accessible is not an OAccessible");
1327
0
                    return pAccessible;
1328
0
                }
1329
0
            }
1330
0
        }
1331
0
    }
1332
0
    else if (!m_pAccessible.is())
1333
0
        m_pAccessible = CreateAccessible();
1334
1335
0
    return m_pAccessible;
1336
0
}
1337
1338
void Menu::SetAccessible(const rtl::Reference<comphelper::OAccessible>& rAccessible)
1339
0
{
1340
0
    m_pAccessible = rAccessible;
1341
0
}
1342
1343
Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, tools::Long& rCheckHeight, tools::Long& rRadioHeight ) const
1344
0
{
1345
0
    tools::Long nCheckWidth = 0, nRadioWidth = 0;
1346
0
    rCheckHeight = rRadioHeight = 0;
1347
1348
0
    if (!IsMenuBar())
1349
0
    {
1350
0
        ImplControlValue aVal;
1351
0
        tools::Rectangle aNativeBounds;
1352
0
        tools::Rectangle aNativeContent;
1353
1354
0
        tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1355
0
        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
1356
0
        {
1357
0
            if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
1358
0
                                              aCtrlRegion, ControlState::ENABLED, aVal,
1359
0
                                              aNativeBounds, aNativeContent))
1360
0
            {
1361
0
                rCheckHeight = aNativeBounds.GetHeight() - 1;
1362
0
                nCheckWidth = aNativeContent.GetWidth() - 1;
1363
0
            }
1364
0
        }
1365
0
        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
1366
0
        {
1367
0
            if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
1368
0
                                                      aCtrlRegion, ControlState::ENABLED, aVal,
1369
0
                                                      aNativeBounds, aNativeContent))
1370
0
            {
1371
0
                rRadioHeight = aNativeBounds.GetHeight() - 1;
1372
0
                nRadioWidth = aNativeContent.GetWidth() - 1;
1373
0
            }
1374
0
        }
1375
0
    }
1376
0
    return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
1377
0
}
1378
1379
bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, tools::Long& rArrowSpacing)
1380
0
{
1381
0
    ImplControlValue aVal;
1382
0
    tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1383
0
    if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
1384
0
    {
1385
0
        tools::Rectangle aNativeContent;
1386
0
        tools::Rectangle aNativeBounds;
1387
0
        if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
1388
0
                                                  aCtrlRegion, ControlState::ENABLED,
1389
0
                                                  aVal, aNativeBounds, aNativeContent))
1390
0
        {
1391
0
            Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
1392
0
            rArrowSize = aSize;
1393
0
            rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
1394
0
            return true;
1395
0
        }
1396
0
    }
1397
0
    return false;
1398
0
}
1399
1400
void Menu::ImplAddDel( ImplMenuDelData& rDel )
1401
0
{
1402
0
    SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1403
0
    if( !rDel.mpMenu )
1404
0
    {
1405
0
        rDel.mpMenu = this;
1406
0
        rDel.mpNext = m_pFirstDel;
1407
0
        m_pFirstDel = &rDel;
1408
0
    }
1409
0
}
1410
1411
void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
1412
0
{
1413
0
    rDel.mpMenu = nullptr;
1414
0
    if (m_pFirstDel == &rDel)
1415
0
    {
1416
0
        m_pFirstDel = rDel.mpNext;
1417
0
    }
1418
0
    else
1419
0
    {
1420
0
        ImplMenuDelData* pData = m_pFirstDel;
1421
0
        while ( pData && (pData->mpNext != &rDel) )
1422
0
            pData = pData->mpNext;
1423
1424
0
        SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1425
0
        if( pData )
1426
0
            pData->mpNext = rDel.mpNext;
1427
0
    }
1428
0
}
1429
1430
Size Menu::ImplCalcSize(vcl::Window& rWin)
1431
0
{
1432
    // | Check/Radio/Image| Text| Accel/Popup|
1433
1434
    // for symbols: nFontHeight x nFontHeight
1435
0
    tools::Long nFontHeight = rWin.GetTextHeight();
1436
0
    tools::Long nExtra = nFontHeight/4;
1437
1438
0
    tools::Long nMinMenuItemHeight = nFontHeight;
1439
0
    tools::Long nCheckHeight = 0, nRadioHeight = 0;
1440
0
    Size aMarkSize = ImplGetNativeCheckAndRadioSize(*rWin.GetOutDev(), nCheckHeight, nRadioHeight);
1441
0
    if( aMarkSize.Height() > nMinMenuItemHeight )
1442
0
        nMinMenuItemHeight = aMarkSize.Height();
1443
1444
0
    tools::Long aMaxImgWidth = 0;
1445
1446
0
    const StyleSettings& rSettings = rWin.GetSettings().GetStyleSettings();
1447
0
    if ( rSettings.GetUseImagesInMenus() )
1448
0
    {
1449
0
        if ( 16 > nMinMenuItemHeight )
1450
0
            nMinMenuItemHeight = 16;
1451
0
        for (size_t i = m_pItemList->size(); i;)
1452
0
        {
1453
0
            MenuItemData* pData = m_pItemList->GetDataFromPos(--i);
1454
0
            if ( ImplIsVisible( i )
1455
0
               && (  ( pData->eType == MenuItemType::IMAGE )
1456
0
                  || ( pData->eType == MenuItemType::STRINGIMAGE )
1457
0
                  )
1458
0
               )
1459
0
            {
1460
0
                Size aImgSz = pData->aImage.GetSizePixel();
1461
0
                if ( aImgSz.Width() > aMaxImgWidth )
1462
0
                    aMaxImgWidth = aImgSz.Width();
1463
0
                if ( aImgSz.Height() > nMinMenuItemHeight )
1464
0
                    nMinMenuItemHeight = aImgSz.Height();
1465
0
                break;
1466
0
            }
1467
0
        }
1468
0
    }
1469
1470
0
    Size aSz;
1471
0
    tools::Long nMaxWidth = 0;
1472
1473
0
    for (size_t n = m_pItemList->size(); n;)
1474
0
    {
1475
0
        MenuItemData* pData = m_pItemList->GetDataFromPos(--n);
1476
1477
0
        pData->aSz.setHeight( 0 );
1478
0
        pData->aSz.setWidth( 0 );
1479
1480
0
        if ( ImplIsVisible( n ) )
1481
0
        {
1482
0
            tools::Long nWidth = 0;
1483
1484
            // Separator
1485
0
            if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
1486
0
            {
1487
0
                pData->aSz.setHeight( 4 );
1488
0
            }
1489
1490
            // Image:
1491
0
            if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1492
0
            {
1493
0
                tools::Long aImgHeight = pData->aImage.GetSizePixel().Height();
1494
1495
0
                aImgHeight += 4; // add a border for native marks
1496
0
                if (aImgHeight > pData->aSz.Height())
1497
0
                    pData->aSz.setHeight(aImgHeight);
1498
0
            }
1499
1500
            // Check Buttons:
1501
0
            if (!IsMenuBar() && pData->HasCheck())
1502
0
            {
1503
                // checks / images take the same place
1504
0
                if( ( pData->eType != MenuItemType::IMAGE ) && ( pData->eType != MenuItemType::STRINGIMAGE ) )
1505
0
                {
1506
0
                    nWidth += aMarkSize.Width() + nExtra * 2;
1507
0
                    if (aMarkSize.Height() > pData->aSz.Height())
1508
0
                        pData->aSz.setHeight(aMarkSize.Height());
1509
0
                }
1510
0
            }
1511
1512
            // Text:
1513
0
            if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
1514
0
            {
1515
0
                const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(rWin.GetOutDev());
1516
0
                tools::Long nTextWidth = rWin.GetOutDev()->GetCtrlTextWidth(pData->aText, pGlyphs);
1517
0
                tools::Long nTextHeight = rWin.GetTextHeight() + EXTRAITEMHEIGHT;
1518
1519
0
                if (IsMenuBar())
1520
0
                {
1521
0
                    if ( nTextHeight > pData->aSz.Height() )
1522
0
                        pData->aSz.setHeight( nTextHeight );
1523
1524
0
                    pData->aSz.setWidth( nTextWidth + 4*nExtra );
1525
0
                    aSz.AdjustWidth(pData->aSz.Width() );
1526
0
                }
1527
0
                else
1528
0
                    pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1529
1530
0
                nWidth += nTextWidth;
1531
0
            }
1532
1533
            // Accel
1534
0
            if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !officecfg::VCL::VCLSettings::Menu::SuppressAccelerators::get())
1535
0
            {
1536
0
                OUString aName = pData->aAccelKey.GetName();
1537
0
                tools::Long nAccWidth = rWin.GetTextWidth(aName);
1538
0
                nAccWidth += nExtra;
1539
0
                nWidth += nAccWidth;
1540
0
            }
1541
1542
            // SubMenu?
1543
0
            if (!IsMenuBar() && pData->pSubMenu)
1544
0
            {
1545
0
                if ( nFontHeight > nWidth )
1546
0
                    nWidth += nFontHeight;
1547
1548
0
                pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1549
0
            }
1550
1551
0
            if (!IsMenuBar())
1552
0
                aSz.AdjustHeight(pData->aSz.Height() );
1553
1554
0
            if ( nWidth > nMaxWidth )
1555
0
                nMaxWidth = nWidth;
1556
1557
0
        }
1558
0
    }
1559
1560
    // Additional space for title
1561
0
    m_nTitleHeight = 0;
1562
0
    if (!IsMenuBar() && m_aTitleText.getLength() > 0)
1563
0
    {
1564
        // Set expected font
1565
0
        auto popIt = rWin.GetOutDev()->ScopedPush(PushFlags::FONT);
1566
0
        vcl::Font aFont = rWin.GetFont();
1567
0
        aFont.SetWeight(WEIGHT_BOLD);
1568
0
        rWin.SetFont(aFont);
1569
1570
        // Compute text bounding box
1571
0
        tools::Rectangle aTextBoundRect;
1572
0
        rWin.GetOutDev()->GetTextBoundRect(aTextBoundRect, m_aTitleText);
1573
1574
        // Vertically, one height of char + extra space for decoration
1575
0
        m_nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE;
1576
0
        aSz.AdjustHeight(m_nTitleHeight);
1577
1578
0
        tools::Long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
1579
0
        if ( nWidth > nMaxWidth )
1580
0
            nMaxWidth = nWidth;
1581
0
    }
1582
1583
0
    if (!IsMenuBar())
1584
0
    {
1585
        // popup menus should not be wider than half the screen
1586
        // except on rather small screens
1587
        // TODO: move GetScreenNumber from SystemWindow to Window ?
1588
        // currently we rely on internal privileges
1589
0
        unsigned int nDisplayScreen
1590
0
            = rWin.ImplGetWindowImpl()->mpFrame->GetUnmirroredGeometry().screen();
1591
0
        tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
1592
0
        tools::Long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
1593
0
        if( nMaxWidth > nScreenWidth/2 )
1594
0
            nMaxWidth = nScreenWidth/2;
1595
1596
0
        sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
1597
0
        m_nImgOrChkPos = static_cast<sal_uInt16>(nExtra);
1598
0
        tools::Long nImgOrChkWidth = 0;
1599
0
        if( aMarkSize.Height() > 0 ) // NWF case
1600
0
            nImgOrChkWidth = aMarkSize.Height() + nExtra;
1601
0
        else // non NWF case
1602
0
            nImgOrChkWidth = nFontHeight/2 + gfxExtra;
1603
0
        nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgWidth + gfxExtra );
1604
0
        m_nTextPos = static_cast<sal_uInt16>(m_nImgOrChkPos + nImgOrChkWidth);
1605
0
        m_nTextPos = m_nTextPos + gfxExtra;
1606
1607
0
        aSz.setWidth(m_nTextPos + nMaxWidth + nExtra);
1608
0
        aSz.AdjustWidth(4*nExtra );   // a _little_ more ...
1609
1610
0
        aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX );
1611
0
        aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1612
0
    }
1613
0
    else
1614
0
    {
1615
0
        m_nTextPos = static_cast<sal_uInt16>(2 * nExtra);
1616
0
        aSz.setHeight( nFontHeight+6 );
1617
1618
        // get menubar height from native methods if supported
1619
0
        if (m_pWindow->IsNativeControlSupported(ControlType::Menubar, ControlPart::Entire))
1620
0
        {
1621
0
            ImplControlValue aVal;
1622
0
            tools::Rectangle aNativeBounds;
1623
0
            tools::Rectangle aNativeContent;
1624
0
            Point tmp( 0, 0 );
1625
0
            tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
1626
0
            if (m_pWindow->GetNativeControlRegion(ControlType::Menubar,
1627
0
                                                  ControlPart::Entire,
1628
0
                                                  aCtrlRegion,
1629
0
                                                  ControlState::ENABLED,
1630
0
                                                  aVal,
1631
0
                                                  aNativeBounds,
1632
0
                                                  aNativeContent)
1633
0
            )
1634
0
            {
1635
0
                int nNativeHeight = aNativeBounds.GetHeight();
1636
0
                if( nNativeHeight > aSz.Height() )
1637
0
                    aSz.setHeight( nNativeHeight );
1638
0
            }
1639
0
        }
1640
1641
        // account for the size of the close button, which actually is a toolbox
1642
        // due to NWF this is variable
1643
0
        tools::Long nCloseButtonHeight = static_cast<MenuBarWindow*>(m_pWindow.get())->MinCloseButtonSize().Height();
1644
0
        if (aSz.Height() < nCloseButtonHeight)
1645
0
            aSz.setHeight( nCloseButtonHeight );
1646
0
    }
1647
1648
0
    return aSz;
1649
0
}
1650
1651
static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight)
1652
0
{
1653
0
    bool bNativeOk = false;
1654
1655
0
    if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
1656
0
    {
1657
0
        ImplControlValue    aControlValue;
1658
0
        aControlValue.setTristateVal(ButtonValue::On);
1659
0
        tools::Rectangle r = i_rRect;
1660
0
        r.AdjustBottom(1);
1661
1662
0
        bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
1663
0
                                                     r,
1664
0
                                                     ControlState::PRESSED | ControlState::ENABLED,
1665
0
                                                     aControlValue,
1666
0
                                                     OUString());
1667
0
    }
1668
1669
0
    if (!bNativeOk)
1670
0
    {
1671
0
        const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1672
0
        Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
1673
0
        rRenderContext.DrawSelectionBackground(i_rRect, rWindow.GetBackgroundColor(), 0,
1674
0
                                               i_bHighlight, true, false, nullptr, 2, &aColor);
1675
0
    }
1676
0
}
1677
1678
static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, tools::Long i_nMaxWidth )
1679
0
{
1680
0
    sal_Int32 nPos = -1;
1681
0
    OUString aNonMnem(removeMnemonicFromString(i_rLong, nPos));
1682
0
    aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
1683
    // re-insert mnemonic
1684
0
    if (nPos != -1)
1685
0
    {
1686
0
        if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
1687
0
            aNonMnem = OUString::Concat(aNonMnem.subView(0, nPos)) + "~" + aNonMnem.subView(nPos);
1688
0
    }
1689
0
    return aNonMnem;
1690
0
}
1691
1692
void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const
1693
0
{
1694
    // Save previous graphical settings, set new one
1695
0
    rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
1696
0
    Wallpaper aOldBackground = rRenderContext.GetBackground();
1697
1698
0
    Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
1699
0
    rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
1700
0
    rRenderContext.SetFillColor(aBackgroundColor);
1701
0
    vcl::Font aFont = rRenderContext.GetFont();
1702
0
    aFont.SetWeight(WEIGHT_BOLD);
1703
0
    rRenderContext.SetFont(aFont);
1704
1705
    // Draw background rectangle
1706
0
    tools::Rectangle aBgRect(rRect);
1707
0
    int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1708
0
    aBgRect.Move(SPACE_AROUND_TITLE, SPACE_AROUND_TITLE);
1709
0
    aBgRect.setWidth(aBgRect.getOpenWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
1710
0
    aBgRect.setHeight(m_nTitleHeight - 2 * SPACE_AROUND_TITLE);
1711
0
    rRenderContext.DrawRect(aBgRect);
1712
1713
    // Draw the text centered
1714
0
    Point aTextTopLeft(aBgRect.TopLeft());
1715
0
    tools::Rectangle aTextBoundRect;
1716
0
    rRenderContext.GetTextBoundRect(aTextBoundRect, m_aTitleText);
1717
0
    aTextTopLeft.AdjustX((aBgRect.getOpenWidth() - aTextBoundRect.GetSize().Width()) / 2 );
1718
0
    aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
1719
0
                        - aTextBoundRect.Top() );
1720
0
    rRenderContext.DrawText(aTextTopLeft, m_aTitleText, 0, m_aTitleText.getLength());
1721
1722
    // Restore
1723
0
    rRenderContext.Pop();
1724
0
    rRenderContext.SetBackground(aOldBackground);
1725
0
}
1726
1727
void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize,
1728
                     sal_uInt16 nBorder, tools::Long nStartY, MenuItemData const * pThisItemOnly,
1729
                     bool bHighlighted, bool bLayout, bool bRollover) const
1730
0
{
1731
    // for symbols: nFontHeight x nFontHeight
1732
0
    tools::Long nFontHeight = rRenderContext.GetTextHeight();
1733
0
    tools::Long nExtra = nFontHeight / 4;
1734
1735
0
    tools::Long nCheckHeight = 0, nRadioHeight = 0;
1736
0
    ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
1737
1738
0
    DecorationView aDecoView(&rRenderContext);
1739
0
    const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1740
1741
0
    Point aTopLeft, aTmpPos;
1742
1743
0
    int nOuterSpaceX = 0;
1744
0
    if (!IsMenuBar())
1745
0
    {
1746
0
        nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1747
0
        aTopLeft.AdjustX(nOuterSpaceX );
1748
0
        aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1749
0
    }
1750
1751
    // for the computations, use size of the underlying window, not of RenderContext
1752
0
    Size aOutSz(rSize);
1753
1754
0
    size_t nCount = m_pItemList->size();
1755
0
    if (bLayout)
1756
0
        m_pLayoutData->m_aVisibleItemBoundRects.clear();
1757
1758
    // Paint title
1759
0
    if (!pThisItemOnly && !IsMenuBar() && m_nTitleHeight > 0)
1760
0
        ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz));
1761
1762
0
    bool bHiddenItems = false; // are any items on the GUI hidden
1763
1764
0
    for (size_t n = 0; n < nCount; n++)
1765
0
    {
1766
0
        MenuItemData* pData = m_pItemList->GetDataFromPos(n);
1767
0
        if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
1768
0
        {
1769
0
            if (pThisItemOnly)
1770
0
            {
1771
0
                if (IsMenuBar())
1772
0
                {
1773
0
                    if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
1774
0
                    {
1775
0
                        if (bRollover)
1776
0
                            rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1777
0
                        else if (bHighlighted)
1778
0
                            rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1779
0
                    }
1780
0
                    else
1781
0
                    {
1782
0
                        if (bHighlighted)
1783
0
                            rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1784
0
                        else if (bRollover)
1785
0
                            rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1786
0
                    }
1787
0
                    if (!bRollover && !bHighlighted)
1788
0
                        rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
1789
0
                }
1790
0
                else if (bHighlighted)
1791
0
                    rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1792
0
            }
1793
1794
0
            Point aPos(aTopLeft);
1795
0
            aPos.AdjustY(nBorder );
1796
0
            aPos.AdjustY(nStartY );
1797
1798
0
            if (aPos.Y() >= 0)
1799
0
            {
1800
0
                tools::Long nTextOffsetY = (pData->aSz.Height() - nFontHeight) / 2;
1801
0
                if (IsMenuBar())
1802
0
                    nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
1803
0
                DrawTextFlags   nTextStyle   = DrawTextFlags::NONE;
1804
0
                DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
1805
0
                DrawImageFlags  nImageStyle  = DrawImageFlags::NONE;
1806
1807
                // submenus without items are not disabled when no items are
1808
                // contained. The application itself should check for this!
1809
                // Otherwise it could happen entries are disabled due to
1810
                // asynchronous loading
1811
0
                if (!pData->bEnabled || !m_pWindow->IsEnabled())
1812
0
                {
1813
0
                    nTextStyle   |= DrawTextFlags::Disable;
1814
0
                    nSymbolStyle |= DrawSymbolFlags::Disable;
1815
0
                    nImageStyle  |= DrawImageFlags::Disable;
1816
0
                }
1817
1818
                // Separator
1819
0
                if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
1820
0
                {
1821
0
                    bool bNativeOk = false;
1822
0
                    if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
1823
0
                    {
1824
0
                        ControlState nState = ControlState::NONE;
1825
0
                        if (pData->bEnabled && m_pWindow->IsEnabled())
1826
0
                            nState |= ControlState::ENABLED;
1827
0
                        if (bHighlighted)
1828
0
                            nState |= ControlState::SELECTED;
1829
0
                        Size aSz(pData->aSz);
1830
0
                        aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX );
1831
0
                        tools::Rectangle aItemRect(aPos, aSz);
1832
0
                        MenupopupValue aVal(m_nTextPos - GUTTERBORDER, aItemRect);
1833
0
                        bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
1834
0
                                                                     aItemRect, nState, aVal, OUString());
1835
0
                    }
1836
0
                    if (!bNativeOk)
1837
0
                    {
1838
0
                        aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) );
1839
0
                        aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX );
1840
0
                        rRenderContext.SetLineColor(rSettings.GetShadowColor());
1841
0
                        rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1842
0
                        aTmpPos.AdjustY( 1 );
1843
0
                        rRenderContext.SetLineColor(rSettings.GetLightColor());
1844
0
                        rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1845
0
                        rRenderContext.SetLineColor();
1846
0
                    }
1847
0
                }
1848
1849
0
                tools::Rectangle aOuterCheckRect(Point(aPos.X() + m_nImgOrChkPos, aPos.Y()),
1850
0
                                                 Size(pData->aSz.Height(), pData->aSz.Height()));
1851
1852
                // CheckMark
1853
0
                if (!bLayout && !IsMenuBar() && pData->HasCheck())
1854
0
                {
1855
                    // draw selection transparent marker if checked
1856
                    // onto that either a checkmark or the item image
1857
                    // will be painted
1858
                    // however do not do this if native checks will be painted since
1859
                    // the selection color too often does not fit the theme's check and/or radio
1860
1861
0
                    if( (pData->eType != MenuItemType::IMAGE) && (pData->eType != MenuItemType::STRINGIMAGE))
1862
0
                    {
1863
0
                        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
1864
0
                                                                    (pData->nBits & MenuItemBits::RADIOCHECK)
1865
0
                                                                        ? ControlPart::MenuItemCheckMark
1866
0
                                                                        : ControlPart::MenuItemRadioMark))
1867
0
                        {
1868
0
                            ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
1869
0
                                                 ? ControlPart::MenuItemRadioMark
1870
0
                                                 : ControlPart::MenuItemCheckMark);
1871
1872
0
                            ControlState nState = ControlState::NONE;
1873
1874
0
                            if (pData->bChecked)
1875
0
                                nState |= ControlState::PRESSED;
1876
1877
0
                            if (pData->bEnabled && m_pWindow->IsEnabled())
1878
0
                                nState |= ControlState::ENABLED;
1879
1880
0
                            if (bHighlighted)
1881
0
                                nState |= ControlState::SELECTED;
1882
1883
0
                            tools::Long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
1884
0
                            aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 );
1885
0
                            aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 );
1886
1887
0
                            tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
1888
0
                            Size aSz(pData->aSz);
1889
0
                            aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX );
1890
0
                            tools::Rectangle aItemRect(aPos, aSz);
1891
0
                            MenupopupValue aVal(m_nTextPos - GUTTERBORDER, aItemRect);
1892
0
                            rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
1893
0
                                                             nState, aVal, OUString());
1894
0
                        }
1895
0
                        else if (pData->bChecked) // by default do nothing for unchecked items
1896
0
                        {
1897
0
                            ImplPaintCheckBackground(rRenderContext, *m_pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1898
1899
0
                            SymbolType eSymbol;
1900
0
                            Size aSymbolSize;
1901
0
                            if (pData->nBits & MenuItemBits::RADIOCHECK)
1902
0
                            {
1903
0
                                eSymbol = SymbolType::RADIOCHECKMARK;
1904
0
                                aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
1905
0
                            }
1906
0
                            else
1907
0
                            {
1908
0
                                eSymbol = SymbolType::CHECKMARK;
1909
0
                                aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
1910
0
                            }
1911
0
                            aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 );
1912
0
                            aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 );
1913
0
                            tools::Rectangle aRect(aTmpPos, aSymbolSize);
1914
0
                            aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
1915
0
                        }
1916
0
                    }
1917
0
                }
1918
1919
                // Image:
1920
0
                if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1921
0
                {
1922
                    // Don't render an image for a check thing
1923
0
                    if (pData->bChecked)
1924
0
                        ImplPaintCheckBackground(rRenderContext, *m_pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1925
1926
0
                    Image aImage = pData->aImage;
1927
1928
0
                    aTmpPos = aOuterCheckRect.TopLeft();
1929
0
                    aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 );
1930
0
                    aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 );
1931
0
                    rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
1932
0
                }
1933
1934
                // Text:
1935
0
                if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
1936
0
                {
1937
0
                    aTmpPos.setX(aPos.X() + m_nTextPos);
1938
0
                    aTmpPos.setY( aPos.Y() );
1939
0
                    aTmpPos.AdjustY(nTextOffsetY );
1940
0
                    DrawTextFlags nStyle = nTextStyle;
1941
1942
0
                    const Menu *pMenu = this;
1943
0
                    while (!pMenu->IsMenuBar() && pMenu->m_pStartedFrom)
1944
0
                        pMenu = pMenu->m_pStartedFrom;
1945
0
                    if (!pMenu->IsMenuBar() || !static_cast<MenuBarWindow*>(pMenu->m_pWindow.get())->GetMBWHideAccel())
1946
0
                        nStyle |= DrawTextFlags::Mnemonic;
1947
1948
0
                    if (pData->bIsTemporary)
1949
0
                        nStyle |= DrawTextFlags::Disable;
1950
0
                    std::vector<tools::Rectangle>* pVector
1951
0
                        = bLayout ? &m_pLayoutData->m_aUnicodeBoundRects : nullptr;
1952
0
                    OUString* pDisplayText = bLayout ? &m_pLayoutData->m_aDisplayText : nullptr;
1953
0
                    if (bLayout)
1954
0
                    {
1955
0
                        m_pLayoutData->m_aLineIndices.push_back(
1956
0
                            m_pLayoutData->m_aDisplayText.getLength());
1957
0
                        m_pLayoutData->m_aLineItemIds.push_back(pData->nId);
1958
0
                    }
1959
                    // #i47946# with NWF painted menus the background is transparent
1960
                    // since DrawCtrlText can depend on the background (e.g. for
1961
                    // DrawTextFlags::Disable), temporarily set a background which
1962
                    // hopefully matches the NWF background since it is read
1963
                    // from the system style settings
1964
0
                    bool bSetTmpBackground = !rRenderContext.IsBackground()
1965
0
                                           && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire);
1966
0
                    if (bSetTmpBackground)
1967
0
                    {
1968
0
                        Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor()
1969
0
                                                : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor();
1970
0
                        rRenderContext.SetBackground(Wallpaper(aBg));
1971
0
                    }
1972
                    // how much space is there for the text?
1973
0
                    tools::Long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX;
1974
0
                    if (!IsMenuBar() && pData->aAccelKey.GetCode() && !officecfg::VCL::VCLSettings::Menu::SuppressAccelerators::get())
1975
0
                    {
1976
0
                        OUString aAccText = pData->aAccelKey.GetName();
1977
0
                        nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra;
1978
0
                    }
1979
0
                    if (!IsMenuBar() && pData->pSubMenu)
1980
0
                    {
1981
0
                        nMaxItemTextWidth -= nFontHeight - nExtra;
1982
0
                    }
1983
1984
0
                    OUString aItemText(pData->aText);
1985
0
                    pData->bHiddenOnGUI = false;
1986
1987
0
                    if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1988
0
                    {
1989
0
                        if (nMaxItemTextWidth < rRenderContext.GetTextWidth(aItemText))
1990
0
                        {
1991
0
                            aItemText = "";
1992
0
                            pData->bHiddenOnGUI = true;
1993
0
                            bHiddenItems = true;
1994
0
                        }
1995
0
                    }
1996
0
                    else
1997
0
                    {
1998
0
                        aItemText = getShortenedString(aItemText, rRenderContext, nMaxItemTextWidth);
1999
0
                        pData->bHiddenOnGUI = false;
2000
0
                    }
2001
2002
0
                    const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(&rRenderContext);
2003
0
                    if (aItemText != pData->aText)
2004
                        // Can't use pre-computed glyphs, item text was
2005
                        // changed.
2006
0
                        pGlyphs = nullptr;
2007
0
                    rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(),
2008
0
                                                nStyle, pVector, pDisplayText, pGlyphs);
2009
0
                    if (bSetTmpBackground)
2010
0
                        rRenderContext.SetBackground();
2011
0
                }
2012
2013
                // Accel
2014
0
                if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !officecfg::VCL::VCLSettings::Menu::SuppressAccelerators::get())
2015
0
                {
2016
0
                    OUString aAccText = pData->aAccelKey.GetName();
2017
0
                    aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) );
2018
0
                    aTmpPos.AdjustX( -(4 * nExtra) );
2019
2020
0
                    aTmpPos.AdjustX( -nOuterSpaceX );
2021
0
                    aTmpPos.setY( aPos.Y() );
2022
0
                    aTmpPos.AdjustY(nTextOffsetY );
2023
0
                    rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
2024
0
                }
2025
2026
                // SubMenu?
2027
0
                if (!bLayout && !IsMenuBar() && pData->pSubMenu)
2028
0
                {
2029
0
                    bool bNativeOk = false;
2030
0
                    if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
2031
0
                    {
2032
0
                        ControlState nState = ControlState::NONE;
2033
0
                        Size aTmpSz(0, 0);
2034
0
                        tools::Long aSpacing = 0;
2035
2036
0
                        if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
2037
0
                        {
2038
0
                            aTmpSz = Size(nFontHeight, nFontHeight);
2039
0
                            aSpacing = nOuterSpaceX;
2040
0
                        }
2041
2042
0
                        if (pData->bEnabled && m_pWindow->IsEnabled())
2043
0
                            nState |= ControlState::ENABLED;
2044
0
                        if (bHighlighted)
2045
0
                            nState |= ControlState::SELECTED;
2046
2047
0
                        aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX );
2048
0
                        aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 );
2049
0
                        aTmpPos.AdjustY(nExtra / 2 );
2050
2051
0
                        tools::Rectangle aItemRect(aTmpPos, aTmpSz);
2052
0
                        MenupopupValue aVal(m_nTextPos - GUTTERBORDER, aItemRect);
2053
0
                        bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
2054
0
                                                                     aItemRect, nState, aVal, OUString());
2055
0
                    }
2056
0
                    if (!bNativeOk)
2057
0
                    {
2058
0
                        aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX );
2059
0
                        aTmpPos.setY( aPos.Y() );
2060
0
                        aTmpPos.AdjustY(nExtra/2 );
2061
0
                        aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) );
2062
0
                        if (pData->nBits & MenuItemBits::POPUPSELECT)
2063
0
                        {
2064
0
                            rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2065
0
                            Point aTmpPos2(aPos);
2066
0
                            aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 );
2067
0
                            aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
2068
0
                                                                         pData->aSz.Height())),
2069
0
                                                DrawFrameStyle::Group);
2070
0
                        }
2071
0
                        aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
2072
0
                                             SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
2073
0
                    }
2074
0
                }
2075
2076
0
                if (pThisItemOnly && bHighlighted)
2077
0
                {
2078
                    // This restores the normal menu or menu bar text
2079
                    // color for when it is no longer highlighted.
2080
0
                    if (IsMenuBar())
2081
0
                        rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
2082
0
                    else
2083
0
                        rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2084
0
                }
2085
0
            }
2086
0
            if( bLayout )
2087
0
            {
2088
0
                if (!IsMenuBar())
2089
0
                    m_pLayoutData->m_aVisibleItemBoundRects[n]
2090
0
                        = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
2091
0
                else
2092
0
                    m_pLayoutData->m_aVisibleItemBoundRects[n]
2093
0
                        = tools::Rectangle(aTopLeft, pData->aSz);
2094
0
            }
2095
0
        }
2096
2097
0
        if (!IsMenuBar())
2098
0
            aTopLeft.AdjustY(pData->aSz.Height() );
2099
0
        else
2100
0
            aTopLeft.AdjustX(pData->aSz.Width() );
2101
0
    }
2102
2103
    // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2104
0
    if (bHiddenItems)
2105
0
    {
2106
0
        sal_Int32 nSize = nFontHeight;
2107
0
        tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize));
2108
0
        lclDrawMoreIndicator(rRenderContext, aRectangle);
2109
0
    }
2110
0
}
2111
2112
Menu* Menu::ImplGetStartMenu()
2113
0
{
2114
0
    Menu* pStart = this;
2115
0
    while (pStart && pStart->m_pStartedFrom && (pStart->m_pStartedFrom != pStart))
2116
0
        pStart = pStart->m_pStartedFrom;
2117
0
    return pStart;
2118
0
}
2119
2120
void Menu::ImplCallHighlight(sal_uInt16 nItem)
2121
0
{
2122
0
    ImplMenuDelData aDelData( this );
2123
2124
0
    m_nSelectedId = 0;
2125
0
    m_sSelectedIdent.clear();
2126
0
    MenuItemData* pData = m_pItemList->GetDataFromPos(nItem);
2127
0
    if (pData)
2128
0
    {
2129
0
        m_nSelectedId = pData->nId;
2130
0
        m_sSelectedIdent = pData->sIdent;
2131
0
    }
2132
0
    ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) );
2133
2134
0
    if( !aDelData.isDeleted() )
2135
0
    {
2136
0
        m_nSelectedId = 0;
2137
0
        m_sSelectedIdent.clear();
2138
0
    }
2139
0
}
2140
2141
IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
2142
0
{
2143
0
    m_nEventId = nullptr;
2144
0
    Select();
2145
0
}
2146
2147
Menu* Menu::ImplFindSelectMenu()
2148
0
{
2149
0
    Menu* pSelMenu = m_nEventId ? this : nullptr;
2150
2151
0
    for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2152
0
    {
2153
0
        MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2154
2155
0
        if ( pData->pSubMenu )
2156
0
            pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
2157
0
    }
2158
2159
0
    return pSelMenu;
2160
0
}
2161
2162
Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
2163
0
{
2164
0
    Menu* pSelMenu = nullptr;
2165
2166
0
    for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2167
0
    {
2168
0
        MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2169
2170
0
        if( pData->nId == nItemId )
2171
0
            pSelMenu = this;
2172
0
        else if ( pData->pSubMenu )
2173
0
            pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
2174
0
    }
2175
2176
0
    return pSelMenu;
2177
0
}
2178
2179
void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups )
2180
0
{
2181
0
    sal_uInt16 n = 0;
2182
0
    while (n < GetItemCount())
2183
0
    {
2184
0
        bool bRemove = false;
2185
0
        MenuItemData* pItem = m_pItemList->GetDataFromPos(n);
2186
0
        if ( pItem->eType == MenuItemType::SEPARATOR )
2187
0
        {
2188
0
            if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
2189
0
                bRemove = true;
2190
0
        }
2191
0
        else
2192
0
            bRemove = !pItem->bEnabled;
2193
2194
0
        if ( pItem->pSubMenu )
2195
0
        {
2196
0
            pItem->pSubMenu->RemoveDisabledEntries();
2197
0
            if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
2198
0
                bRemove = true;
2199
0
        }
2200
2201
0
        if (bRemove)
2202
0
            RemoveItem(n);
2203
0
        else
2204
0
            ++n;
2205
0
    }
2206
2207
0
    if ( GetItemCount() )
2208
0
    {
2209
0
        sal_uInt16 nLast = GetItemCount() - 1;
2210
0
        MenuItemData* pItem = m_pItemList->GetDataFromPos(nLast);
2211
0
        if ( pItem->eType == MenuItemType::SEPARATOR )
2212
0
            RemoveItem( nLast );
2213
0
    }
2214
0
    m_pLayoutData.reset();
2215
0
}
2216
2217
void Menu::UpdateNativeMenu()
2218
0
{
2219
0
    if ( ImplGetSalMenu() )
2220
0
        ImplGetSalMenu()->Update();
2221
0
}
2222
2223
void Menu::MenuBarKeyInput(const KeyEvent&)
2224
0
{
2225
0
}
2226
2227
0
void Menu::ImplKillLayoutData() const { m_pLayoutData.reset(); }
2228
2229
void Menu::ImplFillLayoutData() const
2230
0
{
2231
0
    if (!(m_pWindow && m_pWindow->IsReallyVisible()))
2232
0
        return;
2233
2234
0
    m_pLayoutData.reset(new MenuLayoutData);
2235
0
    if (IsMenuBar())
2236
0
    {
2237
0
        ImplPaint(*m_pWindow->GetOutDev(), m_pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2238
0
    }
2239
0
    else
2240
0
    {
2241
0
        MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(m_pWindow.get());
2242
0
        ImplPaint(*m_pWindow->GetOutDev(), m_pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(),
2243
0
                  nullptr, false, true); //FIXME
2244
0
    }
2245
0
}
2246
2247
tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, tools::Long nIndex ) const
2248
0
{
2249
0
    tools::Long nItemIndex = -1;
2250
0
    if (!m_pLayoutData)
2251
0
        ImplFillLayoutData();
2252
0
    if (m_pLayoutData)
2253
0
    {
2254
0
        auto it = std::find(m_pLayoutData->m_aLineItemIds.begin(),
2255
0
                            m_pLayoutData->m_aLineItemIds.end(), nItemID);
2256
0
        if (it != m_pLayoutData->m_aLineItemIds.end())
2257
0
        {
2258
0
            nItemIndex
2259
0
                = m_pLayoutData
2260
0
                      ->m_aLineIndices[std::distance(m_pLayoutData->m_aLineItemIds.begin(), it)];
2261
0
        }
2262
0
    }
2263
0
    return (m_pLayoutData && nItemIndex != -1)
2264
0
               ? m_pLayoutData->GetCharacterBounds(nItemIndex + nIndex)
2265
0
               : tools::Rectangle();
2266
0
}
2267
2268
tools::Long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
2269
0
{
2270
0
    rItemID = 0;
2271
0
    if (!m_pLayoutData)
2272
0
        ImplFillLayoutData();
2273
0
    if (m_pLayoutData)
2274
0
    {
2275
0
        tools::Long nIndex = m_pLayoutData->GetIndexForPoint(rPoint);
2276
0
        for (size_t i = 0; i < m_pLayoutData->m_aLineIndices.size(); i++)
2277
0
        {
2278
0
            if (m_pLayoutData->m_aLineIndices[i] <= nIndex
2279
0
                && (i == m_pLayoutData->m_aLineIndices.size() - 1
2280
0
                    || m_pLayoutData->m_aLineIndices[i + 1] > nIndex))
2281
0
            {
2282
0
                rItemID = m_pLayoutData->m_aLineItemIds[i];
2283
                // return index relative to item
2284
0
                return nIndex - m_pLayoutData->m_aLineIndices[i];
2285
0
            }
2286
0
        }
2287
0
    }
2288
0
    return -1;
2289
0
}
2290
2291
tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
2292
0
{
2293
0
    tools::Rectangle aRet;
2294
2295
0
    if (!m_pLayoutData)
2296
0
        ImplFillLayoutData();
2297
0
    if (m_pLayoutData)
2298
0
    {
2299
0
        std::map<sal_uInt16, tools::Rectangle>::const_iterator it
2300
0
            = m_pLayoutData->m_aVisibleItemBoundRects.find(nPos);
2301
0
        if (it != m_pLayoutData->m_aVisibleItemBoundRects.end())
2302
0
            aRet = it->second;
2303
0
    }
2304
0
    return aRet;
2305
0
}
2306
2307
void Menu::SetAccessibleName( sal_uInt16 nItemId, const OUString& rStr )
2308
0
{
2309
0
    size_t nPos;
2310
0
    MenuItemData* pData = m_pItemList->GetData(nItemId, nPos);
2311
2312
0
    if (pData && !rStr.equals(pData->aAccessibleName))
2313
0
    {
2314
0
        pData->aAccessibleName = rStr;
2315
0
        ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged, nPos);
2316
0
    }
2317
0
}
2318
2319
OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
2320
0
{
2321
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
2322
2323
0
    if ( pData )
2324
0
        return pData->aAccessibleName;
2325
2326
0
    return OUString();
2327
0
}
2328
2329
void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const OUString& rStr )
2330
0
{
2331
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
2332
2333
0
    if ( pData )
2334
0
        pData->aAccessibleDescription = rStr;
2335
0
}
2336
2337
OUString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const
2338
0
{
2339
0
    MenuItemData* pData = m_pItemList->GetData(nItemId);
2340
2341
0
    if (pData && !pData->aAccessibleDescription.isEmpty())
2342
0
        return pData->aAccessibleDescription;
2343
2344
0
    return GetHelpText(nItemId);
2345
0
}
2346
2347
void Menu::GetSystemMenuData(SystemMenuData& rData) const
2348
0
{
2349
0
    if (ImplGetSalMenu())
2350
0
    {
2351
0
        ImplGetSalMenu()->GetSystemMenuData(rData);
2352
0
    }
2353
0
}
2354
2355
bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
2356
0
{
2357
0
    bool bRet = false;
2358
2359
0
    if (m_pWindow)
2360
0
    {
2361
0
        if (IsMenuBar())
2362
0
            bRet = ( nItemPos == static_cast< MenuBarWindow * > (m_pWindow.get())->GetHighlightedItem() );
2363
0
        else
2364
0
            bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (m_pWindow.get())->GetHighlightedItem() );
2365
0
    }
2366
2367
0
    return bRet;
2368
0
}
2369
2370
void Menu::HighlightItem( sal_uInt16 nItemPos )
2371
0
{
2372
0
    if ( !m_pWindow )
2373
0
        return;
2374
2375
0
    if (IsMenuBar())
2376
0
    {
2377
0
        MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( m_pWindow.get() );
2378
0
        pMenuWin->SetAutoPopup( false );
2379
0
        pMenuWin->ChangeHighlightItem( nItemPos, false );
2380
0
    }
2381
0
    else
2382
0
    {
2383
0
        static_cast< MenuFloatingWindow* >( m_pWindow.get() )->ChangeHighlightItem( nItemPos, false );
2384
0
    }
2385
0
}
2386
2387
MenuBarWindow* MenuBar::getMenuBarWindow()
2388
0
{
2389
    // so far just a dynamic_cast, hopefully to be turned into something saner
2390
    // at some stage
2391
0
    MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(m_pWindow.get());
2392
    //either there is no window (fdo#87663) or it is a MenuBarWindow
2393
0
    assert(!m_pWindow || pWin);
2394
0
    return pWin;
2395
0
}
2396
2397
MenuBar::MenuBar()
2398
0
    : mbCloseBtnVisible(false),
2399
0
      mbFloatBtnVisible(false),
2400
0
      mbHideBtnVisible(false),
2401
0
      mbDisplayable(true)
2402
0
{
2403
0
    m_pSalMenu = GetSalInstance()->CreateMenu(true, this);
2404
0
}
2405
2406
MenuBar::MenuBar( const MenuBar& rMenu )
2407
0
    : mbCloseBtnVisible(false),
2408
0
      mbFloatBtnVisible(false),
2409
0
      mbHideBtnVisible(false),
2410
0
      mbDisplayable(true)
2411
0
{
2412
0
    m_pSalMenu = GetSalInstance()->CreateMenu(true, this);
2413
0
    *this = rMenu;
2414
0
}
2415
2416
MenuBar::~MenuBar()
2417
0
{
2418
0
    disposeOnce();
2419
0
}
2420
2421
void MenuBar::dispose()
2422
0
{
2423
0
    ImplDestroy(true);
2424
0
    Menu::dispose();
2425
0
}
2426
2427
void MenuBar::ClosePopup(PopupMenu *pPopupMenu)
2428
0
{
2429
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2430
0
    if (!pMenuWin)
2431
0
        return;
2432
0
    pMenuWin->PopupClosed(pPopupMenu);
2433
0
}
2434
2435
void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
2436
0
{
2437
0
    m_pWindow->KeyInput(rEvent);
2438
0
}
2439
2440
void MenuBar::ShowCloseButton(bool bShow)
2441
0
{
2442
0
    ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
2443
0
}
2444
2445
void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
2446
0
{
2447
0
    if ((bClose != mbCloseBtnVisible) ||
2448
0
        (bFloat != mbFloatBtnVisible) ||
2449
0
        (bHide  != mbHideBtnVisible))
2450
0
    {
2451
0
        mbCloseBtnVisible = bClose;
2452
0
        mbFloatBtnVisible = bFloat;
2453
0
        mbHideBtnVisible = bHide;
2454
0
        MenuBarWindow* pMenuWin = getMenuBarWindow();
2455
0
        if (pMenuWin)
2456
0
            pMenuWin->ShowButtons(bClose, bFloat, bHide);
2457
0
    }
2458
0
}
2459
2460
void MenuBar::LayoutChanged()
2461
0
{
2462
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2463
0
    if (pMenuWin)
2464
0
        pMenuWin->LayoutChanged();
2465
0
}
2466
2467
void MenuBar::SetDisplayable( bool bDisplayable )
2468
0
{
2469
0
    if( bDisplayable != mbDisplayable )
2470
0
    {
2471
0
        if ( ImplGetSalMenu() )
2472
0
            ImplGetSalMenu()->ShowMenuBar( bDisplayable );
2473
2474
0
        mbDisplayable = bDisplayable;
2475
0
        LayoutChanged();
2476
0
    }
2477
0
}
2478
2479
VclPtr<MenuBarWindow> MenuBar::ImplCreate(vcl::Window* pParent, MenuBarWindow* pWindow)
2480
0
{
2481
0
    VclPtr<MenuBarWindow> pMenuBarWindow = pWindow;
2482
0
    if (!pMenuBarWindow)
2483
0
    {
2484
0
        pMenuBarWindow = VclPtr<MenuBarWindow>::Create(pParent);
2485
0
    }
2486
2487
0
    m_pStartedFrom = nullptr;
2488
0
    m_pWindow = pMenuBarWindow;
2489
0
    pMenuBarWindow->SetMenu(this);
2490
0
    tools::Long nHeight = ImplCalcSize(*pMenuBarWindow).Height();
2491
2492
    // depending on the native implementation or the displayable flag
2493
    // the menubar windows is suppressed (ie, height=0)
2494
0
    if (!IsDisplayable() || (ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar()))
2495
0
    {
2496
0
        nHeight = 0;
2497
0
    }
2498
2499
0
    pMenuBarWindow->SetHeight(nHeight);
2500
0
    return pMenuBarWindow;
2501
0
}
2502
2503
void MenuBar::ImplDestroy(bool bDelete)
2504
0
{
2505
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2506
0
    if (pMenuWin && bDelete)
2507
0
    {
2508
0
        pMenuWin->KillActivePopup();
2509
0
        pMenuWin->disposeOnce();
2510
0
    }
2511
0
    m_pWindow = nullptr;
2512
0
    if (m_pSalMenu)
2513
0
    {
2514
0
        m_pSalMenu->ShowMenuBar(false);
2515
0
    }
2516
0
}
2517
2518
bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
2519
0
{
2520
    // No keyboard processing when our menubar is invisible
2521
0
    if (!IsDisplayable())
2522
0
        return false;
2523
2524
    // No keyboard processing when system handles the menu.
2525
0
    SalMenu *pNativeMenu = ImplGetSalMenu();
2526
0
    if (pNativeMenu && pNativeMenu->VisibleMenuBar())
2527
0
    {
2528
        // Except when the event is the F6 cycle pane event and we can put our
2529
        // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2530
        // where it's not part of the application window)
2531
0
        if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
2532
0
            return false;
2533
0
        if (!pNativeMenu->CanGetFocus())
2534
0
            return false;
2535
0
    }
2536
2537
0
    bool bDone = false;
2538
    // check for enabled, if this method is called from another window...
2539
0
    vcl::Window* pWin = GetWindow();
2540
0
    if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled()  && !pWin->IsInModalMode())
2541
0
    {
2542
0
        MenuBarWindow* pMenuWin = getMenuBarWindow();
2543
0
        bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
2544
0
    }
2545
0
    return bDone;
2546
0
}
2547
2548
bool MenuBar::ImplHandleCmdEvent( const CommandEvent& rCEvent )
2549
0
{
2550
    // No keyboard processing when system handles the menu or our menubar is invisible
2551
0
    if( !IsDisplayable() ||
2552
0
        ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2553
0
        return false;
2554
2555
    // check for enabled, if this method is called from another window...
2556
0
    MenuBarWindow* pWin = static_cast<MenuBarWindow*>(GetWindow());
2557
0
    if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled()  && ! pWin->IsInModalMode() )
2558
0
    {
2559
0
        if (rCEvent.GetCommand() == CommandEventId::ModKeyChange && ImplGetSVData()->maNWFData.mbAutoAccel)
2560
0
        {
2561
0
            const CommandModKeyData* pCData = rCEvent.GetModKeyData ();
2562
0
            if (pWin->m_nHighlightedItem == ITEMPOS_INVALID)
2563
0
            {
2564
0
                if (pCData && pCData->IsMod2() && pCData->IsDown())
2565
0
                    pWin->SetMBWHideAccel(false);
2566
0
                pWin->Invalidate(InvalidateFlags::Update);
2567
0
            }
2568
0
            return true;
2569
0
        }
2570
0
    }
2571
0
    return false;
2572
0
}
2573
2574
void MenuBar::SelectItem(sal_uInt16 nId)
2575
0
{
2576
0
    if (!m_pWindow)
2577
0
        return;
2578
2579
0
    m_pWindow->GrabFocus();
2580
0
    nId = GetItemPos( nId );
2581
2582
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2583
0
    if (pMenuWin)
2584
0
    {
2585
        // #99705# popup the selected menu
2586
0
        pMenuWin->SetAutoPopup( true );
2587
0
        if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
2588
0
        {
2589
0
            pMenuWin->KillActivePopup();
2590
0
            pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
2591
0
        }
2592
0
        if (nId != ITEMPOS_INVALID)
2593
0
            pMenuWin->ChangeHighlightItem( nId, false );
2594
0
    }
2595
0
}
2596
2597
// handler for native menu selection and command events
2598
bool Menu::HandleMenuActivateEvent(Menu* pMenu)
2599
0
{
2600
0
    if( pMenu )
2601
0
    {
2602
0
        ImplMenuDelData aDelData( this );
2603
2604
0
        pMenu->m_pStartedFrom = this;
2605
0
        pMenu->m_bInCallback = true;
2606
0
        pMenu->Activate();
2607
2608
0
        if( !aDelData.isDeleted() )
2609
0
            pMenu->m_bInCallback = false;
2610
0
    }
2611
0
    return true;
2612
0
}
2613
2614
bool Menu::HandleMenuDeActivateEvent(Menu* pMenu)
2615
0
{
2616
0
    if( pMenu )
2617
0
    {
2618
0
        ImplMenuDelData aDelData( this );
2619
2620
0
        pMenu->m_pStartedFrom = this;
2621
0
        pMenu->m_bInCallback = true;
2622
0
        pMenu->Deactivate();
2623
0
        if( !aDelData.isDeleted() )
2624
0
            pMenu->m_bInCallback = false;
2625
0
    }
2626
0
    return true;
2627
0
}
2628
2629
bool MenuBar::HandleMenuHighlightEvent(Menu* pMenu, sal_uInt16 nHighlightEventId)
2630
0
{
2631
0
    if( !pMenu )
2632
0
        pMenu = ImplFindMenu(nHighlightEventId);
2633
0
    if( pMenu )
2634
0
    {
2635
0
        ImplMenuDelData aDelData( pMenu );
2636
2637
0
        if (m_nHighlightedItemPos != ITEMPOS_INVALID)
2638
0
            pMenu->ImplCallEventListeners(VclEventId::MenuDehighlight, m_nHighlightedItemPos);
2639
2640
0
        if( !aDelData.isDeleted() )
2641
0
        {
2642
0
            pMenu->m_nHighlightedItemPos = pMenu->GetItemPos(nHighlightEventId);
2643
0
            pMenu->m_nSelectedId = nHighlightEventId;
2644
0
            pMenu->m_sSelectedIdent = pMenu->GetItemIdent(nHighlightEventId);
2645
0
            pMenu->m_pStartedFrom = this;
2646
0
            pMenu->ImplCallHighlight(pMenu->m_nHighlightedItemPos);
2647
0
        }
2648
0
        return true;
2649
0
    }
2650
0
    else
2651
0
        return false;
2652
0
}
2653
2654
bool Menu::HandleMenuCommandEvent(Menu* pMenu, sal_uInt16 nCommandEventId)
2655
0
{
2656
0
    if( !pMenu )
2657
0
        pMenu = ImplFindMenu(nCommandEventId);
2658
0
    if( pMenu )
2659
0
    {
2660
0
        pMenu->m_nSelectedId = nCommandEventId;
2661
0
        pMenu->m_sSelectedIdent = pMenu->GetItemIdent(nCommandEventId);
2662
0
        pMenu->m_pStartedFrom = this;
2663
0
        pMenu->ImplSelect();
2664
0
        return true;
2665
0
    }
2666
0
    else
2667
0
        return false;
2668
0
}
2669
2670
sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
2671
0
{
2672
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2673
0
    return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
2674
0
}
2675
2676
void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>& rLink )
2677
0
{
2678
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2679
0
    if (!pMenuWin)
2680
0
        return;
2681
0
    pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
2682
0
}
2683
2684
void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
2685
0
{
2686
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2687
0
    if (!pMenuWin)
2688
0
        return;
2689
0
    pMenuWin->RemoveMenuBarButton(nId);
2690
0
}
2691
2692
tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
2693
0
{
2694
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2695
0
    return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle();
2696
0
}
2697
2698
bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
2699
0
{
2700
0
    MenuBarWindow* pMenuWin = getMenuBarWindow();
2701
0
    return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
2702
0
}
2703
2704
int MenuBar::GetMenuBarHeight() const
2705
0
{
2706
0
    const SalMenu *pNativeMenu = ImplGetSalMenu();
2707
0
    int nMenubarHeight;
2708
0
    if (pNativeMenu)
2709
0
        nMenubarHeight = pNativeMenu->GetMenuBarHeight();
2710
0
    else
2711
0
    {
2712
0
        vcl::Window* pMenubarWin = GetWindow();
2713
0
        nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputSizePixel().Height() : 0;
2714
0
    }
2715
0
    return nMenubarHeight;
2716
0
}
2717
2718
0
MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
2719
0
    return static_cast<MenuFloatingWindow*>(GetWindow());
2720
0
}
2721
2722
0
PopupMenu::PopupMenu() { m_pSalMenu = GetSalInstance()->CreateMenu(false, this); }
2723
2724
PopupMenu::PopupMenu( const PopupMenu& rMenu )
2725
0
{
2726
0
    m_pSalMenu = GetSalInstance()->CreateMenu(false, this);
2727
0
    *this = rMenu;
2728
0
}
2729
2730
PopupMenu::~PopupMenu()
2731
0
{
2732
0
    disposeOnce();
2733
0
}
2734
2735
void PopupMenu::ClosePopup(PopupMenu* pPopupMenu)
2736
0
{
2737
0
    MenuFloatingWindow* p = ImplGetFloatingWindow();
2738
0
    if (p && pPopupMenu && p->GetActivePopup() == pPopupMenu)
2739
0
        p->KillActivePopup();
2740
0
}
2741
2742
namespace vcl
2743
{
2744
    bool IsInPopupMenuExecute()
2745
0
    {
2746
0
        return PopupMenu::GetActivePopupMenu() != nullptr;
2747
0
    }
2748
}
2749
2750
PopupMenu* PopupMenu::GetActivePopupMenu()
2751
0
{
2752
0
    ImplSVData* pSVData = ImplGetSVData();
2753
0
    return pSVData->maAppData.mpActivePopupMenu;
2754
0
}
2755
2756
void PopupMenu::EndExecute()
2757
0
{
2758
0
    if (GetWindow())
2759
0
        ImplGetFloatingWindow()->EndExecute( 0 );
2760
0
}
2761
2762
void PopupMenu::SelectItem(sal_uInt16 nId)
2763
0
{
2764
0
    if (!GetWindow())
2765
0
        return;
2766
2767
0
    if( nId != ITEMPOS_INVALID )
2768
0
    {
2769
0
        size_t nPos = 0;
2770
0
        MenuItemData* pData = GetItemList()->GetData( nId, nPos );
2771
0
        if (pData && pData->pSubMenu)
2772
0
            ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
2773
0
        else
2774
0
            ImplGetFloatingWindow()->EndExecute( nId );
2775
0
    }
2776
0
    else
2777
0
    {
2778
0
        MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
2779
0
        pFloat->GrabFocus();
2780
2781
0
        for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
2782
0
        {
2783
0
            MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
2784
0
            if( pData->pSubMenu )
2785
0
            {
2786
0
                pFloat->KillActivePopup();
2787
0
            }
2788
0
        }
2789
0
        pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
2790
0
    }
2791
0
}
2792
2793
void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
2794
0
{
2795
0
    m_nSelectedId = nId;
2796
0
    m_sSelectedIdent = GetItemIdent(nId);
2797
0
}
2798
2799
sal_uInt16 PopupMenu::Execute(vcl::Window& rExecWindow, const Point& rPopupPos)
2800
0
{
2801
0
    return Execute(&rExecWindow, tools::Rectangle(rPopupPos, rPopupPos),
2802
0
                   PopupMenuFlags::ExecuteDown);
2803
0
}
2804
2805
static FloatWinPopupFlags lcl_TranslateFlags(PopupMenuFlags nFlags)
2806
0
{
2807
0
    FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
2808
0
    if ( nFlags & PopupMenuFlags::ExecuteDown )
2809
0
        nPopupModeFlags = FloatWinPopupFlags::Down;
2810
0
    else if ( nFlags & PopupMenuFlags::ExecuteUp )
2811
0
        nPopupModeFlags = FloatWinPopupFlags::Up;
2812
0
    else if ( nFlags & PopupMenuFlags::ExecuteRight )
2813
0
        nPopupModeFlags = FloatWinPopupFlags::Right;
2814
0
    else
2815
0
        nPopupModeFlags = FloatWinPopupFlags::Down;
2816
2817
0
    if (nFlags & PopupMenuFlags::NoMouseUpClose )                      // allow popup menus to stay open on mouse button up
2818
0
        nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose;    // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2819
2820
0
    return nPopupModeFlags;
2821
0
}
2822
2823
sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags )
2824
0
{
2825
0
    ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
2826
0
    return ImplExecute( pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false );
2827
0
}
2828
2829
void PopupMenu::ImplFlushPendingSelect()
2830
0
{
2831
    // is there still Select?
2832
0
    Menu* pSelect = ImplFindSelectMenu();
2833
0
    if (pSelect)
2834
0
    {
2835
        // Select should be called prior to leaving execute in a popup menu!
2836
0
        Application::RemoveUserEvent(pSelect->m_nEventId);
2837
0
        pSelect->m_nEventId = nullptr;
2838
0
        pSelect->Select();
2839
0
    }
2840
0
}
2841
2842
sal_uInt16 PopupMenu::ImplExecute(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect,
2843
                                  FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst)
2844
0
{
2845
    // tdf#126054 hold this until after function completes
2846
0
    VclPtr<PopupMenu> xThis(this);
2847
2848
0
    bool bRealExecute = false;
2849
0
    const sal_uInt16 nItemCount = GetItemCount();
2850
0
    if (!pSFrom && (vcl::IsInPopupMenuExecute() || !nItemCount))
2851
0
        return 0;
2852
2853
0
    m_pLayoutData.reset();
2854
2855
0
    ImplSVData* pSVData = ImplGetSVData();
2856
2857
0
    m_pStartedFrom = pSFrom;
2858
0
    m_nSelectedId = 0;
2859
0
    m_sSelectedIdent.clear();
2860
0
    m_bCanceled = false;
2861
2862
0
    VclPtr<vcl::Window> xFocusId;
2863
0
    if (!m_pStartedFrom)
2864
0
    {
2865
0
        pSVData->mpWinData->mbNoDeactivate = true;
2866
0
        xFocusId = Window::SaveFocus();
2867
0
        bRealExecute = true;
2868
0
    }
2869
0
    else
2870
0
    {
2871
        // assure that only one menu is open at a time
2872
0
        if (m_pStartedFrom->IsMenuBar() && pSVData->mpWinData->mpFirstFloat)
2873
0
            pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
2874
0
                                                           | FloatWinPopupEndFlags::CloseAll);
2875
0
    }
2876
2877
0
    tools::Rectangle aRect(rRect);
2878
0
    aRect.SetPos(pParentWin->OutputToScreenPixel(aRect.TopLeft()));
2879
2880
0
    nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose | FloatWinPopupFlags::GrabFocus;
2881
0
    if (bRealExecute)
2882
0
        nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
2883
2884
    // MenuFlags get clobbered in the Activate function. Restore them after calling.
2885
0
    MenuFlags nMenuFlagsSaved = GetMenuFlags();
2886
0
    m_bInCallback = true; // set it here, if Activate overridden
2887
0
    Activate();
2888
0
    m_bInCallback = false;
2889
0
    SetMenuFlags(nMenuFlagsSaved);
2890
2891
0
    if (pParentWin->isDisposed())
2892
0
        return 0;
2893
2894
0
    if (m_bCanceled || m_bKilled)
2895
0
        return 0;
2896
2897
0
    if (!nItemCount)
2898
0
        return 0;
2899
2900
    // The flag MenuFlags::HideDisabledEntries is inherited.
2901
0
    if ( pSFrom )
2902
0
    {
2903
0
        if (pSFrom->m_nMenuFlags & MenuFlags::HideDisabledEntries)
2904
0
            m_nMenuFlags |= MenuFlags::HideDisabledEntries;
2905
0
        else
2906
0
            m_nMenuFlags &= ~MenuFlags::HideDisabledEntries;
2907
0
    }
2908
2909
0
    sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
2910
0
    if ( !nVisibleEntries )
2911
0
    {
2912
0
        OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
2913
2914
0
        MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, {});
2915
0
        size_t nPos = 0;
2916
0
        pData = m_pItemList->GetData(pData->nId, nPos);
2917
0
        assert(pData);
2918
0
        if (pData)
2919
0
        {
2920
0
            pData->bIsTemporary = true;
2921
0
        }
2922
0
        ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos);
2923
0
    }
2924
2925
0
    VclPtr<MenuFloatingWindow> pWin = VclPtrInstance<MenuFloatingWindow>(this, pParentWin, WB_BORDER | WB_SYSTEMWINDOW);
2926
0
    if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu")
2927
0
    {
2928
0
        VclPtr<vcl::Window> xNotifierParent = pParentWin->GetParentWithLOKNotifier();
2929
0
        assert(xNotifierParent && xNotifierParent->GetLOKNotifier() && "editview menu without LOKNotifier");
2930
0
        pWin->SetLOKNotifier(xNotifierParent->GetLOKNotifier());
2931
0
    }
2932
2933
0
    if( pSVData->maNWFData.mbFlatMenu )
2934
0
        pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
2935
0
    else
2936
0
        pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
2937
2938
0
    m_pWindow.disposeAndClear();
2939
0
    m_pWindow = pWin;
2940
2941
0
    Size aSz = ImplCalcSize(*pWin);
2942
2943
0
    AbsoluteScreenPixelRectangle aDesktopRect(pWin->GetDesktopRectPixel());
2944
0
    if( Application::GetScreenCount() > 1 )
2945
0
    {
2946
0
        vcl::Window* pDeskW = m_pWindow->GetWindow( GetWindowType::RealParent );
2947
0
        if( ! pDeskW )
2948
0
            pDeskW = m_pWindow;
2949
0
        AbsoluteScreenPixelPoint aDesktopTL(pDeskW->OutputToAbsoluteScreenPixel(aRect.TopLeft()));
2950
0
        aDesktopRect = Application::GetScreenPosSizePixel(
2951
0
            Application::GetBestScreen(AbsoluteScreenPixelRectangle(aDesktopTL, aRect.GetSize())));
2952
0
    }
2953
2954
0
    tools::Long nMaxHeight = aDesktopRect.GetHeight();
2955
2956
    //rhbz#1021915. If a menu won't fit in the desired location the default
2957
    //mode is to place it somewhere it will fit.  e.g. above, left, right. For
2958
    //some cases, e.g. menubars, it's desirable to limit the options to
2959
    //above/below and force the menu to scroll if it won't fit
2960
0
    if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
2961
0
    {
2962
0
        vcl::Window* pRef = pWin;
2963
0
        if ( pRef->GetParent() )
2964
0
            pRef = pRef->GetParent();
2965
2966
0
        AbsoluteScreenPixelRectangle devRect(pRef->OutputToAbsoluteScreenPixel(aRect.TopLeft()),
2967
0
                                             pRef->OutputToAbsoluteScreenPixel(aRect.BottomRight()));
2968
2969
0
        tools::Long nHeightAbove = devRect.Top() - aDesktopRect.Top();
2970
0
        tools::Long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
2971
0
        nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
2972
0
    }
2973
2974
    // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
2975
    // So assume that the available screen size matches at least the system requirements. With menu origin
2976
    // in the middle, nMaxHeight will be at least half of screen height.
2977
0
    SAL_WARN_IF(nMaxHeight < 768 / 2, "vcl",
2978
0
                "Available height misdetected as " << nMaxHeight
2979
0
                                                   << "px. Setting to 384px instead.");
2980
0
    nMaxHeight = std::max(nMaxHeight, tools::Long(768 / 2));
2981
2982
0
    if (m_pStartedFrom && m_pStartedFrom->IsMenuBar())
2983
0
        nMaxHeight -= pParentWin->GetSizePixel().Height();
2984
0
    sal_Int32 nLeft, nTop, nRight, nBottom;
2985
0
    m_pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
2986
0
    nMaxHeight -= nTop+nBottom;
2987
0
    if ( aSz.Height() > nMaxHeight )
2988
0
    {
2989
0
        pWin->EnableScrollMenu( true );
2990
0
        sal_uInt16 nStart = ImplGetFirstVisible();
2991
0
        sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
2992
0
        aSz.setHeight( ImplCalcHeight( nEntries ) );
2993
0
    }
2994
2995
0
    pWin->SetFocusId( xFocusId );
2996
0
    pWin->SetOutputSizePixel( aSz );
2997
2998
0
    const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect);
2999
0
    FinishRun(pWin, pParentWin, bRealExecute, bNative);
3000
0
    return m_nSelectedId;
3001
0
}
3002
3003
bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExecute, const bool bPreSelectFirst,
3004
                    const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect)
3005
0
{
3006
0
    SalMenu* pMenu = ImplGetSalMenu();
3007
0
    if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags))
3008
0
        return true;
3009
3010
0
    pWin->StartPopupMode(rRect, nPopupModeFlags);
3011
0
    if (pSFrom)
3012
0
    {
3013
0
        sal_uInt16 aPos;
3014
0
        if (pSFrom->IsMenuBar())
3015
0
            aPos = static_cast<MenuBarWindow *>(pSFrom->m_pWindow.get())->GetHighlightedItem();
3016
0
        else
3017
0
            aPos = static_cast<MenuFloatingWindow *>(pSFrom->m_pWindow.get())->GetHighlightedItem();
3018
3019
0
        pWin->SetPosInParent(aPos);  // store position to be sent in SUBMENUDEACTIVATE
3020
0
        pSFrom->ImplCallEventListeners(VclEventId::MenuSubmenuActivate, aPos);
3021
0
    }
3022
3023
0
    if ( bPreSelectFirst )
3024
0
    {
3025
0
        for (size_t n = 0; n < static_cast<size_t>(GetItemCount()); n++)
3026
0
        {
3027
0
            MenuItemData* pData = m_pItemList->GetDataFromPos(n);
3028
0
            if (  (  pData->bEnabled
3029
0
                  || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
3030
0
                  )
3031
0
               && ( pData->eType != MenuItemType::SEPARATOR )
3032
0
               && ImplIsVisible( n )
3033
0
               && ImplIsSelectable( n )
3034
0
               )
3035
0
            {
3036
0
                pWin->ChangeHighlightItem(n, false);
3037
0
                break;
3038
0
            }
3039
0
        }
3040
0
    }
3041
3042
0
    if (bRealExecute)
3043
0
        pWin->Execute();
3044
3045
0
    return false;
3046
0
}
3047
3048
void PopupMenu::FinishRun(const VclPtr<MenuFloatingWindow>& pWin, const VclPtr<vcl::Window>& pParentWin, const bool bRealExecute, const bool bIsNativeMenu)
3049
0
{
3050
0
    if (!bRealExecute || pWin->isDisposed())
3051
0
        return;
3052
3053
0
    if (!bIsNativeMenu)
3054
0
    {
3055
0
        VclPtr<vcl::Window> xFocusId = pWin->GetFocusId();
3056
0
        assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
3057
0
        pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
3058
3059
0
        if (m_nSelectedId) // then clean up .. ( otherwise done by TH )
3060
0
        {
3061
0
            PopupMenu* pSub = pWin->GetActivePopup();
3062
0
            while ( pSub )
3063
0
            {
3064
0
                pSub->ImplGetFloatingWindow()->EndPopupMode();
3065
0
                pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
3066
0
            }
3067
0
        }
3068
0
    }
3069
0
    else
3070
0
        pWin->StopExecute();
3071
3072
0
    pWin->doShutdown();
3073
0
    ImplClosePopupToolBox(pParentWin);
3074
0
    ImplFlushPendingSelect();
3075
0
}
3076
3077
sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
3078
0
{
3079
0
    nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3080
3081
0
    tools::Long nHeight = 0;
3082
0
    size_t nEntries = m_pItemList->size();
3083
0
    sal_uInt16 nVisEntries = 0;
3084
3085
0
    if ( pLastVisible )
3086
0
        *pLastVisible = 0;
3087
3088
0
    for ( size_t n = nStartEntry; n < nEntries; n++ )
3089
0
    {
3090
0
        if ( ImplIsVisible( n ) )
3091
0
        {
3092
0
            MenuItemData* pData = m_pItemList->GetDataFromPos(n);
3093
0
            nHeight += pData->aSz.Height();
3094
0
            if ( nHeight > nMaxHeight )
3095
0
                break;
3096
3097
0
            if ( pLastVisible )
3098
0
                *pLastVisible = n;
3099
0
            nVisEntries++;
3100
0
        }
3101
0
    }
3102
0
    return nVisEntries;
3103
0
}
3104
3105
tools::Long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
3106
0
{
3107
0
    tools::Long nHeight = 0;
3108
3109
0
    sal_uInt16 nFound = 0;
3110
0
    for (size_t n = 0; (nFound < nEntries) && (n < m_pItemList->size()); n++)
3111
0
    {
3112
0
        if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) )
3113
0
        {
3114
0
            MenuItemData* pData = m_pItemList->GetDataFromPos(n);
3115
0
            nHeight += pData->aSz.Height();
3116
0
            nFound++;
3117
0
        }
3118
0
    }
3119
3120
0
    nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
3121
3122
0
    return nHeight;
3123
0
}
3124
3125
css::uno::Reference<css::awt::XPopupMenu> PopupMenu::CreateMenuInterface()
3126
0
{
3127
0
    UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
3128
0
    if ( pWrapper )
3129
0
        return pWrapper->CreateMenuInterface(this);
3130
0
    return nullptr;
3131
0
}
3132
3133
ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
3134
0
: mpNext( nullptr )
3135
0
, mpMenu( nullptr )
3136
0
{
3137
0
    if( pMenu )
3138
0
        const_cast< Menu* >( pMenu )->ImplAddDel( *this );
3139
0
}
3140
3141
ImplMenuDelData::~ImplMenuDelData()
3142
0
{
3143
0
    if( mpMenu )
3144
0
        const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
3145
0
}
3146
3147
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */