bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / window / menu.cxx
blob2a46a41a12c31373e93d245588eec5cdaeb8725b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <comphelper/diagnose_ex.hxx>
21 #include <sal/log.hxx>
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>
37 #include <salinst.hxx>
38 #include <svdata.hxx>
39 #include <strings.hrc>
40 #include <window.h>
41 #include <salmenu.hxx>
42 #include <salframe.hxx>
44 #include "menubarwindow.hxx"
45 #include "menufloatingwindow.hxx"
46 #include "menuitemlist.hxx"
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>
54 #include <configsettings.hxx>
56 #include <map>
57 #include <string_view>
58 #include <vector>
60 namespace vcl
63 struct MenuLayoutData : public ControlLayoutData
65 std::vector< sal_uInt16 > m_aLineItemIds;
66 std::map< sal_uInt16, tools::Rectangle > m_aVisibleItemBoundRects;
71 using namespace vcl;
73 #define EXTRAITEMHEIGHT 4
74 #define SPACE_AROUND_TITLE 4
76 static bool ImplAccelDisabled()
78 // display of accelerator strings may be suppressed via configuration
79 static int nAccelDisabled = -1;
81 if( nAccelDisabled == -1 )
83 OUString aStr =
84 vcl::SettingsConfigItem::get()->
85 getValue( "Menu", "SuppressAccelerators" );
86 nAccelDisabled = aStr.equalsIgnoreAsciiCase("true") ? 1 : 0;
88 return nAccelDisabled == 1;
91 static void ImplSetMenuItemData( MenuItemData* pData )
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;
102 namespace {
104 void ImplClosePopupToolBox( const VclPtr<vcl::Window>& pWin )
106 if ( pWin->GetType() == WindowType::TOOLBOX && ImplGetDockingManager()->IsInPopupMode( pWin ) )
108 SystemWindow* pFloatingWindow = ImplGetDockingManager()->GetFloatingWindow(pWin);
109 if (pFloatingWindow)
110 static_cast<FloatingWindow*>(pFloatingWindow)->EndPopupMode( FloatWinPopupEndFlags::CloseAll );
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)
118 rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
119 rRenderContext.SetLineColor();
121 if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
122 rRenderContext.SetFillColor(COL_WHITE);
123 else
124 rRenderContext.SetFillColor(COL_BLACK);
125 float fScaleFactor = rRenderContext.GetDPIScaleFactor();
127 int linewidth = 1 * fScaleFactor;
128 int space = 4 * fScaleFactor;
130 tools::Long width = 8 * fScaleFactor;
131 tools::Long height = 5 * fScaleFactor;
133 //Keep odd b/c drawing code works better
134 if ( height % 2 == 0 )
135 height--;
137 tools::Long heightOrig = height;
139 tools::Long x = rRect.Left() + (rRect.getOpenWidth() - width)/2 + 1;
140 tools::Long y = rRect.Top() + (rRect.getOpenHeight() - height)/2 + 1;
141 while( height >= 1)
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--;
152 rRenderContext.Pop();
155 } // end anonymous namespace
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)
176 Menu::~Menu()
178 disposeOnce();
181 void Menu::dispose()
183 ImplCallEventListeners( VclEventId::ObjectDying, ITEMPOS_INVALID );
185 // at the window free the reference to the accessible component
186 // and make sure the MenuFloatingWindow knows about our destruction
187 if ( pWindow )
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 >() );
195 // dispose accessible components
196 if ( mxAccessible.is() )
198 css::uno::Reference< css::lang::XComponent> xComponent( mxAccessible, css::uno::UNO_QUERY );
199 if ( xComponent.is() )
200 xComponent->dispose();
203 if ( nEventId )
204 Application::RemoveUserEvent( nEventId );
206 // Notify deletion of this menu
207 ImplMenuDelData* pDelData = mpFirstDel;
208 while ( pDelData )
210 pDelData->mpMenu = nullptr;
211 pDelData = pDelData->mpNext;
214 bKilled = true;
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;)
220 --n;
221 if (mpSalMenu)
222 mpSalMenu->RemoveItem(n);
223 pItemList->Remove(n);
226 assert(!pItemList->size());
228 mpLayoutData.reset();
230 // Native-support: destroy SalMenu
231 mpSalMenu.reset();
233 pStartedFrom.clear();
234 pWindow.clear();
235 VclReferenceBase::dispose();
238 void Menu::CreateAutoMnemonics()
240 MnemonicGenerator aMnemonicGenerator;
241 size_t n;
242 for ( n = 0; n < pItemList->size(); n++ )
244 MenuItemData* pData = pItemList->GetDataFromPos( n );
245 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
246 aMnemonicGenerator.RegisterMnemonic( pData->aText );
248 for ( n = 0; n < pItemList->size(); n++ )
250 MenuItemData* pData = pItemList->GetDataFromPos( n );
251 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
252 pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText );
256 void Menu::Activate()
258 bInCallback = true;
260 ImplMenuDelData aDelData( this );
262 ImplCallEventListeners( VclEventId::MenuActivate, ITEMPOS_INVALID );
264 if( !aDelData.isDeleted() )
266 if ( !aActivateHdl.Call( this ) )
268 if( !aDelData.isDeleted() )
270 Menu* pStartMenu = ImplGetStartMenu();
271 if ( pStartMenu && ( pStartMenu != this ) )
273 pStartMenu->bInCallback = true;
274 // MT 11/01: Call EventListener here? I don't know...
275 pStartMenu->aActivateHdl.Call( this );
276 pStartMenu->bInCallback = false;
280 bInCallback = false;
283 if (!aDelData.isDeleted() && !(nMenuFlags & MenuFlags::NoAutoMnemonics))
284 CreateAutoMnemonics();
287 void Menu::Deactivate()
289 for ( size_t n = pItemList->size(); n; )
291 MenuItemData* pData = pItemList->GetDataFromPos( --n );
292 if ( pData->bIsTemporary )
294 if ( ImplGetSalMenu() )
295 ImplGetSalMenu()->RemoveItem( n );
297 pItemList->Remove( n );
301 bInCallback = true;
303 ImplMenuDelData aDelData( this );
305 Menu* pStartMenu = ImplGetStartMenu();
306 ImplCallEventListeners( VclEventId::MenuDeactivate, ITEMPOS_INVALID );
308 if( !aDelData.isDeleted() )
310 if ( !aDeactivateHdl.Call( this ) )
312 if( !aDelData.isDeleted() )
314 if ( pStartMenu && ( pStartMenu != this ) )
316 pStartMenu->bInCallback = true;
317 pStartMenu->aDeactivateHdl.Call( this );
318 pStartMenu->bInCallback = false;
324 if( !aDelData.isDeleted() )
326 bInCallback = false;
330 void Menu::ImplSelect()
332 MenuItemData* pData = GetItemList()->GetData( nSelectedId );
333 if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) )
335 bool bChecked = IsItemChecked( nSelectedId );
336 if ( pData->nBits & MenuItemBits::RADIOCHECK )
338 if ( !bChecked )
339 CheckItem( nSelectedId );
341 else
342 CheckItem( nSelectedId, !bChecked );
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 ) );
351 void Menu::Select()
353 ImplMenuDelData aDelData( this );
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 );
370 #if defined(MACOSX)
371 void Menu::ImplSelectWithStart( Menu* pSMenu )
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;
381 #endif
383 void Menu::ImplCallEventListeners( VclEventId nEvent, sal_uInt16 nPos )
385 ImplMenuDelData aDelData( this );
387 VclMenuEvent aEvent( this, nEvent, nPos );
389 // This is needed by atk accessibility bridge
390 if ( nEvent == VclEventId::MenuHighlight )
392 Application::ImplCallEventListeners( aEvent );
395 if ( !aDelData.isDeleted() )
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 )
401 if( std::find(maEventListeners.begin(), maEventListeners.end(), rLink) != maEventListeners.end() )
402 rLink.Call( aEvent );
407 void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener )
409 maEventListeners.push_back( rEventListener );
412 void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener )
414 maEventListeners.remove( rEventListener );
417 MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits,
418 const OUString& rStr, Menu* pMenu,
419 size_t nPos, const OUString &rIdent)
421 // put Item in MenuItemList
422 MenuItemData* pData = pItemList->Insert(nId, MenuItemType::STRING,
423 nBits, rStr, pMenu, nPos, rIdent);
425 // update native menu
426 if (ImplGetSalMenu() && pData->pSalMenuItem)
427 ImplGetSalMenu()->InsertItem(pData->pSalMenuItem.get(), nPos);
429 return pData;
432 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits,
433 const OUString &rIdent, sal_uInt16 nPos)
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" );
439 // if Position > ItemCount, append
440 if ( nPos >= pItemList->size() )
441 nPos = MENU_APPEND;
443 // put Item in MenuItemList
444 NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent);
446 vcl::Window* pWin = ImplGetWindow();
447 mpLayoutData.reset();
448 if ( pWin )
450 ImplCalcSize( pWin );
451 if ( pWin->IsVisible() )
452 pWin->Invalidate();
454 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
457 void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage,
458 MenuItemBits nItemBits, const OUString &rIdent, sal_uInt16 nPos)
460 InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos);
461 SetItemImage( nItemId, rImage );
464 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr,
465 const Image& rImage, MenuItemBits nItemBits,
466 const OUString &rIdent, sal_uInt16 nPos)
468 InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
469 SetItemImage( nItemId, rImage );
472 void Menu::InsertSeparator(const OUString &rIdent, sal_uInt16 nPos)
474 // do nothing if it's a menu bar
475 if (IsMenuBar())
476 return;
478 // if position > ItemCount, append
479 if ( nPos >= pItemList->size() )
480 nPos = MENU_APPEND;
482 // put separator in item list
483 pItemList->InsertSeparator(rIdent, nPos);
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 );
491 mpLayoutData.reset();
493 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
496 void Menu::RemoveItem( sal_uInt16 nPos )
498 bool bRemove = false;
500 if ( nPos < GetItemCount() )
502 // update native menu
503 if( ImplGetSalMenu() )
504 ImplGetSalMenu()->RemoveItem( nPos );
506 pItemList->Remove( nPos );
507 bRemove = true;
510 vcl::Window* pWin = ImplGetWindow();
511 if ( pWin )
513 ImplCalcSize( pWin );
514 if ( pWin->IsVisible() )
515 pWin->Invalidate();
517 mpLayoutData.reset();
519 if ( bRemove )
520 ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos );
523 static void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
525 MenuItemType eType = rMenu.GetItemType( nPos );
527 if ( eType == MenuItemType::DONTKNOW )
528 return;
530 if ( eType == MenuItemType::SEPARATOR )
531 pThis->InsertSeparator( {}, nNewPos );
532 else
534 sal_uInt16 nId = rMenu.GetItemId( nPos );
536 SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
537 "Menu::CopyItem(): ItemId already exists" );
539 MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
541 if (!pData)
542 return;
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 );
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 );
561 PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
562 if ( pSubMenu )
564 // create auto-copy
565 VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
566 pThis->SetPopupMenu( nId, pNewMenu );
571 void Menu::Clear()
573 for ( sal_uInt16 i = GetItemCount(); i; i-- )
574 RemoveItem( 0 );
577 sal_uInt16 Menu::GetItemCount() const
579 return static_cast<sal_uInt16>(pItemList->size());
582 sal_uInt16 Menu::ImplGetVisibleItemCount() const
584 sal_uInt16 nItems = 0;
585 for ( size_t n = pItemList->size(); n; )
587 if ( ImplIsVisible( --n ) )
588 nItems++;
590 return nItems;
593 sal_uInt16 Menu::ImplGetFirstVisible() const
595 for ( size_t n = 0; n < pItemList->size(); n++ )
597 if ( ImplIsVisible( n ) )
598 return n;
600 return ITEMPOS_INVALID;
603 sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
605 for ( size_t n = nPos; n; )
607 if (ImplIsVisible(--n))
608 return n;
610 return ITEMPOS_INVALID;
613 sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
615 for ( size_t n = nPos+1; n < pItemList->size(); n++ )
617 if ( ImplIsVisible( n ) )
618 return n;
620 return ITEMPOS_INVALID;
623 sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
625 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
627 if ( pData )
628 return pData->nId;
629 else
630 return 0;
633 sal_uInt16 Menu::GetItemId(std::u16string_view rIdent) const
635 for (size_t n = 0; n < pItemList->size(); ++n)
637 MenuItemData* pData = pItemList->GetDataFromPos(n);
638 if (pData && pData->sIdent == rIdent)
639 return pData->nId;
641 return MENU_ITEM_NOTFOUND;
644 sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
646 size_t nPos;
647 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
649 if ( pData )
650 return static_cast<sal_uInt16>(nPos);
651 else
652 return MENU_ITEM_NOTFOUND;
655 MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
657 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
659 if ( pData )
660 return pData->eType;
661 else
662 return MenuItemType::DONTKNOW;
665 OUString Menu::GetItemIdent(sal_uInt16 nId) const
667 const MenuItemData* pData = pItemList->GetData(nId);
668 return pData ? pData->sIdent : OUString();
671 void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
673 size_t nPos;
674 MenuItemData* pData = pItemList->GetData(nItemId, nPos);
676 if (pData && (pData->nBits != nBits))
678 pData->nBits = nBits;
680 // update native menu
681 if (ImplGetSalMenu())
682 ImplGetSalMenu()->SetItemBits(nPos, nBits);
686 MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
688 MenuItemBits nBits = MenuItemBits::NONE;
689 MenuItemData* pData = pItemList->GetData( nItemId );
690 if ( pData )
691 nBits = pData->nBits;
692 return nBits;
695 void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc)
697 MenuItemData* pData = pItemList->GetData(nItemId);
698 if (pData)
700 if (pData->aUserValueReleaseFunc)
701 pData->aUserValueReleaseFunc(pData->nUserValue);
702 pData->aUserValueReleaseFunc = aFunc;
703 pData->nUserValue = nUserValue;
707 void* Menu::GetUserValue( sal_uInt16 nItemId ) const
709 MenuItemData* pData = pItemList->GetData( nItemId );
710 return pData ? pData->nUserValue : nullptr;
713 void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
715 size_t nPos;
716 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
718 // Item does not exist -> return NULL
719 if ( !pData )
720 return;
722 // same menu, nothing to do
723 if ( static_cast<PopupMenu*>(pData->pSubMenu.get()) == pMenu )
724 return;
726 // remove old menu
727 auto oldSubMenu = pData->pSubMenu;
729 // data exchange
730 pData->pSubMenu = pMenu;
732 // #112023# Make sure pStartedFrom does not point to invalid (old) data
733 if ( pData->pSubMenu )
734 pData->pSubMenu->pStartedFrom = nullptr;
736 // set native submenu
737 if( ImplGetSalMenu() && pData->pSalMenuItem )
739 if( pMenu )
740 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), pMenu->ImplGetSalMenu(), nPos );
741 else
742 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), nullptr, nPos );
745 oldSubMenu.disposeAndClear();
747 ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos );
750 PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
752 MenuItemData* pData = pItemList->GetData( nItemId );
754 if ( pData )
755 return static_cast<PopupMenu*>(pData->pSubMenu.get());
756 else
757 return nullptr;
760 void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
762 size_t nPos;
763 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
765 if ( !pData )
766 return;
768 if ( pData->aAccelKey == rKeyCode )
769 return;
771 pData->aAccelKey = rKeyCode;
773 // update native menu
774 if( ImplGetSalMenu() && pData->pSalMenuItem )
775 ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem.get(), rKeyCode, rKeyCode.GetName() );
778 KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
780 MenuItemData* pData = pItemList->GetData( nItemId );
782 if ( pData )
783 return pData->aAccelKey;
784 else
785 return KeyCode();
788 KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
790 KeyEvent aRet;
791 MenuItemData* pData = pItemList->GetData( nItemId );
792 if( pData )
794 sal_Int32 nPos = pData->aText.indexOf( '~' );
795 if( nPos != -1 && nPos < pData->aText.getLength()-1 )
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');
806 aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
810 return aRet;
813 void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
815 size_t nPos;
816 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
818 if ( !pData || pData->bChecked == bCheck )
819 return;
821 // if radio-check, then uncheck previous
822 if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
823 (pData->nBits & MenuItemBits::RADIOCHECK) )
825 MenuItemData* pGroupData;
826 sal_uInt16 nGroupPos;
827 sal_uInt16 nItemCount = GetItemCount();
828 bool bFound = false;
830 nGroupPos = nPos;
831 while ( nGroupPos )
833 pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
834 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
836 if ( IsItemChecked( pGroupData->nId ) )
838 CheckItem( pGroupData->nId, false );
839 bFound = true;
840 break;
843 else
844 break;
845 nGroupPos--;
848 if ( !bFound )
850 nGroupPos = nPos+1;
851 while ( nGroupPos < nItemCount )
853 pGroupData = pItemList->GetDataFromPos( nGroupPos );
854 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
856 if ( IsItemChecked( pGroupData->nId ) )
858 CheckItem( pGroupData->nId, false );
859 break;
862 else
863 break;
864 nGroupPos++;
869 pData->bChecked = bCheck;
871 // update native menu
872 if( ImplGetSalMenu() )
873 ImplGetSalMenu()->CheckItem( nPos, bCheck );
875 ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos );
878 void Menu::CheckItem( std::u16string_view rIdent , bool bCheck )
880 CheckItem( GetItemId( rIdent ), bCheck );
883 bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
885 size_t nPos;
886 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
888 if ( !pData )
889 return false;
891 return pData->bChecked;
894 void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
896 size_t nPos;
897 MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
899 if ( !(pItemData && ( pItemData->bEnabled != bEnable )) )
900 return;
902 pItemData->bEnabled = bEnable;
904 vcl::Window* pWin = ImplGetWindow();
905 if ( pWin && pWin->IsVisible() )
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++ )
912 MenuItemData* pData = pItemList->GetDataFromPos( n );
913 if ( n == nPos )
915 pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
916 break;
918 nX += pData->aSz.Width();
921 // update native menu
922 if( ImplGetSalMenu() )
923 ImplGetSalMenu()->EnableItem( nPos, bEnable );
925 ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos );
928 bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
930 size_t nPos;
931 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
933 if ( !pData )
934 return false;
936 return pData->bEnabled;
939 void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
941 size_t nPos;
942 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
944 SAL_WARN_IF(IsMenuBar() && !bVisible , "vcl", "Menu::ShowItem - ignored for menu bar entries!");
945 if (IsMenuBar() || !pData || (pData->bVisible == bVisible))
946 return;
948 vcl::Window* pWin = ImplGetWindow();
949 if ( pWin && pWin->IsVisible() )
951 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
952 return;
954 pData->bVisible = bVisible;
956 // update native menu
957 if( ImplGetSalMenu() )
958 ImplGetSalMenu()->ShowItem( nPos, bVisible );
961 void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
963 size_t nPos;
964 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
966 if ( !pData )
967 return;
969 if ( rStr == pData->aText )
970 return;
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 );
980 vcl::Window* pWin = ImplGetWindow();
981 mpLayoutData.reset();
982 if (pWin && IsMenuBar())
984 ImplCalcSize( pWin );
985 if ( pWin->IsVisible() )
986 pWin->Invalidate();
989 ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos );
992 OUString Menu::GetItemText( sal_uInt16 nItemId ) const
994 size_t nPos;
995 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
997 if ( pData )
998 return pData->aText;
1000 return OUString();
1003 void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1005 size_t nPos;
1006 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1008 if ( !pData )
1009 return;
1011 pData->aImage = rImage;
1012 ImplSetMenuItemData( pData );
1014 // update native menu
1015 if( ImplGetSalMenu() && pData->pSalMenuItem )
1016 ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem.get(), rImage );
1019 Image Menu::GetItemImage( sal_uInt16 nItemId ) const
1021 MenuItemData* pData = pItemList->GetData( nItemId );
1023 if ( pData )
1024 return pData->aImage;
1025 else
1026 return Image();
1029 void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1031 size_t nPos;
1032 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1034 if ( pData )
1035 pData->aCommandStr = rCommand;
1038 OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
1040 MenuItemData* pData = pItemList->GetData( nItemId );
1042 if (pData)
1043 return pData->aCommandStr;
1045 return OUString();
1048 void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
1050 MenuItemData* pData = pItemList->GetData( nItemId );
1052 if ( pData )
1053 pData->aHelpCommandStr = rStr;
1056 OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
1058 MenuItemData* pData = pItemList->GetData( nItemId );
1060 if ( pData )
1061 return pData->aHelpCommandStr;
1063 return OUString();
1066 void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
1068 MenuItemData* pData = pItemList->GetData( nItemId );
1070 if ( pData )
1071 pData->aHelpText = rStr;
1074 OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
1076 MenuItemData* pData = pItemList->GetData( nItemId );
1078 if (!pData)
1079 return OUString();
1081 if ( pData->aHelpText.isEmpty() &&
1082 (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() )))
1084 Help* pHelp = Application::GetHelp();
1085 if ( pHelp )
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( pData->aHelpId, static_cast<weld::Widget*>(nullptr) );
1094 //Fallback to Menu::GetAccessibleDescription without reentry to GetHelpText()
1095 if (pData->aHelpText.isEmpty())
1096 return pData->aAccessibleDescription;
1097 return pData->aHelpText;
1100 OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
1102 return ImplGetHelpText( nItemId );
1105 void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
1107 MenuItemData* pData = pItemList->GetData( nItemId );
1109 if ( pData )
1110 pData->aTipHelpText = rStr;
1113 OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
1115 MenuItemData* pData = pItemList->GetData( nItemId );
1117 if ( pData )
1118 return pData->aTipHelpText;
1120 return OUString();
1123 void Menu::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
1125 MenuItemData* pData = pItemList->GetData( nItemId );
1127 if ( pData )
1128 pData->aHelpId = rHelpId;
1131 OUString Menu::GetHelpId( sal_uInt16 nItemId ) const
1133 OUString aRet;
1135 MenuItemData* pData = pItemList->GetData( nItemId );
1137 if ( pData )
1139 if ( !pData->aHelpId.isEmpty() )
1140 aRet = pData->aHelpId;
1141 else
1142 aRet = pData->aCommandStr;
1145 return aRet;
1148 Menu& Menu::operator=( const Menu& rMenu )
1150 if(this == &rMenu)
1151 return *this;
1153 // clean up
1154 Clear();
1156 // copy items
1157 sal_uInt16 nCount = rMenu.GetItemCount();
1158 for ( sal_uInt16 i = 0; i < nCount; i++ )
1159 ImplCopyItem( this, rMenu, i, MENU_APPEND );
1161 aActivateHdl = rMenu.aActivateHdl;
1162 aDeactivateHdl = rMenu.aDeactivateHdl;
1163 aSelectHdl = rMenu.aSelectHdl;
1164 aTitleText = rMenu.aTitleText;
1165 nTitleHeight = rMenu.nTitleHeight;
1167 return *this;
1170 // Returns true if the item is completely hidden on the GUI and shouldn't
1171 // be possible to interact with
1172 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const
1174 MenuItemData* pData = pItemList->GetDataFromPos(nPos);
1175 if (pData)
1177 MenuItemData* pPreviousData = pItemList->GetDataFromPos( nPos - 1 );
1178 if (pPreviousData && pPreviousData->bHiddenOnGUI)
1180 return true;
1183 return false;
1186 bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
1188 bool bVisible = true;
1190 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1191 // check general visibility first
1192 if( pData && !pData->bVisible )
1193 bVisible = false;
1195 if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
1197 if( nPos == 0 ) // no separator should be shown at the very beginning
1198 bVisible = false;
1199 else
1201 // always avoid adjacent separators
1202 size_t nCount = pItemList->size();
1203 size_t n;
1204 MenuItemData* pNextData = nullptr;
1205 // search next visible item
1206 for( n = nPos + 1; n < nCount; n++ )
1208 pNextData = pItemList->GetDataFromPos( n );
1209 if( pNextData && pNextData->bVisible )
1211 if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
1212 break;
1215 if( n == nCount ) // no next visible item
1216 bVisible = false;
1217 // check for separator
1218 if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
1219 bVisible = false;
1221 if( bVisible )
1223 for( n = nPos; n > 0; n-- )
1225 pNextData = pItemList->GetDataFromPos( n-1 );
1226 if( pNextData && pNextData->bVisible )
1228 if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
1229 break;
1232 if( n == 0 ) // no previous visible item
1233 bVisible = false;
1238 // not allowed for menubar, as I do not know
1239 // whether a menu-entry will disappear or will appear
1240 if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) &&
1241 !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
1243 if( !pData ) // e.g. nPos == ITEMPOS_INVALID
1244 bVisible = false;
1245 else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
1247 // tdf#86850 Always display clipboard functions
1248 if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" ||
1249 pData->sIdent == ".uno:Cut" || pData->sIdent == ".uno:Copy" || pData->sIdent == ".uno:Paste" )
1250 bVisible = true;
1251 else
1252 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1253 bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
1257 return bVisible;
1260 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
1262 return IsMenuVisible() && ImplIsVisible( nItemPos );
1265 bool Menu::IsMenuVisible() const
1267 return pWindow && pWindow->IsReallyVisible();
1270 bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
1272 bool bSelectable = true;
1274 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1275 // check general visibility first
1276 if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
1277 bSelectable = false;
1279 return bSelectable;
1282 css::uno::Reference<css::accessibility::XAccessible> Menu::GetAccessible()
1284 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1285 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1286 // mxAccessible member only for sub menus.
1287 if (pStartedFrom && pStartedFrom != this)
1289 for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
1291 sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
1292 if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
1294 css::uno::Reference<css::accessibility::XAccessible> xParent = pStartedFrom->GetAccessible();
1295 if ( xParent.is() )
1297 css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( xParent->getAccessibleContext() );
1298 if (xParentContext.is())
1299 return xParentContext->getAccessibleChild( i );
1304 else if ( !mxAccessible.is() )
1306 UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
1307 if ( pWrapper )
1308 mxAccessible = pWrapper->CreateAccessible(this, IsMenuBar());
1311 return mxAccessible;
1314 void Menu::SetAccessible(const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible )
1316 mxAccessible = rxAccessible;
1319 Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, tools::Long& rCheckHeight, tools::Long& rRadioHeight ) const
1321 tools::Long nCheckWidth = 0, nRadioWidth = 0;
1322 rCheckHeight = rRadioHeight = 0;
1324 if (!IsMenuBar())
1326 ImplControlValue aVal;
1327 tools::Rectangle aNativeBounds;
1328 tools::Rectangle aNativeContent;
1330 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1331 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
1333 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
1334 aCtrlRegion, ControlState::ENABLED, aVal,
1335 aNativeBounds, aNativeContent))
1337 rCheckHeight = aNativeBounds.GetHeight() - 1;
1338 nCheckWidth = aNativeContent.GetWidth() - 1;
1341 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
1343 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
1344 aCtrlRegion, ControlState::ENABLED, aVal,
1345 aNativeBounds, aNativeContent))
1347 rRadioHeight = aNativeBounds.GetHeight() - 1;
1348 nRadioWidth = aNativeContent.GetWidth() - 1;
1352 return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
1355 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, tools::Long& rArrowSpacing)
1357 ImplControlValue aVal;
1358 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1359 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
1361 tools::Rectangle aNativeContent;
1362 tools::Rectangle aNativeBounds;
1363 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
1364 aCtrlRegion, ControlState::ENABLED,
1365 aVal, aNativeBounds, aNativeContent))
1367 Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
1368 rArrowSize = aSize;
1369 rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
1370 return true;
1373 return false;
1376 void Menu::ImplAddDel( ImplMenuDelData& rDel )
1378 SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1379 if( !rDel.mpMenu )
1381 rDel.mpMenu = this;
1382 rDel.mpNext = mpFirstDel;
1383 mpFirstDel = &rDel;
1387 void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
1389 rDel.mpMenu = nullptr;
1390 if ( mpFirstDel == &rDel )
1392 mpFirstDel = rDel.mpNext;
1394 else
1396 ImplMenuDelData* pData = mpFirstDel;
1397 while ( pData && (pData->mpNext != &rDel) )
1398 pData = pData->mpNext;
1400 SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1401 if( pData )
1402 pData->mpNext = rDel.mpNext;
1406 Size Menu::ImplCalcSize( vcl::Window* pWin )
1408 // | Check/Radio/Image| Text| Accel/Popup|
1410 // for symbols: nFontHeight x nFontHeight
1411 tools::Long nFontHeight = pWin->GetTextHeight();
1412 tools::Long nExtra = nFontHeight/4;
1414 tools::Long nMinMenuItemHeight = nFontHeight;
1415 tools::Long nCheckHeight = 0, nRadioHeight = 0;
1416 Size aMarkSize = ImplGetNativeCheckAndRadioSize(*pWin->GetOutDev(), nCheckHeight, nRadioHeight);
1417 if( aMarkSize.Height() > nMinMenuItemHeight )
1418 nMinMenuItemHeight = aMarkSize.Height();
1420 tools::Long aMaxImgWidth = 0;
1422 const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
1423 if ( rSettings.GetUseImagesInMenus() )
1425 if ( 16 > nMinMenuItemHeight )
1426 nMinMenuItemHeight = 16;
1427 for ( size_t i = pItemList->size(); i; )
1429 MenuItemData* pData = pItemList->GetDataFromPos( --i );
1430 if ( ImplIsVisible( i )
1431 && ( ( pData->eType == MenuItemType::IMAGE )
1432 || ( pData->eType == MenuItemType::STRINGIMAGE )
1436 Size aImgSz = pData->aImage.GetSizePixel();
1437 if ( aImgSz.Width() > aMaxImgWidth )
1438 aMaxImgWidth = aImgSz.Width();
1439 if ( aImgSz.Height() > nMinMenuItemHeight )
1440 nMinMenuItemHeight = aImgSz.Height();
1441 break;
1446 Size aSz;
1447 tools::Long nMaxWidth = 0;
1449 for ( size_t n = pItemList->size(); n; )
1451 MenuItemData* pData = pItemList->GetDataFromPos( --n );
1453 pData->aSz.setHeight( 0 );
1454 pData->aSz.setWidth( 0 );
1456 if ( ImplIsVisible( n ) )
1458 tools::Long nWidth = 0;
1460 // Separator
1461 if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
1463 pData->aSz.setHeight( 4 );
1466 // Image:
1467 if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1469 tools::Long aImgHeight = pData->aImage.GetSizePixel().Height();
1471 aImgHeight += 4; // add a border for native marks
1472 if (aImgHeight > pData->aSz.Height())
1473 pData->aSz.setHeight(aImgHeight);
1476 // Check Buttons:
1477 if (!IsMenuBar() && pData->HasCheck())
1479 // checks / images take the same place
1480 if( ( pData->eType != MenuItemType::IMAGE ) && ( pData->eType != MenuItemType::STRINGIMAGE ) )
1482 nWidth += aMarkSize.Width() + nExtra * 2;
1483 if (aMarkSize.Height() > pData->aSz.Height())
1484 pData->aSz.setHeight(aMarkSize.Height());
1488 // Text:
1489 if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
1491 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(pWin->GetOutDev());
1492 tools::Long nTextWidth = pWin->GetOutDev()->GetCtrlTextWidth(pData->aText, pGlyphs);
1493 tools::Long nTextHeight = pWin->GetTextHeight() + EXTRAITEMHEIGHT;
1495 if (IsMenuBar())
1497 if ( nTextHeight > pData->aSz.Height() )
1498 pData->aSz.setHeight( nTextHeight );
1500 pData->aSz.setWidth( nTextWidth + 4*nExtra );
1501 aSz.AdjustWidth(pData->aSz.Width() );
1503 else
1504 pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1506 nWidth += nTextWidth;
1509 // Accel
1510 if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1512 OUString aName = pData->aAccelKey.GetName();
1513 tools::Long nAccWidth = pWin->GetTextWidth( aName );
1514 nAccWidth += nExtra;
1515 nWidth += nAccWidth;
1518 // SubMenu?
1519 if (!IsMenuBar() && pData->pSubMenu)
1521 if ( nFontHeight > nWidth )
1522 nWidth += nFontHeight;
1524 pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1527 if (!IsMenuBar())
1528 aSz.AdjustHeight(pData->aSz.Height() );
1530 if ( nWidth > nMaxWidth )
1531 nMaxWidth = nWidth;
1536 // Additional space for title
1537 nTitleHeight = 0;
1538 if (!IsMenuBar() && aTitleText.getLength() > 0) {
1539 // Set expected font
1540 pWin->GetOutDev()->Push(PushFlags::FONT);
1541 vcl::Font aFont = pWin->GetFont();
1542 aFont.SetWeight(WEIGHT_BOLD);
1543 pWin->SetFont(aFont);
1545 // Compute text bounding box
1546 tools::Rectangle aTextBoundRect;
1547 pWin->GetOutDev()->GetTextBoundRect(aTextBoundRect, aTitleText);
1549 // Vertically, one height of char + extra space for decoration
1550 nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ;
1551 aSz.AdjustHeight(nTitleHeight );
1553 tools::Long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
1554 pWin->GetOutDev()->Pop();
1555 if ( nWidth > nMaxWidth )
1556 nMaxWidth = nWidth;
1559 if (!IsMenuBar())
1561 // popup menus should not be wider than half the screen
1562 // except on rather small screens
1563 // TODO: move GetScreenNumber from SystemWindow to Window ?
1564 // currently we rely on internal privileges
1565 unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.screen();
1566 tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
1567 tools::Long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
1568 if( nMaxWidth > nScreenWidth/2 )
1569 nMaxWidth = nScreenWidth/2;
1571 sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
1572 nImgOrChkPos = static_cast<sal_uInt16>(nExtra);
1573 tools::Long nImgOrChkWidth = 0;
1574 if( aMarkSize.Height() > 0 ) // NWF case
1575 nImgOrChkWidth = aMarkSize.Height() + nExtra;
1576 else // non NWF case
1577 nImgOrChkWidth = nFontHeight/2 + gfxExtra;
1578 nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgWidth + gfxExtra );
1579 nTextPos = static_cast<sal_uInt16>(nImgOrChkPos + nImgOrChkWidth);
1580 nTextPos = nTextPos + gfxExtra;
1582 aSz.setWidth( nTextPos + nMaxWidth + nExtra );
1583 aSz.AdjustWidth(4*nExtra ); // a _little_ more ...
1585 aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX );
1586 aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1588 else
1590 nTextPos = static_cast<sal_uInt16>(2*nExtra);
1591 aSz.setHeight( nFontHeight+6 );
1593 // get menubar height from native methods if supported
1594 if( pWindow->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) )
1596 ImplControlValue aVal;
1597 tools::Rectangle aNativeBounds;
1598 tools::Rectangle aNativeContent;
1599 Point tmp( 0, 0 );
1600 tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
1601 if( pWindow->GetNativeControlRegion( ControlType::Menubar,
1602 ControlPart::Entire,
1603 aCtrlRegion,
1604 ControlState::ENABLED,
1605 aVal,
1606 aNativeBounds,
1607 aNativeContent )
1610 int nNativeHeight = aNativeBounds.GetHeight();
1611 if( nNativeHeight > aSz.Height() )
1612 aSz.setHeight( nNativeHeight );
1616 // account for the size of the close button, which actually is a toolbox
1617 // due to NWF this is variable
1618 tools::Long nCloseButtonHeight = static_cast<MenuBarWindow*>(pWindow.get())->MinCloseButtonSize().Height();
1619 if (aSz.Height() < nCloseButtonHeight)
1620 aSz.setHeight( nCloseButtonHeight );
1623 return aSz;
1626 static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight)
1628 bool bNativeOk = false;
1629 if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
1631 ImplControlValue aControlValue;
1632 aControlValue.setTristateVal(ButtonValue::On);
1633 tools::Rectangle r = i_rRect;
1634 r.AdjustBottom(1);
1636 bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
1638 ControlState::PRESSED | ControlState::ENABLED,
1639 aControlValue,
1640 OUString());
1643 if (!bNativeOk)
1645 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1646 Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
1647 RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor);
1651 static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, tools::Long i_nMaxWidth )
1653 sal_Int32 nPos = -1;
1654 OUString aNonMnem(removeMnemonicFromString(i_rLong, nPos));
1655 aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
1656 // re-insert mnemonic
1657 if (nPos != -1)
1659 if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
1661 OUString aTmp = OUString::Concat(aNonMnem.subView(0, nPos)) + "~" + aNonMnem.subView(nPos);
1662 aNonMnem = aTmp;
1665 return aNonMnem;
1668 void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const
1670 // Save previous graphical settings, set new one
1671 rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
1672 Wallpaper aOldBackground = rRenderContext.GetBackground();
1674 Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
1675 rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
1676 rRenderContext.SetFillColor(aBackgroundColor);
1677 vcl::Font aFont = rRenderContext.GetFont();
1678 aFont.SetWeight(WEIGHT_BOLD);
1679 rRenderContext.SetFont(aFont);
1681 // Draw background rectangle
1682 tools::Rectangle aBgRect(rRect);
1683 int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1684 aBgRect.Move(SPACE_AROUND_TITLE, SPACE_AROUND_TITLE);
1685 aBgRect.setWidth(aBgRect.getOpenWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
1686 aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE);
1687 rRenderContext.DrawRect(aBgRect);
1689 // Draw the text centered
1690 Point aTextTopLeft(aBgRect.TopLeft());
1691 tools::Rectangle aTextBoundRect;
1692 rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText );
1693 aTextTopLeft.AdjustX((aBgRect.getOpenWidth() - aTextBoundRect.GetSize().Width()) / 2 );
1694 aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
1695 - aTextBoundRect.Top() );
1696 rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength());
1698 // Restore
1699 rRenderContext.Pop();
1700 rRenderContext.SetBackground(aOldBackground);
1703 void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize,
1704 sal_uInt16 nBorder, tools::Long nStartY, MenuItemData const * pThisItemOnly,
1705 bool bHighlighted, bool bLayout, bool bRollover) const
1707 // for symbols: nFontHeight x nFontHeight
1708 tools::Long nFontHeight = rRenderContext.GetTextHeight();
1709 tools::Long nExtra = nFontHeight / 4;
1711 tools::Long nCheckHeight = 0, nRadioHeight = 0;
1712 ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
1714 DecorationView aDecoView(&rRenderContext);
1715 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1717 Point aTopLeft, aTmpPos;
1719 int nOuterSpaceX = 0;
1720 if (!IsMenuBar())
1722 nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1723 aTopLeft.AdjustX(nOuterSpaceX );
1724 aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1727 // for the computations, use size of the underlying window, not of RenderContext
1728 Size aOutSz(rSize);
1730 size_t nCount = pItemList->size();
1731 if (bLayout)
1732 mpLayoutData->m_aVisibleItemBoundRects.clear();
1734 // Paint title
1735 if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0)
1736 ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz));
1738 bool bHiddenItems = false; // are any items on the GUI hidden
1740 for (size_t n = 0; n < nCount; n++)
1742 MenuItemData* pData = pItemList->GetDataFromPos( n );
1743 if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
1745 if (pThisItemOnly)
1747 if (IsMenuBar())
1749 if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
1751 if (bRollover)
1752 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1753 else if (bHighlighted)
1754 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1756 else
1758 if (bHighlighted)
1759 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1760 else if (bRollover)
1761 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1763 if (!bRollover && !bHighlighted)
1764 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
1766 else if (bHighlighted)
1767 rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1770 Point aPos(aTopLeft);
1771 aPos.AdjustY(nBorder );
1772 aPos.AdjustY(nStartY );
1774 if (aPos.Y() >= 0)
1776 tools::Long nTextOffsetY = (pData->aSz.Height() - nFontHeight) / 2;
1777 if (IsMenuBar())
1778 nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
1779 DrawTextFlags nTextStyle = DrawTextFlags::NONE;
1780 DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
1781 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1783 // submenus without items are not disabled when no items are
1784 // contained. The application itself should check for this!
1785 // Otherwise it could happen entries are disabled due to
1786 // asynchronous loading
1787 if (!pData->bEnabled || !pWindow->IsEnabled())
1789 nTextStyle |= DrawTextFlags::Disable;
1790 nSymbolStyle |= DrawSymbolFlags::Disable;
1791 nImageStyle |= DrawImageFlags::Disable;
1794 // Separator
1795 if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
1797 bool bNativeOk = false;
1798 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
1800 ControlState nState = ControlState::NONE;
1801 if (pData->bEnabled && pWindow->IsEnabled())
1802 nState |= ControlState::ENABLED;
1803 if (bHighlighted)
1804 nState |= ControlState::SELECTED;
1805 Size aSz(pData->aSz);
1806 aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX );
1807 tools::Rectangle aItemRect(aPos, aSz);
1808 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1809 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
1810 aItemRect, nState, aVal, OUString());
1812 if (!bNativeOk)
1814 aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) );
1815 aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX );
1816 rRenderContext.SetLineColor(rSettings.GetShadowColor());
1817 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1818 aTmpPos.AdjustY( 1 );
1819 rRenderContext.SetLineColor(rSettings.GetLightColor());
1820 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1821 rRenderContext.SetLineColor();
1825 tools::Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()),
1826 Size(pData->aSz.Height(), pData->aSz.Height()));
1828 // CheckMark
1829 if (!bLayout && !IsMenuBar() && pData->HasCheck())
1831 // draw selection transparent marker if checked
1832 // onto that either a checkmark or the item image
1833 // will be painted
1834 // however do not do this if native checks will be painted since
1835 // the selection color too often does not fit the theme's check and/or radio
1837 if( (pData->eType != MenuItemType::IMAGE) && (pData->eType != MenuItemType::STRINGIMAGE))
1839 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
1840 (pData->nBits & MenuItemBits::RADIOCHECK)
1841 ? ControlPart::MenuItemCheckMark
1842 : ControlPart::MenuItemRadioMark))
1844 ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
1845 ? ControlPart::MenuItemRadioMark
1846 : ControlPart::MenuItemCheckMark);
1848 ControlState nState = ControlState::NONE;
1850 if (pData->bChecked)
1851 nState |= ControlState::PRESSED;
1853 if (pData->bEnabled && pWindow->IsEnabled())
1854 nState |= ControlState::ENABLED;
1856 if (bHighlighted)
1857 nState |= ControlState::SELECTED;
1859 tools::Long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
1860 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 );
1861 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 );
1863 tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
1864 Size aSz(pData->aSz);
1865 aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX );
1866 tools::Rectangle aItemRect(aPos, aSz);
1867 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1868 rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
1869 nState, aVal, OUString());
1871 else if (pData->bChecked) // by default do nothing for unchecked items
1873 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1875 SymbolType eSymbol;
1876 Size aSymbolSize;
1877 if (pData->nBits & MenuItemBits::RADIOCHECK)
1879 eSymbol = SymbolType::RADIOCHECKMARK;
1880 aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
1882 else
1884 eSymbol = SymbolType::CHECKMARK;
1885 aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
1887 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 );
1888 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 );
1889 tools::Rectangle aRect(aTmpPos, aSymbolSize);
1890 aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
1895 // Image:
1896 if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1898 // Don't render an image for a check thing
1899 if (pData->bChecked)
1900 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1902 Image aImage = pData->aImage;
1904 aTmpPos = aOuterCheckRect.TopLeft();
1905 aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 );
1906 aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 );
1907 rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
1910 // Text:
1911 if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
1913 aTmpPos.setX( aPos.X() + nTextPos );
1914 aTmpPos.setY( aPos.Y() );
1915 aTmpPos.AdjustY(nTextOffsetY );
1916 DrawTextFlags nStyle = nTextStyle | DrawTextFlags::Mnemonic;
1918 if (pData->bIsTemporary)
1919 nStyle |= DrawTextFlags::Disable;
1920 std::vector< tools::Rectangle >* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr;
1921 OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr;
1922 if (bLayout)
1924 mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength());
1925 mpLayoutData->m_aLineItemIds.push_back(pData->nId);
1927 // #i47946# with NWF painted menus the background is transparent
1928 // since DrawCtrlText can depend on the background (e.g. for
1929 // DrawTextFlags::Disable), temporarily set a background which
1930 // hopefully matches the NWF background since it is read
1931 // from the system style settings
1932 bool bSetTmpBackground = !rRenderContext.IsBackground()
1933 && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire);
1934 if (bSetTmpBackground)
1936 Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor()
1937 : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor();
1938 rRenderContext.SetBackground(Wallpaper(aBg));
1940 // how much space is there for the text?
1941 tools::Long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX;
1942 if (!IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1944 OUString aAccText = pData->aAccelKey.GetName();
1945 nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra;
1947 if (!IsMenuBar() && pData->pSubMenu)
1949 nMaxItemTextWidth -= nFontHeight - nExtra;
1952 OUString aItemText(pData->aText);
1953 pData->bHiddenOnGUI = false;
1955 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1957 if (nMaxItemTextWidth < rRenderContext.GetTextWidth(aItemText))
1959 aItemText = "";
1960 pData->bHiddenOnGUI = true;
1961 bHiddenItems = true;
1964 else
1966 aItemText = getShortenedString(aItemText, rRenderContext, nMaxItemTextWidth);
1967 pData->bHiddenOnGUI = false;
1970 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(&rRenderContext);
1971 if (aItemText != pData->aText)
1972 // Can't use pre-computed glyphs, item text was
1973 // changed.
1974 pGlyphs = nullptr;
1975 rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(),
1976 nStyle, pVector, pDisplayText, pGlyphs);
1977 if (bSetTmpBackground)
1978 rRenderContext.SetBackground();
1981 // Accel
1982 if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1984 OUString aAccText = pData->aAccelKey.GetName();
1985 aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) );
1986 aTmpPos.AdjustX( -(4 * nExtra) );
1988 aTmpPos.AdjustX( -nOuterSpaceX );
1989 aTmpPos.setY( aPos.Y() );
1990 aTmpPos.AdjustY(nTextOffsetY );
1991 rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
1994 // SubMenu?
1995 if (!bLayout && !IsMenuBar() && pData->pSubMenu)
1997 bool bNativeOk = false;
1998 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
2000 ControlState nState = ControlState::NONE;
2001 Size aTmpSz(0, 0);
2002 tools::Long aSpacing = 0;
2004 if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
2006 aTmpSz = Size(nFontHeight, nFontHeight);
2007 aSpacing = nOuterSpaceX;
2010 if (pData->bEnabled && pWindow->IsEnabled())
2011 nState |= ControlState::ENABLED;
2012 if (bHighlighted)
2013 nState |= ControlState::SELECTED;
2015 aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX );
2016 aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 );
2017 aTmpPos.AdjustY(nExtra / 2 );
2019 tools::Rectangle aItemRect(aTmpPos, aTmpSz);
2020 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
2021 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
2022 aItemRect, nState, aVal, OUString());
2024 if (!bNativeOk)
2026 aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX );
2027 aTmpPos.setY( aPos.Y() );
2028 aTmpPos.AdjustY(nExtra/2 );
2029 aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) );
2030 if (pData->nBits & MenuItemBits::POPUPSELECT)
2032 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2033 Point aTmpPos2(aPos);
2034 aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 );
2035 aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
2036 pData->aSz.Height())),
2037 DrawFrameStyle::Group);
2039 aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
2040 SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
2044 if (pThisItemOnly && bHighlighted)
2046 // This restores the normal menu or menu bar text
2047 // color for when it is no longer highlighted.
2048 if (IsMenuBar())
2049 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
2050 else
2051 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2054 if( bLayout )
2056 if (!IsMenuBar())
2057 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
2058 else
2059 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, pData->aSz);
2063 if (!IsMenuBar())
2064 aTopLeft.AdjustY(pData->aSz.Height() );
2065 else
2066 aTopLeft.AdjustX(pData->aSz.Width() );
2069 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2070 if (bHiddenItems)
2072 sal_Int32 nSize = nFontHeight;
2073 tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize));
2074 lclDrawMoreIndicator(rRenderContext, aRectangle);
2078 Menu* Menu::ImplGetStartMenu()
2080 Menu* pStart = this;
2081 while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
2082 pStart = pStart->pStartedFrom;
2083 return pStart;
2086 void Menu::ImplCallHighlight(sal_uInt16 nItem)
2088 ImplMenuDelData aDelData( this );
2090 nSelectedId = 0;
2091 sSelectedIdent.clear();
2092 MenuItemData* pData = pItemList->GetDataFromPos(nItem);
2093 if (pData)
2095 nSelectedId = pData->nId;
2096 sSelectedIdent = pData->sIdent;
2098 ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) );
2100 if( !aDelData.isDeleted() )
2102 nSelectedId = 0;
2103 sSelectedIdent.clear();
2107 IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
2109 nEventId = nullptr;
2110 Select();
2113 Menu* Menu::ImplFindSelectMenu()
2115 Menu* pSelMenu = nEventId ? this : nullptr;
2117 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2119 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2121 if ( pData->pSubMenu )
2122 pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
2125 return pSelMenu;
2128 Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
2130 Menu* pSelMenu = nullptr;
2132 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2134 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2136 if( pData->nId == nItemId )
2137 pSelMenu = this;
2138 else if ( pData->pSubMenu )
2139 pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
2142 return pSelMenu;
2145 void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups )
2147 for ( sal_uInt16 n = 0; n < GetItemCount(); n++ )
2149 bool bRemove = false;
2150 MenuItemData* pItem = pItemList->GetDataFromPos( n );
2151 if ( pItem->eType == MenuItemType::SEPARATOR )
2153 if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
2154 bRemove = true;
2156 else
2157 bRemove = !pItem->bEnabled;
2159 if ( pItem->pSubMenu )
2161 pItem->pSubMenu->RemoveDisabledEntries();
2162 if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
2163 bRemove = true;
2166 if ( bRemove )
2167 RemoveItem( n-- );
2170 if ( GetItemCount() )
2172 sal_uInt16 nLast = GetItemCount() - 1;
2173 MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
2174 if ( pItem->eType == MenuItemType::SEPARATOR )
2175 RemoveItem( nLast );
2177 mpLayoutData.reset();
2180 void Menu::UpdateNativeMenu()
2182 if ( ImplGetSalMenu() )
2183 ImplGetSalMenu()->Update();
2186 void Menu::MenuBarKeyInput(const KeyEvent&)
2190 void Menu::ImplKillLayoutData() const
2192 mpLayoutData.reset();
2195 void Menu::ImplFillLayoutData() const
2197 if (!(pWindow && pWindow->IsReallyVisible()))
2198 return;
2200 mpLayoutData.reset(new MenuLayoutData);
2201 if (IsMenuBar())
2203 ImplPaint(*pWindow->GetOutDev(), pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2205 else
2207 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
2208 ImplPaint(*pWindow->GetOutDev(), pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(),
2209 nullptr, false, true); //FIXME
2213 tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, tools::Long nIndex ) const
2215 tools::Long nItemIndex = -1;
2216 if( ! mpLayoutData )
2217 ImplFillLayoutData();
2218 if( mpLayoutData )
2220 for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
2222 if( mpLayoutData->m_aLineItemIds[i] == nItemID )
2224 nItemIndex = mpLayoutData->m_aLineIndices[i];
2225 break;
2229 return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
2232 tools::Long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
2234 tools::Long nIndex = -1;
2235 rItemID = 0;
2236 if( ! mpLayoutData )
2237 ImplFillLayoutData();
2238 if( mpLayoutData )
2240 nIndex = mpLayoutData->GetIndexForPoint( rPoint );
2241 for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
2243 if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
2244 (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
2246 // make index relative to item
2247 nIndex -= mpLayoutData->m_aLineIndices[i];
2248 rItemID = mpLayoutData->m_aLineItemIds[i];
2249 break;
2253 return nIndex;
2256 tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
2258 tools::Rectangle aRet;
2260 if (!mpLayoutData )
2261 ImplFillLayoutData();
2262 if (mpLayoutData)
2264 std::map< sal_uInt16, tools::Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
2265 if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
2266 aRet = it->second;
2268 return aRet;
2271 void Menu::SetAccessibleName( sal_uInt16 nItemId, const OUString& rStr )
2273 size_t nPos;
2274 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
2276 if (pData && !rStr.equals(pData->aAccessibleName))
2278 pData->aAccessibleName = rStr;
2279 ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged, nPos);
2283 OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
2285 MenuItemData* pData = pItemList->GetData( nItemId );
2287 if ( pData )
2288 return pData->aAccessibleName;
2290 return OUString();
2293 void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const OUString& rStr )
2295 MenuItemData* pData = pItemList->GetData( nItemId );
2297 if ( pData )
2298 pData->aAccessibleDescription = rStr;
2301 OUString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const
2303 MenuItemData* pData = pItemList->GetData( nItemId );
2305 if (pData && !pData->aAccessibleDescription.isEmpty())
2306 return pData->aAccessibleDescription;
2308 return GetHelpText(nItemId);
2311 void Menu::GetSystemMenuData( SystemMenuData* pData ) const
2313 Menu* pMenu = const_cast<Menu*>(this);
2314 if( pData && pMenu->ImplGetSalMenu() )
2316 pMenu->ImplGetSalMenu()->GetSystemMenuData( pData );
2320 bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
2322 bool bRet = false;
2324 if( pWindow )
2326 if (IsMenuBar())
2327 bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow.get())->GetHighlightedItem() );
2328 else
2329 bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow.get())->GetHighlightedItem() );
2332 return bRet;
2335 void Menu::HighlightItem( sal_uInt16 nItemPos )
2337 if ( !pWindow )
2338 return;
2340 if (IsMenuBar())
2342 MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow.get() );
2343 pMenuWin->SetAutoPopup( false );
2344 pMenuWin->ChangeHighlightItem( nItemPos, false );
2346 else
2348 static_cast< MenuFloatingWindow* >( pWindow.get() )->ChangeHighlightItem( nItemPos, false );
2352 MenuBarWindow* MenuBar::getMenuBarWindow()
2354 // so far just a dynamic_cast, hopefully to be turned into something saner
2355 // at some stage
2356 MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(pWindow.get());
2357 //either there is no window (fdo#87663) or it is a MenuBarWindow
2358 assert(!pWindow || pWin);
2359 return pWin;
2362 MenuBar::MenuBar()
2363 : mbCloseBtnVisible(false),
2364 mbFloatBtnVisible(false),
2365 mbHideBtnVisible(false),
2366 mbDisplayable(true)
2368 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2371 MenuBar::MenuBar( const MenuBar& rMenu )
2372 : mbCloseBtnVisible(false),
2373 mbFloatBtnVisible(false),
2374 mbHideBtnVisible(false),
2375 mbDisplayable(true)
2377 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2378 *this = rMenu;
2381 MenuBar::~MenuBar()
2383 disposeOnce();
2386 void MenuBar::dispose()
2388 ImplDestroy( this, true );
2389 Menu::dispose();
2392 void MenuBar::ClosePopup(Menu *pMenu)
2394 MenuBarWindow* pMenuWin = getMenuBarWindow();
2395 if (!pMenuWin)
2396 return;
2397 pMenuWin->PopupClosed(pMenu);
2400 void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
2402 pWindow->KeyInput(rEvent);
2405 void MenuBar::ShowCloseButton(bool bShow)
2407 ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
2410 void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
2412 if ((bClose != mbCloseBtnVisible) ||
2413 (bFloat != mbFloatBtnVisible) ||
2414 (bHide != mbHideBtnVisible))
2416 mbCloseBtnVisible = bClose;
2417 mbFloatBtnVisible = bFloat;
2418 mbHideBtnVisible = bHide;
2419 MenuBarWindow* pMenuWin = getMenuBarWindow();
2420 if (pMenuWin)
2421 pMenuWin->ShowButtons(bClose, bFloat, bHide);
2425 void MenuBar::LayoutChanged()
2427 MenuBarWindow* pMenuWin = getMenuBarWindow();
2428 if (pMenuWin)
2429 pMenuWin->LayoutChanged();
2432 void MenuBar::SetDisplayable( bool bDisplayable )
2434 if( bDisplayable != mbDisplayable )
2436 if ( ImplGetSalMenu() )
2437 ImplGetSalMenu()->ShowMenuBar( bDisplayable );
2439 mbDisplayable = bDisplayable;
2440 LayoutChanged();
2444 VclPtr<vcl::Window> MenuBar::ImplCreate(vcl::Window* pParent, vcl::Window* pWindow, MenuBar* pMenu)
2446 VclPtr<MenuBarWindow> pMenuBarWindow = dynamic_cast<MenuBarWindow*>(pWindow);
2447 if (!pMenuBarWindow)
2449 pWindow = pMenuBarWindow = VclPtr<MenuBarWindow>::Create( pParent );
2452 pMenu->pStartedFrom = nullptr;
2453 pMenu->pWindow = pWindow;
2454 pMenuBarWindow->SetMenu(pMenu);
2455 tools::Long nHeight = pWindow ? pMenu->ImplCalcSize(pWindow).Height() : 0;
2457 // depending on the native implementation or the displayable flag
2458 // the menubar windows is suppressed (ie, height=0)
2459 if (!pMenu->IsDisplayable() || (pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar()))
2461 nHeight = 0;
2464 pMenuBarWindow->SetHeight(nHeight);
2465 return pWindow;
2468 void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete )
2470 vcl::Window *pWindow = pMenu->ImplGetWindow();
2471 if (pWindow && bDelete)
2473 MenuBarWindow* pMenuWin = pMenu->getMenuBarWindow();
2474 if (pMenuWin)
2475 pMenuWin->KillActivePopup();
2476 pWindow->disposeOnce();
2478 pMenu->pWindow = nullptr;
2479 if (pMenu->mpSalMenu) {
2480 pMenu->mpSalMenu->ShowMenuBar(false);
2484 bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
2486 // No keyboard processing when our menubar is invisible
2487 if (!IsDisplayable())
2488 return false;
2490 // No keyboard processing when system handles the menu.
2491 SalMenu *pNativeMenu = ImplGetSalMenu();
2492 if (pNativeMenu && pNativeMenu->VisibleMenuBar())
2494 // Except when the event is the F6 cycle pane event and we can put our
2495 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2496 // where it's not part of the application window)
2497 if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
2498 return false;
2499 if (!pNativeMenu->CanGetFocus())
2500 return false;
2503 bool bDone = false;
2504 // check for enabled, if this method is called from another window...
2505 vcl::Window* pWin = ImplGetWindow();
2506 if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode())
2508 MenuBarWindow* pMenuWin = getMenuBarWindow();
2509 bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
2511 return bDone;
2514 void MenuBar::SelectItem(sal_uInt16 nId)
2516 if (!pWindow)
2517 return;
2519 pWindow->GrabFocus();
2520 nId = GetItemPos( nId );
2522 MenuBarWindow* pMenuWin = getMenuBarWindow();
2523 if (pMenuWin)
2525 // #99705# popup the selected menu
2526 pMenuWin->SetAutoPopup( true );
2527 if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
2529 pMenuWin->KillActivePopup();
2530 pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
2532 if (nId != ITEMPOS_INVALID)
2533 pMenuWin->ChangeHighlightItem( nId, false );
2537 // handler for native menu selection and command events
2538 bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const
2540 if( pMenu )
2542 ImplMenuDelData aDelData( this );
2544 pMenu->pStartedFrom = const_cast<Menu*>(this);
2545 pMenu->bInCallback = true;
2546 pMenu->Activate();
2548 if( !aDelData.isDeleted() )
2549 pMenu->bInCallback = false;
2551 return true;
2554 bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const
2556 if( pMenu )
2558 ImplMenuDelData aDelData( this );
2560 pMenu->pStartedFrom = const_cast<Menu*>(this);
2561 pMenu->bInCallback = true;
2562 pMenu->Deactivate();
2563 if( !aDelData.isDeleted() )
2564 pMenu->bInCallback = false;
2566 return true;
2569 bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const
2571 if( !pMenu )
2572 pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nHighlightEventId);
2573 if( pMenu )
2575 ImplMenuDelData aDelData( pMenu );
2577 if( mnHighlightedItemPos != ITEMPOS_INVALID )
2578 pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, mnHighlightedItemPos );
2580 if( !aDelData.isDeleted() )
2582 pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
2583 pMenu->nSelectedId = nHighlightEventId;
2584 pMenu->sSelectedIdent = pMenu->GetItemIdent( nHighlightEventId );
2585 pMenu->pStartedFrom = const_cast<MenuBar*>(this);
2586 pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
2588 return true;
2590 else
2591 return false;
2594 bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
2596 if( !pMenu )
2597 pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId);
2598 if( pMenu )
2600 pMenu->nSelectedId = nCommandEventId;
2601 pMenu->sSelectedIdent = pMenu->GetItemIdent(nCommandEventId);
2602 pMenu->pStartedFrom = const_cast<Menu*>(this);
2603 pMenu->ImplSelect();
2604 return true;
2606 else
2607 return false;
2610 sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
2612 MenuBarWindow* pMenuWin = getMenuBarWindow();
2613 return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
2616 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>& rLink )
2618 MenuBarWindow* pMenuWin = getMenuBarWindow();
2619 if (!pMenuWin)
2620 return;
2621 pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
2624 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
2626 MenuBarWindow* pMenuWin = getMenuBarWindow();
2627 if (!pMenuWin)
2628 return;
2629 pMenuWin->RemoveMenuBarButton(nId);
2632 tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
2634 MenuBarWindow* pMenuWin = getMenuBarWindow();
2635 return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle();
2638 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
2640 MenuBarWindow* pMenuWin = getMenuBarWindow();
2641 return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
2644 int MenuBar::GetMenuBarHeight() const
2646 MenuBar* pMenuBar = const_cast<MenuBar*>(this);
2647 const SalMenu *pNativeMenu = pMenuBar->ImplGetSalMenu();
2648 int nMenubarHeight;
2649 if (pNativeMenu)
2650 nMenubarHeight = pNativeMenu->GetMenuBarHeight();
2651 else
2653 vcl::Window* pMenubarWin = GetWindow();
2654 nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputSizePixel().Height() : 0;
2656 return nMenubarHeight;
2659 // bool PopupMenu::bAnyPopupInExecute = false;
2661 MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
2662 return static_cast<MenuFloatingWindow *>(Menu::ImplGetWindow());
2665 PopupMenu::PopupMenu()
2667 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2670 PopupMenu::PopupMenu( const PopupMenu& rMenu )
2672 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2673 *this = rMenu;
2676 PopupMenu::~PopupMenu()
2678 disposeOnce();
2681 void PopupMenu::ClosePopup(Menu* pMenu)
2683 MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow());
2684 PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu);
2685 if (p && pPopup)
2686 p->KillActivePopup(pPopup);
2689 namespace vcl
2691 bool IsInPopupMenuExecute()
2693 return PopupMenu::GetActivePopupMenu() != nullptr;
2697 PopupMenu* PopupMenu::GetActivePopupMenu()
2699 ImplSVData* pSVData = ImplGetSVData();
2700 return pSVData->maAppData.mpActivePopupMenu;
2703 void PopupMenu::EndExecute()
2705 if ( ImplGetWindow() )
2706 ImplGetFloatingWindow()->EndExecute( 0 );
2709 void PopupMenu::SelectItem(sal_uInt16 nId)
2711 if ( !ImplGetWindow() )
2712 return;
2714 if( nId != ITEMPOS_INVALID )
2716 size_t nPos = 0;
2717 MenuItemData* pData = GetItemList()->GetData( nId, nPos );
2718 if (pData && pData->pSubMenu)
2719 ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
2720 else
2721 ImplGetFloatingWindow()->EndExecute( nId );
2723 else
2725 MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
2726 pFloat->GrabFocus();
2728 for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
2730 MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
2731 if( pData->pSubMenu )
2733 pFloat->KillActivePopup();
2736 pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
2740 void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
2742 nSelectedId = nId;
2743 sSelectedIdent = GetItemIdent(nId);
2746 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos )
2748 return Execute( pExecWindow, tools::Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown );
2751 static FloatWinPopupFlags lcl_TranslateFlags(PopupMenuFlags nFlags)
2753 FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
2754 if ( nFlags & PopupMenuFlags::ExecuteDown )
2755 nPopupModeFlags = FloatWinPopupFlags::Down;
2756 else if ( nFlags & PopupMenuFlags::ExecuteUp )
2757 nPopupModeFlags = FloatWinPopupFlags::Up;
2758 else if ( nFlags & PopupMenuFlags::ExecuteRight )
2759 nPopupModeFlags = FloatWinPopupFlags::Right;
2760 else
2761 nPopupModeFlags = FloatWinPopupFlags::Down;
2763 if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up
2764 nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2766 return nPopupModeFlags;
2769 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags )
2771 ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
2772 return ImplExecute( pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false );
2775 void PopupMenu::ImplFlushPendingSelect()
2777 // is there still Select?
2778 Menu* pSelect = ImplFindSelectMenu();
2779 if (pSelect)
2781 // Select should be called prior to leaving execute in a popup menu!
2782 Application::RemoveUserEvent( pSelect->nEventId );
2783 pSelect->nEventId = nullptr;
2784 pSelect->Select();
2788 bool PopupMenu::PrepareRun(const VclPtr<vcl::Window>& pParentWin, tools::Rectangle& rRect,
2789 FloatWinPopupFlags& nPopupModeFlags, Menu* pSFrom,
2790 bool& bRealExecute, VclPtr<MenuFloatingWindow>& pWin)
2792 bRealExecute = false;
2793 const sal_uInt16 nItemCount = GetItemCount();
2794 if (!pSFrom && (vcl::IsInPopupMenuExecute() || !nItemCount))
2795 return false;
2797 mpLayoutData.reset();
2799 ImplSVData* pSVData = ImplGetSVData();
2801 pStartedFrom = pSFrom;
2802 nSelectedId = 0;
2803 sSelectedIdent.clear();
2804 bCanceled = false;
2806 VclPtr<vcl::Window> xFocusId;
2807 if ( !pStartedFrom )
2809 pSVData->mpWinData->mbNoDeactivate = true;
2810 xFocusId = Window::SaveFocus();
2811 bRealExecute = true;
2813 else
2815 // assure that only one menu is open at a time
2816 if (pStartedFrom->IsMenuBar() && pSVData->mpWinData->mpFirstFloat)
2817 pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
2818 | FloatWinPopupEndFlags::CloseAll);
2821 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
2822 rRect.SetPos(pParentWin->OutputToScreenPixel(rRect.TopLeft()));
2824 nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose | FloatWinPopupFlags::GrabFocus;
2825 if (bRealExecute)
2826 nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
2828 bInCallback = true; // set it here, if Activate overridden
2829 Activate();
2830 bInCallback = false;
2832 if (pParentWin->isDisposed())
2833 return false;
2835 if ( bCanceled || bKilled )
2836 return false;
2838 if (!nItemCount)
2839 return false;
2841 // The flag MenuFlags::HideDisabledEntries is inherited.
2842 if ( pSFrom )
2844 if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries )
2845 nMenuFlags |= MenuFlags::HideDisabledEntries;
2846 else
2847 nMenuFlags &= ~MenuFlags::HideDisabledEntries;
2849 else
2850 // #102790# context menus shall never show disabled entries
2851 nMenuFlags |= MenuFlags::HideDisabledEntries;
2853 sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
2854 if ( !nVisibleEntries )
2856 OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
2858 MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, {});
2859 size_t nPos = 0;
2860 pData = pItemList->GetData( pData->nId, nPos );
2861 assert(pData);
2862 if (pData)
2864 pData->bIsTemporary = true;
2866 ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos);
2869 pWin = VclPtrInstance<MenuFloatingWindow>(this, pParentWin, WB_BORDER | WB_SYSTEMWINDOW);
2870 if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu")
2872 VclPtr<vcl::Window> xNotifierParent = pParentWin->GetParentWithLOKNotifier();
2873 assert(xNotifierParent && xNotifierParent->GetLOKNotifier() && "editview menu without LOKNotifier");
2874 pWin->SetLOKNotifier(xNotifierParent->GetLOKNotifier());
2877 if( pSVData->maNWFData.mbFlatMenu )
2878 pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
2879 else
2880 pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
2881 pWindow = pWin;
2883 Size aSz = ImplCalcSize( pWin );
2885 tools::Rectangle aDesktopRect(pWin->GetDesktopRectPixel());
2886 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() )
2888 vcl::Window* pDeskW = pWindow->GetWindow( GetWindowType::RealParent );
2889 if( ! pDeskW )
2890 pDeskW = pWindow;
2891 Point aDesktopTL(pDeskW->OutputToAbsoluteScreenPixel(rRect.TopLeft()));
2892 aDesktopRect = Application::GetScreenPosSizePixel(
2893 Application::GetBestScreen(tools::Rectangle(aDesktopTL, rRect.GetSize())));
2896 tools::Long nMaxHeight = aDesktopRect.GetHeight();
2898 //rhbz#1021915. If a menu won't fit in the desired location the default
2899 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2900 //some cases, e.g. menubars, it's desirable to limit the options to
2901 //above/below and force the menu to scroll if it won't fit
2902 if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
2904 vcl::Window* pRef = pWin;
2905 if ( pRef->GetParent() )
2906 pRef = pRef->GetParent();
2908 tools::Rectangle devRect(pRef->OutputToAbsoluteScreenPixel(rRect.TopLeft()),
2909 pRef->OutputToAbsoluteScreenPixel(rRect.BottomRight()));
2911 tools::Long nHeightAbove = devRect.Top() - aDesktopRect.Top();
2912 tools::Long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
2913 nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
2916 // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
2917 // So assume that the available screen size matches at least the system requirements
2918 SAL_WARN_IF(nMaxHeight < 768, "vcl",
2919 "Available height misdetected as " << nMaxHeight
2920 << "px. Setting to 768px instead.");
2921 nMaxHeight = std::max(nMaxHeight, tools::Long(768));
2923 if (pStartedFrom && pStartedFrom->IsMenuBar())
2924 nMaxHeight -= pParentWin->GetSizePixel().Height();
2925 sal_Int32 nLeft, nTop, nRight, nBottom;
2926 pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
2927 nMaxHeight -= nTop+nBottom;
2928 if ( aSz.Height() > nMaxHeight )
2930 pWin->EnableScrollMenu( true );
2931 sal_uInt16 nStart = ImplGetFirstVisible();
2932 sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
2933 aSz.setHeight( ImplCalcHeight( nEntries ) );
2936 pWin->SetFocusId( xFocusId );
2937 pWin->SetOutputSizePixel( aSz );
2938 return true;
2941 bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExecute, const bool bPreSelectFirst,
2942 const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect)
2944 SalMenu* pMenu = ImplGetSalMenu();
2945 if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags))
2946 return true;
2948 pWin->StartPopupMode(rRect, nPopupModeFlags);
2949 if (pSFrom)
2951 sal_uInt16 aPos;
2952 if (pSFrom->IsMenuBar())
2953 aPos = static_cast<MenuBarWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
2954 else
2955 aPos = static_cast<MenuFloatingWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
2957 pWin->SetPosInParent(aPos); // store position to be sent in SUBMENUDEACTIVATE
2958 pSFrom->ImplCallEventListeners(VclEventId::MenuSubmenuActivate, aPos);
2961 if ( bPreSelectFirst )
2963 for (size_t n = 0; n < static_cast<size_t>(GetItemCount()); n++)
2965 MenuItemData* pData = pItemList->GetDataFromPos( n );
2966 if ( ( pData->bEnabled
2967 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
2969 && ( pData->eType != MenuItemType::SEPARATOR )
2970 && ImplIsVisible( n )
2971 && ImplIsSelectable( n )
2974 pWin->ChangeHighlightItem(n, false);
2975 break;
2980 if (bRealExecute)
2981 pWin->Execute();
2983 return false;
2986 void PopupMenu::FinishRun(const VclPtr<MenuFloatingWindow>& pWin, const VclPtr<vcl::Window>& pParentWin, const bool bRealExecute, const bool bIsNativeMenu)
2988 if (!bRealExecute || pWin->isDisposed())
2989 return;
2991 if (!bIsNativeMenu)
2993 VclPtr<vcl::Window> xFocusId = pWin->GetFocusId();
2994 assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
2995 pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
2997 if (nSelectedId) // then clean up .. ( otherwise done by TH )
2999 PopupMenu* pSub = pWin->GetActivePopup();
3000 while ( pSub )
3002 pSub->ImplGetFloatingWindow()->EndPopupMode();
3003 pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
3007 else
3008 pWin->StopExecute();
3010 pWin->doShutdown();
3011 pWindow.disposeAndClear();
3012 ImplClosePopupToolBox(pParentWin);
3013 ImplFlushPendingSelect();
3016 sal_uInt16 PopupMenu::ImplExecute(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect,
3017 FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst)
3019 // tdf#126054 hold this until after function completes
3020 VclPtr<PopupMenu> xThis(this);
3021 bool bRealExecute = false;
3022 tools::Rectangle aRect(rRect);
3023 VclPtr<MenuFloatingWindow> pWin;
3024 if (!PrepareRun(pParentWin, aRect, nPopupModeFlags, pSFrom, bRealExecute, pWin))
3025 return 0;
3026 const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect);
3027 FinishRun(pWin, pParentWin, bRealExecute, bNative);
3028 return nSelectedId;
3031 sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
3033 nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3035 tools::Long nHeight = 0;
3036 size_t nEntries = pItemList->size();
3037 sal_uInt16 nVisEntries = 0;
3039 if ( pLastVisible )
3040 *pLastVisible = 0;
3042 for ( size_t n = nStartEntry; n < nEntries; n++ )
3044 if ( ImplIsVisible( n ) )
3046 MenuItemData* pData = pItemList->GetDataFromPos( n );
3047 nHeight += pData->aSz.Height();
3048 if ( nHeight > nMaxHeight )
3049 break;
3051 if ( pLastVisible )
3052 *pLastVisible = n;
3053 nVisEntries++;
3056 return nVisEntries;
3059 tools::Long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
3061 tools::Long nHeight = 0;
3063 sal_uInt16 nFound = 0;
3064 for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ )
3066 if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) )
3068 MenuItemData* pData = pItemList->GetDataFromPos( n );
3069 nHeight += pData->aSz.Height();
3070 nFound++;
3074 nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
3076 return nHeight;
3079 css::uno::Reference<css::awt::XPopupMenu> PopupMenu::CreateMenuInterface()
3081 UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
3082 if ( pWrapper )
3083 return pWrapper->CreateMenuInterface(this);
3084 return nullptr;
3087 ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
3088 : mpNext( nullptr )
3089 , mpMenu( nullptr )
3091 if( pMenu )
3092 const_cast< Menu* >( pMenu )->ImplAddDel( *this );
3095 ImplMenuDelData::~ImplMenuDelData()
3097 if( mpMenu )
3098 const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
3101 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */