nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / window / menu.cxx
blob792f3aed5ff54e893537b6f823d204a2174b8175
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 <tools/diagnose_ex.h>
21 #include <sal/log.hxx>
23 #include <comphelper/lok.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/mnemonic.hxx>
26 #include <vcl/image.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/help.hxx>
29 #include <vcl/floatwin.hxx>
30 #include <vcl/decoview.hxx>
31 #include <vcl/menu.hxx>
32 #include <vcl/taskpanelist.hxx>
33 #include <vcl/toolkit/controllayout.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 ImplDockingWindowWrapper* pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( pWin );
109 if ( pWrapper && pWrapper->GetFloatingWindow() )
110 pWrapper->GetFloatingWindow()->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.getWidth() - width)/2 + 1;
140 tools::Long y = rRect.Top() + (rRect.getHeight() - 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 OString &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 OString &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 OString &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 OString &rIdent, sal_uInt16 nPos)
468 InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
469 SetItemImage( nItemId, rImage );
472 void Menu::InsertItem(const OUString& rCommand, const css::uno::Reference<css::frame::XFrame>& rFrame)
474 sal_uInt16 nItemId = GetItemCount() + 1;
476 if (rFrame.is())
478 OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
479 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName);
480 OUString aLabel(CommandInfoProvider::GetPopupLabelForCommand(aProperties));
481 OUString aTooltip(CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame));
482 Image aImage(CommandInfoProvider::GetImageForCommand(rCommand, rFrame));
484 InsertItem(nItemId, aLabel, aImage);
485 SetHelpText(nItemId, aTooltip);
487 else
488 InsertItem(nItemId, OUString());
490 SetItemCommand(nItemId, rCommand);
494 void Menu::InsertSeparator(const OString &rIdent, sal_uInt16 nPos)
496 // do nothing if it's a menu bar
497 if (IsMenuBar())
498 return;
500 // if position > ItemCount, append
501 if ( nPos >= pItemList->size() )
502 nPos = MENU_APPEND;
504 // put separator in item list
505 pItemList->InsertSeparator(rIdent, nPos);
507 // update native menu
508 size_t itemPos = ( nPos != MENU_APPEND ) ? nPos : pItemList->size() - 1;
509 MenuItemData *pData = pItemList->GetDataFromPos( itemPos );
510 if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
511 ImplGetSalMenu()->InsertItem( pData->pSalMenuItem.get(), nPos );
513 mpLayoutData.reset();
515 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
518 void Menu::RemoveItem( sal_uInt16 nPos )
520 bool bRemove = false;
522 if ( nPos < GetItemCount() )
524 // update native menu
525 if( ImplGetSalMenu() )
526 ImplGetSalMenu()->RemoveItem( nPos );
528 pItemList->Remove( nPos );
529 bRemove = true;
532 vcl::Window* pWin = ImplGetWindow();
533 if ( pWin )
535 ImplCalcSize( pWin );
536 if ( pWin->IsVisible() )
537 pWin->Invalidate();
539 mpLayoutData.reset();
541 if ( bRemove )
542 ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos );
545 static void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
547 MenuItemType eType = rMenu.GetItemType( nPos );
549 if ( eType == MenuItemType::DONTKNOW )
550 return;
552 if ( eType == MenuItemType::SEPARATOR )
553 pThis->InsertSeparator( OString(), nNewPos );
554 else
556 sal_uInt16 nId = rMenu.GetItemId( nPos );
558 SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
559 "Menu::CopyItem(): ItemId already exists" );
561 MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
563 if (!pData)
564 return;
566 if ( eType == MenuItemType::STRINGIMAGE )
567 pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
568 else if ( eType == MenuItemType::STRING )
569 pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos );
570 else
571 pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
573 if ( rMenu.IsItemChecked( nId ) )
574 pThis->CheckItem( nId );
575 if ( !rMenu.IsItemEnabled( nId ) )
576 pThis->EnableItem( nId, false );
577 pThis->SetHelpId( nId, pData->aHelpId );
578 pThis->SetHelpText( nId, pData->aHelpText );
579 pThis->SetAccelKey( nId, pData->aAccelKey );
580 pThis->SetItemCommand( nId, pData->aCommandStr );
581 pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
583 PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
584 if ( pSubMenu )
586 // create auto-copy
587 VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
588 pThis->SetPopupMenu( nId, pNewMenu );
593 void Menu::Clear()
595 for ( sal_uInt16 i = GetItemCount(); i; i-- )
596 RemoveItem( 0 );
599 sal_uInt16 Menu::GetItemCount() const
601 return static_cast<sal_uInt16>(pItemList->size());
604 sal_uInt16 Menu::ImplGetVisibleItemCount() const
606 sal_uInt16 nItems = 0;
607 for ( size_t n = pItemList->size(); n; )
609 if ( ImplIsVisible( --n ) )
610 nItems++;
612 return nItems;
615 sal_uInt16 Menu::ImplGetFirstVisible() const
617 for ( size_t n = 0; n < pItemList->size(); n++ )
619 if ( ImplIsVisible( n ) )
620 return n;
622 return ITEMPOS_INVALID;
625 sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
627 for ( size_t n = nPos; n; )
629 if (ImplIsVisible(--n))
630 return n;
632 return ITEMPOS_INVALID;
635 sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
637 for ( size_t n = nPos+1; n < pItemList->size(); n++ )
639 if ( ImplIsVisible( n ) )
640 return n;
642 return ITEMPOS_INVALID;
645 sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
647 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
649 if ( pData )
650 return pData->nId;
651 else
652 return 0;
655 sal_uInt16 Menu::GetItemId(const OString &rIdent) const
657 for (size_t n = 0; n < pItemList->size(); ++n)
659 MenuItemData* pData = pItemList->GetDataFromPos(n);
660 if (pData && pData->sIdent == rIdent)
661 return pData->nId;
663 return MENU_ITEM_NOTFOUND;
666 sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
668 size_t nPos;
669 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
671 if ( pData )
672 return static_cast<sal_uInt16>(nPos);
673 else
674 return MENU_ITEM_NOTFOUND;
677 MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
679 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
681 if ( pData )
682 return pData->eType;
683 else
684 return MenuItemType::DONTKNOW;
687 OString Menu::GetItemIdent(sal_uInt16 nId) const
689 const MenuItemData* pData = pItemList->GetData(nId);
690 return pData ? pData->sIdent : OString();
693 void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
695 size_t nPos;
696 MenuItemData* pData = pItemList->GetData(nItemId, nPos);
698 if (pData && (pData->nBits != nBits))
700 pData->nBits = nBits;
702 // update native menu
703 if (ImplGetSalMenu())
704 ImplGetSalMenu()->SetItemBits(nPos, nBits);
708 MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
710 MenuItemBits nBits = MenuItemBits::NONE;
711 MenuItemData* pData = pItemList->GetData( nItemId );
712 if ( pData )
713 nBits = pData->nBits;
714 return nBits;
717 void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc)
719 MenuItemData* pData = pItemList->GetData(nItemId);
720 if (pData)
722 if (pData->aUserValueReleaseFunc)
723 pData->aUserValueReleaseFunc(pData->nUserValue);
724 pData->aUserValueReleaseFunc = aFunc;
725 pData->nUserValue = nUserValue;
729 void* Menu::GetUserValue( sal_uInt16 nItemId ) const
731 MenuItemData* pData = pItemList->GetData( nItemId );
732 return pData ? pData->nUserValue : nullptr;
735 void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
737 size_t nPos;
738 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
740 // Item does not exist -> return NULL
741 if ( !pData )
742 return;
744 // same menu, nothing to do
745 if ( static_cast<PopupMenu*>(pData->pSubMenu.get()) == pMenu )
746 return;
748 // remove old menu
749 auto oldSubMenu = pData->pSubMenu;
751 // data exchange
752 pData->pSubMenu = pMenu;
754 // #112023# Make sure pStartedFrom does not point to invalid (old) data
755 if ( pData->pSubMenu )
756 pData->pSubMenu->pStartedFrom = nullptr;
758 // set native submenu
759 if( ImplGetSalMenu() && pData->pSalMenuItem )
761 if( pMenu )
762 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), pMenu->ImplGetSalMenu(), nPos );
763 else
764 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), nullptr, nPos );
767 oldSubMenu.disposeAndClear();
769 ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos );
772 PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
774 MenuItemData* pData = pItemList->GetData( nItemId );
776 if ( pData )
777 return static_cast<PopupMenu*>(pData->pSubMenu.get());
778 else
779 return nullptr;
782 void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
784 size_t nPos;
785 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
787 if ( !pData )
788 return;
790 if ( pData->aAccelKey == rKeyCode )
791 return;
793 pData->aAccelKey = rKeyCode;
795 // update native menu
796 if( ImplGetSalMenu() && pData->pSalMenuItem )
797 ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem.get(), rKeyCode, rKeyCode.GetName() );
800 KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
802 MenuItemData* pData = pItemList->GetData( nItemId );
804 if ( pData )
805 return pData->aAccelKey;
806 else
807 return KeyCode();
810 KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
812 KeyEvent aRet;
813 MenuItemData* pData = pItemList->GetData( nItemId );
814 if( pData )
816 sal_Int32 nPos = pData->aText.indexOf( '~' );
817 if( nPos != -1 && nPos < pData->aText.getLength()-1 )
819 sal_uInt16 nCode = 0;
820 sal_Unicode cAccel = pData->aText[nPos+1];
821 if( cAccel >= 'a' && cAccel <= 'z' )
822 nCode = KEY_A + (cAccel-'a');
823 else if( cAccel >= 'A' && cAccel <= 'Z' )
824 nCode = KEY_A + (cAccel-'A');
825 else if( cAccel >= '0' && cAccel <= '9' )
826 nCode = KEY_0 + (cAccel-'0');
828 aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
832 return aRet;
835 void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
837 size_t nPos;
838 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
840 if ( !pData || pData->bChecked == bCheck )
841 return;
843 // if radio-check, then uncheck previous
844 if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
845 (pData->nBits & MenuItemBits::RADIOCHECK) )
847 MenuItemData* pGroupData;
848 sal_uInt16 nGroupPos;
849 sal_uInt16 nItemCount = GetItemCount();
850 bool bFound = false;
852 nGroupPos = nPos;
853 while ( nGroupPos )
855 pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
856 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
858 if ( IsItemChecked( pGroupData->nId ) )
860 CheckItem( pGroupData->nId, false );
861 bFound = true;
862 break;
865 else
866 break;
867 nGroupPos--;
870 if ( !bFound )
872 nGroupPos = nPos+1;
873 while ( nGroupPos < nItemCount )
875 pGroupData = pItemList->GetDataFromPos( nGroupPos );
876 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
878 if ( IsItemChecked( pGroupData->nId ) )
880 CheckItem( pGroupData->nId, false );
881 break;
884 else
885 break;
886 nGroupPos++;
891 pData->bChecked = bCheck;
893 // update native menu
894 if( ImplGetSalMenu() )
895 ImplGetSalMenu()->CheckItem( nPos, bCheck );
897 ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos );
900 void Menu::CheckItem( const OString &rIdent , bool bCheck )
902 CheckItem( GetItemId( rIdent ), bCheck );
905 bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
907 size_t nPos;
908 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
910 if ( !pData )
911 return false;
913 return pData->bChecked;
916 void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
918 size_t nPos;
919 MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
921 if ( !(pItemData && ( pItemData->bEnabled != bEnable )) )
922 return;
924 pItemData->bEnabled = bEnable;
926 vcl::Window* pWin = ImplGetWindow();
927 if ( pWin && pWin->IsVisible() )
929 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
930 tools::Long nX = 0;
931 size_t nCount = pItemList->size();
932 for ( size_t n = 0; n < nCount; n++ )
934 MenuItemData* pData = pItemList->GetDataFromPos( n );
935 if ( n == nPos )
937 pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
938 break;
940 nX += pData->aSz.Width();
943 // update native menu
944 if( ImplGetSalMenu() )
945 ImplGetSalMenu()->EnableItem( nPos, bEnable );
947 ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos );
950 bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
952 size_t nPos;
953 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
955 if ( !pData )
956 return false;
958 return pData->bEnabled;
961 void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
963 size_t nPos;
964 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
966 SAL_WARN_IF(IsMenuBar() && !bVisible , "vcl", "Menu::ShowItem - ignored for menu bar entries!");
967 if (IsMenuBar() || !pData || (pData->bVisible == bVisible))
968 return;
970 vcl::Window* pWin = ImplGetWindow();
971 if ( pWin && pWin->IsVisible() )
973 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
974 return;
976 pData->bVisible = bVisible;
978 // update native menu
979 if( ImplGetSalMenu() )
980 ImplGetSalMenu()->ShowItem( nPos, bVisible );
983 void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
985 size_t nPos;
986 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
988 if ( !pData )
989 return;
991 if ( rStr == pData->aText )
992 return;
994 pData->aText = rStr;
995 // Clear layout for aText.
996 pData->aTextGlyphs.Invalidate();
997 ImplSetMenuItemData( pData );
998 // update native menu
999 if( ImplGetSalMenu() && pData->pSalMenuItem )
1000 ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem.get(), rStr );
1002 vcl::Window* pWin = ImplGetWindow();
1003 mpLayoutData.reset();
1004 if (pWin && IsMenuBar())
1006 ImplCalcSize( pWin );
1007 if ( pWin->IsVisible() )
1008 pWin->Invalidate();
1011 ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos );
1014 OUString Menu::GetItemText( sal_uInt16 nItemId ) const
1016 size_t nPos;
1017 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1019 if ( pData )
1020 return pData->aText;
1022 return OUString();
1025 void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1027 size_t nPos;
1028 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1030 if ( !pData )
1031 return;
1033 pData->aImage = rImage;
1034 ImplSetMenuItemData( pData );
1036 // update native menu
1037 if( ImplGetSalMenu() && pData->pSalMenuItem )
1038 ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem.get(), rImage );
1041 Image Menu::GetItemImage( sal_uInt16 nItemId ) const
1043 MenuItemData* pData = pItemList->GetData( nItemId );
1045 if ( pData )
1046 return pData->aImage;
1047 else
1048 return Image();
1051 void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1053 size_t nPos;
1054 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1056 if ( pData )
1057 pData->aCommandStr = rCommand;
1060 OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
1062 MenuItemData* pData = pItemList->GetData( nItemId );
1064 if (pData)
1065 return pData->aCommandStr;
1067 return OUString();
1070 void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
1072 MenuItemData* pData = pItemList->GetData( nItemId );
1074 if ( pData )
1075 pData->aHelpCommandStr = rStr;
1078 OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
1080 MenuItemData* pData = pItemList->GetData( nItemId );
1082 if ( pData )
1083 return pData->aHelpCommandStr;
1085 return OUString();
1088 void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
1090 MenuItemData* pData = pItemList->GetData( nItemId );
1092 if ( pData )
1093 pData->aHelpText = rStr;
1096 OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
1098 MenuItemData* pData = pItemList->GetData( nItemId );
1100 if (!pData)
1101 return OUString();
1103 if ( pData->aHelpText.isEmpty() &&
1104 (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() )))
1106 Help* pHelp = Application::GetHelp();
1107 if ( pHelp )
1109 if (!pData->aCommandStr.isEmpty())
1110 pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr, static_cast<weld::Widget*>(nullptr) );
1111 if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty())
1112 pData->aHelpText = pHelp->GetHelpText( OStringToOUString( pData->aHelpId, RTL_TEXTENCODING_UTF8 ), static_cast<weld::Widget*>(nullptr) );
1116 return pData->aHelpText;
1119 OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
1121 return ImplGetHelpText( nItemId );
1124 void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
1126 MenuItemData* pData = pItemList->GetData( nItemId );
1128 if ( pData )
1129 pData->aTipHelpText = rStr;
1132 OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
1134 MenuItemData* pData = pItemList->GetData( nItemId );
1136 if ( pData )
1137 return pData->aTipHelpText;
1139 return OUString();
1142 void Menu::SetHelpId( sal_uInt16 nItemId, const OString& rHelpId )
1144 MenuItemData* pData = pItemList->GetData( nItemId );
1146 if ( pData )
1147 pData->aHelpId = rHelpId;
1150 OString Menu::GetHelpId( sal_uInt16 nItemId ) const
1152 OString aRet;
1154 MenuItemData* pData = pItemList->GetData( nItemId );
1156 if ( pData )
1158 if ( !pData->aHelpId.isEmpty() )
1159 aRet = pData->aHelpId;
1160 else
1161 aRet = OUStringToOString( pData->aCommandStr, RTL_TEXTENCODING_UTF8 );
1164 return aRet;
1167 Menu& Menu::operator=( const Menu& rMenu )
1169 if(this == &rMenu)
1170 return *this;
1172 // clean up
1173 Clear();
1175 // copy items
1176 sal_uInt16 nCount = rMenu.GetItemCount();
1177 for ( sal_uInt16 i = 0; i < nCount; i++ )
1178 ImplCopyItem( this, rMenu, i, MENU_APPEND );
1180 aActivateHdl = rMenu.aActivateHdl;
1181 aDeactivateHdl = rMenu.aDeactivateHdl;
1182 aSelectHdl = rMenu.aSelectHdl;
1183 aTitleText = rMenu.aTitleText;
1184 nTitleHeight = rMenu.nTitleHeight;
1186 return *this;
1189 // Returns true if the item is completely hidden on the GUI and shouldn't
1190 // be possible to interact with
1191 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const
1193 MenuItemData* pData = pItemList->GetDataFromPos(nPos);
1194 if (pData)
1196 MenuItemData* pPreviousData = pItemList->GetDataFromPos( nPos - 1 );
1197 if (pPreviousData && pPreviousData->bHiddenOnGUI)
1199 return true;
1202 return false;
1205 bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
1207 bool bVisible = true;
1209 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1210 // check general visibility first
1211 if( pData && !pData->bVisible )
1212 bVisible = false;
1214 if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
1216 if( nPos == 0 ) // no separator should be shown at the very beginning
1217 bVisible = false;
1218 else
1220 // always avoid adjacent separators
1221 size_t nCount = pItemList->size();
1222 size_t n;
1223 MenuItemData* pNextData = nullptr;
1224 // search next visible item
1225 for( n = nPos + 1; n < nCount; n++ )
1227 pNextData = pItemList->GetDataFromPos( n );
1228 if( pNextData && pNextData->bVisible )
1230 if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
1231 break;
1234 if( n == nCount ) // no next visible item
1235 bVisible = false;
1236 // check for separator
1237 if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
1238 bVisible = false;
1240 if( bVisible )
1242 for( n = nPos; n > 0; n-- )
1244 pNextData = pItemList->GetDataFromPos( n-1 );
1245 if( pNextData && pNextData->bVisible )
1247 if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
1248 break;
1251 if( n == 0 ) // no previous visible item
1252 bVisible = false;
1257 // not allowed for menubar, as I do not know
1258 // whether a menu-entry will disappear or will appear
1259 if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) &&
1260 !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
1262 if( !pData ) // e.g. nPos == ITEMPOS_INVALID
1263 bVisible = false;
1264 else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
1266 // tdf#86850 Always display clipboard functions
1267 if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" )
1268 bVisible = true;
1269 else
1270 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1271 bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
1275 return bVisible;
1278 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
1280 return IsMenuVisible() && ImplIsVisible( nItemPos );
1283 bool Menu::IsMenuVisible() const
1285 return pWindow && pWindow->IsReallyVisible();
1288 bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
1290 bool bSelectable = true;
1292 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1293 // check general visibility first
1294 if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
1295 bSelectable = false;
1297 return bSelectable;
1300 css::uno::Reference<css::accessibility::XAccessible> Menu::GetAccessible()
1302 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1303 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1304 // mxAccessible member only for sub menus.
1305 if (pStartedFrom && pStartedFrom != this)
1307 for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
1309 sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
1310 if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
1312 css::uno::Reference<css::accessibility::XAccessible> xParent = pStartedFrom->GetAccessible();
1313 if ( xParent.is() )
1315 css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( xParent->getAccessibleContext() );
1316 if (xParentContext.is())
1317 return xParentContext->getAccessibleChild( i );
1322 else if ( !mxAccessible.is() )
1324 UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
1325 if ( pWrapper )
1326 mxAccessible = pWrapper->CreateAccessible(this, IsMenuBar());
1329 return mxAccessible;
1332 void Menu::SetAccessible(const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible )
1334 mxAccessible = rxAccessible;
1337 Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, tools::Long& rCheckHeight, tools::Long& rRadioHeight ) const
1339 tools::Long nCheckWidth = 0, nRadioWidth = 0;
1340 rCheckHeight = rRadioHeight = 0;
1342 if (!IsMenuBar())
1344 ImplControlValue aVal;
1345 tools::Rectangle aNativeBounds;
1346 tools::Rectangle aNativeContent;
1348 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1349 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
1351 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
1352 aCtrlRegion, ControlState::ENABLED, aVal,
1353 aNativeBounds, aNativeContent))
1355 rCheckHeight = aNativeBounds.GetHeight();
1356 nCheckWidth = aNativeContent.GetWidth();
1359 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
1361 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
1362 aCtrlRegion, ControlState::ENABLED, aVal,
1363 aNativeBounds, aNativeContent))
1365 rRadioHeight = aNativeBounds.GetHeight();
1366 nRadioWidth = aNativeContent.GetWidth();
1370 return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
1373 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, tools::Long& rArrowSpacing)
1375 ImplControlValue aVal;
1376 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1377 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
1379 tools::Rectangle aNativeContent;
1380 tools::Rectangle aNativeBounds;
1381 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
1382 aCtrlRegion, ControlState::ENABLED,
1383 aVal, aNativeBounds, aNativeContent))
1385 Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
1386 rArrowSize = aSize;
1387 rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
1388 return true;
1391 return false;
1394 void Menu::ImplAddDel( ImplMenuDelData& rDel )
1396 SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1397 if( !rDel.mpMenu )
1399 rDel.mpMenu = this;
1400 rDel.mpNext = mpFirstDel;
1401 mpFirstDel = &rDel;
1405 void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
1407 rDel.mpMenu = nullptr;
1408 if ( mpFirstDel == &rDel )
1410 mpFirstDel = rDel.mpNext;
1412 else
1414 ImplMenuDelData* pData = mpFirstDel;
1415 while ( pData && (pData->mpNext != &rDel) )
1416 pData = pData->mpNext;
1418 SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1419 if( pData )
1420 pData->mpNext = rDel.mpNext;
1424 Size Menu::ImplCalcSize( vcl::Window* pWin )
1426 // | Check/Radio/Image| Text| Accel/Popup|
1428 // for symbols: nFontHeight x nFontHeight
1429 tools::Long nFontHeight = pWin->GetTextHeight();
1430 tools::Long nExtra = nFontHeight/4;
1432 tools::Long nMinMenuItemHeight = nFontHeight;
1433 tools::Long nCheckHeight = 0, nRadioHeight = 0;
1434 Size aMaxSize = ImplGetNativeCheckAndRadioSize(*pWin, nCheckHeight, nRadioHeight); // FIXME
1435 if( aMaxSize.Height() > nMinMenuItemHeight )
1436 nMinMenuItemHeight = aMaxSize.Height();
1438 Size aMaxImgSz;
1440 const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
1441 if ( rSettings.GetUseImagesInMenus() )
1443 if ( 16 > nMinMenuItemHeight )
1444 nMinMenuItemHeight = 16;
1445 for ( size_t i = pItemList->size(); i; )
1447 MenuItemData* pData = pItemList->GetDataFromPos( --i );
1448 if ( ImplIsVisible( i )
1449 && ( ( pData->eType == MenuItemType::IMAGE )
1450 || ( pData->eType == MenuItemType::STRINGIMAGE )
1454 Size aImgSz = pData->aImage.GetSizePixel();
1455 if ( aImgSz.Height() > aMaxImgSz.Height() )
1456 aMaxImgSz.setHeight( aImgSz.Height() );
1457 if ( aImgSz.Height() > nMinMenuItemHeight )
1458 nMinMenuItemHeight = aImgSz.Height();
1459 break;
1464 Size aSz;
1465 tools::Long nCheckWidth = 0;
1466 tools::Long nMaxWidth = 0;
1468 for ( size_t n = pItemList->size(); n; )
1470 MenuItemData* pData = pItemList->GetDataFromPos( --n );
1472 pData->aSz.setHeight( 0 );
1473 pData->aSz.setWidth( 0 );
1475 if ( ImplIsVisible( n ) )
1477 tools::Long nWidth = 0;
1479 // Separator
1480 if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
1482 pData->aSz.setHeight( 4 );
1485 // Image:
1486 if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1488 Size aImgSz = pData->aImage.GetSizePixel();
1490 aImgSz.AdjustHeight(4 ); // add a border for native marks
1491 aImgSz.AdjustWidth(4 ); // add a border for native marks
1492 if ( aImgSz.Width() > aMaxImgSz.Width() )
1493 aMaxImgSz.setWidth( aImgSz.Width() );
1494 if ( aImgSz.Height() > aMaxImgSz.Height() )
1495 aMaxImgSz.setHeight( aImgSz.Height() );
1496 if ( aImgSz.Height() > pData->aSz.Height() )
1497 pData->aSz.setHeight( aImgSz.Height() );
1500 // Check Buttons:
1501 if (!IsMenuBar() && pData->HasCheck())
1503 nCheckWidth = aMaxSize.Width();
1504 // checks / images take the same place
1505 if( ( pData->eType != MenuItemType::IMAGE ) && ( pData->eType != MenuItemType::STRINGIMAGE ) )
1506 nWidth += nCheckWidth + nExtra * 2;
1509 // Text:
1510 if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
1512 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(pWin);
1513 tools::Long nTextWidth = pWin->GetCtrlTextWidth(pData->aText, pGlyphs);
1514 tools::Long nTextHeight = pWin->GetTextHeight();
1516 if (IsMenuBar())
1518 if ( nTextHeight > pData->aSz.Height() )
1519 pData->aSz.setHeight( nTextHeight );
1521 pData->aSz.setWidth( nTextWidth + 4*nExtra );
1522 aSz.AdjustWidth(pData->aSz.Width() );
1524 else
1525 pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1527 nWidth += nTextWidth;
1530 // Accel
1531 if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1533 OUString aName = pData->aAccelKey.GetName();
1534 tools::Long nAccWidth = pWin->GetTextWidth( aName );
1535 nAccWidth += nExtra;
1536 nWidth += nAccWidth;
1539 // SubMenu?
1540 if (!IsMenuBar() && pData->pSubMenu)
1542 if ( nFontHeight > nWidth )
1543 nWidth += nFontHeight;
1545 pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1548 pData->aSz.AdjustHeight(EXTRAITEMHEIGHT ); // little bit more distance
1550 if (!IsMenuBar())
1551 aSz.AdjustHeight(pData->aSz.Height() );
1553 if ( nWidth > nMaxWidth )
1554 nMaxWidth = nWidth;
1559 // Additional space for title
1560 nTitleHeight = 0;
1561 if (!IsMenuBar() && aTitleText.getLength() > 0) {
1562 // Set expected font
1563 pWin->Push(PushFlags::FONT);
1564 vcl::Font aFont = pWin->GetFont();
1565 aFont.SetWeight(WEIGHT_BOLD);
1566 pWin->SetFont(aFont);
1568 // Compute text bounding box
1569 tools::Rectangle aTextBoundRect;
1570 pWin->GetTextBoundRect(aTextBoundRect, aTitleText);
1572 // Vertically, one height of char + extra space for decoration
1573 nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ;
1574 aSz.AdjustHeight(nTitleHeight );
1576 tools::Long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
1577 pWin->Pop();
1578 if ( nWidth > nMaxWidth )
1579 nMaxWidth = nWidth;
1582 if (!IsMenuBar())
1584 // popup menus should not be wider than half the screen
1585 // except on rather small screens
1586 // TODO: move GetScreenNumber from SystemWindow to Window ?
1587 // currently we rely on internal privileges
1588 unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.nDisplayScreenNumber;
1589 tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
1590 tools::Long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
1591 if( nMaxWidth > nScreenWidth/2 )
1592 nMaxWidth = nScreenWidth/2;
1594 sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
1595 nImgOrChkPos = static_cast<sal_uInt16>(nExtra);
1596 tools::Long nImgOrChkWidth = 0;
1597 if( aMaxSize.Height() > 0 ) // NWF case
1598 nImgOrChkWidth = aMaxSize.Height() + nExtra;
1599 else // non NWF case
1600 nImgOrChkWidth = nFontHeight/2 + gfxExtra;
1601 nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgSz.Width() + gfxExtra );
1602 nTextPos = static_cast<sal_uInt16>(nImgOrChkPos + nImgOrChkWidth);
1603 nTextPos = nTextPos + gfxExtra;
1605 aSz.setWidth( nTextPos + nMaxWidth + nExtra );
1606 aSz.AdjustWidth(4*nExtra ); // a _little_ more ...
1608 aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX );
1609 aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1611 else
1613 nTextPos = static_cast<sal_uInt16>(2*nExtra);
1614 aSz.setHeight( nFontHeight+6 );
1616 // get menubar height from native methods if supported
1617 if( pWindow->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) )
1619 ImplControlValue aVal;
1620 tools::Rectangle aNativeBounds;
1621 tools::Rectangle aNativeContent;
1622 Point tmp( 0, 0 );
1623 tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
1624 if( pWindow->GetNativeControlRegion( ControlType::Menubar,
1625 ControlPart::Entire,
1626 aCtrlRegion,
1627 ControlState::ENABLED,
1628 aVal,
1629 aNativeBounds,
1630 aNativeContent )
1633 int nNativeHeight = aNativeBounds.GetHeight();
1634 if( nNativeHeight > aSz.Height() )
1635 aSz.setHeight( nNativeHeight );
1639 // account for the size of the close button, which actually is a toolbox
1640 // due to NWF this is variable
1641 tools::Long nCloseButtonHeight = static_cast<MenuBarWindow*>(pWindow.get())->MinCloseButtonSize().Height();
1642 if (aSz.Height() < nCloseButtonHeight)
1643 aSz.setHeight( nCloseButtonHeight );
1646 return aSz;
1649 static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight)
1651 bool bNativeOk = false;
1652 if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
1654 ImplControlValue aControlValue;
1655 aControlValue.setTristateVal(ButtonValue::On);
1657 bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
1658 i_rRect,
1659 ControlState::PRESSED | ControlState::ENABLED,
1660 aControlValue,
1661 OUString());
1664 if (!bNativeOk)
1666 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1667 Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
1668 RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor);
1672 static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, tools::Long i_nMaxWidth )
1674 sal_Int32 nPos = -1;
1675 OUString aNonMnem(OutputDevice::GetNonMnemonicString(i_rLong, nPos));
1676 aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
1677 // re-insert mnemonic
1678 if (nPos != -1)
1680 if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
1682 OUStringBuffer aBuf( i_rLong.getLength() );
1683 aBuf.append( std::u16string_view(aNonMnem).substr(0, nPos) );
1684 aBuf.append( '~' );
1685 aBuf.append( std::u16string_view(aNonMnem).substr(nPos) );
1686 aNonMnem = aBuf.makeStringAndClear();
1689 return aNonMnem;
1692 void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const
1694 // Save previous graphical settings, set new one
1695 rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
1696 Wallpaper aOldBackground = rRenderContext.GetBackground();
1698 Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
1699 rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
1700 rRenderContext.SetFillColor(aBackgroundColor);
1701 vcl::Font aFont = rRenderContext.GetFont();
1702 aFont.SetWeight(WEIGHT_BOLD);
1703 rRenderContext.SetFont(aFont);
1705 // Draw background rectangle
1706 tools::Rectangle aBgRect(rRect);
1707 int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1708 aBgRect.setX(aBgRect.getX() + SPACE_AROUND_TITLE);
1709 aBgRect.setWidth(aBgRect.getWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
1710 aBgRect.setY(aBgRect.getY() + SPACE_AROUND_TITLE);
1711 aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE);
1712 rRenderContext.DrawRect(aBgRect);
1714 // Draw the text centered
1715 Point aTextTopLeft(aBgRect.TopLeft());
1716 tools::Rectangle aTextBoundRect;
1717 rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText );
1718 aTextTopLeft.AdjustX((aBgRect.getWidth() - aTextBoundRect.GetSize().Width()) / 2 );
1719 aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
1720 - aTextBoundRect.TopLeft().Y() );
1721 rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength());
1723 // Restore
1724 rRenderContext.Pop();
1725 rRenderContext.SetBackground(aOldBackground);
1728 void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize,
1729 sal_uInt16 nBorder, tools::Long nStartY, MenuItemData const * pThisItemOnly,
1730 bool bHighlighted, bool bLayout, bool bRollover) const
1732 // for symbols: nFontHeight x nFontHeight
1733 tools::Long nFontHeight = rRenderContext.GetTextHeight();
1734 tools::Long nExtra = nFontHeight / 4;
1736 tools::Long nCheckHeight = 0, nRadioHeight = 0;
1737 ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
1739 DecorationView aDecoView(&rRenderContext);
1740 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1742 Point aTopLeft, aTmpPos;
1744 int nOuterSpaceX = 0;
1745 if (!IsMenuBar())
1747 nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1748 aTopLeft.AdjustX(nOuterSpaceX );
1749 aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1752 // for the computations, use size of the underlying window, not of RenderContext
1753 Size aOutSz(rSize);
1755 size_t nCount = pItemList->size();
1756 if (bLayout)
1757 mpLayoutData->m_aVisibleItemBoundRects.clear();
1759 // Paint title
1760 if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0)
1761 ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz));
1763 bool bHiddenItems = false; // are any items on the GUI hidden
1765 for (size_t n = 0; n < nCount; n++)
1767 MenuItemData* pData = pItemList->GetDataFromPos( n );
1768 if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
1770 if (pThisItemOnly)
1772 if (IsMenuBar())
1774 if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
1776 if (bRollover)
1777 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1778 else if (bHighlighted)
1779 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1781 else
1783 if (bHighlighted)
1784 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1785 else if (bRollover)
1786 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1788 if (!bRollover && !bHighlighted)
1789 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
1791 else if (bHighlighted)
1792 rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1795 Point aPos(aTopLeft);
1796 aPos.AdjustY(nBorder );
1797 aPos.AdjustY(nStartY );
1799 if (aPos.Y() >= 0)
1801 tools::Long nTextOffsetY = (pData->aSz.Height() - nFontHeight) / 2;
1802 if (IsMenuBar())
1803 nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
1804 DrawTextFlags nTextStyle = DrawTextFlags::NONE;
1805 DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
1806 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1808 // submenus without items are not disabled when no items are
1809 // contained. The application itself should check for this!
1810 // Otherwise it could happen entries are disabled due to
1811 // asynchronous loading
1812 if (!pData->bEnabled || !pWindow->IsEnabled())
1814 nTextStyle |= DrawTextFlags::Disable;
1815 nSymbolStyle |= DrawSymbolFlags::Disable;
1816 nImageStyle |= DrawImageFlags::Disable;
1819 // Separator
1820 if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
1822 bool bNativeOk = false;
1823 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
1825 ControlState nState = ControlState::NONE;
1826 if (pData->bEnabled && pWindow->IsEnabled())
1827 nState |= ControlState::ENABLED;
1828 if (bHighlighted)
1829 nState |= ControlState::SELECTED;
1830 Size aSz(pData->aSz);
1831 aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX );
1832 tools::Rectangle aItemRect(aPos, aSz);
1833 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1834 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
1835 aItemRect, nState, aVal, OUString());
1837 if (!bNativeOk)
1839 aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) );
1840 aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX );
1841 rRenderContext.SetLineColor(rSettings.GetShadowColor());
1842 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1843 aTmpPos.AdjustY( 1 );
1844 rRenderContext.SetLineColor(rSettings.GetLightColor());
1845 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1846 rRenderContext.SetLineColor();
1850 tools::Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()),
1851 Size(pData->aSz.Height(), pData->aSz.Height()));
1852 aOuterCheckRect.AdjustLeft(1 );
1853 aOuterCheckRect.AdjustRight( -1 );
1854 aOuterCheckRect.AdjustTop(1 );
1855 aOuterCheckRect.AdjustBottom( -1 );
1857 // CheckMark
1858 if (!bLayout && !IsMenuBar() && pData->HasCheck())
1860 // draw selection transparent marker if checked
1861 // onto that either a checkmark or the item image
1862 // will be painted
1863 // however do not do this if native checks will be painted since
1864 // the selection color too often does not fit the theme's check and/or radio
1866 if( (pData->eType != MenuItemType::IMAGE) && (pData->eType != MenuItemType::STRINGIMAGE))
1868 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
1869 (pData->nBits & MenuItemBits::RADIOCHECK)
1870 ? ControlPart::MenuItemCheckMark
1871 : ControlPart::MenuItemRadioMark))
1873 ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
1874 ? ControlPart::MenuItemRadioMark
1875 : ControlPart::MenuItemCheckMark);
1877 ControlState nState = ControlState::NONE;
1879 if (pData->bChecked)
1880 nState |= ControlState::PRESSED;
1882 if (pData->bEnabled && pWindow->IsEnabled())
1883 nState |= ControlState::ENABLED;
1885 if (bHighlighted)
1886 nState |= ControlState::SELECTED;
1888 tools::Long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
1889 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 );
1890 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 );
1892 tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
1893 Size aSz(pData->aSz);
1894 aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX );
1895 tools::Rectangle aItemRect(aPos, aSz);
1896 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1897 rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
1898 nState, aVal, OUString());
1900 else if (pData->bChecked) // by default do nothing for unchecked items
1902 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1904 SymbolType eSymbol;
1905 Size aSymbolSize;
1906 if (pData->nBits & MenuItemBits::RADIOCHECK)
1908 eSymbol = SymbolType::RADIOCHECKMARK;
1909 aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
1911 else
1913 eSymbol = SymbolType::CHECKMARK;
1914 aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
1916 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 );
1917 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 );
1918 tools::Rectangle aRect(aTmpPos, aSymbolSize);
1919 aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
1924 // Image:
1925 if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1927 // Don't render an image for a check thing
1928 if (pData->bChecked)
1929 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1931 Image aImage = pData->aImage;
1933 aTmpPos = aOuterCheckRect.TopLeft();
1934 aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 );
1935 aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 );
1936 rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
1939 // Text:
1940 if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
1942 aTmpPos.setX( aPos.X() + nTextPos );
1943 aTmpPos.setY( aPos.Y() );
1944 aTmpPos.AdjustY(nTextOffsetY );
1945 DrawTextFlags nStyle = nTextStyle | DrawTextFlags::Mnemonic;
1947 const Menu *pMenu = this;
1948 while (!pMenu->IsMenuBar() && pMenu->pStartedFrom)
1949 pMenu = pMenu->pStartedFrom;
1950 if (pMenu->IsMenuBar() && static_cast<MenuBarWindow*>(pMenu->pWindow.get())->GetMBWHideAccel())
1951 nStyle |= DrawTextFlags::HideMnemonic;
1953 if (pData->bIsTemporary)
1954 nStyle |= DrawTextFlags::Disable;
1955 MetricVector* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr;
1956 OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr;
1957 if (bLayout)
1959 mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength());
1960 mpLayoutData->m_aLineItemIds.push_back(pData->nId);
1962 // #i47946# with NWF painted menus the background is transparent
1963 // since DrawCtrlText can depend on the background (e.g. for
1964 // DrawTextFlags::Disable), temporarily set a background which
1965 // hopefully matches the NWF background since it is read
1966 // from the system style settings
1967 bool bSetTmpBackground = !rRenderContext.IsBackground()
1968 && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire);
1969 if (bSetTmpBackground)
1971 Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor()
1972 : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor();
1973 rRenderContext.SetBackground(Wallpaper(aBg));
1975 // how much space is there for the text?
1976 tools::Long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX;
1977 if (!IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1979 OUString aAccText = pData->aAccelKey.GetName();
1980 nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra;
1982 if (!IsMenuBar() && pData->pSubMenu)
1984 nMaxItemTextWidth -= nFontHeight - nExtra;
1987 OUString aItemText(pData->aText);
1988 pData->bHiddenOnGUI = false;
1990 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1992 if (nMaxItemTextWidth < rRenderContext.GetTextWidth(aItemText))
1994 aItemText = "";
1995 pData->bHiddenOnGUI = true;
1996 bHiddenItems = true;
1999 else
2001 aItemText = getShortenedString(aItemText, rRenderContext, nMaxItemTextWidth);
2002 pData->bHiddenOnGUI = false;
2005 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(&rRenderContext);
2006 if (aItemText != pData->aText)
2007 // Can't use pre-computed glyphs, item text was
2008 // changed.
2009 pGlyphs = nullptr;
2010 rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(),
2011 nStyle, pVector, pDisplayText, pGlyphs);
2012 if (bSetTmpBackground)
2013 rRenderContext.SetBackground();
2016 // Accel
2017 if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
2019 OUString aAccText = pData->aAccelKey.GetName();
2020 aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) );
2021 aTmpPos.AdjustX( -(4 * nExtra) );
2023 aTmpPos.AdjustX( -nOuterSpaceX );
2024 aTmpPos.setY( aPos.Y() );
2025 aTmpPos.AdjustY(nTextOffsetY );
2026 rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
2029 // SubMenu?
2030 if (!bLayout && !IsMenuBar() && pData->pSubMenu)
2032 bool bNativeOk = false;
2033 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
2035 ControlState nState = ControlState::NONE;
2036 Size aTmpSz(0, 0);
2037 tools::Long aSpacing = 0;
2039 if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
2041 aTmpSz = Size(nFontHeight, nFontHeight);
2042 aSpacing = nOuterSpaceX;
2045 if (pData->bEnabled && pWindow->IsEnabled())
2046 nState |= ControlState::ENABLED;
2047 if (bHighlighted)
2048 nState |= ControlState::SELECTED;
2050 aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX );
2051 aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 );
2052 aTmpPos.AdjustY(nExtra / 2 );
2054 tools::Rectangle aItemRect(aTmpPos, aTmpSz);
2055 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
2056 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
2057 aItemRect, nState, aVal, OUString());
2059 if (!bNativeOk)
2061 aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX );
2062 aTmpPos.setY( aPos.Y() );
2063 aTmpPos.AdjustY(nExtra/2 );
2064 aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) );
2065 if (pData->nBits & MenuItemBits::POPUPSELECT)
2067 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2068 Point aTmpPos2(aPos);
2069 aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 );
2070 aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
2071 pData->aSz.Height())),
2072 DrawFrameStyle::Group);
2074 aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
2075 SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
2079 if (pThisItemOnly && bHighlighted)
2081 // This restores the normal menu or menu bar text
2082 // color for when it is no longer highlighted.
2083 if (IsMenuBar())
2084 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
2085 else
2086 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2089 if( bLayout )
2091 if (!IsMenuBar())
2092 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
2093 else
2094 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, pData->aSz);
2098 if (!IsMenuBar())
2099 aTopLeft.AdjustY(pData->aSz.Height() );
2100 else
2101 aTopLeft.AdjustX(pData->aSz.Width() );
2104 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2105 if (bHiddenItems)
2107 sal_Int32 nSize = nFontHeight;
2108 tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize));
2109 lclDrawMoreIndicator(rRenderContext, aRectangle);
2113 Menu* Menu::ImplGetStartMenu()
2115 Menu* pStart = this;
2116 while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
2117 pStart = pStart->pStartedFrom;
2118 return pStart;
2121 void Menu::ImplCallHighlight(sal_uInt16 nItem)
2123 ImplMenuDelData aDelData( this );
2125 nSelectedId = 0;
2126 sSelectedIdent.clear();
2127 MenuItemData* pData = pItemList->GetDataFromPos(nItem);
2128 if (pData)
2130 nSelectedId = pData->nId;
2131 sSelectedIdent = pData->sIdent;
2133 ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) );
2135 if( !aDelData.isDeleted() )
2137 nSelectedId = 0;
2138 sSelectedIdent.clear();
2142 IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
2144 nEventId = nullptr;
2145 Select();
2148 Menu* Menu::ImplFindSelectMenu()
2150 Menu* pSelMenu = nEventId ? this : nullptr;
2152 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2154 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2156 if ( pData->pSubMenu )
2157 pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
2160 return pSelMenu;
2163 Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
2165 Menu* pSelMenu = nullptr;
2167 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2169 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2171 if( pData->nId == nItemId )
2172 pSelMenu = this;
2173 else if ( pData->pSubMenu )
2174 pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
2177 return pSelMenu;
2180 void Menu::RemoveDisabledEntries( bool bCheckPopups, bool bRemoveEmptyPopups )
2182 for ( sal_uInt16 n = 0; n < GetItemCount(); n++ )
2184 bool bRemove = false;
2185 MenuItemData* pItem = pItemList->GetDataFromPos( n );
2186 if ( pItem->eType == MenuItemType::SEPARATOR )
2188 if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
2189 bRemove = true;
2191 else
2192 bRemove = !pItem->bEnabled;
2194 if ( bCheckPopups && pItem->pSubMenu )
2196 pItem->pSubMenu->RemoveDisabledEntries();
2197 if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
2198 bRemove = true;
2201 if ( bRemove )
2202 RemoveItem( n-- );
2205 if ( GetItemCount() )
2207 sal_uInt16 nLast = GetItemCount() - 1;
2208 MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
2209 if ( pItem->eType == MenuItemType::SEPARATOR )
2210 RemoveItem( nLast );
2212 mpLayoutData.reset();
2215 void Menu::UpdateNativeMenu()
2217 if ( ImplGetSalMenu() )
2218 ImplGetSalMenu()->Update();
2221 void Menu::MenuBarKeyInput(const KeyEvent&)
2225 void Menu::ImplKillLayoutData() const
2227 mpLayoutData.reset();
2230 void Menu::ImplFillLayoutData() const
2232 if (!(pWindow && pWindow->IsReallyVisible()))
2233 return;
2235 mpLayoutData.reset(new MenuLayoutData);
2236 if (IsMenuBar())
2238 ImplPaint(*pWindow, pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2240 else
2242 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
2243 ImplPaint(*pWindow, pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(),
2244 nullptr, false, true); //FIXME
2248 tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, tools::Long nIndex ) const
2250 tools::Long nItemIndex = -1;
2251 if( ! mpLayoutData )
2252 ImplFillLayoutData();
2253 if( mpLayoutData )
2255 for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
2257 if( mpLayoutData->m_aLineItemIds[i] == nItemID )
2259 nItemIndex = mpLayoutData->m_aLineIndices[i];
2260 break;
2264 return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
2267 tools::Long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
2269 tools::Long nIndex = -1;
2270 rItemID = 0;
2271 if( ! mpLayoutData )
2272 ImplFillLayoutData();
2273 if( mpLayoutData )
2275 nIndex = mpLayoutData->GetIndexForPoint( rPoint );
2276 for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
2278 if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
2279 (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
2281 // make index relative to item
2282 nIndex -= mpLayoutData->m_aLineIndices[i];
2283 rItemID = mpLayoutData->m_aLineItemIds[i];
2284 break;
2288 return nIndex;
2291 tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
2293 tools::Rectangle aRet;
2295 if (!mpLayoutData )
2296 ImplFillLayoutData();
2297 if (mpLayoutData)
2299 std::map< sal_uInt16, tools::Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
2300 if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
2301 aRet = it->second;
2303 return aRet;
2306 void Menu::SetAccessibleName( sal_uInt16 nItemId, const OUString& rStr )
2308 size_t nPos;
2309 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
2311 if (pData && !rStr.equals(pData->aAccessibleName))
2313 pData->aAccessibleName = rStr;
2314 ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged, nPos);
2318 OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
2320 MenuItemData* pData = pItemList->GetData( nItemId );
2322 if ( pData )
2323 return pData->aAccessibleName;
2325 return OUString();
2328 void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const OUString& rStr )
2330 MenuItemData* pData = pItemList->GetData( nItemId );
2332 if ( pData )
2333 pData->aAccessibleDescription = rStr;
2336 OUString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const
2338 MenuItemData* pData = pItemList->GetData( nItemId );
2340 if (pData && !pData->aAccessibleDescription.isEmpty())
2341 return pData->aAccessibleDescription;
2343 return GetHelpText(nItemId);
2346 void Menu::GetSystemMenuData( SystemMenuData* pData ) const
2348 Menu* pMenu = const_cast<Menu*>(this);
2349 if( pData && pMenu->ImplGetSalMenu() )
2351 pMenu->ImplGetSalMenu()->GetSystemMenuData( pData );
2355 bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
2357 bool bRet = false;
2359 if( pWindow )
2361 if (IsMenuBar())
2362 bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow.get())->GetHighlightedItem() );
2363 else
2364 bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow.get())->GetHighlightedItem() );
2367 return bRet;
2370 void Menu::HighlightItem( sal_uInt16 nItemPos )
2372 if ( !pWindow )
2373 return;
2375 if (IsMenuBar())
2377 MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow.get() );
2378 pMenuWin->SetAutoPopup( false );
2379 pMenuWin->ChangeHighlightItem( nItemPos, false );
2381 else
2383 static_cast< MenuFloatingWindow* >( pWindow.get() )->ChangeHighlightItem( nItemPos, false );
2387 MenuBarWindow* MenuBar::getMenuBarWindow()
2389 // so far just a dynamic_cast, hopefully to be turned into something saner
2390 // at some stage
2391 MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(pWindow.get());
2392 //either there is no window (fdo#87663) or it is a MenuBarWindow
2393 assert(!pWindow || pWin);
2394 return pWin;
2397 MenuBar::MenuBar()
2398 : Menu(),
2399 mbCloseBtnVisible(false),
2400 mbFloatBtnVisible(false),
2401 mbHideBtnVisible(false),
2402 mbDisplayable(true)
2404 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2407 MenuBar::MenuBar( const MenuBar& rMenu )
2408 : Menu(),
2409 mbCloseBtnVisible(false),
2410 mbFloatBtnVisible(false),
2411 mbHideBtnVisible(false),
2412 mbDisplayable(true)
2414 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2415 *this = rMenu;
2418 MenuBar::~MenuBar()
2420 disposeOnce();
2423 void MenuBar::dispose()
2425 ImplDestroy( this, true );
2426 Menu::dispose();
2429 void MenuBar::ClosePopup(Menu *pMenu)
2431 MenuBarWindow* pMenuWin = getMenuBarWindow();
2432 if (!pMenuWin)
2433 return;
2434 pMenuWin->PopupClosed(pMenu);
2437 void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
2439 pWindow->KeyInput(rEvent);
2442 void MenuBar::ShowCloseButton(bool bShow)
2444 ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
2447 void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
2449 if ((bClose != mbCloseBtnVisible) ||
2450 (bFloat != mbFloatBtnVisible) ||
2451 (bHide != mbHideBtnVisible))
2453 mbCloseBtnVisible = bClose;
2454 mbFloatBtnVisible = bFloat;
2455 mbHideBtnVisible = bHide;
2456 MenuBarWindow* pMenuWin = getMenuBarWindow();
2457 if (pMenuWin)
2458 pMenuWin->ShowButtons(bClose, bFloat, bHide);
2462 void MenuBar::LayoutChanged()
2464 MenuBarWindow* pMenuWin = getMenuBarWindow();
2465 if (pMenuWin)
2466 pMenuWin->LayoutChanged();
2469 void MenuBar::SetDisplayable( bool bDisplayable )
2471 if( bDisplayable != mbDisplayable )
2473 if ( ImplGetSalMenu() )
2474 ImplGetSalMenu()->ShowMenuBar( bDisplayable );
2476 mbDisplayable = bDisplayable;
2477 LayoutChanged();
2481 VclPtr<vcl::Window> MenuBar::ImplCreate(vcl::Window* pParent, vcl::Window* pWindow, MenuBar* pMenu)
2483 VclPtr<MenuBarWindow> pMenuBarWindow = dynamic_cast<MenuBarWindow*>(pWindow);
2484 if (!pMenuBarWindow)
2486 pWindow = pMenuBarWindow = VclPtr<MenuBarWindow>::Create( pParent );
2489 pMenu->pStartedFrom = nullptr;
2490 pMenu->pWindow = pWindow;
2491 pMenuBarWindow->SetMenu(pMenu);
2492 tools::Long nHeight = pWindow ? pMenu->ImplCalcSize(pWindow).Height() : 0;
2494 // depending on the native implementation or the displayable flag
2495 // the menubar windows is suppressed (ie, height=0)
2496 if (!pMenu->IsDisplayable() || (pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar()))
2498 nHeight = 0;
2501 pMenuBarWindow->SetHeight(nHeight);
2502 return pWindow;
2505 void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete )
2507 vcl::Window *pWindow = pMenu->ImplGetWindow();
2508 if (pWindow && bDelete)
2510 MenuBarWindow* pMenuWin = pMenu->getMenuBarWindow();
2511 if (pMenuWin)
2512 pMenuWin->KillActivePopup();
2513 pWindow->disposeOnce();
2515 pMenu->pWindow = nullptr;
2518 bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
2520 // No keyboard processing when our menubar is invisible
2521 if (!IsDisplayable())
2522 return false;
2524 // No keyboard processing when system handles the menu.
2525 SalMenu *pNativeMenu = ImplGetSalMenu();
2526 if (pNativeMenu && pNativeMenu->VisibleMenuBar())
2528 // Except when the event is the F6 cycle pane event and we can put our
2529 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2530 // where it's not part of the application window)
2531 if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
2532 return false;
2533 if (!pNativeMenu->CanGetFocus())
2534 return false;
2537 bool bDone = false;
2538 // check for enabled, if this method is called from another window...
2539 vcl::Window* pWin = ImplGetWindow();
2540 if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode())
2542 MenuBarWindow* pMenuWin = getMenuBarWindow();
2543 bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
2545 return bDone;
2548 bool MenuBar::ImplHandleCmdEvent( const CommandEvent& rCEvent )
2550 // No keyboard processing when system handles the menu or our menubar is invisible
2551 if( !IsDisplayable() ||
2552 ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2553 return false;
2555 // check for enabled, if this method is called from another window...
2556 MenuBarWindow* pWin = static_cast<MenuBarWindow*>(ImplGetWindow());
2557 if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && ! pWin->IsInModalMode() )
2559 if (rCEvent.GetCommand() == CommandEventId::ModKeyChange && ImplGetSVData()->maNWFData.mbAutoAccel)
2561 const CommandModKeyData* pCData = rCEvent.GetModKeyData ();
2562 if (pWin->m_nHighlightedItem == ITEMPOS_INVALID)
2564 if (pCData && pCData->IsMod2() && pCData->IsDown())
2565 pWin->SetMBWHideAccel(false);
2566 else
2567 pWin->SetMBWHideAccel(true);
2568 pWin->Invalidate(InvalidateFlags::Update);
2570 return true;
2573 return false;
2576 void MenuBar::SelectItem(sal_uInt16 nId)
2578 if (!pWindow)
2579 return;
2581 pWindow->GrabFocus();
2582 nId = GetItemPos( nId );
2584 MenuBarWindow* pMenuWin = getMenuBarWindow();
2585 if (pMenuWin)
2587 // #99705# popup the selected menu
2588 pMenuWin->SetAutoPopup( true );
2589 if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
2591 pMenuWin->KillActivePopup();
2592 pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
2594 if (nId != ITEMPOS_INVALID)
2595 pMenuWin->ChangeHighlightItem( nId, false );
2599 // handler for native menu selection and command events
2600 bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const
2602 if( pMenu )
2604 ImplMenuDelData aDelData( this );
2606 pMenu->pStartedFrom = const_cast<Menu*>(this);
2607 pMenu->bInCallback = true;
2608 pMenu->Activate();
2610 if( !aDelData.isDeleted() )
2611 pMenu->bInCallback = false;
2613 return true;
2616 bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const
2618 if( pMenu )
2620 ImplMenuDelData aDelData( this );
2622 pMenu->pStartedFrom = const_cast<Menu*>(this);
2623 pMenu->bInCallback = true;
2624 pMenu->Deactivate();
2625 if( !aDelData.isDeleted() )
2626 pMenu->bInCallback = false;
2628 return true;
2631 bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const
2633 if( !pMenu )
2634 pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nHighlightEventId);
2635 if( pMenu )
2637 ImplMenuDelData aDelData( pMenu );
2639 if( mnHighlightedItemPos != ITEMPOS_INVALID )
2640 pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, mnHighlightedItemPos );
2642 if( !aDelData.isDeleted() )
2644 pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
2645 pMenu->nSelectedId = nHighlightEventId;
2646 pMenu->sSelectedIdent = pMenu->GetItemIdent( nHighlightEventId );
2647 pMenu->pStartedFrom = const_cast<MenuBar*>(this);
2648 pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
2650 return true;
2652 else
2653 return false;
2656 bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
2658 if( !pMenu )
2659 pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId);
2660 if( pMenu )
2662 pMenu->nSelectedId = nCommandEventId;
2663 pMenu->sSelectedIdent = pMenu->GetItemIdent(nCommandEventId);
2664 pMenu->pStartedFrom = const_cast<Menu*>(this);
2665 pMenu->ImplSelect();
2666 return true;
2668 else
2669 return false;
2672 sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBar::MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
2674 MenuBarWindow* pMenuWin = getMenuBarWindow();
2675 return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
2678 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBar::MenuBarButtonCallbackArg&,bool>& rLink )
2680 MenuBarWindow* pMenuWin = getMenuBarWindow();
2681 if (!pMenuWin)
2682 return;
2683 pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
2686 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
2688 MenuBarWindow* pMenuWin = getMenuBarWindow();
2689 if (!pMenuWin)
2690 return;
2691 pMenuWin->RemoveMenuBarButton(nId);
2694 tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
2696 MenuBarWindow* pMenuWin = getMenuBarWindow();
2697 return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle();
2700 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
2702 MenuBarWindow* pMenuWin = getMenuBarWindow();
2703 return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
2706 int MenuBar::GetMenuBarHeight() const
2708 MenuBar* pMenuBar = const_cast<MenuBar*>(this);
2709 const SalMenu *pNativeMenu = pMenuBar->ImplGetSalMenu();
2710 int nMenubarHeight;
2711 if (pNativeMenu)
2712 nMenubarHeight = pNativeMenu->GetMenuBarHeight();
2713 else
2715 vcl::Window* pMenubarWin = GetWindow();
2716 nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputHeightPixel() : 0;
2718 return nMenubarHeight;
2721 // bool PopupMenu::bAnyPopupInExecute = false;
2723 MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
2724 return static_cast<MenuFloatingWindow *>(Menu::ImplGetWindow());
2727 PopupMenu::PopupMenu()
2728 : mpLOKNotifier(nullptr)
2730 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2733 PopupMenu::PopupMenu( const PopupMenu& rMenu )
2734 : Menu(),
2735 mpLOKNotifier(nullptr)
2737 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2738 *this = rMenu;
2741 PopupMenu::~PopupMenu()
2743 disposeOnce();
2746 void PopupMenu::ClosePopup(Menu* pMenu)
2748 MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow());
2749 PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu);
2750 if (p && pPopup)
2751 p->KillActivePopup(pPopup);
2754 bool PopupMenu::IsInExecute()
2756 return GetActivePopupMenu() != nullptr;
2759 PopupMenu* PopupMenu::GetActivePopupMenu()
2761 ImplSVData* pSVData = ImplGetSVData();
2762 return pSVData->maAppData.mpActivePopupMenu;
2765 void PopupMenu::EndExecute()
2767 if ( ImplGetWindow() )
2768 ImplGetFloatingWindow()->EndExecute( 0 );
2771 void PopupMenu::SelectItem(sal_uInt16 nId)
2773 if ( !ImplGetWindow() )
2774 return;
2776 if( nId != ITEMPOS_INVALID )
2778 size_t nPos = 0;
2779 MenuItemData* pData = GetItemList()->GetData( nId, nPos );
2780 if (pData && pData->pSubMenu)
2781 ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
2782 else
2783 ImplGetFloatingWindow()->EndExecute( nId );
2785 else
2787 MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
2788 pFloat->GrabFocus();
2790 for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
2792 MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
2793 if( pData->pSubMenu )
2795 pFloat->KillActivePopup();
2798 pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
2802 void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
2804 nSelectedId = nId;
2805 sSelectedIdent = GetItemIdent(nId);
2808 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos )
2810 return Execute( pExecWindow, tools::Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown );
2813 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags )
2815 ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
2817 FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
2818 if ( nFlags & PopupMenuFlags::ExecuteDown )
2819 nPopupModeFlags = FloatWinPopupFlags::Down;
2820 else if ( nFlags & PopupMenuFlags::ExecuteUp )
2821 nPopupModeFlags = FloatWinPopupFlags::Up;
2822 else if ( nFlags & PopupMenuFlags::ExecuteLeft )
2823 nPopupModeFlags = FloatWinPopupFlags::Left;
2824 else if ( nFlags & PopupMenuFlags::ExecuteRight )
2825 nPopupModeFlags = FloatWinPopupFlags::Right;
2826 else
2827 nPopupModeFlags = FloatWinPopupFlags::Down;
2829 if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up
2830 nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2832 if (nFlags & PopupMenuFlags::NoHorzPlacement)
2833 nPopupModeFlags |= FloatWinPopupFlags::NoHorzPlacement;
2835 return ImplExecute( pExecWindow, rRect, nPopupModeFlags, nullptr, false );
2838 void PopupMenu::ImplFlushPendingSelect()
2840 // is there still Select?
2841 Menu* pSelect = ImplFindSelectMenu();
2842 if (pSelect)
2844 // Select should be called prior to leaving execute in a popup menu!
2845 Application::RemoveUserEvent( pSelect->nEventId );
2846 pSelect->nEventId = nullptr;
2847 pSelect->Select();
2851 sal_uInt16 PopupMenu::ImplExecute( const VclPtr<vcl::Window>& pW, const tools::Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst )
2853 if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) )
2854 return 0;
2856 // set the flag to hide or show accelerators in the menu depending on whether the menu was launched by mouse or keyboard shortcut
2857 if( pSFrom && pSFrom->IsMenuBar())
2859 auto pMenuBarWindow = static_cast<MenuBarWindow*>(pSFrom->pWindow.get());
2860 pMenuBarWindow->SetMBWHideAccel( !(pMenuBarWindow->GetMBWMenuKey()) );
2863 mpLayoutData.reset();
2865 ImplSVData* pSVData = ImplGetSVData();
2867 pStartedFrom = pSFrom;
2868 nSelectedId = 0;
2869 sSelectedIdent.clear();
2870 bCanceled = false;
2872 VclPtr<vcl::Window> xFocusId;
2873 bool bRealExecute = false;
2874 if ( !pStartedFrom )
2876 pSVData->mpWinData->mbNoDeactivate = true;
2877 xFocusId = Window::SaveFocus();
2878 bRealExecute = true;
2880 else
2882 // assure that only one menu is open at a time
2883 if (pStartedFrom->IsMenuBar() && pSVData->mpWinData->mpFirstFloat)
2884 pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
2885 | FloatWinPopupEndFlags::CloseAll);
2888 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
2889 tools::Rectangle aRect( rRect );
2890 aRect.SetPos( pW->OutputToScreenPixel( aRect.TopLeft() ) );
2892 if (bRealExecute)
2893 nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
2894 nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose;
2896 bInCallback = true; // set it here, if Activate overridden
2897 Activate();
2898 bInCallback = false;
2900 if ( pW->IsDisposed() )
2901 return 0; // Error
2903 if ( bCanceled || bKilled )
2904 return 0;
2906 if ( !GetItemCount() )
2907 return 0;
2909 // The flag MenuFlags::HideDisabledEntries is inherited.
2910 if ( pSFrom )
2912 if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries )
2913 nMenuFlags |= MenuFlags::HideDisabledEntries;
2914 else
2915 nMenuFlags &= ~MenuFlags::HideDisabledEntries;
2917 else
2918 // #102790# context menus shall never show disabled entries
2919 nMenuFlags |= MenuFlags::HideDisabledEntries;
2921 sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
2922 if ( !nVisibleEntries )
2924 OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
2926 MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, OString());
2927 size_t nPos = 0;
2928 pData = pItemList->GetData( pData->nId, nPos );
2929 assert(pData);
2930 if (pData)
2932 pData->bIsTemporary = true;
2934 ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos);
2937 VclPtrInstance<MenuFloatingWindow> pWin( this, pW, WB_BORDER | WB_SYSTEMWINDOW );
2938 if (comphelper::LibreOfficeKit::isActive() && mpLOKNotifier)
2939 pWin->SetLOKNotifier(mpLOKNotifier);
2941 if( pSVData->maNWFData.mbFlatMenu )
2942 pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
2943 else
2944 pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
2945 pWindow = pWin;
2947 Size aSz = ImplCalcSize( pWin );
2949 tools::Rectangle aDesktopRect(pWin->GetDesktopRectPixel());
2950 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() )
2952 vcl::Window* pDeskW = pWindow->GetWindow( GetWindowType::RealParent );
2953 if( ! pDeskW )
2954 pDeskW = pWindow;
2955 Point aDesktopTL( pDeskW->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) );
2956 aDesktopRect = Application::GetScreenPosSizePixel(
2957 Application::GetBestScreen( tools::Rectangle( aDesktopTL, aRect.GetSize() ) ));
2960 tools::Long nMaxHeight = aDesktopRect.GetHeight();
2962 //rhbz#1021915. If a menu won't fit in the desired location the default
2963 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2964 //some cases, e.g. menubars, it's desirable to limit the options to
2965 //above/below and force the menu to scroll if it won't fit
2966 if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
2968 vcl::Window* pRef = pWin;
2969 if ( pRef->GetParent() )
2970 pRef = pRef->GetParent();
2972 tools::Rectangle devRect( pRef->OutputToAbsoluteScreenPixel( aRect.TopLeft() ),
2973 pRef->OutputToAbsoluteScreenPixel( aRect.BottomRight() ) );
2975 tools::Long nHeightAbove = devRect.Top() - aDesktopRect.Top();
2976 tools::Long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
2977 nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
2980 // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
2981 // So assume that the available screen size matches at least the system requirements
2982 SAL_WARN_IF(nMaxHeight < 768, "vcl",
2983 "Available height misdetected as " << nMaxHeight
2984 << "px. Setting to 768px instead.");
2985 nMaxHeight = std::max(nMaxHeight, tools::Long(768));
2987 if (pStartedFrom && pStartedFrom->IsMenuBar())
2988 nMaxHeight -= pW->GetSizePixel().Height();
2989 sal_Int32 nLeft, nTop, nRight, nBottom;
2990 pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
2991 nMaxHeight -= nTop+nBottom;
2992 if ( aSz.Height() > nMaxHeight )
2994 pWin->EnableScrollMenu( true );
2995 sal_uInt16 nStart = ImplGetFirstVisible();
2996 sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
2997 aSz.setHeight( ImplCalcHeight( nEntries ) );
3000 // tdf#126054 hold this until after function completes
3001 VclPtr<PopupMenu> xThis(this);
3003 pWin->SetFocusId( xFocusId );
3004 pWin->SetOutputSizePixel( aSz );
3005 if ( GetItemCount() )
3007 SalMenu* pMenu = ImplGetSalMenu();
3008 if( pMenu && bRealExecute && pMenu->ShowNativePopupMenu( pWin, aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus ) )
3010 pWin->StopExecute();
3011 pWin->doShutdown();
3012 pWindow->SetParentToDefaultWindow();
3013 pWindow.disposeAndClear();
3014 ImplClosePopupToolBox(pW);
3015 ImplFlushPendingSelect();
3016 return nSelectedId;
3018 else
3020 pWin->StartPopupMode( aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus );
3022 if( pSFrom )
3024 sal_uInt16 aPos;
3025 if (pSFrom->IsMenuBar())
3026 aPos = static_cast<MenuBarWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
3027 else
3028 aPos = static_cast<MenuFloatingWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
3030 pWin->SetPosInParent( aPos ); // store position to be sent in SUBMENUDEACTIVATE
3031 pSFrom->ImplCallEventListeners( VclEventId::MenuSubmenuActivate, aPos );
3034 if ( bPreSelectFirst )
3036 size_t nCount = pItemList->size();
3037 for ( size_t n = 0; n < nCount; n++ )
3039 MenuItemData* pData = pItemList->GetDataFromPos( n );
3040 if ( ( pData->bEnabled
3041 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
3043 && ( pData->eType != MenuItemType::SEPARATOR )
3044 && ImplIsVisible( n )
3045 && ImplIsSelectable( n )
3048 pWin->ChangeHighlightItem( n, false );
3049 break;
3053 if ( bRealExecute )
3055 pWin->Execute();
3056 if (pWin->IsDisposed())
3057 return 0;
3059 xFocusId = pWin->GetFocusId();
3060 assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
3061 pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
3063 if ( nSelectedId ) // then clean up .. ( otherwise done by TH )
3065 PopupMenu* pSub = pWin->GetActivePopup();
3066 while ( pSub )
3068 pSub->ImplGetFloatingWindow()->EndPopupMode();
3069 pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
3072 pWin->doShutdown();
3073 pWindow->SetParentToDefaultWindow();
3074 pWindow.disposeAndClear();
3075 ImplClosePopupToolBox(pW);
3076 ImplFlushPendingSelect();
3079 return bRealExecute ? nSelectedId : 0;
3082 sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
3084 nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3086 tools::Long nHeight = 0;
3087 size_t nEntries = pItemList->size();
3088 sal_uInt16 nVisEntries = 0;
3090 if ( pLastVisible )
3091 *pLastVisible = 0;
3093 for ( size_t n = nStartEntry; n < nEntries; n++ )
3095 if ( ImplIsVisible( n ) )
3097 MenuItemData* pData = pItemList->GetDataFromPos( n );
3098 nHeight += pData->aSz.Height();
3099 if ( nHeight > nMaxHeight )
3100 break;
3102 if ( pLastVisible )
3103 *pLastVisible = n;
3104 nVisEntries++;
3107 return nVisEntries;
3110 tools::Long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
3112 tools::Long nHeight = 0;
3114 sal_uInt16 nFound = 0;
3115 for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ )
3117 if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) )
3119 MenuItemData* pData = pItemList->GetDataFromPos( n );
3120 nHeight += pData->aSz.Height();
3121 nFound++;
3125 nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
3127 return nHeight;
3130 ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
3131 : mpNext( nullptr )
3132 , mpMenu( nullptr )
3134 if( pMenu )
3135 const_cast< Menu* >( pMenu )->ImplAddDel( *this );
3138 ImplMenuDelData::~ImplMenuDelData()
3140 if( mpMenu )
3141 const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
3144 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */