Coverage Report

Created: 2026-07-10 11:04

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