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