build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / window / menu.cxx
blob3a16b383b13fc05820355b1ed3253e674c9054ae
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/debug.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <tools/rc.h>
23 #include <tools/stream.hxx>
25 #include <comphelper/lok.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/mnemonic.hxx>
28 #include <vcl/image.hxx>
29 #include <vcl/event.hxx>
30 #include <vcl/help.hxx>
31 #include <vcl/floatwin.hxx>
32 #include <vcl/wrkwin.hxx>
33 #include <vcl/timer.hxx>
34 #include <vcl/decoview.hxx>
35 #include <vcl/bitmap.hxx>
36 #include <vcl/menu.hxx>
37 #include <vcl/button.hxx>
38 #include <vcl/gradient.hxx>
39 #include <vcl/i18nhelp.hxx>
40 #include <vcl/taskpanelist.hxx>
41 #include <vcl/controllayout.hxx>
42 #include <vcl/toolbox.hxx>
43 #include <vcl/dockingarea.hxx>
44 #include <vcl/settings.hxx>
45 #include <vcl/commandinfoprovider.hxx>
47 #include <salinst.hxx>
48 #include <svdata.hxx>
49 #include <svids.hrc>
50 #include <window.h>
51 #include <salmenu.hxx>
52 #include <salframe.hxx>
54 #include "menubarwindow.hxx"
55 #include "menufloatingwindow.hxx"
56 #include "menuitemlist.hxx"
58 #include <com/sun/star/uno/Reference.h>
59 #include <com/sun/star/lang/XComponent.hpp>
60 #include <com/sun/star/accessibility/XAccessible.hpp>
61 #include <com/sun/star/accessibility/AccessibleRole.hpp>
62 #include <vcl/unowrap.hxx>
64 #include <vcl/unohelp.hxx>
65 #include <vcl/configsettings.hxx>
67 #include <vcl/lazydelete.hxx>
69 #include <sfx2/sfxsids.hrc>
71 #include <map>
72 #include <vector>
74 namespace vcl
77 struct MenuLayoutData : public ControlLayoutData
79 std::vector< sal_uInt16 > m_aLineItemIds;
80 std::vector< sal_uInt16 > m_aLineItemPositions;
81 std::map< sal_uInt16, Rectangle > m_aVisibleItemBoundRects;
86 using namespace vcl;
88 #define EXTRAITEMHEIGHT 4
89 #define SPACE_AROUND_TITLE 4
91 static bool ImplAccelDisabled()
93 // display of accelerator strings may be suppressed via configuration
94 static int nAccelDisabled = -1;
96 if( nAccelDisabled == -1 )
98 OUString aStr =
99 vcl::SettingsConfigItem::get()->
100 getValue( "Menu", "SuppressAccelerators" );
101 nAccelDisabled = aStr.equalsIgnoreAsciiCase("true") ? 1 : 0;
103 return nAccelDisabled == 1;
106 static void ImplSetMenuItemData( MenuItemData* pData )
108 // convert data
109 if ( !pData->aImage )
110 pData->eType = MenuItemType::STRING;
111 else if ( pData->aText.isEmpty() )
112 pData->eType = MenuItemType::IMAGE;
113 else
114 pData->eType = MenuItemType::STRINGIMAGE;
117 Menu::Menu()
118 : mpFirstDel(nullptr),
119 pItemList(new MenuItemList),
120 pLogo(nullptr),
121 pStartedFrom(nullptr),
122 pWindow(nullptr),
123 nTitleHeight(0),
124 nEventId(nullptr),
125 mnHighlightedItemPos(ITEMPOS_INVALID),
126 nMenuFlags(MenuFlags::NONE),
127 nDefaultItem(0),
128 nSelectedId(0),
129 nImgOrChkPos(0),
130 nTextPos(0),
131 bCanceled(false),
132 bInCallback(false),
133 bKilled(false),
134 mpLayoutData(nullptr),
135 mpSalMenu(nullptr)
139 Menu::~Menu()
141 disposeOnce();
144 void Menu::dispose()
146 ImplCallEventListeners( VCLEVENT_OBJECT_DYING, ITEMPOS_INVALID );
148 // at the window free the reference to the accessible component
149 // and make sure the MenuFloatingWindow knows about our destruction
150 if ( pWindow )
152 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
153 if( pFloat->pMenu.get() == this )
154 pFloat->pMenu.clear();
155 pWindow->SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
158 // dispose accessible components
159 if ( mxAccessible.is() )
161 css::uno::Reference< css::lang::XComponent> xComponent( mxAccessible, css::uno::UNO_QUERY );
162 if ( xComponent.is() )
163 xComponent->dispose();
166 if ( nEventId )
167 Application::RemoveUserEvent( nEventId );
169 // Notify deletion of this menu
170 ImplMenuDelData* pDelData = mpFirstDel;
171 while ( pDelData )
173 pDelData->mpMenu = nullptr;
174 pDelData = pDelData->mpNext;
177 bKilled = true;
179 pItemList->Clear();
180 delete pLogo;
181 pLogo = nullptr;
182 delete mpLayoutData;
183 mpLayoutData = nullptr;
185 // Native-support: destroy SalMenu
186 ImplSetSalMenu( nullptr );
188 pStartedFrom.clear();
189 pWindow.clear();
190 VclReferenceBase::dispose();
193 void Menu::CreateAutoMnemonics()
195 MnemonicGenerator aMnemonicGenerator;
196 size_t n;
197 for ( n = 0; n < pItemList->size(); n++ )
199 MenuItemData* pData = pItemList->GetDataFromPos( n );
200 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
201 aMnemonicGenerator.RegisterMnemonic( pData->aText );
203 for ( n = 0; n < pItemList->size(); n++ )
205 MenuItemData* pData = pItemList->GetDataFromPos( n );
206 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
207 pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText );
211 void Menu::Activate()
213 bInCallback = true;
215 ImplMenuDelData aDelData( this );
217 ImplCallEventListeners( VCLEVENT_MENU_ACTIVATE, ITEMPOS_INVALID );
219 if( !aDelData.isDeleted() )
221 if ( !aActivateHdl.Call( this ) )
223 if( !aDelData.isDeleted() )
225 Menu* pStartMenu = ImplGetStartMenu();
226 if ( pStartMenu && ( pStartMenu != this ) )
228 pStartMenu->bInCallback = true;
229 // MT 11/01: Call EventListener here? I don't know...
230 pStartMenu->aActivateHdl.Call( this );
231 pStartMenu->bInCallback = false;
235 bInCallback = false;
239 void Menu::Deactivate()
241 for ( size_t n = pItemList->size(); n; )
243 MenuItemData* pData = pItemList->GetDataFromPos( --n );
244 if ( pData->bIsTemporary )
246 if ( ImplGetSalMenu() )
247 ImplGetSalMenu()->RemoveItem( n );
249 pItemList->Remove( n );
253 bInCallback = true;
255 ImplMenuDelData aDelData( this );
257 Menu* pStartMenu = ImplGetStartMenu();
258 ImplCallEventListeners( VCLEVENT_MENU_DEACTIVATE, ITEMPOS_INVALID );
260 if( !aDelData.isDeleted() )
262 if ( !aDeactivateHdl.Call( this ) )
264 if( !aDelData.isDeleted() )
266 if ( pStartMenu && ( pStartMenu != this ) )
268 pStartMenu->bInCallback = true;
269 pStartMenu->aDeactivateHdl.Call( this );
270 pStartMenu->bInCallback = false;
276 if( !aDelData.isDeleted() )
278 bInCallback = false;
282 void Menu::ImplSelect()
284 MenuItemData* pData = GetItemList()->GetData( nSelectedId );
285 if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) )
287 bool bChecked = IsItemChecked( nSelectedId );
288 if ( pData->nBits & MenuItemBits::RADIOCHECK )
290 if ( !bChecked )
291 CheckItem( nSelectedId );
293 else
294 CheckItem( nSelectedId, !bChecked );
297 // call select
298 ImplSVData* pSVData = ImplGetSVData();
299 pSVData->maAppData.mpActivePopupMenu = nullptr; // if new execute in select()
300 nEventId = Application::PostUserEvent( LINK( this, Menu, ImplCallSelect ) );
303 void Menu::Select()
305 ImplMenuDelData aDelData( this );
307 ImplCallEventListeners( VCLEVENT_MENU_SELECT, GetItemPos( GetCurItemId() ) );
308 if ( !aDelData.isDeleted() && !aSelectHdl.Call( this ) )
310 if( !aDelData.isDeleted() )
312 Menu* pStartMenu = ImplGetStartMenu();
313 if ( pStartMenu && ( pStartMenu != this ) )
315 pStartMenu->nSelectedId = nSelectedId;
316 pStartMenu->aSelectHdl.Call( this );
322 #if defined(MACOSX)
323 void Menu::ImplSelectWithStart( Menu* pSMenu )
325 auto pOldStartedFrom = pStartedFrom;
326 pStartedFrom = pSMenu;
327 auto pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->pStartedFrom : VclPtr<Menu>();
328 Select();
329 if( pOldStartedFrom )
330 pOldStartedFrom->pStartedFrom = pOldStartedStarted;
331 pStartedFrom = pOldStartedFrom;
333 #endif
335 void Menu::ImplCallEventListeners( sal_uLong nEvent, sal_uInt16 nPos )
337 ImplMenuDelData aDelData( this );
339 VclMenuEvent aEvent( this, nEvent, nPos );
341 // This is needed by atk accessibility bridge
342 if ( nEvent == VCLEVENT_MENU_HIGHLIGHT )
344 Application::ImplCallEventListeners( aEvent );
347 if ( !aDelData.isDeleted() )
349 // Copy the list, because this can be destroyed when calling a Link...
350 std::list<Link<VclMenuEvent&,void>> aCopy( maEventListeners );
351 std::list<Link<VclMenuEvent&,void>>::iterator aIter( aCopy.begin() );
352 std::list<Link<VclMenuEvent&,void>>::const_iterator aEnd( aCopy.end() );
353 while ( aIter != aEnd )
355 Link<VclMenuEvent&,void> &rLink = *aIter;
356 if( std::find(maEventListeners.begin(), maEventListeners.end(), rLink) != maEventListeners.end() )
357 rLink.Call( aEvent );
358 ++aIter;
363 void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener )
365 maEventListeners.push_back( rEventListener );
368 void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener )
370 maEventListeners.remove( rEventListener );
373 MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits,
374 const OUString& rStr, Menu* pMenu,
375 size_t nPos, const OString &rIdent)
377 // put Item in MenuItemList
378 MenuItemData* pData = pItemList->Insert(nId, MenuItemType::STRING,
379 nBits, rStr, Image(), pMenu, nPos, rIdent);
381 // update native menu
382 if (ImplGetSalMenu() && pData->pSalMenuItem)
383 ImplGetSalMenu()->InsertItem(pData->pSalMenuItem, nPos);
385 return pData;
388 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits,
389 const OString &rIdent, sal_uInt16 nPos)
391 SAL_WARN_IF( !nItemId, "vcl", "Menu::InsertItem(): ItemId == 0" );
392 SAL_WARN_IF( GetItemPos( nItemId ) != MENU_ITEM_NOTFOUND, "vcl",
393 "Menu::InsertItem(): ItemId already exists" );
395 // if Position > ItemCount, append
396 if ( nPos >= pItemList->size() )
397 nPos = MENU_APPEND;
399 // put Item in MenuItemList
400 NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent);
402 vcl::Window* pWin = ImplGetWindow();
403 delete mpLayoutData;
404 mpLayoutData = nullptr;
405 if ( pWin )
407 ImplCalcSize( pWin );
408 if ( pWin->IsVisible() )
409 pWin->Invalidate();
411 ImplCallEventListeners( VCLEVENT_MENU_INSERTITEM, nPos );
414 void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage,
415 MenuItemBits nItemBits, const OString &rIdent, sal_uInt16 nPos)
417 InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos);
418 SetItemImage( nItemId, rImage );
421 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr,
422 const Image& rImage, MenuItemBits nItemBits,
423 const OString &rIdent, sal_uInt16 nPos)
425 InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
426 SetItemImage( nItemId, rImage );
429 void Menu::InsertItem( const ResId& rResId )
431 ResMgr* pMgr = rResId.GetResMgr();
432 if( ! pMgr )
433 return;
435 RscMenuItem nObjMask;
437 GetRes( rResId.SetRT( RSC_MENUITEM ) );
438 nObjMask = (RscMenuItem)ReadLongRes();
440 bool bSep = false;
441 if ( nObjMask & RscMenuItem::Separator )
442 bSep = ReadShortRes() != 0;
444 sal_uInt16 nItemId = 1;
445 if ( nObjMask & RscMenuItem::Id )
446 nItemId = sal::static_int_cast<sal_uInt16>(ReadLongRes());
448 MenuItemBits nStatus = MenuItemBits::NONE;
449 if ( nObjMask & RscMenuItem::Status )
450 nStatus = sal::static_int_cast<MenuItemBits>(ReadLongRes());
452 OUString aText;
453 if ( nObjMask & RscMenuItem::Text )
454 aText = ReadStringRes();
456 // create item
457 if (!bSep)
458 InsertItem(nItemId, aText, nStatus);
459 else
460 InsertSeparator();
462 OUString aHelpText;
463 if ( nObjMask & RscMenuItem::HelpText )
465 aHelpText = ReadStringRes();
466 if( !bSep )
467 SetHelpText( nItemId, aHelpText );
470 if ( nObjMask & RscMenuItem::HelpId )
472 OString aHelpId( ReadByteStringRes() );
473 if ( !bSep )
474 SetHelpId( nItemId, aHelpId );
477 if( !bSep )
478 SetHelpText( nItemId, aHelpText );
480 if ( nObjMask & RscMenuItem::Disable )
482 if ( !bSep )
483 EnableItem( nItemId, ReadShortRes() == 0 );
485 if ( nObjMask & RscMenuItem::Command )
487 OUString aCommandStr = ReadStringRes();
488 if ( !bSep )
489 SetItemCommand( nItemId, aCommandStr );
491 if ( nObjMask & RscMenuItem::Menu )
493 if ( !bSep )
495 MenuItemData* pData = GetItemList()->GetData( nItemId );
496 if ( pData )
498 VclPtr<PopupMenu> pSubMenu = VclPtr<PopupMenu>::Create( ResId( static_cast<RSHEADER_TYPE*>(GetClassRes()), *pMgr ) );
499 pData->pAutoSubMenu = pSubMenu;
500 // #111060# keep track of this pointer, may be it will be deleted from outside
501 pSubMenu->pRefAutoSubMenu = &pData->pAutoSubMenu;
502 SetPopupMenu( nItemId, pSubMenu );
505 IncrementRes( GetObjSizeRes( static_cast<RSHEADER_TYPE*>(GetClassRes()) ) );
507 delete mpLayoutData;
508 mpLayoutData = nullptr;
511 void Menu::InsertItem(const OUString& rCommand, const css::uno::Reference<css::frame::XFrame>& rFrame)
513 sal_uInt16 nItemId = GetItemCount() + 1;
515 if (rFrame.is())
517 OUString aLabel(CommandInfoProvider::Instance().GetPopupLabelForCommand(rCommand, rFrame));
518 OUString aTooltip(CommandInfoProvider::Instance().GetTooltipForCommand(rCommand, rFrame));
519 Image aImage(CommandInfoProvider::Instance().GetImageForCommand(rCommand, rFrame));
521 InsertItem(nItemId, aLabel, aImage);
522 SetHelpText(nItemId, aTooltip);
524 else
525 InsertItem(nItemId, OUString());
527 SetItemCommand(nItemId, rCommand);
531 void Menu::InsertSeparator(const OString &rIdent, sal_uInt16 nPos)
533 // do nothing if it's a menu bar
534 if (IsMenuBar())
535 return;
537 // if position > ItemCount, append
538 if ( nPos >= pItemList->size() )
539 nPos = MENU_APPEND;
541 // put separator in item list
542 pItemList->InsertSeparator(rIdent, nPos);
544 // update native menu
545 size_t itemPos = ( nPos != MENU_APPEND ) ? nPos : pItemList->size() - 1;
546 MenuItemData *pData = pItemList->GetDataFromPos( itemPos );
547 if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
548 ImplGetSalMenu()->InsertItem( pData->pSalMenuItem, nPos );
550 delete mpLayoutData;
551 mpLayoutData = nullptr;
553 ImplCallEventListeners( VCLEVENT_MENU_INSERTITEM, nPos );
556 void Menu::RemoveItem( sal_uInt16 nPos )
558 bool bRemove = false;
560 if ( nPos < GetItemCount() )
562 // update native menu
563 if( ImplGetSalMenu() )
564 ImplGetSalMenu()->RemoveItem( nPos );
566 pItemList->Remove( nPos );
567 bRemove = true;
570 vcl::Window* pWin = ImplGetWindow();
571 if ( pWin )
573 ImplCalcSize( pWin );
574 if ( pWin->IsVisible() )
575 pWin->Invalidate();
577 delete mpLayoutData;
578 mpLayoutData = nullptr;
580 if ( bRemove )
581 ImplCallEventListeners( VCLEVENT_MENU_REMOVEITEM, nPos );
584 void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
586 MenuItemType eType = rMenu.GetItemType( nPos );
588 if ( eType == MenuItemType::DONTKNOW )
589 return;
591 if ( eType == MenuItemType::SEPARATOR )
592 pThis->InsertSeparator( OString(), nNewPos );
593 else
595 sal_uInt16 nId = rMenu.GetItemId( nPos );
597 SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
598 "Menu::CopyItem(): ItemId already exists" );
600 MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
602 if (!pData)
603 return;
605 if ( eType == MenuItemType::STRINGIMAGE )
606 pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
607 else if ( eType == MenuItemType::STRING )
608 pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos );
609 else
610 pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
612 if ( rMenu.IsItemChecked( nId ) )
613 pThis->CheckItem( nId );
614 if ( !rMenu.IsItemEnabled( nId ) )
615 pThis->EnableItem( nId, false );
616 pThis->SetHelpId( nId, pData->aHelpId );
617 pThis->SetHelpText( nId, pData->aHelpText );
618 pThis->SetAccelKey( nId, pData->aAccelKey );
619 pThis->SetItemCommand( nId, pData->aCommandStr );
620 pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
622 PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
623 if ( pSubMenu )
625 // create auto-copy
626 VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
627 pThis->SetPopupMenu( nId, pNewMenu );
632 void Menu::CopyItem( const Menu& rMenu, sal_uInt16 nPos )
634 ImplCopyItem( this, rMenu, nPos, MENU_APPEND );
637 void Menu::Clear()
639 for ( sal_uInt16 i = GetItemCount(); i; i-- )
640 RemoveItem( 0 );
643 sal_uInt16 Menu::GetItemCount() const
645 return (sal_uInt16)pItemList->size();
648 sal_uInt16 Menu::ImplGetVisibleItemCount() const
650 sal_uInt16 nItems = 0;
651 for ( size_t n = pItemList->size(); n; )
653 if ( ImplIsVisible( --n ) )
654 nItems++;
656 return nItems;
659 sal_uInt16 Menu::ImplGetFirstVisible() const
661 for ( size_t n = 0; n < pItemList->size(); n++ )
663 if ( ImplIsVisible( n ) )
664 return n;
666 return ITEMPOS_INVALID;
669 sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
671 for ( size_t n = nPos; n; )
673 if ( n && ImplIsVisible( --n ) )
674 return n;
676 return ITEMPOS_INVALID;
679 sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
681 for ( size_t n = nPos+1; n < pItemList->size(); n++ )
683 if ( ImplIsVisible( n ) )
684 return n;
686 return ITEMPOS_INVALID;
689 sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
691 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
693 if ( pData )
694 return pData->nId;
695 else
696 return 0;
699 sal_uInt16 Menu::GetItemId(const OString &rIdent) const
701 for (size_t n = 0; n < pItemList->size(); ++n)
703 MenuItemData* pData = pItemList->GetDataFromPos(n);
704 if (pData && pData->sIdent == rIdent)
705 return pData->nId;
707 return MENU_ITEM_NOTFOUND;
710 sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
712 size_t nPos;
713 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
715 if ( pData )
716 return (sal_uInt16)nPos;
717 else
718 return MENU_ITEM_NOTFOUND;
721 MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
723 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
725 if ( pData )
726 return pData->eType;
727 else
728 return MenuItemType::DONTKNOW;
731 OString Menu::GetCurItemIdent() const
733 const MenuItemData* pData = pItemList->GetData(nSelectedId);
734 return pData ? pData->sIdent : OString();
737 OString Menu::GetItemIdent(sal_uInt16 nId) const
739 const MenuItemData* pData = pItemList->GetData(nId);
740 return pData ? pData->sIdent : OString();
743 void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
745 MenuItemData* pData = pItemList->GetData( nItemId );
746 if ( pData )
747 pData->nBits = nBits;
750 MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
752 MenuItemBits nBits = MenuItemBits::NONE;
753 MenuItemData* pData = pItemList->GetData( nItemId );
754 if ( pData )
755 nBits = pData->nBits;
756 return nBits;
759 void Menu::SetUserValue(sal_uInt16 nItemId, sal_uLong nValue, MenuUserDataReleaseFunction aFunc)
761 MenuItemData* pData = pItemList->GetData(nItemId);
762 if (pData)
764 if (pData->aUserValueReleaseFunc)
765 pData->aUserValueReleaseFunc(pData->nUserValue);
766 pData->aUserValueReleaseFunc = aFunc;
767 pData->nUserValue = nValue;
771 sal_uLong Menu::GetUserValue( sal_uInt16 nItemId ) const
773 MenuItemData* pData = pItemList->GetData( nItemId );
774 return pData ? pData->nUserValue : 0;
777 void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
779 size_t nPos;
780 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
782 // Item does not exist -> return NULL
783 if ( !pData )
784 return;
786 // same menu, nothing to do
787 if ( static_cast<PopupMenu*>(pData->pSubMenu.get()) == pMenu )
788 return;
790 // remove old menu
791 auto oldSubMenu = pData->pSubMenu;
793 // data exchange
794 pData->pSubMenu = pMenu;
796 // #112023# Make sure pStartedFrom does not point to invalid (old) data
797 if ( pData->pSubMenu )
798 pData->pSubMenu->pStartedFrom = nullptr;
800 // set native submenu
801 if( ImplGetSalMenu() && pData->pSalMenuItem )
803 if( pMenu )
804 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem, pMenu->ImplGetSalMenu(), nPos );
805 else
806 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem, nullptr, nPos );
809 oldSubMenu.disposeAndClear();
811 ImplCallEventListeners( VCLEVENT_MENU_SUBMENUCHANGED, nPos );
814 PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
816 MenuItemData* pData = pItemList->GetData( nItemId );
818 if ( pData )
819 return static_cast<PopupMenu*>(pData->pSubMenu.get());
820 else
821 return nullptr;
824 void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
826 size_t nPos;
827 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
829 if ( !pData )
830 return;
832 if ( pData->aAccelKey == rKeyCode )
833 return;
835 pData->aAccelKey = rKeyCode;
837 // update native menu
838 if( ImplGetSalMenu() && pData->pSalMenuItem )
839 ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem, rKeyCode, rKeyCode.GetName() );
842 KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
844 MenuItemData* pData = pItemList->GetData( nItemId );
846 if ( pData )
847 return pData->aAccelKey;
848 else
849 return KeyCode();
852 KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
854 KeyEvent aRet;
855 MenuItemData* pData = pItemList->GetData( nItemId );
856 if( pData )
858 sal_Int32 nPos = pData->aText.indexOf( '~' );
859 if( nPos != -1 && nPos < pData->aText.getLength()-1 )
861 sal_uInt16 nCode = 0;
862 sal_Unicode cAccel = pData->aText[nPos+1];
863 if( cAccel >= 'a' && cAccel <= 'z' )
864 nCode = KEY_A + (cAccel-'a');
865 else if( cAccel >= 'A' && cAccel <= 'Z' )
866 nCode = KEY_A + (cAccel-'A');
867 else if( cAccel >= '0' && cAccel <= '9' )
868 nCode = KEY_0 + (cAccel-'0');
870 aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
874 return aRet;
877 void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
879 size_t nPos;
880 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
882 if ( !pData || pData->bChecked == bCheck )
883 return;
885 // if radio-check, then uncheck previous
886 if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
887 (pData->nBits & MenuItemBits::RADIOCHECK) )
889 MenuItemData* pGroupData;
890 sal_uInt16 nGroupPos;
891 sal_uInt16 nItemCount = GetItemCount();
892 bool bFound = false;
894 nGroupPos = nPos;
895 while ( nGroupPos )
897 pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
898 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
900 if ( IsItemChecked( pGroupData->nId ) )
902 CheckItem( pGroupData->nId, false );
903 bFound = true;
904 break;
907 else
908 break;
909 nGroupPos--;
912 if ( !bFound )
914 nGroupPos = nPos+1;
915 while ( nGroupPos < nItemCount )
917 pGroupData = pItemList->GetDataFromPos( nGroupPos );
918 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
920 if ( IsItemChecked( pGroupData->nId ) )
922 CheckItem( pGroupData->nId, false );
923 break;
926 else
927 break;
928 nGroupPos++;
933 pData->bChecked = bCheck;
935 // update native menu
936 if( ImplGetSalMenu() )
937 ImplGetSalMenu()->CheckItem( nPos, bCheck );
939 ImplCallEventListeners( bCheck ? VCLEVENT_MENU_ITEMCHECKED : VCLEVENT_MENU_ITEMUNCHECKED, nPos );
942 bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
944 size_t nPos;
945 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
947 if ( !pData )
948 return false;
950 return pData->bChecked;
953 void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
955 size_t nPos;
956 MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
958 if ( pItemData && ( pItemData->bEnabled != bEnable ) )
960 pItemData->bEnabled = bEnable;
962 vcl::Window* pWin = ImplGetWindow();
963 if ( pWin && pWin->IsVisible() )
965 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
966 long nX = 0;
967 size_t nCount = pItemList->size();
968 for ( size_t n = 0; n < nCount; n++ )
970 MenuItemData* pData = pItemList->GetDataFromPos( n );
971 if ( n == nPos )
973 pWin->Invalidate( Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
974 break;
976 nX += pData->aSz.Width();
979 // update native menu
980 if( ImplGetSalMenu() )
981 ImplGetSalMenu()->EnableItem( nPos, bEnable );
983 ImplCallEventListeners( bEnable ? VCLEVENT_MENU_ENABLE : VCLEVENT_MENU_DISABLE, nPos );
987 bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
989 size_t nPos;
990 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
992 if ( !pData )
993 return false;
995 return pData->bEnabled;
998 void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
1000 size_t nPos;
1001 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1003 SAL_WARN_IF(IsMenuBar(), "vcl", "Menu::ShowItem - ignored for menu bar entries!");
1004 if (!IsMenuBar()&& pData && (pData->bVisible != bVisible))
1006 vcl::Window* pWin = ImplGetWindow();
1007 if ( pWin && pWin->IsVisible() )
1009 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
1010 return;
1012 pData->bVisible = bVisible;
1014 // update native menu
1015 if( ImplGetSalMenu() )
1016 ImplGetSalMenu()->ShowItem( nPos, bVisible );
1020 void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
1022 size_t nPos;
1023 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1025 if ( !pData )
1026 return;
1028 if ( !rStr.equals( pData->aText ) )
1030 pData->aText = rStr;
1031 ImplSetMenuItemData( pData );
1032 // update native menu
1033 if( ImplGetSalMenu() && pData->pSalMenuItem )
1034 ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem, rStr );
1036 vcl::Window* pWin = ImplGetWindow();
1037 delete mpLayoutData;
1038 mpLayoutData = nullptr;
1039 if (pWin && IsMenuBar())
1041 ImplCalcSize( pWin );
1042 if ( pWin->IsVisible() )
1043 pWin->Invalidate();
1046 ImplCallEventListeners( VCLEVENT_MENU_ITEMTEXTCHANGED, nPos );
1050 OUString Menu::GetItemText( sal_uInt16 nItemId ) const
1052 size_t nPos;
1053 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1055 if ( pData )
1056 return pData->aText;
1058 return OUString();
1061 void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1063 size_t nPos;
1064 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1066 if ( !pData )
1067 return;
1069 pData->aImage = rImage;
1070 ImplSetMenuItemData( pData );
1072 // update native menu
1073 if( ImplGetSalMenu() && pData->pSalMenuItem )
1074 ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem, rImage );
1077 Image Menu::GetItemImage( sal_uInt16 nItemId ) const
1079 MenuItemData* pData = pItemList->GetData( nItemId );
1081 if ( pData )
1082 return pData->aImage;
1083 else
1084 return Image();
1087 void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1089 size_t nPos;
1090 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1092 if ( pData )
1093 pData->aCommandStr = rCommand;
1096 OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
1098 MenuItemData* pData = pItemList->GetData( nItemId );
1100 if (pData)
1101 return pData->aCommandStr;
1103 return OUString();
1106 void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
1108 MenuItemData* pData = pItemList->GetData( nItemId );
1110 if ( pData )
1111 pData->aHelpCommandStr = rStr;
1114 OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
1116 MenuItemData* pData = pItemList->GetData( nItemId );
1118 if ( pData )
1119 return pData->aHelpCommandStr;
1121 return OUString();
1124 void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
1126 MenuItemData* pData = pItemList->GetData( nItemId );
1128 if ( pData )
1129 pData->aHelpText = rStr;
1132 OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
1134 MenuItemData* pData = pItemList->GetData( nItemId );
1136 if ( pData && pData->aHelpText.isEmpty() &&
1137 (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() )))
1139 Help* pHelp = Application::GetHelp();
1140 if ( pHelp )
1142 if (!pData->aCommandStr.isEmpty())
1143 pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr, nullptr );
1144 if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty())
1145 pData->aHelpText = pHelp->GetHelpText( OStringToOUString( pData->aHelpId, RTL_TEXTENCODING_UTF8 ), nullptr );
1149 return OUString();
1152 OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
1154 return ImplGetHelpText( nItemId );
1157 void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
1159 MenuItemData* pData = pItemList->GetData( nItemId );
1161 if ( pData )
1162 pData->aTipHelpText = rStr;
1165 OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
1167 MenuItemData* pData = pItemList->GetData( nItemId );
1169 if ( pData )
1170 return pData->aTipHelpText;
1172 return OUString();
1175 void Menu::SetHelpId( sal_uInt16 nItemId, const OString& rHelpId )
1177 MenuItemData* pData = pItemList->GetData( nItemId );
1179 if ( pData )
1180 pData->aHelpId = rHelpId;
1183 OString Menu::GetHelpId( sal_uInt16 nItemId ) const
1185 OString aRet;
1187 MenuItemData* pData = pItemList->GetData( nItemId );
1189 if ( pData )
1191 if ( !pData->aHelpId.isEmpty() )
1192 aRet = pData->aHelpId;
1193 else
1194 aRet = OUStringToOString( pData->aCommandStr, RTL_TEXTENCODING_UTF8 );
1197 return aRet;
1200 Menu& Menu::operator=( const Menu& rMenu )
1202 // clean up
1203 Clear();
1205 // copy items
1206 sal_uInt16 nCount = rMenu.GetItemCount();
1207 for ( sal_uInt16 i = 0; i < nCount; i++ )
1208 ImplCopyItem( this, rMenu, i, MENU_APPEND );
1210 nDefaultItem = rMenu.nDefaultItem;
1211 aActivateHdl = rMenu.aActivateHdl;
1212 aDeactivateHdl = rMenu.aDeactivateHdl;
1213 aSelectHdl = rMenu.aSelectHdl;
1214 aTitleText = rMenu.aTitleText;
1215 nTitleHeight = rMenu.nTitleHeight;
1217 return *this;
1220 bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
1222 bool bVisible = true;
1224 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1225 // check general visibility first
1226 if( pData && !pData->bVisible )
1227 bVisible = false;
1229 if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
1231 if( nPos == 0 ) // no separator should be shown at the very beginning
1232 bVisible = false;
1233 else
1235 // always avoid adjacent separators
1236 size_t nCount = pItemList->size();
1237 size_t n;
1238 MenuItemData* pNextData = nullptr;
1239 // search next visible item
1240 for( n = nPos + 1; n < nCount; n++ )
1242 pNextData = pItemList->GetDataFromPos( n );
1243 if( pNextData && pNextData->bVisible )
1245 if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
1246 break;
1249 if( n == nCount ) // no next visible item
1250 bVisible = false;
1251 // check for separator
1252 if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
1253 bVisible = false;
1255 if( bVisible )
1257 for( n = nPos; n > 0; n-- )
1259 pNextData = pItemList->GetDataFromPos( n-1 );
1260 if( pNextData && pNextData->bVisible )
1262 if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
1263 break;
1266 if( n == 0 ) // no previous visible item
1267 bVisible = false;
1272 // not allowed for menubar, as I do not know
1273 // whether a menu-entry will disappear or will appear
1274 if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) &&
1275 !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
1277 if( !pData ) // e.g. nPos == ITEMPOS_INVALID
1278 bVisible = false;
1279 else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
1281 // tdf#86850 Always display clipboard functions
1282 if ( pData->nId == SID_CUT || pData->nId == SID_COPY || pData->nId == SID_PASTE ||
1283 pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" )
1284 bVisible = true;
1285 else
1286 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1287 bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
1291 return bVisible;
1294 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
1296 return IsMenuVisible() && ImplIsVisible( nItemPos );
1299 bool Menu::IsMenuVisible() const
1301 return pWindow && pWindow->IsReallyVisible();
1304 bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
1306 bool bSelectable = true;
1308 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1309 // check general visibility first
1310 if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
1311 bSelectable = false;
1313 return bSelectable;
1316 css::uno::Reference<css::accessibility::XAccessible> Menu::GetAccessible()
1318 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1319 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1320 // mxAccessible member only for sub menus.
1321 if ( pStartedFrom )
1323 for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
1325 sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
1326 if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
1328 css::uno::Reference<css::accessibility::XAccessible> xParent = pStartedFrom->GetAccessible();
1329 if ( xParent.is() )
1331 css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( xParent->getAccessibleContext() );
1332 if (xParentContext.is())
1333 return xParentContext->getAccessibleChild( i );
1338 else if ( !mxAccessible.is() )
1340 UnoWrapperBase* pWrapper = Application::GetUnoWrapper();
1341 if ( pWrapper )
1342 mxAccessible = pWrapper->CreateAccessible(this, IsMenuBar());
1345 return mxAccessible;
1348 void Menu::SetAccessible(const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible )
1350 mxAccessible = rxAccessible;
1353 Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext& rRenderContext, long& rCheckHeight, long& rRadioHeight ) const
1355 long nCheckWidth = 0, nRadioWidth = 0;
1356 rCheckHeight = rRadioHeight = 0;
1358 if (!IsMenuBar())
1360 ImplControlValue aVal;
1361 Rectangle aNativeBounds;
1362 Rectangle aNativeContent;
1364 Rectangle aCtrlRegion(Rectangle(Point(), Size(100, 15)));
1365 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
1367 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
1368 aCtrlRegion, ControlState::ENABLED, aVal, OUString(),
1369 aNativeBounds, aNativeContent))
1371 rCheckHeight = aNativeBounds.GetHeight();
1372 nCheckWidth = aNativeContent.GetWidth();
1375 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
1377 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
1378 aCtrlRegion, ControlState::ENABLED, aVal, OUString(),
1379 aNativeBounds, aNativeContent))
1381 rRadioHeight = aNativeBounds.GetHeight();
1382 nRadioWidth = aNativeContent.GetWidth();
1386 return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
1389 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext& rRenderContext, Size& rArrowSize, long& rArrowSpacing)
1391 ImplControlValue aVal;
1392 Rectangle aNativeBounds;
1393 Rectangle aNativeContent;
1394 Rectangle aCtrlRegion(Rectangle(Point(), Size(100, 15)));
1395 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
1397 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
1398 aCtrlRegion, ControlState::ENABLED,
1399 aVal, OUString(), aNativeBounds, aNativeContent))
1401 Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
1402 rArrowSize = aSize;
1403 rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
1404 return true;
1407 return false;
1410 void Menu::ImplAddDel( ImplMenuDelData& rDel )
1412 SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1413 if( !rDel.mpMenu )
1415 rDel.mpMenu = this;
1416 rDel.mpNext = mpFirstDel;
1417 mpFirstDel = &rDel;
1421 void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
1423 rDel.mpMenu = nullptr;
1424 if ( mpFirstDel == &rDel )
1426 mpFirstDel = rDel.mpNext;
1428 else
1430 ImplMenuDelData* pData = mpFirstDel;
1431 while ( pData && (pData->mpNext != &rDel) )
1432 pData = pData->mpNext;
1434 SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1435 if( pData )
1436 pData->mpNext = rDel.mpNext;
1440 Size Menu::ImplCalcSize( vcl::Window* pWin )
1442 // | Check/Radio/Image| Text| Accel/Popup|
1444 // for symbols: nFontHeight x nFontHeight
1445 long nFontHeight = pWin->GetTextHeight();
1446 long nExtra = nFontHeight/4;
1448 long nMinMenuItemHeight = nFontHeight;
1449 long nCheckHeight = 0, nRadioHeight = 0;
1450 Size aMaxSize = ImplGetNativeCheckAndRadioSize(*pWin, nCheckHeight, nRadioHeight); // FIXME
1451 if( aMaxSize.Height() > nMinMenuItemHeight )
1452 nMinMenuItemHeight = aMaxSize.Height();
1454 Size aMaxImgSz;
1456 const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
1457 if ( rSettings.GetUseImagesInMenus() )
1459 if ( 16 > nMinMenuItemHeight )
1460 nMinMenuItemHeight = 16;
1461 for ( size_t i = pItemList->size(); i; )
1463 MenuItemData* pData = pItemList->GetDataFromPos( --i );
1464 if ( ImplIsVisible( i )
1465 && ( ( pData->eType == MenuItemType::IMAGE )
1466 || ( pData->eType == MenuItemType::STRINGIMAGE )
1470 Size aImgSz = pData->aImage.GetSizePixel();
1471 if ( aImgSz.Height() > aMaxImgSz.Height() )
1472 aMaxImgSz.Height() = aImgSz.Height();
1473 if ( aImgSz.Height() > nMinMenuItemHeight )
1474 nMinMenuItemHeight = aImgSz.Height();
1475 break;
1480 Size aSz;
1481 long nCheckWidth = 0;
1482 long nMaxWidth = 0;
1484 for ( size_t n = pItemList->size(); n; )
1486 MenuItemData* pData = pItemList->GetDataFromPos( --n );
1488 pData->aSz.Height() = 0;
1489 pData->aSz.Width() = 0;
1491 if ( ImplIsVisible( n ) )
1493 long nWidth = 0;
1495 // Separator
1496 if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
1498 //Useless: SAL_WARN_IF( IsMenuBar(), "vcl", "Separator in MenuBar ?! " );
1499 pData->aSz.Height() = 4;
1502 // Image:
1503 if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1505 Size aImgSz = pData->aImage.GetSizePixel();
1507 aImgSz.Height() += 4; // add a border for native marks
1508 aImgSz.Width() += 4; // add a border for native marks
1509 if ( aImgSz.Width() > aMaxImgSz.Width() )
1510 aMaxImgSz.Width() = aImgSz.Width();
1511 if ( aImgSz.Height() > aMaxImgSz.Height() )
1512 aMaxImgSz.Height() = aImgSz.Height();
1513 if ( aImgSz.Height() > pData->aSz.Height() )
1514 pData->aSz.Height() = aImgSz.Height();
1517 // Check Buttons:
1518 if (!IsMenuBar() && pData->HasCheck())
1520 nCheckWidth = aMaxSize.Width();
1521 // checks / images take the same place
1522 if( ! ( ( pData->eType == MenuItemType::IMAGE ) || ( pData->eType == MenuItemType::STRINGIMAGE ) ) )
1523 nWidth += nCheckWidth + nExtra * 2;
1526 // Text:
1527 if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
1529 long nTextWidth = pWin->GetCtrlTextWidth( pData->aText );
1530 long nTextHeight = pWin->GetTextHeight();
1532 if (IsMenuBar())
1534 if ( nTextHeight > pData->aSz.Height() )
1535 pData->aSz.Height() = nTextHeight;
1537 pData->aSz.Width() = nTextWidth + 4*nExtra;
1538 aSz.Width() += pData->aSz.Width();
1540 else
1541 pData->aSz.Height() = std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight );
1543 nWidth += nTextWidth;
1546 // Accel
1547 if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1549 OUString aName = pData->aAccelKey.GetName();
1550 long nAccWidth = pWin->GetTextWidth( aName );
1551 nAccWidth += nExtra;
1552 nWidth += nAccWidth;
1555 // SubMenu?
1556 if (!IsMenuBar() && pData->pSubMenu)
1558 if ( nFontHeight > nWidth )
1559 nWidth += nFontHeight;
1561 pData->aSz.Height() = std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight );
1564 pData->aSz.Height() += EXTRAITEMHEIGHT; // little bit more distance
1566 if (!IsMenuBar())
1567 aSz.Height() += (long)pData->aSz.Height();
1569 if ( nWidth > nMaxWidth )
1570 nMaxWidth = nWidth;
1575 // Additional space for title
1576 nTitleHeight = 0;
1577 if (!IsMenuBar() && aTitleText.getLength() > 0) {
1578 // Set expected font
1579 pWin->Push(PushFlags::FONT);
1580 vcl::Font aFont = pWin->GetFont();
1581 aFont.SetWeight(WEIGHT_BOLD);
1582 pWin->SetFont(aFont);
1584 // Compute text bounding box
1585 Rectangle aTextBoundRect;
1586 pWin->GetTextBoundRect(aTextBoundRect, aTitleText);
1588 // Vertically, one height of char + extra space for decoration
1589 nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ;
1590 aSz.Height() += nTitleHeight;
1592 long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
1593 pWin->Pop();
1594 if ( nWidth > nMaxWidth )
1595 nMaxWidth = nWidth;
1598 if (!IsMenuBar())
1600 // popup menus should not be wider than half the screen
1601 // except on rather small screens
1602 // TODO: move GetScreenNumber from SystemWindow to Window ?
1603 // currently we rely on internal privileges
1604 unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.nDisplayScreenNumber;
1605 Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
1606 long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
1607 if( nMaxWidth > nScreenWidth/2 )
1608 nMaxWidth = nScreenWidth/2;
1610 sal_uInt16 gfxExtra = (sal_uInt16) std::max( nExtra, 7L ); // #107710# increase space between checkmarks/images/text
1611 nImgOrChkPos = (sal_uInt16)nExtra;
1612 long nImgOrChkWidth = 0;
1613 if( aMaxSize.Height() > 0 ) // NWF case
1614 nImgOrChkWidth = aMaxSize.Height() + nExtra;
1615 else // non NWF case
1616 nImgOrChkWidth = nFontHeight/2 + gfxExtra;
1617 nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgSz.Width() + gfxExtra );
1618 nTextPos = (sal_uInt16)(nImgOrChkPos + nImgOrChkWidth);
1619 nTextPos = nTextPos + gfxExtra;
1621 aSz.Width() = nTextPos + nMaxWidth + nExtra;
1622 aSz.Width() += 4*nExtra; // a _little_ more ...
1624 aSz.Width() += 2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1625 aSz.Height() += 2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY;
1627 else
1629 nTextPos = (sal_uInt16)(2*nExtra);
1630 aSz.Height() = nFontHeight+6;
1632 // get menubar height from native methods if supported
1633 if( pWindow->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) )
1635 ImplControlValue aVal;
1636 Rectangle aNativeBounds;
1637 Rectangle aNativeContent;
1638 Point tmp( 0, 0 );
1639 Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
1640 if( pWindow->GetNativeControlRegion( ControlType::Menubar,
1641 ControlPart::Entire,
1642 aCtrlRegion,
1643 ControlState::ENABLED,
1644 aVal,
1645 OUString(),
1646 aNativeBounds,
1647 aNativeContent )
1650 int nNativeHeight = aNativeBounds.GetHeight();
1651 if( nNativeHeight > aSz.Height() )
1652 aSz.Height() = nNativeHeight;
1656 // account for the size of the close button, which actually is a toolbox
1657 // due to NWF this is variable
1658 long nCloseButtonHeight = static_cast<MenuBarWindow*>(pWindow.get())->MinCloseButtonSize().Height();
1659 if (aSz.Height() < nCloseButtonHeight)
1660 aSz.Height() = nCloseButtonHeight;
1663 if ( pLogo )
1664 aSz.Width() += pLogo->aBitmap.GetSizePixel().Width();
1666 return aSz;
1669 static void ImplPaintCheckBackground(vcl::RenderContext& rRenderContext, vcl::Window& rWindow, const Rectangle& i_rRect, bool i_bHighlight)
1671 bool bNativeOk = false;
1672 if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
1674 ImplControlValue aControlValue;
1675 Rectangle aCtrlRegion( i_rRect );
1676 ControlState nState = ControlState::PRESSED | ControlState::ENABLED;
1678 aControlValue.setTristateVal(ButtonValue::On);
1680 bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
1681 aCtrlRegion, nState, aControlValue,
1682 OUString());
1685 if (!bNativeOk)
1687 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1688 Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
1689 RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor);
1693 static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext& rRenderContext, long i_nMaxWidth )
1695 sal_Int32 nPos = -1;
1696 OUString aNonMnem(OutputDevice::GetNonMnemonicString(i_rLong, nPos));
1697 aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
1698 // re-insert mnemonic
1699 if (nPos != -1)
1701 if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
1703 OUStringBuffer aBuf( i_rLong.getLength() );
1704 aBuf.append( aNonMnem.copy( 0, nPos) );
1705 aBuf.append( '~' );
1706 aBuf.append( aNonMnem.copy(nPos) );
1707 aNonMnem = aBuf.makeStringAndClear();
1710 return aNonMnem;
1713 void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const Rectangle& rRect ) const
1715 // Save previous graphical settings, set new one
1716 rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
1717 Wallpaper aOldBackground = rRenderContext.GetBackground();
1719 Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
1720 rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
1721 rRenderContext.SetFillColor(aBackgroundColor);
1722 vcl::Font aFont = rRenderContext.GetFont();
1723 aFont.SetWeight(WEIGHT_BOLD);
1724 rRenderContext.SetFont(aFont);
1726 // Draw background rectangle
1727 Rectangle aBgRect(rRect);
1728 int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1729 aBgRect.setX(aBgRect.getX() + SPACE_AROUND_TITLE);
1730 aBgRect.setWidth(aBgRect.getWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
1731 aBgRect.setY(aBgRect.getY() + SPACE_AROUND_TITLE);
1732 aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE);
1733 rRenderContext.DrawRect(aBgRect);
1735 // Draw the text centered
1736 Point aTextTopLeft(aBgRect.TopLeft());
1737 Rectangle aTextBoundRect;
1738 rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText );
1739 aTextTopLeft.X() += (aBgRect.getWidth() - aTextBoundRect.GetSize().Width()) / 2;
1740 aTextTopLeft.Y() += (aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
1741 - aTextBoundRect.TopLeft().Y();
1742 rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength());
1744 // Restore
1745 rRenderContext.Pop();
1746 rRenderContext.SetBackground(aOldBackground);
1749 void Menu::ImplPaint(vcl::RenderContext& rRenderContext,
1750 sal_uInt16 nBorder, long nStartY, MenuItemData* pThisItemOnly,
1751 bool bHighlighted, bool bLayout, bool bRollover) const
1753 // for symbols: nFontHeight x nFontHeight
1754 long nFontHeight = rRenderContext.GetTextHeight();
1755 long nExtra = nFontHeight / 4;
1757 long nCheckHeight = 0, nRadioHeight = 0;
1758 ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
1760 DecorationView aDecoView(&rRenderContext);
1761 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1763 Point aTopLeft, aTmpPos;
1765 if (pLogo)
1766 aTopLeft.X() = pLogo->aBitmap.GetSizePixel().Width();
1768 int nOuterSpaceX = 0;
1769 if (!IsMenuBar())
1771 nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1772 aTopLeft.X() += nOuterSpaceX;
1773 aTopLeft.Y() += ImplGetSVData()->maNWFData.mnMenuFormatBorderY;
1776 // for the computations, use size of the underlying window, not of RenderContext
1777 Size aOutSz = pWindow->GetOutputSizePixel();
1779 size_t nCount = pItemList->size();
1780 if (bLayout)
1781 mpLayoutData->m_aVisibleItemBoundRects.clear();
1783 // Paint title
1784 if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0)
1785 ImplPaintMenuTitle(rRenderContext, Rectangle(aTopLeft, aOutSz));
1787 for (size_t n = 0; n < nCount; n++)
1789 MenuItemData* pData = pItemList->GetDataFromPos( n );
1790 if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
1792 if (pThisItemOnly)
1794 if (IsMenuBar())
1796 if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
1798 if (bRollover)
1799 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1800 else if (bHighlighted)
1801 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1803 else
1805 if (bHighlighted)
1806 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1807 else if (bRollover)
1808 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1810 if (!bRollover && !bHighlighted)
1811 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
1813 else if (bHighlighted)
1814 rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1817 Point aPos(aTopLeft);
1818 aPos.Y() += nBorder;
1819 aPos.Y() += nStartY;
1821 if (aPos.Y() >= 0)
1823 long nTextOffsetY = ((pData->aSz.Height() - nFontHeight) / 2);
1824 if (IsMenuBar())
1825 nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
1826 DrawTextFlags nTextStyle = DrawTextFlags::NONE;
1827 DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
1828 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1830 // submenus without items are not disabled when no items are
1831 // contained. The application itself should check for this!
1832 // Otherwise it could happen entries are disabled due to
1833 // asynchronous loading
1834 if (!pData->bEnabled || !pWindow->IsEnabled())
1836 nTextStyle |= DrawTextFlags::Disable;
1837 nSymbolStyle |= DrawSymbolFlags::Disable;
1838 nImageStyle |= DrawImageFlags::Disable;
1841 // Separator
1842 if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
1844 bool bNativeOk = false;
1845 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
1847 ControlState nState = ControlState::NONE;
1848 if (pData->bEnabled && pWindow->IsEnabled())
1849 nState |= ControlState::ENABLED;
1850 if (bHighlighted)
1851 nState |= ControlState::SELECTED;
1852 int nSepPad = ImplGetSVData()->maNWFData.mnMenuSeparatorBorderX;
1853 Point aMpos (aPos);
1854 aMpos.X() += nSepPad;
1855 Size aSz(pData->aSz);
1856 aSz.Width() = aOutSz.Width() - 2*nOuterSpaceX - 2 * nSepPad;
1857 Rectangle aItemRect(aMpos, aSz);
1858 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1859 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
1860 aItemRect, nState, aVal, OUString());
1862 if (!bNativeOk)
1864 aTmpPos.Y() = aPos.Y() + ((pData->aSz.Height() - 2) / 2);
1865 aTmpPos.X() = aPos.X() + 2 + nOuterSpaceX;
1866 rRenderContext.SetLineColor(rSettings.GetShadowColor());
1867 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1868 aTmpPos.Y()++;
1869 rRenderContext.SetLineColor(rSettings.GetLightColor());
1870 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1871 rRenderContext.SetLineColor();
1875 Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()),
1876 Size(pData->aSz.Height(), pData->aSz.Height()));
1877 aOuterCheckRect.Left() += 1;
1878 aOuterCheckRect.Right() -= 1;
1879 aOuterCheckRect.Top() += 1;
1880 aOuterCheckRect.Bottom() -= 1;
1882 // CheckMark
1883 if (!bLayout && !IsMenuBar() && pData->HasCheck())
1885 // draw selection transparent marker if checked
1886 // onto that either a checkmark or the item image
1887 // will be painted
1888 // however do not do this if native checks will be painted since
1889 // the selection color too often does not fit the theme's check and/or radio
1891 if( !((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1893 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
1894 (pData->nBits & MenuItemBits::RADIOCHECK)
1895 ? ControlPart::MenuItemCheckMark
1896 : ControlPart::MenuItemRadioMark))
1898 ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
1899 ? ControlPart::MenuItemRadioMark
1900 : ControlPart::MenuItemCheckMark);
1902 ControlState nState = ControlState::NONE;
1904 if (pData->bChecked)
1905 nState |= ControlState::PRESSED;
1907 if (pData->bEnabled && pWindow->IsEnabled())
1908 nState |= ControlState::ENABLED;
1910 if (bHighlighted)
1911 nState |= ControlState::SELECTED;
1913 long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
1914 aTmpPos.X() = aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2;
1915 aTmpPos.Y() = aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2;
1917 Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
1918 Size aSz(pData->aSz);
1919 aSz.Width() = aOutSz.Width() - 2 * nOuterSpaceX;
1920 Rectangle aItemRect(aPos, aSz);
1921 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1922 rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
1923 nState, aVal, OUString());
1925 else if (pData->bChecked) // by default do nothing for unchecked items
1927 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1929 SymbolType eSymbol;
1930 Size aSymbolSize;
1931 if (pData->nBits & MenuItemBits::RADIOCHECK)
1933 eSymbol = SymbolType::RADIOCHECKMARK;
1934 aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
1936 else
1938 eSymbol = SymbolType::CHECKMARK;
1939 aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
1941 aTmpPos.X() = aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2;
1942 aTmpPos.Y() = aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2;
1943 Rectangle aRect(aTmpPos, aSymbolSize);
1944 aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
1949 // Image:
1950 if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1952 // Don't render an image for a check thing
1953 if (pData->bChecked)
1954 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1956 Image aImage = pData->aImage;
1958 aTmpPos = aOuterCheckRect.TopLeft();
1959 aTmpPos.X() += (aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2;
1960 aTmpPos.Y() += (aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2;
1961 rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
1964 // Text:
1965 if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
1967 aTmpPos.X() = aPos.X() + nTextPos;
1968 aTmpPos.Y() = aPos.Y();
1969 aTmpPos.Y() += nTextOffsetY;
1970 DrawTextFlags nStyle = nTextStyle | DrawTextFlags::Mnemonic;
1972 const Menu *pMenu = this;
1973 while (!pMenu->IsMenuBar() && pMenu->pStartedFrom)
1974 pMenu = pMenu->pStartedFrom;
1975 if (pMenu->IsMenuBar() && (static_cast<MenuBarWindow*>(pMenu->pWindow.get()))->GetMBWHideAccel())
1976 nStyle |= DrawTextFlags::HideMnemonic;
1978 if (pData->bIsTemporary)
1979 nStyle |= DrawTextFlags::Disable;
1980 MetricVector* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr;
1981 OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr;
1982 if (bLayout)
1984 mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength());
1985 mpLayoutData->m_aLineItemIds.push_back(pData->nId);
1986 mpLayoutData->m_aLineItemPositions.push_back(n);
1988 // #i47946# with NWF painted menus the background is transparent
1989 // since DrawCtrlText can depend on the background (e.g. for
1990 // DrawTextFlags::Disable), temporarily set a background which
1991 // hopefully matches the NWF background since it is read
1992 // from the system style settings
1993 bool bSetTmpBackground = !rRenderContext.IsBackground()
1994 && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire);
1995 if (bSetTmpBackground)
1997 Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor()
1998 : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor();
1999 rRenderContext.SetBackground(Wallpaper(aBg));
2001 // how much space is there for the text ?
2002 long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX;
2003 if (!IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
2005 OUString aAccText = pData->aAccelKey.GetName();
2006 nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra;
2008 if (!IsMenuBar() && pData->pSubMenu)
2010 nMaxItemTextWidth -= nFontHeight - nExtra;
2012 OUString aItemText(getShortenedString(pData->aText, rRenderContext, nMaxItemTextWidth));
2013 rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(), nStyle, pVector, pDisplayText);
2014 if (bSetTmpBackground)
2015 rRenderContext.SetBackground();
2018 // Accel
2019 if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
2021 OUString aAccText = pData->aAccelKey.GetName();
2022 aTmpPos.X() = aOutSz.Width() - rRenderContext.GetTextWidth(aAccText);
2023 aTmpPos.X() -= 4 * nExtra;
2025 aTmpPos.X() -= nOuterSpaceX;
2026 aTmpPos.Y() = aPos.Y();
2027 aTmpPos.Y() += nTextOffsetY;
2028 rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
2031 // SubMenu?
2032 if (!bLayout && !IsMenuBar() && pData->pSubMenu)
2034 bool bNativeOk = false;
2035 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
2037 ControlState nState = ControlState::NONE;
2038 Size aTmpSz(0, 0);
2039 long aSpacing = 0;
2041 if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
2043 aTmpSz = Size(nFontHeight, nFontHeight);
2044 aSpacing = nOuterSpaceX;
2047 if (pData->bEnabled && pWindow->IsEnabled())
2048 nState |= ControlState::ENABLED;
2049 if (bHighlighted)
2050 nState |= ControlState::SELECTED;
2052 aTmpPos.X() = aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX;
2053 aTmpPos.Y() = aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2;
2054 aTmpPos.Y() += nExtra / 2;
2056 Rectangle aItemRect(aTmpPos, aTmpSz);
2057 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
2058 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
2059 aItemRect, nState, aVal, OUString());
2061 if (!bNativeOk)
2063 aTmpPos.X() = aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX;
2064 aTmpPos.Y() = aPos.Y();
2065 aTmpPos.Y() += nExtra/2;
2066 aTmpPos.Y() += (pData->aSz.Height() / 2) - (nFontHeight / 4);
2067 if (pData->nBits & MenuItemBits::POPUPSELECT)
2069 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2070 Point aTmpPos2(aPos);
2071 aTmpPos2.X() = aOutSz.Width() - nFontHeight - nFontHeight/4;
2072 aDecoView.DrawFrame(Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
2073 pData->aSz.Height())),
2074 DrawFrameStyle::Group);
2076 aDecoView.DrawSymbol(Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
2077 SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
2081 if (pThisItemOnly && bHighlighted)
2083 // This restores the normal menu or menu bar text
2084 // color for when it is no longer highlighted.
2085 if (IsMenuBar())
2086 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
2087 else
2088 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2091 if( bLayout )
2093 if (!IsMenuBar())
2094 mpLayoutData->m_aVisibleItemBoundRects[ n ] = Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
2095 else
2096 mpLayoutData->m_aVisibleItemBoundRects[ n ] = Rectangle(aTopLeft, pData->aSz);
2100 if (!IsMenuBar())
2101 aTopLeft.Y() += pData->aSz.Height();
2102 else
2103 aTopLeft.X() += pData->aSz.Width();
2106 if (!bLayout && !pThisItemOnly && pLogo)
2108 Size aLogoSz = pLogo->aBitmap.GetSizePixel();
2110 Rectangle aRect(Point(), Point(aLogoSz.Width() - 1, aOutSz.Height()));
2111 if (rRenderContext.GetColorCount() >= 256)
2113 Gradient aGrad(GradientStyle::Linear, pLogo->aStartColor, pLogo->aEndColor);
2114 aGrad.SetAngle(1800);
2115 aGrad.SetBorder(15);
2116 rRenderContext.DrawGradient(aRect, aGrad);
2118 else
2120 rRenderContext.SetFillColor(pLogo->aStartColor);
2121 rRenderContext.DrawRect(aRect);
2124 Point aLogoPos(0, aOutSz.Height() - aLogoSz.Height());
2125 pLogo->aBitmap.Draw(&rRenderContext, aLogoPos);
2129 Menu* Menu::ImplGetStartMenu()
2131 Menu* pStart = this;
2132 while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
2133 pStart = pStart->pStartedFrom;
2134 return pStart;
2137 void Menu::ImplCallHighlight(sal_uInt16 nItem)
2139 ImplMenuDelData aDelData( this );
2141 nSelectedId = 0;
2142 MenuItemData* pData = pItemList->GetDataFromPos(nItem);
2143 if ( pData )
2144 nSelectedId = pData->nId;
2145 ImplCallEventListeners( VCLEVENT_MENU_HIGHLIGHT, GetItemPos( GetCurItemId() ) );
2147 if( !aDelData.isDeleted() )
2148 nSelectedId = 0;
2151 IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
2153 nEventId = nullptr;
2154 Select();
2157 Menu* Menu::ImplFindSelectMenu()
2159 Menu* pSelMenu = nEventId ? this : nullptr;
2161 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2163 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2165 if ( pData->pSubMenu )
2166 pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
2169 return pSelMenu;
2172 Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
2174 Menu* pSelMenu = nullptr;
2176 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2178 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2180 if( pData->nId == nItemId )
2181 pSelMenu = this;
2182 else if ( pData->pSubMenu )
2183 pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
2186 return pSelMenu;
2189 void Menu::RemoveDisabledEntries( bool bCheckPopups, bool bRemoveEmptyPopups )
2191 for ( sal_uInt16 n = 0; n < GetItemCount(); n++ )
2193 bool bRemove = false;
2194 MenuItemData* pItem = pItemList->GetDataFromPos( n );
2195 if ( pItem->eType == MenuItemType::SEPARATOR )
2197 if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
2198 bRemove = true;
2200 else
2201 bRemove = !pItem->bEnabled;
2203 if ( bCheckPopups && pItem->pSubMenu )
2205 pItem->pSubMenu->RemoveDisabledEntries();
2206 if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
2207 bRemove = true;
2210 if ( bRemove )
2211 RemoveItem( n-- );
2214 if ( GetItemCount() )
2216 sal_uInt16 nLast = GetItemCount() - 1;
2217 MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
2218 if ( pItem->eType == MenuItemType::SEPARATOR )
2219 RemoveItem( nLast );
2221 delete mpLayoutData;
2222 mpLayoutData = nullptr;
2225 bool Menu::HasValidEntries()
2227 bool bValidEntries = false;
2228 sal_uInt16 nCount = GetItemCount();
2229 for ( sal_uInt16 n = 0; !bValidEntries && ( n < nCount ); n++ )
2231 MenuItemData* pItem = pItemList->GetDataFromPos( n );
2232 if ( pItem->bEnabled && ( pItem->eType != MenuItemType::SEPARATOR ) )
2234 if ( pItem->pSubMenu )
2235 bValidEntries = pItem->pSubMenu->HasValidEntries();
2236 else
2237 bValidEntries = true;
2240 return bValidEntries;
2243 void Menu::UpdateNativeMenu()
2245 if ( ImplGetSalMenu() )
2246 ImplGetSalMenu()->Update();
2249 void Menu::MenuBarKeyInput(const KeyEvent&)
2253 void Menu::ImplKillLayoutData() const
2255 delete mpLayoutData;
2256 mpLayoutData = nullptr;
2259 void Menu::ImplFillLayoutData() const
2261 if (pWindow && pWindow->IsReallyVisible())
2263 mpLayoutData = new MenuLayoutData();
2264 if (IsMenuBar())
2266 ImplPaint(*pWindow, 0, 0, nullptr, false, true); // FIXME
2268 else
2270 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
2271 ImplPaint(*pWindow, pFloat->nScrollerHeight, pFloat->ImplGetStartY(), nullptr, false, true); //FIXME
2276 Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, long nIndex ) const
2278 long nItemIndex = -1;
2279 if( ! mpLayoutData )
2280 ImplFillLayoutData();
2281 if( mpLayoutData )
2283 for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
2285 if( mpLayoutData->m_aLineItemIds[i] == nItemID )
2287 nItemIndex = mpLayoutData->m_aLineIndices[i];
2288 break;
2292 return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : Rectangle();
2295 long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
2297 long nIndex = -1;
2298 rItemID = 0;
2299 if( ! mpLayoutData )
2300 ImplFillLayoutData();
2301 if( mpLayoutData )
2303 nIndex = mpLayoutData->GetIndexForPoint( rPoint );
2304 for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
2306 if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
2307 (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
2309 // make index relative to item
2310 nIndex -= mpLayoutData->m_aLineIndices[i];
2311 rItemID = mpLayoutData->m_aLineItemIds[i];
2312 break;
2316 return nIndex;
2319 Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
2321 Rectangle aRet;
2323 if (!mpLayoutData )
2324 ImplFillLayoutData();
2325 if (mpLayoutData)
2327 std::map< sal_uInt16, Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
2328 if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
2329 aRet = it->second;
2331 return aRet;
2334 OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
2336 MenuItemData* pData = pItemList->GetData( nItemId );
2338 if ( pData )
2339 return pData->aAccessibleName;
2341 return OUString();
2344 void Menu::ImplSetSalMenu( SalMenu *pSalMenu )
2346 if( mpSalMenu )
2347 ImplGetSVData()->mpDefInst->DestroyMenu( mpSalMenu );
2348 mpSalMenu = pSalMenu;
2351 bool Menu::GetSystemMenuData( SystemMenuData* pData ) const
2353 Menu* pMenu = const_cast<Menu*>(this);
2354 if( pData && pMenu->ImplGetSalMenu() )
2356 pMenu->ImplGetSalMenu()->GetSystemMenuData( pData );
2357 return true;
2359 else
2360 return false;
2363 bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
2365 bool bRet = false;
2367 if( pWindow )
2369 if (IsMenuBar())
2370 bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow.get())->GetHighlightedItem() );
2371 else
2372 bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow.get())->GetHighlightedItem() );
2375 return bRet;
2378 void Menu::HighlightItem( sal_uInt16 nItemPos )
2380 if ( pWindow )
2382 if (IsMenuBar())
2384 MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow.get() );
2385 pMenuWin->SetAutoPopup( false );
2386 pMenuWin->ChangeHighlightItem( nItemPos, false );
2388 else
2390 static_cast< MenuFloatingWindow* >( pWindow.get() )->ChangeHighlightItem( nItemPos, false );
2395 MenuBarWindow* MenuBar::getMenuBarWindow()
2397 // so far just a dynamic_cast, hopefully to be turned into something saner
2398 // at some stage
2399 MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(pWindow.get());
2400 //either there is no window (fdo#87663) or it is a MenuBarWindow
2401 assert(!pWindow || pWin);
2402 return pWin;
2405 MenuBar::MenuBar()
2406 : Menu(),
2407 mbCloseBtnVisible(false),
2408 mbFloatBtnVisible(false),
2409 mbHideBtnVisible(false),
2410 mbDisplayable(true)
2412 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2415 MenuBar::MenuBar( const MenuBar& rMenu )
2416 : Menu(),
2417 mbCloseBtnVisible(false),
2418 mbFloatBtnVisible(false),
2419 mbHideBtnVisible(false),
2420 mbDisplayable(true)
2422 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2423 *this = rMenu;
2426 MenuBar::~MenuBar()
2428 disposeOnce();
2431 void MenuBar::dispose()
2433 ImplDestroy( this, true );
2434 Menu::dispose();
2437 void MenuBar::ClosePopup(Menu *pMenu)
2439 MenuBarWindow* pMenuWin = getMenuBarWindow();
2440 if (!pMenuWin)
2441 return;
2442 pMenuWin->PopupClosed(pMenu);
2445 void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
2447 pWindow->KeyInput(rEvent);
2450 void MenuBar::ShowCloseButton(bool bShow)
2452 ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
2455 void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
2457 if ((bClose != mbCloseBtnVisible) ||
2458 (bFloat != mbFloatBtnVisible) ||
2459 (bHide != mbHideBtnVisible))
2461 mbCloseBtnVisible = bClose;
2462 mbFloatBtnVisible = bFloat;
2463 mbHideBtnVisible = bHide;
2464 MenuBarWindow* pMenuWin = getMenuBarWindow();
2465 if (pMenuWin)
2466 pMenuWin->ShowButtons(bClose, bFloat, bHide);
2470 void MenuBar::LayoutChanged()
2472 MenuBarWindow* pMenuWin = getMenuBarWindow();
2473 if (pMenuWin)
2474 pMenuWin->LayoutChanged();
2477 void MenuBar::SetDisplayable( bool bDisplayable )
2479 if( bDisplayable != mbDisplayable )
2481 if ( ImplGetSalMenu() )
2482 ImplGetSalMenu()->ShowMenuBar( bDisplayable );
2484 mbDisplayable = bDisplayable;
2485 LayoutChanged();
2489 VclPtr<vcl::Window> MenuBar::ImplCreate(vcl::Window* pParent, vcl::Window* pWindow, MenuBar* pMenu)
2491 VclPtr<MenuBarWindow> pMenuBarWindow = dynamic_cast<MenuBarWindow*>(pWindow);
2492 if (!pMenuBarWindow)
2494 pWindow = pMenuBarWindow = VclPtr<MenuBarWindow>::Create( pParent );
2497 pMenu->pStartedFrom = nullptr;
2498 pMenu->pWindow = pWindow;
2499 pMenuBarWindow->SetMenu(pMenu);
2500 long nHeight = pWindow ? pMenu->ImplCalcSize(pWindow).Height() : 0;
2502 // depending on the native implementation or the displayable flag
2503 // the menubar windows is suppressed (ie, height=0)
2504 if (!pMenu->IsDisplayable() || (pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar()))
2506 nHeight = 0;
2509 pMenuBarWindow->SetHeight(nHeight);
2510 return pWindow;
2513 void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete )
2515 vcl::Window *pWindow = pMenu->ImplGetWindow();
2516 if (pWindow && bDelete)
2518 MenuBarWindow* pMenuWin = pMenu->getMenuBarWindow();
2519 if (pMenuWin)
2520 pMenuWin->KillActivePopup();
2521 pWindow->disposeOnce();
2523 pMenu->pWindow = nullptr;
2526 bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
2528 // No keyboard processing when our menubar is invisible
2529 if (!IsDisplayable())
2530 return false;
2532 // No keyboard processing when system handles the menu.
2533 SalMenu *pNativeMenu = ImplGetSalMenu();
2534 if (pNativeMenu && pNativeMenu->VisibleMenuBar())
2536 // Except when the event is the F6 cycle pane event and we can put our
2537 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2538 // where it's not part of the application window)
2539 if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
2540 return false;
2541 if (!pNativeMenu->CanGetFocus())
2542 return false;
2545 bool bDone = false;
2546 // check for enabled, if this method is called from another window...
2547 vcl::Window* pWin = ImplGetWindow();
2548 if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode())
2550 MenuBarWindow* pMenuWin = getMenuBarWindow();
2551 bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
2553 return bDone;
2556 bool MenuBar::ImplHandleCmdEvent( const CommandEvent& rCEvent )
2558 // No keyboard processing when system handles the menu or our menubar is invisible
2559 if( !IsDisplayable() ||
2560 ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2561 return false;
2563 // check for enabled, if this method is called from another window...
2564 MenuBarWindow* pWin = static_cast<MenuBarWindow*>(ImplGetWindow());
2565 if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && ! pWin->IsInModalMode() )
2567 if (rCEvent.GetCommand() == CommandEventId::ModKeyChange && ImplGetSVData()->maNWFData.mbAutoAccel)
2569 const CommandModKeyData* pCData = rCEvent.GetModKeyData ();
2570 if (pWin->nHighlightedItem == ITEMPOS_INVALID)
2572 if (pCData && pCData->IsMod2() && pCData->IsDown())
2573 pWin->SetMBWHideAccel(false);
2574 else
2575 pWin->SetMBWHideAccel(true);
2576 pWin->Invalidate(InvalidateFlags::Update);
2578 return true;
2581 return false;
2584 void MenuBar::SelectItem(sal_uInt16 nId)
2586 if (pWindow)
2588 pWindow->GrabFocus();
2589 nId = GetItemPos( nId );
2591 MenuBarWindow* pMenuWin = getMenuBarWindow();
2592 if (pMenuWin)
2594 // #99705# popup the selected menu
2595 pMenuWin->SetAutoPopup( true );
2596 if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
2598 pMenuWin->KillActivePopup();
2599 pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
2601 if (nId != ITEMPOS_INVALID)
2602 pMenuWin->ChangeHighlightItem( nId, false );
2607 // handler for native menu selection and command events
2608 bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const
2610 if( pMenu )
2612 ImplMenuDelData aDelData( this );
2614 pMenu->pStartedFrom = const_cast<Menu*>(this);
2615 pMenu->bInCallback = true;
2616 pMenu->Activate();
2618 if( !aDelData.isDeleted() )
2619 pMenu->bInCallback = false;
2621 return true;
2624 bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const
2626 if( pMenu )
2628 ImplMenuDelData aDelData( this );
2630 pMenu->pStartedFrom = const_cast<Menu*>(this);
2631 pMenu->bInCallback = true;
2632 pMenu->Deactivate();
2633 if( !aDelData.isDeleted() )
2634 pMenu->bInCallback = false;
2636 return true;
2639 bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const
2641 if( !pMenu )
2642 pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nHighlightEventId);
2643 if( pMenu )
2645 ImplMenuDelData aDelData( pMenu );
2647 if( mnHighlightedItemPos != ITEMPOS_INVALID )
2648 pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, mnHighlightedItemPos );
2650 if( !aDelData.isDeleted() )
2652 pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
2653 pMenu->nSelectedId = nHighlightEventId;
2654 pMenu->pStartedFrom = const_cast<MenuBar*>(this);
2655 pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
2657 return true;
2659 else
2660 return false;
2663 bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
2665 if( !pMenu )
2666 pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId);
2667 if( pMenu )
2669 pMenu->nSelectedId = nCommandEventId;
2670 pMenu->pStartedFrom = const_cast<Menu*>(this);
2671 pMenu->ImplSelect();
2672 return true;
2674 else
2675 return false;
2678 sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBar::MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
2680 MenuBarWindow* pMenuWin = getMenuBarWindow();
2681 return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
2684 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBar::MenuBarButtonCallbackArg&,bool>& rLink )
2686 MenuBarWindow* pMenuWin = getMenuBarWindow();
2687 if (!pMenuWin)
2688 return;
2689 pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
2692 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
2694 MenuBarWindow* pMenuWin = getMenuBarWindow();
2695 if (!pMenuWin)
2696 return;
2697 pMenuWin->RemoveMenuBarButton(nId);
2700 Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
2702 MenuBarWindow* pMenuWin = getMenuBarWindow();
2703 return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : Rectangle();
2706 bool MenuBar::HandleMenuButtonEvent( Menu *, sal_uInt16 i_nButtonId )
2708 MenuBarWindow* pMenuWin = getMenuBarWindow();
2709 return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
2712 // bool PopupMenu::bAnyPopupInExecute = false;
2714 MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
2715 return static_cast<MenuFloatingWindow *>(Menu::ImplGetWindow());
2718 PopupMenu::PopupMenu()
2719 : pRefAutoSubMenu(nullptr),
2720 mpLOKNotifier(nullptr)
2722 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2725 PopupMenu::PopupMenu( const ResId& rResId )
2726 : pRefAutoSubMenu(nullptr),
2727 mpLOKNotifier(nullptr)
2729 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2731 ResMgr* pMgr = rResId.GetResMgr();
2732 if( ! pMgr )
2733 return;
2735 rResId.SetRT( RSC_MENU );
2736 GetRes( rResId );
2738 RscMenu nObjMask = (RscMenu)ReadLongRes();
2740 if( nObjMask & RscMenu::Items )
2742 sal_uLong nObjFollows = ReadLongRes();
2743 // insert menu items
2744 for( sal_uLong i = 0; i < nObjFollows; i++ )
2746 InsertItem( ResId( static_cast<RSHEADER_TYPE*>(GetClassRes()), *pMgr ) );
2747 IncrementRes( GetObjSizeRes( static_cast<RSHEADER_TYPE*>(GetClassRes()) ) );
2751 if( nObjMask & RscMenu::Text )
2753 aTitleText = ReadStringRes();
2755 if( nObjMask & RscMenu::DefaultItemId )
2756 SetDefaultItem( sal::static_int_cast<sal_uInt16>(ReadLongRes()) );
2759 PopupMenu::PopupMenu( const PopupMenu& rMenu )
2760 : Menu(),
2761 pRefAutoSubMenu(nullptr),
2762 mpLOKNotifier(nullptr)
2764 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2765 *this = rMenu;
2768 PopupMenu::~PopupMenu()
2770 disposeOnce();
2773 void PopupMenu::dispose()
2775 if( pRefAutoSubMenu && *pRefAutoSubMenu == this )
2776 *pRefAutoSubMenu = nullptr; // #111060# avoid second delete in ~MenuItemData
2777 Menu::dispose();
2780 void PopupMenu::ClosePopup(Menu* pMenu)
2782 MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow());
2783 PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu);
2784 if (p && pMenu)
2785 p->KillActivePopup(pPopup);
2788 bool PopupMenu::IsInExecute()
2790 return GetActivePopupMenu() != nullptr;
2793 PopupMenu* PopupMenu::GetActivePopupMenu()
2795 ImplSVData* pSVData = ImplGetSVData();
2796 return pSVData->maAppData.mpActivePopupMenu;
2799 void PopupMenu::EndExecute()
2801 if ( ImplGetWindow() )
2802 ImplGetFloatingWindow()->EndExecute( 0 );
2805 void PopupMenu::SelectItem(sal_uInt16 nId)
2807 if ( ImplGetWindow() )
2809 if( nId != ITEMPOS_INVALID )
2811 size_t nPos = 0;
2812 MenuItemData* pData = GetItemList()->GetData( nId, nPos );
2813 if (pData && pData->pSubMenu)
2814 ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
2815 else
2816 ImplGetFloatingWindow()->EndExecute( nId );
2818 else
2820 MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
2821 pFloat->GrabFocus();
2823 for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
2825 MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
2826 if( pData->pSubMenu )
2828 pFloat->KillActivePopup();
2831 pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
2836 void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
2838 nSelectedId = nId;
2841 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos )
2843 return Execute( pExecWindow, Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown );
2846 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Rectangle& rRect, PopupMenuFlags nFlags )
2848 ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
2850 FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
2851 if ( nFlags & PopupMenuFlags::ExecuteDown )
2852 nPopupModeFlags = FloatWinPopupFlags::Down;
2853 else if ( nFlags & PopupMenuFlags::ExecuteUp )
2854 nPopupModeFlags = FloatWinPopupFlags::Up;
2855 else if ( nFlags & PopupMenuFlags::ExecuteLeft )
2856 nPopupModeFlags = FloatWinPopupFlags::Left;
2857 else if ( nFlags & PopupMenuFlags::ExecuteRight )
2858 nPopupModeFlags = FloatWinPopupFlags::Right;
2859 else
2860 nPopupModeFlags = FloatWinPopupFlags::Down;
2862 if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up
2863 nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2865 if (nFlags & PopupMenuFlags::NoHorzPlacement)
2866 nPopupModeFlags |= FloatWinPopupFlags::NoHorzPlacement;
2868 return ImplExecute( pExecWindow, rRect, nPopupModeFlags, nullptr, false );
2871 void PopupMenu::ImplFlushPendingSelect()
2873 // is there still Select?
2874 Menu* pSelect = ImplFindSelectMenu();
2875 if (pSelect)
2877 // Select should be called prior to leaving execute in a popup menu!
2878 Application::RemoveUserEvent( pSelect->nEventId );
2879 pSelect->nEventId = nullptr;
2880 pSelect->Select();
2884 sal_uInt16 PopupMenu::ImplExecute( const VclPtr<vcl::Window>& pW, const Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst )
2886 if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) )
2887 return 0;
2889 // set the flag to hide or show accelerators in the menu depending on whether the menu was launched by mouse or keyboard shortcut
2890 if( pSFrom && pSFrom->IsMenuBar())
2891 ((static_cast<MenuBarWindow*>(pSFrom->pWindow.get())))->SetMBWHideAccel(!(static_cast<MenuBarWindow*>(pSFrom->pWindow.get())->GetMBWMenuKey()));
2893 delete mpLayoutData;
2894 mpLayoutData = nullptr;
2896 ImplSVData* pSVData = ImplGetSVData();
2898 pStartedFrom = pSFrom;
2899 nSelectedId = 0;
2900 bCanceled = false;
2902 VclPtr<vcl::Window> xFocusId;
2903 bool bRealExecute = false;
2904 if ( !pStartedFrom )
2906 pSVData->maWinData.mbNoDeactivate = true;
2907 xFocusId = Window::SaveFocus();
2908 bRealExecute = true;
2910 else
2912 // assure that only one menu is open at a time
2913 if (pStartedFrom->IsMenuBar() && pSVData->maWinData.mpFirstFloat)
2914 pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
2917 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
2918 Rectangle aRect( rRect );
2919 aRect.SetPos( pW->OutputToScreenPixel( aRect.TopLeft() ) );
2921 WinBits nStyle = WB_BORDER;
2922 if (bRealExecute)
2923 nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
2924 nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose;
2926 bInCallback = true; // set it here, if Activate overridden
2927 Activate();
2928 bInCallback = false;
2930 if ( pW->IsDisposed() )
2931 return 0; // Error
2933 if ( bCanceled || bKilled )
2934 return 0;
2936 if ( !GetItemCount() )
2937 return 0;
2939 // The flag MenuFlags::HideDisabledEntries is inherited.
2940 if ( pSFrom )
2942 if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries )
2943 nMenuFlags |= MenuFlags::HideDisabledEntries;
2944 else
2945 nMenuFlags &= ~MenuFlags::HideDisabledEntries;
2947 else
2948 // #102790# context menus shall never show disabled entries
2949 nMenuFlags |= MenuFlags::HideDisabledEntries;
2951 sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
2952 if ( !nVisibleEntries )
2954 ResMgr* pResMgr = ImplGetResMgr();
2955 if( pResMgr )
2957 OUString aTmpEntryText( ResId( SV_RESID_STRING_NOSELECTIONPOSSIBLE, *pResMgr ) );
2959 MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, OString());
2960 size_t nPos = 0;
2961 pData = pItemList->GetData( pData->nId, nPos );
2962 assert(pData);
2963 if (pData)
2965 pData->bIsTemporary = true;
2967 ImplCallEventListeners(VCLEVENT_MENU_SUBMENUCHANGED, nPos);
2970 else if (!(nMenuFlags & MenuFlags::NoAutoMnemonics))
2972 CreateAutoMnemonics();
2975 VclPtrInstance<MenuFloatingWindow> pWin( this, pW, nStyle | WB_SYSTEMWINDOW );
2976 if (comphelper::LibreOfficeKit::isActive() && mpLOKNotifier)
2977 pWin->SetLOKNotifier(mpLOKNotifier);
2979 if( pSVData->maNWFData.mbFlatMenu )
2980 pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
2981 else
2982 pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
2983 pWindow = pWin;
2985 Size aSz = ImplCalcSize( pWin );
2987 Rectangle aDesktopRect(pWin->GetDesktopRectPixel());
2988 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() )
2990 vcl::Window* pDeskW = pWindow->GetWindow( GetWindowType::RealParent );
2991 if( ! pDeskW )
2992 pDeskW = pWindow;
2993 Point aDesktopTL( pDeskW->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) );
2994 aDesktopRect = Application::GetScreenPosSizePixel(
2995 Application::GetBestScreen( Rectangle( aDesktopTL, aRect.GetSize() ) ));
2998 long nMaxHeight = aDesktopRect.GetHeight();
3000 //rhbz#1021915. If a menu won't fit in the desired location the default
3001 //mode is to place it somewhere it will fit. e.g. above, left, right. For
3002 //some cases, e.g. menubars, it's desirable to limit the options to
3003 //above/below and force the menu to scroll if it won't fit
3004 if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
3006 vcl::Window* pRef = pWin;
3007 if ( pRef->GetParent() )
3008 pRef = pRef->GetParent();
3010 Rectangle devRect( pRef->OutputToAbsoluteScreenPixel( aRect.TopLeft() ),
3011 pRef->OutputToAbsoluteScreenPixel( aRect.BottomRight() ) );
3013 long nHeightAbove = devRect.Top() - aDesktopRect.Top();
3014 long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
3015 nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
3018 if (pStartedFrom && pStartedFrom->IsMenuBar())
3019 nMaxHeight -= pW->GetSizePixel().Height();
3020 sal_Int32 nLeft, nTop, nRight, nBottom;
3021 pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
3022 nMaxHeight -= nTop+nBottom;
3023 if ( aSz.Height() > nMaxHeight )
3025 pWin->EnableScrollMenu( true );
3026 sal_uInt16 nStart = ImplGetFirstVisible();
3027 sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
3028 aSz.Height() = ImplCalcHeight( nEntries );
3031 pWin->SetFocusId( xFocusId );
3032 pWin->SetOutputSizePixel( aSz );
3033 if ( GetItemCount() )
3035 SalMenu* pMenu = ImplGetSalMenu();
3036 if( pMenu && bRealExecute && pMenu->ShowNativePopupMenu( pWin, aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus ) )
3038 ImplFlushPendingSelect();
3039 pWin->StopExecute();
3040 pWin->doShutdown();
3041 pWindow->doLazyDelete();
3042 pWindow = nullptr;
3043 return nSelectedId;
3045 else
3047 pWin->StartPopupMode( aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus );
3049 if( pSFrom )
3051 sal_uInt16 aPos;
3052 if (pSFrom->IsMenuBar())
3053 aPos = static_cast<MenuBarWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
3054 else
3055 aPos = static_cast<MenuFloatingWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
3057 pWin->SetPosInParent( aPos ); // store position to be sent in SUBMENUDEACTIVATE
3058 pSFrom->ImplCallEventListeners( VCLEVENT_MENU_SUBMENUACTIVATE, aPos );
3061 if ( bPreSelectFirst )
3063 size_t nCount = pItemList->size();
3064 for ( size_t n = 0; n < nCount; n++ )
3066 MenuItemData* pData = pItemList->GetDataFromPos( n );
3067 if ( ( pData->bEnabled
3068 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
3070 && ( pData->eType != MenuItemType::SEPARATOR )
3071 && ImplIsVisible( n )
3072 && ImplIsSelectable( n )
3075 pWin->ChangeHighlightItem( n, false );
3076 break;
3080 if ( bRealExecute )
3082 pWin->Execute();
3083 if (pWin->IsDisposed())
3084 return 0;
3086 xFocusId = pWin->GetFocusId();
3087 assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
3088 pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
3090 if ( nSelectedId ) // then clean up .. ( otherwise done by TH )
3092 PopupMenu* pSub = pWin->GetActivePopup();
3093 while ( pSub )
3095 pSub->ImplGetFloatingWindow()->EndPopupMode();
3096 pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
3099 pWin->doShutdown();
3100 pWindow->doLazyDelete();
3101 pWindow = nullptr;
3103 ImplFlushPendingSelect();
3106 return bRealExecute ? nSelectedId : 0;
3109 sal_uInt16 PopupMenu::ImplCalcVisEntries( long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
3111 nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3113 long nHeight = 0;
3114 size_t nEntries = pItemList->size();
3115 sal_uInt16 nVisEntries = 0;
3117 if ( pLastVisible )
3118 *pLastVisible = 0;
3120 for ( size_t n = nStartEntry; n < nEntries; n++ )
3122 if ( ImplIsVisible( n ) )
3124 MenuItemData* pData = pItemList->GetDataFromPos( n );
3125 nHeight += pData->aSz.Height();
3126 if ( nHeight > nMaxHeight )
3127 break;
3129 if ( pLastVisible )
3130 *pLastVisible = n;
3131 nVisEntries++;
3134 return nVisEntries;
3137 long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
3139 long nHeight = 0;
3141 sal_uInt16 nFound = 0;
3142 for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ )
3144 if ( ImplIsVisible( (sal_uInt16) n ) )
3146 MenuItemData* pData = pItemList->GetDataFromPos( n );
3147 nHeight += pData->aSz.Height();
3148 nFound++;
3152 nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
3154 return nHeight;
3157 ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
3158 : mpNext( nullptr )
3159 , mpMenu( nullptr )
3161 if( pMenu )
3162 const_cast< Menu* >( pMenu )->ImplAddDel( *this );
3165 ImplMenuDelData::~ImplMenuDelData()
3167 if( mpMenu )
3168 const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
3171 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */