lokdialog: Tunnel the spell-checking context menu with recommendations.
[LibreOffice.git] / vcl / source / window / menu.cxx
blob6c6c4715047e5a7edab9b6bcac5a5dcb13452352
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 <tools/stream.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/wrkwin.hxx>
31 #include <vcl/timer.hxx>
32 #include <vcl/decoview.hxx>
33 #include <vcl/bitmap.hxx>
34 #include <vcl/menu.hxx>
35 #include <vcl/button.hxx>
36 #include <vcl/gradient.hxx>
37 #include <vcl/i18nhelp.hxx>
38 #include <vcl/taskpanelist.hxx>
39 #include <vcl/controllayout.hxx>
40 #include <vcl/toolbox.hxx>
41 #include <vcl/dockingarea.hxx>
42 #include <vcl/settings.hxx>
43 #include <vcl/commandinfoprovider.hxx>
44 #include <vcl/IDialogRenderable.hxx>
46 #include <salinst.hxx>
47 #include <svdata.hxx>
48 #include <strings.hrc>
49 #include <window.h>
50 #include <salmenu.hxx>
51 #include <salframe.hxx>
53 #include "menubarwindow.hxx"
54 #include "menufloatingwindow.hxx"
55 #include "menuitemlist.hxx"
57 #include <com/sun/star/uno/Reference.h>
58 #include <com/sun/star/lang/XComponent.hpp>
59 #include <com/sun/star/accessibility/XAccessible.hpp>
60 #include <com/sun/star/accessibility/AccessibleRole.hpp>
61 #include <vcl/unowrap.hxx>
63 #include <vcl/unohelp.hxx>
64 #include <vcl/configsettings.hxx>
66 #include <vcl/lazydelete.hxx>
68 #include <map>
69 #include <vector>
71 namespace vcl
74 struct MenuLayoutData : public ControlLayoutData
76 std::vector< sal_uInt16 > m_aLineItemIds;
77 std::vector< sal_uInt16 > m_aLineItemPositions;
78 std::map< sal_uInt16, tools::Rectangle > m_aVisibleItemBoundRects;
83 using namespace vcl;
85 #define EXTRAITEMHEIGHT 4
86 #define SPACE_AROUND_TITLE 4
88 static bool ImplAccelDisabled()
90 // display of accelerator strings may be suppressed via configuration
91 static int nAccelDisabled = -1;
93 if( nAccelDisabled == -1 )
95 OUString aStr =
96 vcl::SettingsConfigItem::get()->
97 getValue( "Menu", "SuppressAccelerators" );
98 nAccelDisabled = aStr.equalsIgnoreAsciiCase("true") ? 1 : 0;
100 return nAccelDisabled == 1;
103 static void ImplSetMenuItemData( MenuItemData* pData )
105 // convert data
106 if ( !pData->aImage )
107 pData->eType = MenuItemType::STRING;
108 else if ( pData->aText.isEmpty() )
109 pData->eType = MenuItemType::IMAGE;
110 else
111 pData->eType = MenuItemType::STRINGIMAGE;
114 namespace {
116 void ImplClosePopupToolBox( const VclPtr<vcl::Window>& pWin )
118 if ( pWin->GetType() == WindowType::TOOLBOX && ImplGetDockingManager()->IsInPopupMode( pWin ) )
120 ImplDockingWindowWrapper* pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( pWin );
121 if ( pWrapper && pWrapper->GetFloatingWindow() )
122 pWrapper->GetFloatingWindow()->EndPopupMode( FloatWinPopupEndFlags::CloseAll );
126 // TODO: Move to common code with the same function in toolbox
127 // Draw the ">>" - more indicator at the coordinates
128 void lclDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
130 rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
131 rRenderContext.SetLineColor();
133 if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
134 rRenderContext.SetFillColor(COL_WHITE);
135 else
136 rRenderContext.SetFillColor(COL_BLACK);
137 float fScaleFactor = rRenderContext.GetDPIScaleFactor();
139 int linewidth = 1 * fScaleFactor;
140 int space = 4 * fScaleFactor;
142 long width = 8 * fScaleFactor;
143 long height = 5 * fScaleFactor;
145 //Keep odd b/c drawing code works better
146 if ( height % 2 == 0 )
147 height--;
149 long heightOrig = height;
151 long x = rRect.Left() + (rRect.getWidth() - width)/2 + 1;
152 long y = rRect.Top() + (rRect.getHeight() - height)/2 + 1;
153 while( height >= 1)
155 rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
156 x += space;
157 rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
158 x -= space;
159 y++;
160 if( height <= heightOrig / 2 + 1) x--;
161 else x++;
162 height--;
164 rRenderContext.Pop();
167 } // end anonymouse namespace
170 Menu::Menu()
171 : mpFirstDel(nullptr),
172 pItemList(new MenuItemList),
173 pStartedFrom(nullptr),
174 pWindow(nullptr),
175 nTitleHeight(0),
176 nEventId(nullptr),
177 mnHighlightedItemPos(ITEMPOS_INVALID),
178 nMenuFlags(MenuFlags::NONE),
179 nSelectedId(0),
180 nImgOrChkPos(0),
181 nTextPos(0),
182 bCanceled(false),
183 bInCallback(false),
184 bKilled(false),
185 mpLayoutData(nullptr),
186 mpSalMenu(nullptr)
190 Menu::~Menu()
192 disposeOnce();
195 void Menu::dispose()
197 ImplCallEventListeners( VclEventId::ObjectDying, ITEMPOS_INVALID );
199 // at the window free the reference to the accessible component
200 // and make sure the MenuFloatingWindow knows about our destruction
201 if ( pWindow )
203 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
204 if( pFloat->pMenu.get() == this )
205 pFloat->pMenu.clear();
206 pWindow->SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
209 // dispose accessible components
210 if ( mxAccessible.is() )
212 css::uno::Reference< css::lang::XComponent> xComponent( mxAccessible, css::uno::UNO_QUERY );
213 if ( xComponent.is() )
214 xComponent->dispose();
217 if ( nEventId )
218 Application::RemoveUserEvent( nEventId );
220 // Notify deletion of this menu
221 ImplMenuDelData* pDelData = mpFirstDel;
222 while ( pDelData )
224 pDelData->mpMenu = nullptr;
225 pDelData = pDelData->mpNext;
228 bKilled = true;
230 pItemList->Clear();
231 delete mpLayoutData;
232 mpLayoutData = nullptr;
234 // Native-support: destroy SalMenu
235 ImplClearSalMenu();
237 pStartedFrom.clear();
238 pWindow.clear();
239 VclReferenceBase::dispose();
242 void Menu::CreateAutoMnemonics()
244 MnemonicGenerator aMnemonicGenerator;
245 size_t n;
246 for ( n = 0; n < pItemList->size(); n++ )
248 MenuItemData* pData = pItemList->GetDataFromPos( n );
249 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
250 aMnemonicGenerator.RegisterMnemonic( pData->aText );
252 for ( n = 0; n < pItemList->size(); n++ )
254 MenuItemData* pData = pItemList->GetDataFromPos( n );
255 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
256 pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText );
260 void Menu::Activate()
262 bInCallback = true;
264 ImplMenuDelData aDelData( this );
266 ImplCallEventListeners( VclEventId::MenuActivate, ITEMPOS_INVALID );
268 if( !aDelData.isDeleted() )
270 if ( !aActivateHdl.Call( this ) )
272 if( !aDelData.isDeleted() )
274 Menu* pStartMenu = ImplGetStartMenu();
275 if ( pStartMenu && ( pStartMenu != this ) )
277 pStartMenu->bInCallback = true;
278 // MT 11/01: Call EventListener here? I don't know...
279 pStartMenu->aActivateHdl.Call( this );
280 pStartMenu->bInCallback = false;
284 bInCallback = false;
287 if (!aDelData.isDeleted() && !(nMenuFlags & MenuFlags::NoAutoMnemonics))
288 CreateAutoMnemonics();
291 void Menu::Deactivate()
293 for ( size_t n = pItemList->size(); n; )
295 MenuItemData* pData = pItemList->GetDataFromPos( --n );
296 if ( pData->bIsTemporary )
298 if ( ImplGetSalMenu() )
299 ImplGetSalMenu()->RemoveItem( n );
301 pItemList->Remove( n );
305 bInCallback = true;
307 ImplMenuDelData aDelData( this );
309 Menu* pStartMenu = ImplGetStartMenu();
310 ImplCallEventListeners( VclEventId::MenuDeactivate, ITEMPOS_INVALID );
312 if( !aDelData.isDeleted() )
314 if ( !aDeactivateHdl.Call( this ) )
316 if( !aDelData.isDeleted() )
318 if ( pStartMenu && ( pStartMenu != this ) )
320 pStartMenu->bInCallback = true;
321 pStartMenu->aDeactivateHdl.Call( this );
322 pStartMenu->bInCallback = false;
328 if( !aDelData.isDeleted() )
330 bInCallback = false;
334 void Menu::ImplSelect()
336 MenuItemData* pData = GetItemList()->GetData( nSelectedId );
337 if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) )
339 bool bChecked = IsItemChecked( nSelectedId );
340 if ( pData->nBits & MenuItemBits::RADIOCHECK )
342 if ( !bChecked )
343 CheckItem( nSelectedId );
345 else
346 CheckItem( nSelectedId, !bChecked );
349 // call select
350 ImplSVData* pSVData = ImplGetSVData();
351 pSVData->maAppData.mpActivePopupMenu = nullptr; // if new execute in select()
352 nEventId = Application::PostUserEvent( LINK( this, Menu, ImplCallSelect ) );
355 void Menu::Select()
357 ImplMenuDelData aDelData( this );
359 ImplCallEventListeners( VclEventId::MenuSelect, GetItemPos( GetCurItemId() ) );
360 if ( !aDelData.isDeleted() && !aSelectHdl.Call( this ) )
362 if( !aDelData.isDeleted() )
364 Menu* pStartMenu = ImplGetStartMenu();
365 if ( pStartMenu && ( pStartMenu != this ) )
367 pStartMenu->nSelectedId = nSelectedId;
368 pStartMenu->aSelectHdl.Call( this );
374 #if defined(MACOSX)
375 void Menu::ImplSelectWithStart( Menu* pSMenu )
377 auto pOldStartedFrom = pStartedFrom;
378 pStartedFrom = pSMenu;
379 auto pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->pStartedFrom : VclPtr<Menu>();
380 Select();
381 if( pOldStartedFrom )
382 pOldStartedFrom->pStartedFrom = pOldStartedStarted;
383 pStartedFrom = pOldStartedFrom;
385 #endif
387 void Menu::ImplCallEventListeners( VclEventId nEvent, sal_uInt16 nPos )
389 ImplMenuDelData aDelData( this );
391 VclMenuEvent aEvent( this, nEvent, nPos );
393 // This is needed by atk accessibility bridge
394 if ( nEvent == VclEventId::MenuHighlight )
396 Application::ImplCallEventListeners( aEvent );
399 if ( !aDelData.isDeleted() )
401 // Copy the list, because this can be destroyed when calling a Link...
402 std::list<Link<VclMenuEvent&,void>> aCopy( maEventListeners );
403 std::list<Link<VclMenuEvent&,void>>::iterator aIter( aCopy.begin() );
404 std::list<Link<VclMenuEvent&,void>>::const_iterator aEnd( aCopy.end() );
405 while ( aIter != aEnd )
407 Link<VclMenuEvent&,void> &rLink = *aIter;
408 if( std::find(maEventListeners.begin(), maEventListeners.end(), rLink) != maEventListeners.end() )
409 rLink.Call( aEvent );
410 ++aIter;
415 void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener )
417 maEventListeners.push_back( rEventListener );
420 void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener )
422 maEventListeners.remove( rEventListener );
425 MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits,
426 const OUString& rStr, Menu* pMenu,
427 size_t nPos, const OString &rIdent)
429 // put Item in MenuItemList
430 MenuItemData* pData = pItemList->Insert(nId, MenuItemType::STRING,
431 nBits, rStr, pMenu, nPos, rIdent);
433 // update native menu
434 if (ImplGetSalMenu() && pData->pSalMenuItem)
435 ImplGetSalMenu()->InsertItem(pData->pSalMenuItem, nPos);
437 return pData;
440 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits,
441 const OString &rIdent, sal_uInt16 nPos)
443 SAL_WARN_IF( !nItemId, "vcl", "Menu::InsertItem(): ItemId == 0" );
444 SAL_WARN_IF( GetItemPos( nItemId ) != MENU_ITEM_NOTFOUND, "vcl",
445 "Menu::InsertItem(): ItemId already exists" );
447 // if Position > ItemCount, append
448 if ( nPos >= pItemList->size() )
449 nPos = MENU_APPEND;
451 // put Item in MenuItemList
452 NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent);
454 vcl::Window* pWin = ImplGetWindow();
455 delete mpLayoutData;
456 mpLayoutData = nullptr;
457 if ( pWin )
459 ImplCalcSize( pWin );
460 if ( pWin->IsVisible() )
461 pWin->Invalidate();
463 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
466 void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage,
467 MenuItemBits nItemBits, const OString &rIdent, sal_uInt16 nPos)
469 InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos);
470 SetItemImage( nItemId, rImage );
473 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr,
474 const Image& rImage, MenuItemBits nItemBits,
475 const OString &rIdent, sal_uInt16 nPos)
477 InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
478 SetItemImage( nItemId, rImage );
481 void Menu::InsertItem(const OUString& rCommand, const css::uno::Reference<css::frame::XFrame>& rFrame)
483 sal_uInt16 nItemId = GetItemCount() + 1;
485 if (rFrame.is())
487 OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
488 OUString aLabel(CommandInfoProvider::GetPopupLabelForCommand(rCommand, aModuleName));
489 OUString aTooltip(CommandInfoProvider::GetTooltipForCommand(rCommand, rFrame));
490 Image aImage(CommandInfoProvider::GetImageForCommand(rCommand, rFrame));
492 InsertItem(nItemId, aLabel, aImage);
493 SetHelpText(nItemId, aTooltip);
495 else
496 InsertItem(nItemId, OUString());
498 SetItemCommand(nItemId, rCommand);
502 void Menu::InsertSeparator(const OString &rIdent, sal_uInt16 nPos)
504 // do nothing if it's a menu bar
505 if (IsMenuBar())
506 return;
508 // if position > ItemCount, append
509 if ( nPos >= pItemList->size() )
510 nPos = MENU_APPEND;
512 // put separator in item list
513 pItemList->InsertSeparator(rIdent, nPos);
515 // update native menu
516 size_t itemPos = ( nPos != MENU_APPEND ) ? nPos : pItemList->size() - 1;
517 MenuItemData *pData = pItemList->GetDataFromPos( itemPos );
518 if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
519 ImplGetSalMenu()->InsertItem( pData->pSalMenuItem, nPos );
521 delete mpLayoutData;
522 mpLayoutData = nullptr;
524 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
527 void Menu::RemoveItem( sal_uInt16 nPos )
529 bool bRemove = false;
531 if ( nPos < GetItemCount() )
533 // update native menu
534 if( ImplGetSalMenu() )
535 ImplGetSalMenu()->RemoveItem( nPos );
537 pItemList->Remove( nPos );
538 bRemove = true;
541 vcl::Window* pWin = ImplGetWindow();
542 if ( pWin )
544 ImplCalcSize( pWin );
545 if ( pWin->IsVisible() )
546 pWin->Invalidate();
548 delete mpLayoutData;
549 mpLayoutData = nullptr;
551 if ( bRemove )
552 ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos );
555 void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
557 MenuItemType eType = rMenu.GetItemType( nPos );
559 if ( eType == MenuItemType::DONTKNOW )
560 return;
562 if ( eType == MenuItemType::SEPARATOR )
563 pThis->InsertSeparator( OString(), nNewPos );
564 else
566 sal_uInt16 nId = rMenu.GetItemId( nPos );
568 SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
569 "Menu::CopyItem(): ItemId already exists" );
571 MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
573 if (!pData)
574 return;
576 if ( eType == MenuItemType::STRINGIMAGE )
577 pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
578 else if ( eType == MenuItemType::STRING )
579 pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos );
580 else
581 pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
583 if ( rMenu.IsItemChecked( nId ) )
584 pThis->CheckItem( nId );
585 if ( !rMenu.IsItemEnabled( nId ) )
586 pThis->EnableItem( nId, false );
587 pThis->SetHelpId( nId, pData->aHelpId );
588 pThis->SetHelpText( nId, pData->aHelpText );
589 pThis->SetAccelKey( nId, pData->aAccelKey );
590 pThis->SetItemCommand( nId, pData->aCommandStr );
591 pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
593 PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
594 if ( pSubMenu )
596 // create auto-copy
597 VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
598 pThis->SetPopupMenu( nId, pNewMenu );
603 void Menu::Clear()
605 for ( sal_uInt16 i = GetItemCount(); i; i-- )
606 RemoveItem( 0 );
609 sal_uInt16 Menu::GetItemCount() const
611 return static_cast<sal_uInt16>(pItemList->size());
614 sal_uInt16 Menu::ImplGetVisibleItemCount() const
616 sal_uInt16 nItems = 0;
617 for ( size_t n = pItemList->size(); n; )
619 if ( ImplIsVisible( --n ) )
620 nItems++;
622 return nItems;
625 sal_uInt16 Menu::ImplGetFirstVisible() const
627 for ( size_t n = 0; n < pItemList->size(); n++ )
629 if ( ImplIsVisible( n ) )
630 return n;
632 return ITEMPOS_INVALID;
635 sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
637 for ( size_t n = nPos; n; )
639 if ( n && ImplIsVisible( --n ) )
640 return n;
642 return ITEMPOS_INVALID;
645 sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
647 for ( size_t n = nPos+1; n < pItemList->size(); n++ )
649 if ( ImplIsVisible( n ) )
650 return n;
652 return ITEMPOS_INVALID;
655 sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
657 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
659 if ( pData )
660 return pData->nId;
661 else
662 return 0;
665 sal_uInt16 Menu::GetItemId(const OString &rIdent) const
667 for (size_t n = 0; n < pItemList->size(); ++n)
669 MenuItemData* pData = pItemList->GetDataFromPos(n);
670 if (pData && pData->sIdent == rIdent)
671 return pData->nId;
673 return MENU_ITEM_NOTFOUND;
676 sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
678 size_t nPos;
679 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
681 if ( pData )
682 return static_cast<sal_uInt16>(nPos);
683 else
684 return MENU_ITEM_NOTFOUND;
687 MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
689 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
691 if ( pData )
692 return pData->eType;
693 else
694 return MenuItemType::DONTKNOW;
697 OString Menu::GetCurItemIdent() const
699 const MenuItemData* pData = pItemList->GetData(nSelectedId);
700 return pData ? pData->sIdent : OString();
703 OString Menu::GetItemIdent(sal_uInt16 nId) const
705 const MenuItemData* pData = pItemList->GetData(nId);
706 return pData ? pData->sIdent : OString();
709 void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
711 MenuItemData* pData = pItemList->GetData( nItemId );
712 if ( pData )
713 pData->nBits = nBits;
716 MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
718 MenuItemBits nBits = MenuItemBits::NONE;
719 MenuItemData* pData = pItemList->GetData( nItemId );
720 if ( pData )
721 nBits = pData->nBits;
722 return nBits;
725 void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc)
727 MenuItemData* pData = pItemList->GetData(nItemId);
728 if (pData)
730 if (pData->aUserValueReleaseFunc)
731 pData->aUserValueReleaseFunc(pData->nUserValue);
732 pData->aUserValueReleaseFunc = aFunc;
733 pData->nUserValue = nUserValue;
737 void* Menu::GetUserValue( sal_uInt16 nItemId ) const
739 MenuItemData* pData = pItemList->GetData( nItemId );
740 return pData ? pData->nUserValue : nullptr;
743 void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
745 size_t nPos;
746 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
748 // Item does not exist -> return NULL
749 if ( !pData )
750 return;
752 // same menu, nothing to do
753 if ( static_cast<PopupMenu*>(pData->pSubMenu.get()) == pMenu )
754 return;
756 // remove old menu
757 auto oldSubMenu = pData->pSubMenu;
759 // data exchange
760 pData->pSubMenu = pMenu;
762 // #112023# Make sure pStartedFrom does not point to invalid (old) data
763 if ( pData->pSubMenu )
764 pData->pSubMenu->pStartedFrom = nullptr;
766 // set native submenu
767 if( ImplGetSalMenu() && pData->pSalMenuItem )
769 if( pMenu )
770 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem, pMenu->ImplGetSalMenu(), nPos );
771 else
772 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem, nullptr, nPos );
775 oldSubMenu.disposeAndClear();
777 ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos );
780 PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
782 MenuItemData* pData = pItemList->GetData( nItemId );
784 if ( pData )
785 return static_cast<PopupMenu*>(pData->pSubMenu.get());
786 else
787 return nullptr;
790 void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
792 size_t nPos;
793 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
795 if ( !pData )
796 return;
798 if ( pData->aAccelKey == rKeyCode )
799 return;
801 pData->aAccelKey = rKeyCode;
803 // update native menu
804 if( ImplGetSalMenu() && pData->pSalMenuItem )
805 ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem, rKeyCode, rKeyCode.GetName() );
808 KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
810 MenuItemData* pData = pItemList->GetData( nItemId );
812 if ( pData )
813 return pData->aAccelKey;
814 else
815 return KeyCode();
818 KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
820 KeyEvent aRet;
821 MenuItemData* pData = pItemList->GetData( nItemId );
822 if( pData )
824 sal_Int32 nPos = pData->aText.indexOf( '~' );
825 if( nPos != -1 && nPos < pData->aText.getLength()-1 )
827 sal_uInt16 nCode = 0;
828 sal_Unicode cAccel = pData->aText[nPos+1];
829 if( cAccel >= 'a' && cAccel <= 'z' )
830 nCode = KEY_A + (cAccel-'a');
831 else if( cAccel >= 'A' && cAccel <= 'Z' )
832 nCode = KEY_A + (cAccel-'A');
833 else if( cAccel >= '0' && cAccel <= '9' )
834 nCode = KEY_0 + (cAccel-'0');
836 aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
840 return aRet;
843 void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
845 size_t nPos;
846 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
848 if ( !pData || pData->bChecked == bCheck )
849 return;
851 // if radio-check, then uncheck previous
852 if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
853 (pData->nBits & MenuItemBits::RADIOCHECK) )
855 MenuItemData* pGroupData;
856 sal_uInt16 nGroupPos;
857 sal_uInt16 nItemCount = GetItemCount();
858 bool bFound = false;
860 nGroupPos = nPos;
861 while ( nGroupPos )
863 pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
864 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
866 if ( IsItemChecked( pGroupData->nId ) )
868 CheckItem( pGroupData->nId, false );
869 bFound = true;
870 break;
873 else
874 break;
875 nGroupPos--;
878 if ( !bFound )
880 nGroupPos = nPos+1;
881 while ( nGroupPos < nItemCount )
883 pGroupData = pItemList->GetDataFromPos( nGroupPos );
884 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
886 if ( IsItemChecked( pGroupData->nId ) )
888 CheckItem( pGroupData->nId, false );
889 break;
892 else
893 break;
894 nGroupPos++;
899 pData->bChecked = bCheck;
901 // update native menu
902 if( ImplGetSalMenu() )
903 ImplGetSalMenu()->CheckItem( nPos, bCheck );
905 ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos );
908 bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
910 size_t nPos;
911 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
913 if ( !pData )
914 return false;
916 return pData->bChecked;
919 void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
921 size_t nPos;
922 MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
924 if ( pItemData && ( pItemData->bEnabled != bEnable ) )
926 pItemData->bEnabled = bEnable;
928 vcl::Window* pWin = ImplGetWindow();
929 if ( pWin && pWin->IsVisible() )
931 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
932 long nX = 0;
933 size_t nCount = pItemList->size();
934 for ( size_t n = 0; n < nCount; n++ )
936 MenuItemData* pData = pItemList->GetDataFromPos( n );
937 if ( n == nPos )
939 pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
940 break;
942 nX += pData->aSz.Width();
945 // update native menu
946 if( ImplGetSalMenu() )
947 ImplGetSalMenu()->EnableItem( nPos, bEnable );
949 ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos );
953 bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
955 size_t nPos;
956 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
958 if ( !pData )
959 return false;
961 return pData->bEnabled;
964 void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
966 size_t nPos;
967 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
969 SAL_WARN_IF(IsMenuBar(), "vcl", "Menu::ShowItem - ignored for menu bar entries!");
970 if (!IsMenuBar()&& pData && (pData->bVisible != bVisible))
972 vcl::Window* pWin = ImplGetWindow();
973 if ( pWin && pWin->IsVisible() )
975 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
976 return;
978 pData->bVisible = bVisible;
980 // update native menu
981 if( ImplGetSalMenu() )
982 ImplGetSalMenu()->ShowItem( nPos, bVisible );
986 void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
988 size_t nPos;
989 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
991 if ( !pData )
992 return;
994 if ( rStr != pData->aText )
996 pData->aText = rStr;
997 ImplSetMenuItemData( pData );
998 // update native menu
999 if( ImplGetSalMenu() && pData->pSalMenuItem )
1000 ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem, rStr );
1002 vcl::Window* pWin = ImplGetWindow();
1003 delete mpLayoutData;
1004 mpLayoutData = nullptr;
1005 if (pWin && IsMenuBar())
1007 ImplCalcSize( pWin );
1008 if ( pWin->IsVisible() )
1009 pWin->Invalidate();
1012 ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos );
1016 OUString Menu::GetItemText( sal_uInt16 nItemId ) const
1018 size_t nPos;
1019 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1021 if ( pData )
1022 return pData->aText;
1024 return OUString();
1027 void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1029 size_t nPos;
1030 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1032 if ( !pData )
1033 return;
1035 pData->aImage = rImage;
1036 ImplSetMenuItemData( pData );
1038 // update native menu
1039 if( ImplGetSalMenu() && pData->pSalMenuItem )
1040 ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem, rImage );
1043 Image Menu::GetItemImage( sal_uInt16 nItemId ) const
1045 MenuItemData* pData = pItemList->GetData( nItemId );
1047 if ( pData )
1048 return pData->aImage;
1049 else
1050 return Image();
1053 void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1055 size_t nPos;
1056 MenuItemData* pData = pItemList->GetData( nItemId, nPos );
1058 if ( pData )
1059 pData->aCommandStr = rCommand;
1062 OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
1064 MenuItemData* pData = pItemList->GetData( nItemId );
1066 if (pData)
1067 return pData->aCommandStr;
1069 return OUString();
1072 void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
1074 MenuItemData* pData = pItemList->GetData( nItemId );
1076 if ( pData )
1077 pData->aHelpCommandStr = rStr;
1080 OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
1082 MenuItemData* pData = pItemList->GetData( nItemId );
1084 if ( pData )
1085 return pData->aHelpCommandStr;
1087 return OUString();
1090 void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
1092 MenuItemData* pData = pItemList->GetData( nItemId );
1094 if ( pData )
1095 pData->aHelpText = rStr;
1098 OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
1100 MenuItemData* pData = pItemList->GetData( nItemId );
1102 if ( pData && pData->aHelpText.isEmpty() &&
1103 (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() )))
1105 Help* pHelp = Application::GetHelp();
1106 if ( pHelp )
1108 if (!pData->aCommandStr.isEmpty())
1109 pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr, nullptr );
1110 if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty())
1111 pData->aHelpText = pHelp->GetHelpText( OStringToOUString( pData->aHelpId, RTL_TEXTENCODING_UTF8 ), nullptr );
1115 return OUString();
1118 OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
1120 return ImplGetHelpText( nItemId );
1123 void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
1125 MenuItemData* pData = pItemList->GetData( nItemId );
1127 if ( pData )
1128 pData->aTipHelpText = rStr;
1131 OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
1133 MenuItemData* pData = pItemList->GetData( nItemId );
1135 if ( pData )
1136 return pData->aTipHelpText;
1138 return OUString();
1141 void Menu::SetHelpId( sal_uInt16 nItemId, const OString& rHelpId )
1143 MenuItemData* pData = pItemList->GetData( nItemId );
1145 if ( pData )
1146 pData->aHelpId = rHelpId;
1149 OString Menu::GetHelpId( sal_uInt16 nItemId ) const
1151 OString aRet;
1153 MenuItemData* pData = pItemList->GetData( nItemId );
1155 if ( pData )
1157 if ( !pData->aHelpId.isEmpty() )
1158 aRet = pData->aHelpId;
1159 else
1160 aRet = OUStringToOString( pData->aCommandStr, RTL_TEXTENCODING_UTF8 );
1163 return aRet;
1166 Menu& Menu::operator=( const Menu& rMenu )
1168 // clean up
1169 Clear();
1171 // copy items
1172 sal_uInt16 nCount = rMenu.GetItemCount();
1173 for ( sal_uInt16 i = 0; i < nCount; i++ )
1174 ImplCopyItem( this, rMenu, i, MENU_APPEND );
1176 aActivateHdl = rMenu.aActivateHdl;
1177 aDeactivateHdl = rMenu.aDeactivateHdl;
1178 aSelectHdl = rMenu.aSelectHdl;
1179 aTitleText = rMenu.aTitleText;
1180 nTitleHeight = rMenu.nTitleHeight;
1182 return *this;
1185 // Returns true if the item is completely hidden on the GUI and shouldn't
1186 // be possible to interact with
1187 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const
1189 MenuItemData* pData = pItemList->GetDataFromPos(nPos);
1190 if (pData)
1192 MenuItemData* pPreviousData = pItemList->GetDataFromPos( nPos - 1 );
1193 if (pPreviousData && pPreviousData->bHiddenOnGUI)
1195 return true;
1198 return false;
1201 bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
1203 bool bVisible = true;
1205 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1206 // check general visibility first
1207 if( pData && !pData->bVisible )
1208 bVisible = false;
1210 if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
1212 if( nPos == 0 ) // no separator should be shown at the very beginning
1213 bVisible = false;
1214 else
1216 // always avoid adjacent separators
1217 size_t nCount = pItemList->size();
1218 size_t n;
1219 MenuItemData* pNextData = nullptr;
1220 // search next visible item
1221 for( n = nPos + 1; n < nCount; n++ )
1223 pNextData = pItemList->GetDataFromPos( n );
1224 if( pNextData && pNextData->bVisible )
1226 if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
1227 break;
1230 if( n == nCount ) // no next visible item
1231 bVisible = false;
1232 // check for separator
1233 if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
1234 bVisible = false;
1236 if( bVisible )
1238 for( n = nPos; n > 0; n-- )
1240 pNextData = pItemList->GetDataFromPos( n-1 );
1241 if( pNextData && pNextData->bVisible )
1243 if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
1244 break;
1247 if( n == 0 ) // no previous visible item
1248 bVisible = false;
1253 // not allowed for menubar, as I do not know
1254 // whether a menu-entry will disappear or will appear
1255 if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) &&
1256 !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
1258 if( !pData ) // e.g. nPos == ITEMPOS_INVALID
1259 bVisible = false;
1260 else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
1262 // tdf#86850 Always display clipboard functions
1263 if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" )
1264 bVisible = true;
1265 else
1266 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1267 bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
1271 return bVisible;
1274 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
1276 return IsMenuVisible() && ImplIsVisible( nItemPos );
1279 bool Menu::IsMenuVisible() const
1281 return pWindow && pWindow->IsReallyVisible();
1284 bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
1286 bool bSelectable = true;
1288 MenuItemData* pData = pItemList->GetDataFromPos( nPos );
1289 // check general visibility first
1290 if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
1291 bSelectable = false;
1293 return bSelectable;
1296 css::uno::Reference<css::accessibility::XAccessible> Menu::GetAccessible()
1298 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1299 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1300 // mxAccessible member only for sub menus.
1301 if ( pStartedFrom )
1303 for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
1305 sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
1306 if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
1308 css::uno::Reference<css::accessibility::XAccessible> xParent = pStartedFrom->GetAccessible();
1309 if ( xParent.is() )
1311 css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( xParent->getAccessibleContext() );
1312 if (xParentContext.is())
1313 return xParentContext->getAccessibleChild( i );
1318 else if ( !mxAccessible.is() )
1320 UnoWrapperBase* pWrapper = Application::GetUnoWrapper();
1321 if ( pWrapper )
1322 mxAccessible = pWrapper->CreateAccessible(this, IsMenuBar());
1325 return mxAccessible;
1328 void Menu::SetAccessible(const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible )
1330 mxAccessible = rxAccessible;
1333 Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, long& rCheckHeight, long& rRadioHeight ) const
1335 long nCheckWidth = 0, nRadioWidth = 0;
1336 rCheckHeight = rRadioHeight = 0;
1338 if (!IsMenuBar())
1340 ImplControlValue aVal;
1341 tools::Rectangle aNativeBounds;
1342 tools::Rectangle aNativeContent;
1344 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1345 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
1347 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
1348 aCtrlRegion, ControlState::ENABLED, aVal,
1349 aNativeBounds, aNativeContent))
1351 rCheckHeight = aNativeBounds.GetHeight();
1352 nCheckWidth = aNativeContent.GetWidth();
1355 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
1357 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
1358 aCtrlRegion, ControlState::ENABLED, aVal,
1359 aNativeBounds, aNativeContent))
1361 rRadioHeight = aNativeBounds.GetHeight();
1362 nRadioWidth = aNativeContent.GetWidth();
1366 return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
1369 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, long& rArrowSpacing)
1371 ImplControlValue aVal;
1372 tools::Rectangle aNativeBounds;
1373 tools::Rectangle aNativeContent;
1374 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1375 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
1377 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
1378 aCtrlRegion, ControlState::ENABLED,
1379 aVal, aNativeBounds, aNativeContent))
1381 Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
1382 rArrowSize = aSize;
1383 rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
1384 return true;
1387 return false;
1390 void Menu::ImplAddDel( ImplMenuDelData& rDel )
1392 SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1393 if( !rDel.mpMenu )
1395 rDel.mpMenu = this;
1396 rDel.mpNext = mpFirstDel;
1397 mpFirstDel = &rDel;
1401 void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
1403 rDel.mpMenu = nullptr;
1404 if ( mpFirstDel == &rDel )
1406 mpFirstDel = rDel.mpNext;
1408 else
1410 ImplMenuDelData* pData = mpFirstDel;
1411 while ( pData && (pData->mpNext != &rDel) )
1412 pData = pData->mpNext;
1414 SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1415 if( pData )
1416 pData->mpNext = rDel.mpNext;
1420 Size Menu::ImplCalcSize( vcl::Window* pWin )
1422 // | Check/Radio/Image| Text| Accel/Popup|
1424 // for symbols: nFontHeight x nFontHeight
1425 long nFontHeight = pWin->GetTextHeight();
1426 long nExtra = nFontHeight/4;
1428 long nMinMenuItemHeight = nFontHeight;
1429 long nCheckHeight = 0, nRadioHeight = 0;
1430 Size aMaxSize = ImplGetNativeCheckAndRadioSize(*pWin, nCheckHeight, nRadioHeight); // FIXME
1431 if( aMaxSize.Height() > nMinMenuItemHeight )
1432 nMinMenuItemHeight = aMaxSize.Height();
1434 Size aMaxImgSz;
1436 const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
1437 if ( rSettings.GetUseImagesInMenus() )
1439 if ( 16 > nMinMenuItemHeight )
1440 nMinMenuItemHeight = 16;
1441 for ( size_t i = pItemList->size(); i; )
1443 MenuItemData* pData = pItemList->GetDataFromPos( --i );
1444 if ( ImplIsVisible( i )
1445 && ( ( pData->eType == MenuItemType::IMAGE )
1446 || ( pData->eType == MenuItemType::STRINGIMAGE )
1450 Size aImgSz = pData->aImage.GetSizePixel();
1451 if ( aImgSz.Height() > aMaxImgSz.Height() )
1452 aMaxImgSz.setHeight( aImgSz.Height() );
1453 if ( aImgSz.Height() > nMinMenuItemHeight )
1454 nMinMenuItemHeight = aImgSz.Height();
1455 break;
1460 Size aSz;
1461 long nCheckWidth = 0;
1462 long nMaxWidth = 0;
1464 for ( size_t n = pItemList->size(); n; )
1466 MenuItemData* pData = pItemList->GetDataFromPos( --n );
1468 pData->aSz.setHeight( 0 );
1469 pData->aSz.setWidth( 0 );
1471 if ( ImplIsVisible( n ) )
1473 long nWidth = 0;
1475 // Separator
1476 if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
1478 //Useless: SAL_WARN_IF( IsMenuBar(), "vcl", "Separator in MenuBar ?! " );
1479 pData->aSz.setHeight( 4 );
1482 // Image:
1483 if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1485 Size aImgSz = pData->aImage.GetSizePixel();
1487 aImgSz.AdjustHeight(4 ); // add a border for native marks
1488 aImgSz.AdjustWidth(4 ); // add a border for native marks
1489 if ( aImgSz.Width() > aMaxImgSz.Width() )
1490 aMaxImgSz.setWidth( aImgSz.Width() );
1491 if ( aImgSz.Height() > aMaxImgSz.Height() )
1492 aMaxImgSz.setHeight( aImgSz.Height() );
1493 if ( aImgSz.Height() > pData->aSz.Height() )
1494 pData->aSz.setHeight( aImgSz.Height() );
1497 // Check Buttons:
1498 if (!IsMenuBar() && pData->HasCheck())
1500 nCheckWidth = aMaxSize.Width();
1501 // checks / images take the same place
1502 if( ! ( ( pData->eType == MenuItemType::IMAGE ) || ( pData->eType == MenuItemType::STRINGIMAGE ) ) )
1503 nWidth += nCheckWidth + nExtra * 2;
1506 // Text:
1507 if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
1509 long nTextWidth = pWin->GetCtrlTextWidth( pData->aText );
1510 long nTextHeight = pWin->GetTextHeight();
1512 if (IsMenuBar())
1514 if ( nTextHeight > pData->aSz.Height() )
1515 pData->aSz.setHeight( nTextHeight );
1517 pData->aSz.setWidth( nTextWidth + 4*nExtra );
1518 aSz.AdjustWidth(pData->aSz.Width() );
1520 else
1521 pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1523 nWidth += nTextWidth;
1526 // Accel
1527 if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !ImplAccelDisabled())
1529 OUString aName = pData->aAccelKey.GetName();
1530 long nAccWidth = pWin->GetTextWidth( aName );
1531 nAccWidth += nExtra;
1532 nWidth += nAccWidth;
1535 // SubMenu?
1536 if (!IsMenuBar() && pData->pSubMenu)
1538 if ( nFontHeight > nWidth )
1539 nWidth += nFontHeight;
1541 pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
1544 pData->aSz.AdjustHeight(EXTRAITEMHEIGHT ); // little bit more distance
1546 if (!IsMenuBar())
1547 aSz.AdjustHeight(pData->aSz.Height() );
1549 if ( nWidth > nMaxWidth )
1550 nMaxWidth = nWidth;
1555 // Additional space for title
1556 nTitleHeight = 0;
1557 if (!IsMenuBar() && aTitleText.getLength() > 0) {
1558 // Set expected font
1559 pWin->Push(PushFlags::FONT);
1560 vcl::Font aFont = pWin->GetFont();
1561 aFont.SetWeight(WEIGHT_BOLD);
1562 pWin->SetFont(aFont);
1564 // Compute text bounding box
1565 tools::Rectangle aTextBoundRect;
1566 pWin->GetTextBoundRect(aTextBoundRect, aTitleText);
1568 // Vertically, one height of char + extra space for decoration
1569 nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ;
1570 aSz.AdjustHeight(nTitleHeight );
1572 long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
1573 pWin->Pop();
1574 if ( nWidth > nMaxWidth )
1575 nMaxWidth = nWidth;
1578 if (!IsMenuBar())
1580 // popup menus should not be wider than half the screen
1581 // except on rather small screens
1582 // TODO: move GetScreenNumber from SystemWindow to Window ?
1583 // currently we rely on internal privileges
1584 unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.nDisplayScreenNumber;
1585 tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
1586 long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
1587 if( nMaxWidth > nScreenWidth/2 )
1588 nMaxWidth = nScreenWidth/2;
1590 sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, 7L )); // #107710# increase space between checkmarks/images/text
1591 nImgOrChkPos = static_cast<sal_uInt16>(nExtra);
1592 long nImgOrChkWidth = 0;
1593 if( aMaxSize.Height() > 0 ) // NWF case
1594 nImgOrChkWidth = aMaxSize.Height() + nExtra;
1595 else // non NWF case
1596 nImgOrChkWidth = nFontHeight/2 + gfxExtra;
1597 nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgSz.Width() + gfxExtra );
1598 nTextPos = static_cast<sal_uInt16>(nImgOrChkPos + nImgOrChkWidth);
1599 nTextPos = nTextPos + gfxExtra;
1601 aSz.setWidth( nTextPos + nMaxWidth + nExtra );
1602 aSz.AdjustWidth(4*nExtra ); // a _little_ more ...
1604 aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX );
1605 aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1607 else
1609 nTextPos = static_cast<sal_uInt16>(2*nExtra);
1610 aSz.setHeight( nFontHeight+6 );
1612 // get menubar height from native methods if supported
1613 if( pWindow->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) )
1615 ImplControlValue aVal;
1616 tools::Rectangle aNativeBounds;
1617 tools::Rectangle aNativeContent;
1618 Point tmp( 0, 0 );
1619 tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
1620 if( pWindow->GetNativeControlRegion( ControlType::Menubar,
1621 ControlPart::Entire,
1622 aCtrlRegion,
1623 ControlState::ENABLED,
1624 aVal,
1625 aNativeBounds,
1626 aNativeContent )
1629 int nNativeHeight = aNativeBounds.GetHeight();
1630 if( nNativeHeight > aSz.Height() )
1631 aSz.setHeight( nNativeHeight );
1635 // account for the size of the close button, which actually is a toolbox
1636 // due to NWF this is variable
1637 long nCloseButtonHeight = static_cast<MenuBarWindow*>(pWindow.get())->MinCloseButtonSize().Height();
1638 if (aSz.Height() < nCloseButtonHeight)
1639 aSz.setHeight( nCloseButtonHeight );
1642 return aSz;
1645 static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight)
1647 bool bNativeOk = false;
1648 if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
1650 ImplControlValue aControlValue;
1651 aControlValue.setTristateVal(ButtonValue::On);
1653 bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
1654 i_rRect,
1655 ControlState::PRESSED | ControlState::ENABLED,
1656 aControlValue,
1657 OUString());
1660 if (!bNativeOk)
1662 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1663 Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
1664 RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor);
1668 static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, long i_nMaxWidth )
1670 sal_Int32 nPos = -1;
1671 OUString aNonMnem(OutputDevice::GetNonMnemonicString(i_rLong, nPos));
1672 aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
1673 // re-insert mnemonic
1674 if (nPos != -1)
1676 if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
1678 OUStringBuffer aBuf( i_rLong.getLength() );
1679 aBuf.append( aNonMnem.copy( 0, nPos) );
1680 aBuf.append( '~' );
1681 aBuf.append( aNonMnem.copy(nPos) );
1682 aNonMnem = aBuf.makeStringAndClear();
1685 return aNonMnem;
1688 void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const
1690 // Save previous graphical settings, set new one
1691 rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
1692 Wallpaper aOldBackground = rRenderContext.GetBackground();
1694 Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
1695 rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
1696 rRenderContext.SetFillColor(aBackgroundColor);
1697 vcl::Font aFont = rRenderContext.GetFont();
1698 aFont.SetWeight(WEIGHT_BOLD);
1699 rRenderContext.SetFont(aFont);
1701 // Draw background rectangle
1702 tools::Rectangle aBgRect(rRect);
1703 int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1704 aBgRect.setX(aBgRect.getX() + SPACE_AROUND_TITLE);
1705 aBgRect.setWidth(aBgRect.getWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
1706 aBgRect.setY(aBgRect.getY() + SPACE_AROUND_TITLE);
1707 aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE);
1708 rRenderContext.DrawRect(aBgRect);
1710 // Draw the text centered
1711 Point aTextTopLeft(aBgRect.TopLeft());
1712 tools::Rectangle aTextBoundRect;
1713 rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText );
1714 aTextTopLeft.AdjustX((aBgRect.getWidth() - aTextBoundRect.GetSize().Width()) / 2 );
1715 aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
1716 - aTextBoundRect.TopLeft().Y() );
1717 rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength());
1719 // Restore
1720 rRenderContext.Pop();
1721 rRenderContext.SetBackground(aOldBackground);
1724 void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize,
1725 sal_uInt16 nBorder, long nStartY, MenuItemData const * pThisItemOnly,
1726 bool bHighlighted, bool bLayout, bool bRollover) const
1728 // for symbols: nFontHeight x nFontHeight
1729 long nFontHeight = rRenderContext.GetTextHeight();
1730 long nExtra = nFontHeight / 4;
1732 long nCheckHeight = 0, nRadioHeight = 0;
1733 ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
1735 DecorationView aDecoView(&rRenderContext);
1736 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1738 Point aTopLeft, aTmpPos;
1740 int nOuterSpaceX = 0;
1741 if (!IsMenuBar())
1743 nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
1744 aTopLeft.AdjustX(nOuterSpaceX );
1745 aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
1748 // for the computations, use size of the underlying window, not of RenderContext
1749 Size aOutSz(rSize);
1751 size_t nCount = pItemList->size();
1752 if (bLayout)
1753 mpLayoutData->m_aVisibleItemBoundRects.clear();
1755 // Paint title
1756 if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0)
1757 ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz));
1759 bool bHiddenItems = false; // are any items on the GUI hidden
1761 for (size_t n = 0; n < nCount; n++)
1763 MenuItemData* pData = pItemList->GetDataFromPos( n );
1764 if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
1766 if (pThisItemOnly)
1768 if (IsMenuBar())
1770 if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
1772 if (bRollover)
1773 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1774 else if (bHighlighted)
1775 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1777 else
1779 if (bHighlighted)
1780 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
1781 else if (bRollover)
1782 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
1784 if (!bRollover && !bHighlighted)
1785 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
1787 else if (bHighlighted)
1788 rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1791 Point aPos(aTopLeft);
1792 aPos.AdjustY(nBorder );
1793 aPos.AdjustY(nStartY );
1795 if (aPos.Y() >= 0)
1797 long nTextOffsetY = ((pData->aSz.Height() - nFontHeight) / 2);
1798 if (IsMenuBar())
1799 nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
1800 DrawTextFlags nTextStyle = DrawTextFlags::NONE;
1801 DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
1802 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1804 // submenus without items are not disabled when no items are
1805 // contained. The application itself should check for this!
1806 // Otherwise it could happen entries are disabled due to
1807 // asynchronous loading
1808 if (!pData->bEnabled || !pWindow->IsEnabled())
1810 nTextStyle |= DrawTextFlags::Disable;
1811 nSymbolStyle |= DrawSymbolFlags::Disable;
1812 nImageStyle |= DrawImageFlags::Disable;
1815 // Separator
1816 if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
1818 bool bNativeOk = false;
1819 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
1821 ControlState nState = ControlState::NONE;
1822 if (pData->bEnabled && pWindow->IsEnabled())
1823 nState |= ControlState::ENABLED;
1824 if (bHighlighted)
1825 nState |= ControlState::SELECTED;
1826 int nSepPad = ImplGetSVData()->maNWFData.mnMenuSeparatorBorderX;
1827 Point aMpos (aPos);
1828 aMpos.AdjustX(nSepPad );
1829 Size aSz(pData->aSz);
1830 aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX - 2 * nSepPad );
1831 tools::Rectangle aItemRect(aMpos, aSz);
1832 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1833 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
1834 aItemRect, nState, aVal, OUString());
1836 if (!bNativeOk)
1838 aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) );
1839 aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX );
1840 rRenderContext.SetLineColor(rSettings.GetShadowColor());
1841 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1842 aTmpPos.AdjustY( 1 );
1843 rRenderContext.SetLineColor(rSettings.GetLightColor());
1844 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
1845 rRenderContext.SetLineColor();
1849 tools::Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()),
1850 Size(pData->aSz.Height(), pData->aSz.Height()));
1851 aOuterCheckRect.AdjustLeft(1 );
1852 aOuterCheckRect.AdjustRight( -1 );
1853 aOuterCheckRect.AdjustTop(1 );
1854 aOuterCheckRect.AdjustBottom( -1 );
1856 // CheckMark
1857 if (!bLayout && !IsMenuBar() && pData->HasCheck())
1859 // draw selection transparent marker if checked
1860 // onto that either a checkmark or the item image
1861 // will be painted
1862 // however do not do this if native checks will be painted since
1863 // the selection color too often does not fit the theme's check and/or radio
1865 if( !((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1867 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
1868 (pData->nBits & MenuItemBits::RADIOCHECK)
1869 ? ControlPart::MenuItemCheckMark
1870 : ControlPart::MenuItemRadioMark))
1872 ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
1873 ? ControlPart::MenuItemRadioMark
1874 : ControlPart::MenuItemCheckMark);
1876 ControlState nState = ControlState::NONE;
1878 if (pData->bChecked)
1879 nState |= ControlState::PRESSED;
1881 if (pData->bEnabled && pWindow->IsEnabled())
1882 nState |= ControlState::ENABLED;
1884 if (bHighlighted)
1885 nState |= ControlState::SELECTED;
1887 long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
1888 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 );
1889 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 );
1891 tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
1892 Size aSz(pData->aSz);
1893 aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX );
1894 tools::Rectangle aItemRect(aPos, aSz);
1895 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
1896 rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
1897 nState, aVal, OUString());
1899 else if (pData->bChecked) // by default do nothing for unchecked items
1901 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1903 SymbolType eSymbol;
1904 Size aSymbolSize;
1905 if (pData->nBits & MenuItemBits::RADIOCHECK)
1907 eSymbol = SymbolType::RADIOCHECKMARK;
1908 aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
1910 else
1912 eSymbol = SymbolType::CHECKMARK;
1913 aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
1915 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 );
1916 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 );
1917 tools::Rectangle aRect(aTmpPos, aSymbolSize);
1918 aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
1923 // Image:
1924 if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
1926 // Don't render an image for a check thing
1927 if (pData->bChecked)
1928 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
1930 Image aImage = pData->aImage;
1932 aTmpPos = aOuterCheckRect.TopLeft();
1933 aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 );
1934 aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 );
1935 rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
1938 // Text:
1939 if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
1941 aTmpPos.setX( aPos.X() + nTextPos );
1942 aTmpPos.setY( aPos.Y() );
1943 aTmpPos.AdjustY(nTextOffsetY );
1944 DrawTextFlags nStyle = nTextStyle | DrawTextFlags::Mnemonic;
1946 const Menu *pMenu = this;
1947 while (!pMenu->IsMenuBar() && pMenu->pStartedFrom)
1948 pMenu = pMenu->pStartedFrom;
1949 if (pMenu->IsMenuBar() && static_cast<MenuBarWindow*>(pMenu->pWindow.get())->GetMBWHideAccel())
1950 nStyle |= DrawTextFlags::HideMnemonic;
1952 if (pData->bIsTemporary)
1953 nStyle |= DrawTextFlags::Disable;
1954 MetricVector* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr;
1955 OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr;
1956 if (bLayout)
1958 mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength());
1959 mpLayoutData->m_aLineItemIds.push_back(pData->nId);
1960 mpLayoutData->m_aLineItemPositions.push_back(n);
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 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 rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(), nStyle, pVector, pDisplayText);
2006 if (bSetTmpBackground)
2007 rRenderContext.SetBackground();
2010 // Accel
2011 if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
2013 OUString aAccText = pData->aAccelKey.GetName();
2014 aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) );
2015 aTmpPos.AdjustX( -(4 * nExtra) );
2017 aTmpPos.AdjustX( -nOuterSpaceX );
2018 aTmpPos.setY( aPos.Y() );
2019 aTmpPos.AdjustY(nTextOffsetY );
2020 rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
2023 // SubMenu?
2024 if (!bLayout && !IsMenuBar() && pData->pSubMenu)
2026 bool bNativeOk = false;
2027 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
2029 ControlState nState = ControlState::NONE;
2030 Size aTmpSz(0, 0);
2031 long aSpacing = 0;
2033 if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
2035 aTmpSz = Size(nFontHeight, nFontHeight);
2036 aSpacing = nOuterSpaceX;
2039 if (pData->bEnabled && pWindow->IsEnabled())
2040 nState |= ControlState::ENABLED;
2041 if (bHighlighted)
2042 nState |= ControlState::SELECTED;
2044 aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX );
2045 aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 );
2046 aTmpPos.AdjustY(nExtra / 2 );
2048 tools::Rectangle aItemRect(aTmpPos, aTmpSz);
2049 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
2050 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
2051 aItemRect, nState, aVal, OUString());
2053 if (!bNativeOk)
2055 aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX );
2056 aTmpPos.setY( aPos.Y() );
2057 aTmpPos.AdjustY(nExtra/2 );
2058 aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) );
2059 if (pData->nBits & MenuItemBits::POPUPSELECT)
2061 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2062 Point aTmpPos2(aPos);
2063 aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 );
2064 aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
2065 pData->aSz.Height())),
2066 DrawFrameStyle::Group);
2068 aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
2069 SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
2073 if (pThisItemOnly && bHighlighted)
2075 // This restores the normal menu or menu bar text
2076 // color for when it is no longer highlighted.
2077 if (IsMenuBar())
2078 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
2079 else
2080 rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
2083 if( bLayout )
2085 if (!IsMenuBar())
2086 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
2087 else
2088 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, pData->aSz);
2092 if (!IsMenuBar())
2093 aTopLeft.AdjustY(pData->aSz.Height() );
2094 else
2095 aTopLeft.AdjustX(pData->aSz.Width() );
2098 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2099 if (bHiddenItems)
2101 sal_Int32 nSize = nFontHeight;
2102 tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize));
2103 lclDrawMoreIndicator(rRenderContext, aRectangle);
2107 Menu* Menu::ImplGetStartMenu()
2109 Menu* pStart = this;
2110 while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
2111 pStart = pStart->pStartedFrom;
2112 return pStart;
2115 void Menu::ImplCallHighlight(sal_uInt16 nItem)
2117 ImplMenuDelData aDelData( this );
2119 nSelectedId = 0;
2120 MenuItemData* pData = pItemList->GetDataFromPos(nItem);
2121 if ( pData )
2122 nSelectedId = pData->nId;
2123 ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) );
2125 if( !aDelData.isDeleted() )
2126 nSelectedId = 0;
2129 IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
2131 nEventId = nullptr;
2132 Select();
2135 Menu* Menu::ImplFindSelectMenu()
2137 Menu* pSelMenu = nEventId ? this : nullptr;
2139 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2141 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2143 if ( pData->pSubMenu )
2144 pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
2147 return pSelMenu;
2150 Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
2152 Menu* pSelMenu = nullptr;
2154 for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
2156 MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
2158 if( pData->nId == nItemId )
2159 pSelMenu = this;
2160 else if ( pData->pSubMenu )
2161 pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
2164 return pSelMenu;
2167 void Menu::RemoveDisabledEntries( bool bCheckPopups, bool bRemoveEmptyPopups )
2169 for ( sal_uInt16 n = 0; n < GetItemCount(); n++ )
2171 bool bRemove = false;
2172 MenuItemData* pItem = pItemList->GetDataFromPos( n );
2173 if ( pItem->eType == MenuItemType::SEPARATOR )
2175 if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
2176 bRemove = true;
2178 else
2179 bRemove = !pItem->bEnabled;
2181 if ( bCheckPopups && pItem->pSubMenu )
2183 pItem->pSubMenu->RemoveDisabledEntries();
2184 if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
2185 bRemove = true;
2188 if ( bRemove )
2189 RemoveItem( n-- );
2192 if ( GetItemCount() )
2194 sal_uInt16 nLast = GetItemCount() - 1;
2195 MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
2196 if ( pItem->eType == MenuItemType::SEPARATOR )
2197 RemoveItem( nLast );
2199 delete mpLayoutData;
2200 mpLayoutData = nullptr;
2203 void Menu::UpdateNativeMenu()
2205 if ( ImplGetSalMenu() )
2206 ImplGetSalMenu()->Update();
2209 void Menu::MenuBarKeyInput(const KeyEvent&)
2213 void Menu::ImplKillLayoutData() const
2215 delete mpLayoutData;
2216 mpLayoutData = nullptr;
2219 void Menu::ImplFillLayoutData() const
2221 if (pWindow && pWindow->IsReallyVisible())
2223 mpLayoutData = new MenuLayoutData;
2224 if (IsMenuBar())
2226 ImplPaint(*pWindow, pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2228 else
2230 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
2231 ImplPaint(*pWindow, pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(),
2232 nullptr, false, true); //FIXME
2237 tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, long nIndex ) const
2239 long nItemIndex = -1;
2240 if( ! mpLayoutData )
2241 ImplFillLayoutData();
2242 if( mpLayoutData )
2244 for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
2246 if( mpLayoutData->m_aLineItemIds[i] == nItemID )
2248 nItemIndex = mpLayoutData->m_aLineIndices[i];
2249 break;
2253 return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
2256 long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
2258 long nIndex = -1;
2259 rItemID = 0;
2260 if( ! mpLayoutData )
2261 ImplFillLayoutData();
2262 if( mpLayoutData )
2264 nIndex = mpLayoutData->GetIndexForPoint( rPoint );
2265 for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
2267 if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
2268 (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
2270 // make index relative to item
2271 nIndex -= mpLayoutData->m_aLineIndices[i];
2272 rItemID = mpLayoutData->m_aLineItemIds[i];
2273 break;
2277 return nIndex;
2280 tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
2282 tools::Rectangle aRet;
2284 if (!mpLayoutData )
2285 ImplFillLayoutData();
2286 if (mpLayoutData)
2288 std::map< sal_uInt16, tools::Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
2289 if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
2290 aRet = it->second;
2292 return aRet;
2295 OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
2297 MenuItemData* pData = pItemList->GetData( nItemId );
2299 if ( pData )
2300 return pData->aAccessibleName;
2302 return OUString();
2305 void Menu::ImplClearSalMenu()
2307 if( mpSalMenu )
2308 ImplGetSVData()->mpDefInst->DestroyMenu( mpSalMenu );
2309 mpSalMenu = nullptr;
2312 void Menu::GetSystemMenuData( SystemMenuData* pData ) const
2314 Menu* pMenu = const_cast<Menu*>(this);
2315 if( pData && pMenu->ImplGetSalMenu() )
2317 pMenu->ImplGetSalMenu()->GetSystemMenuData( pData );
2321 bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
2323 bool bRet = false;
2325 if( pWindow )
2327 if (IsMenuBar())
2328 bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow.get())->GetHighlightedItem() );
2329 else
2330 bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow.get())->GetHighlightedItem() );
2333 return bRet;
2336 void Menu::HighlightItem( sal_uInt16 nItemPos )
2338 if ( pWindow )
2340 if (IsMenuBar())
2342 MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow.get() );
2343 pMenuWin->SetAutoPopup( false );
2344 pMenuWin->ChangeHighlightItem( nItemPos, false );
2346 else
2348 static_cast< MenuFloatingWindow* >( pWindow.get() )->ChangeHighlightItem( nItemPos, false );
2353 MenuBarWindow* MenuBar::getMenuBarWindow()
2355 // so far just a dynamic_cast, hopefully to be turned into something saner
2356 // at some stage
2357 MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(pWindow.get());
2358 //either there is no window (fdo#87663) or it is a MenuBarWindow
2359 assert(!pWindow || pWin);
2360 return pWin;
2363 MenuBar::MenuBar()
2364 : Menu(),
2365 mbCloseBtnVisible(false),
2366 mbFloatBtnVisible(false),
2367 mbHideBtnVisible(false),
2368 mbDisplayable(true)
2370 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2373 MenuBar::MenuBar( const MenuBar& rMenu )
2374 : Menu(),
2375 mbCloseBtnVisible(false),
2376 mbFloatBtnVisible(false),
2377 mbHideBtnVisible(false),
2378 mbDisplayable(true)
2380 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
2381 *this = rMenu;
2384 MenuBar::~MenuBar()
2386 disposeOnce();
2389 void MenuBar::dispose()
2391 ImplDestroy( this, true );
2392 Menu::dispose();
2395 void MenuBar::ClosePopup(Menu *pMenu)
2397 MenuBarWindow* pMenuWin = getMenuBarWindow();
2398 if (!pMenuWin)
2399 return;
2400 pMenuWin->PopupClosed(pMenu);
2403 void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
2405 pWindow->KeyInput(rEvent);
2408 void MenuBar::ShowCloseButton(bool bShow)
2410 ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
2413 void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
2415 if ((bClose != mbCloseBtnVisible) ||
2416 (bFloat != mbFloatBtnVisible) ||
2417 (bHide != mbHideBtnVisible))
2419 mbCloseBtnVisible = bClose;
2420 mbFloatBtnVisible = bFloat;
2421 mbHideBtnVisible = bHide;
2422 MenuBarWindow* pMenuWin = getMenuBarWindow();
2423 if (pMenuWin)
2424 pMenuWin->ShowButtons(bClose, bFloat, bHide);
2428 void MenuBar::LayoutChanged()
2430 MenuBarWindow* pMenuWin = getMenuBarWindow();
2431 if (pMenuWin)
2432 pMenuWin->LayoutChanged();
2435 void MenuBar::SetDisplayable( bool bDisplayable )
2437 if( bDisplayable != mbDisplayable )
2439 if ( ImplGetSalMenu() )
2440 ImplGetSalMenu()->ShowMenuBar( bDisplayable );
2442 mbDisplayable = bDisplayable;
2443 LayoutChanged();
2447 VclPtr<vcl::Window> MenuBar::ImplCreate(vcl::Window* pParent, vcl::Window* pWindow, MenuBar* pMenu)
2449 VclPtr<MenuBarWindow> pMenuBarWindow = dynamic_cast<MenuBarWindow*>(pWindow);
2450 if (!pMenuBarWindow)
2452 pWindow = pMenuBarWindow = VclPtr<MenuBarWindow>::Create( pParent );
2455 pMenu->pStartedFrom = nullptr;
2456 pMenu->pWindow = pWindow;
2457 pMenuBarWindow->SetMenu(pMenu);
2458 long nHeight = pWindow ? pMenu->ImplCalcSize(pWindow).Height() : 0;
2460 // depending on the native implementation or the displayable flag
2461 // the menubar windows is suppressed (ie, height=0)
2462 if (!pMenu->IsDisplayable() || (pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar()))
2464 nHeight = 0;
2467 pMenuBarWindow->SetHeight(nHeight);
2468 return pWindow;
2471 void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete )
2473 vcl::Window *pWindow = pMenu->ImplGetWindow();
2474 if (pWindow && bDelete)
2476 MenuBarWindow* pMenuWin = pMenu->getMenuBarWindow();
2477 if (pMenuWin)
2478 pMenuWin->KillActivePopup();
2479 pWindow->disposeOnce();
2481 pMenu->pWindow = nullptr;
2484 bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
2486 // No keyboard processing when our menubar is invisible
2487 if (!IsDisplayable())
2488 return false;
2490 // No keyboard processing when system handles the menu.
2491 SalMenu *pNativeMenu = ImplGetSalMenu();
2492 if (pNativeMenu && pNativeMenu->VisibleMenuBar())
2494 // Except when the event is the F6 cycle pane event and we can put our
2495 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2496 // where it's not part of the application window)
2497 if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
2498 return false;
2499 if (!pNativeMenu->CanGetFocus())
2500 return false;
2503 bool bDone = false;
2504 // check for enabled, if this method is called from another window...
2505 vcl::Window* pWin = ImplGetWindow();
2506 if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode())
2508 MenuBarWindow* pMenuWin = getMenuBarWindow();
2509 bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
2511 return bDone;
2514 bool MenuBar::ImplHandleCmdEvent( const CommandEvent& rCEvent )
2516 // No keyboard processing when system handles the menu or our menubar is invisible
2517 if( !IsDisplayable() ||
2518 ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2519 return false;
2521 // check for enabled, if this method is called from another window...
2522 MenuBarWindow* pWin = static_cast<MenuBarWindow*>(ImplGetWindow());
2523 if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && ! pWin->IsInModalMode() )
2525 if (rCEvent.GetCommand() == CommandEventId::ModKeyChange && ImplGetSVData()->maNWFData.mbAutoAccel)
2527 const CommandModKeyData* pCData = rCEvent.GetModKeyData ();
2528 if (pWin->nHighlightedItem == ITEMPOS_INVALID)
2530 if (pCData && pCData->IsMod2() && pCData->IsDown())
2531 pWin->SetMBWHideAccel(false);
2532 else
2533 pWin->SetMBWHideAccel(true);
2534 pWin->Invalidate(InvalidateFlags::Update);
2536 return true;
2539 return false;
2542 void MenuBar::SelectItem(sal_uInt16 nId)
2544 if (pWindow)
2546 pWindow->GrabFocus();
2547 nId = GetItemPos( nId );
2549 MenuBarWindow* pMenuWin = getMenuBarWindow();
2550 if (pMenuWin)
2552 // #99705# popup the selected menu
2553 pMenuWin->SetAutoPopup( true );
2554 if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
2556 pMenuWin->KillActivePopup();
2557 pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
2559 if (nId != ITEMPOS_INVALID)
2560 pMenuWin->ChangeHighlightItem( nId, false );
2565 // handler for native menu selection and command events
2566 bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const
2568 if( pMenu )
2570 ImplMenuDelData aDelData( this );
2572 pMenu->pStartedFrom = const_cast<Menu*>(this);
2573 pMenu->bInCallback = true;
2574 pMenu->Activate();
2576 if( !aDelData.isDeleted() )
2577 pMenu->bInCallback = false;
2579 return true;
2582 bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const
2584 if( pMenu )
2586 ImplMenuDelData aDelData( this );
2588 pMenu->pStartedFrom = const_cast<Menu*>(this);
2589 pMenu->bInCallback = true;
2590 pMenu->Deactivate();
2591 if( !aDelData.isDeleted() )
2592 pMenu->bInCallback = false;
2594 return true;
2597 bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const
2599 if( !pMenu )
2600 pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nHighlightEventId);
2601 if( pMenu )
2603 ImplMenuDelData aDelData( pMenu );
2605 if( mnHighlightedItemPos != ITEMPOS_INVALID )
2606 pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, mnHighlightedItemPos );
2608 if( !aDelData.isDeleted() )
2610 pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
2611 pMenu->nSelectedId = nHighlightEventId;
2612 pMenu->pStartedFrom = const_cast<MenuBar*>(this);
2613 pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
2615 return true;
2617 else
2618 return false;
2621 bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
2623 if( !pMenu )
2624 pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId);
2625 if( pMenu )
2627 pMenu->nSelectedId = nCommandEventId;
2628 pMenu->pStartedFrom = const_cast<Menu*>(this);
2629 pMenu->ImplSelect();
2630 return true;
2632 else
2633 return false;
2636 sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBar::MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
2638 MenuBarWindow* pMenuWin = getMenuBarWindow();
2639 return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
2642 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBar::MenuBarButtonCallbackArg&,bool>& rLink )
2644 MenuBarWindow* pMenuWin = getMenuBarWindow();
2645 if (!pMenuWin)
2646 return;
2647 pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
2650 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
2652 MenuBarWindow* pMenuWin = getMenuBarWindow();
2653 if (!pMenuWin)
2654 return;
2655 pMenuWin->RemoveMenuBarButton(nId);
2658 tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
2660 MenuBarWindow* pMenuWin = getMenuBarWindow();
2661 return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle();
2664 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
2666 MenuBarWindow* pMenuWin = getMenuBarWindow();
2667 return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
2670 // bool PopupMenu::bAnyPopupInExecute = false;
2672 MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
2673 return static_cast<MenuFloatingWindow *>(Menu::ImplGetWindow());
2676 PopupMenu::PopupMenu()
2677 : mpLOKNotifier(nullptr)
2679 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2682 PopupMenu::PopupMenu( const PopupMenu& rMenu )
2683 : Menu(),
2684 mpLOKNotifier(nullptr)
2686 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
2687 *this = rMenu;
2690 PopupMenu::~PopupMenu()
2692 disposeOnce();
2695 void PopupMenu::ClosePopup(Menu* pMenu)
2697 MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow());
2698 PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu);
2699 if (p && pMenu)
2700 p->KillActivePopup(pPopup);
2703 bool PopupMenu::IsInExecute()
2705 return GetActivePopupMenu() != nullptr;
2708 PopupMenu* PopupMenu::GetActivePopupMenu()
2710 ImplSVData* pSVData = ImplGetSVData();
2711 return pSVData->maAppData.mpActivePopupMenu;
2714 void PopupMenu::EndExecute()
2716 if ( ImplGetWindow() )
2717 ImplGetFloatingWindow()->EndExecute( 0 );
2720 void PopupMenu::SelectItem(sal_uInt16 nId)
2722 if ( ImplGetWindow() )
2724 if( nId != ITEMPOS_INVALID )
2726 size_t nPos = 0;
2727 MenuItemData* pData = GetItemList()->GetData( nId, nPos );
2728 if (pData && pData->pSubMenu)
2729 ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
2730 else
2731 ImplGetFloatingWindow()->EndExecute( nId );
2733 else
2735 MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
2736 pFloat->GrabFocus();
2738 for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
2740 MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
2741 if( pData->pSubMenu )
2743 pFloat->KillActivePopup();
2746 pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
2751 void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
2753 nSelectedId = nId;
2756 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos )
2758 return Execute( pExecWindow, tools::Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown );
2761 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags )
2763 ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
2765 FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
2766 if ( nFlags & PopupMenuFlags::ExecuteDown )
2767 nPopupModeFlags = FloatWinPopupFlags::Down;
2768 else if ( nFlags & PopupMenuFlags::ExecuteUp )
2769 nPopupModeFlags = FloatWinPopupFlags::Up;
2770 else if ( nFlags & PopupMenuFlags::ExecuteLeft )
2771 nPopupModeFlags = FloatWinPopupFlags::Left;
2772 else if ( nFlags & PopupMenuFlags::ExecuteRight )
2773 nPopupModeFlags = FloatWinPopupFlags::Right;
2774 else
2775 nPopupModeFlags = FloatWinPopupFlags::Down;
2777 if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up
2778 nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2780 if (nFlags & PopupMenuFlags::NoHorzPlacement)
2781 nPopupModeFlags |= FloatWinPopupFlags::NoHorzPlacement;
2783 return ImplExecute( pExecWindow, rRect, nPopupModeFlags, nullptr, false );
2786 void PopupMenu::ImplFlushPendingSelect()
2788 // is there still Select?
2789 Menu* pSelect = ImplFindSelectMenu();
2790 if (pSelect)
2792 // Select should be called prior to leaving execute in a popup menu!
2793 Application::RemoveUserEvent( pSelect->nEventId );
2794 pSelect->nEventId = nullptr;
2795 pSelect->Select();
2799 sal_uInt16 PopupMenu::ImplExecute( const VclPtr<vcl::Window>& pW, const tools::Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst )
2801 if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) )
2802 return 0;
2804 // set the flag to hide or show accelerators in the menu depending on whether the menu was launched by mouse or keyboard shortcut
2805 if( pSFrom && pSFrom->IsMenuBar())
2807 auto pMenuBarWindow = static_cast<MenuBarWindow*>(pSFrom->pWindow.get());
2808 pMenuBarWindow->SetMBWHideAccel( !(pMenuBarWindow->GetMBWMenuKey()) );
2811 delete mpLayoutData;
2812 mpLayoutData = nullptr;
2814 ImplSVData* pSVData = ImplGetSVData();
2816 pStartedFrom = pSFrom;
2817 nSelectedId = 0;
2818 bCanceled = false;
2820 VclPtr<vcl::Window> xFocusId;
2821 bool bRealExecute = false;
2822 if ( !pStartedFrom )
2824 pSVData->maWinData.mbNoDeactivate = true;
2825 xFocusId = Window::SaveFocus();
2826 bRealExecute = true;
2828 else
2830 // assure that only one menu is open at a time
2831 if (pStartedFrom->IsMenuBar() && pSVData->maWinData.mpFirstFloat)
2832 pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
2835 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
2836 tools::Rectangle aRect( rRect );
2837 aRect.SetPos( pW->OutputToScreenPixel( aRect.TopLeft() ) );
2839 if (bRealExecute)
2840 nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
2841 nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose;
2843 bInCallback = true; // set it here, if Activate overridden
2844 Activate();
2845 bInCallback = false;
2847 if ( pW->IsDisposed() )
2848 return 0; // Error
2850 if ( bCanceled || bKilled )
2851 return 0;
2853 if ( !GetItemCount() )
2854 return 0;
2856 // The flag MenuFlags::HideDisabledEntries is inherited.
2857 if ( pSFrom )
2859 if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries )
2860 nMenuFlags |= MenuFlags::HideDisabledEntries;
2861 else
2862 nMenuFlags &= ~MenuFlags::HideDisabledEntries;
2864 else
2865 // #102790# context menus shall never show disabled entries
2866 nMenuFlags |= MenuFlags::HideDisabledEntries;
2868 sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
2869 if ( !nVisibleEntries )
2871 OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
2873 MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, OString());
2874 size_t nPos = 0;
2875 pData = pItemList->GetData( pData->nId, nPos );
2876 assert(pData);
2877 if (pData)
2879 pData->bIsTemporary = true;
2881 ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos);
2884 VclPtrInstance<MenuFloatingWindow> pWin( this, pW, WB_BORDER | WB_SYSTEMWINDOW );
2885 if (comphelper::LibreOfficeKit::isActive() && mpLOKNotifier)
2886 pWin->SetLOKNotifier(mpLOKNotifier);
2888 if( pSVData->maNWFData.mbFlatMenu )
2889 pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
2890 else
2891 pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
2892 pWindow = pWin;
2894 Size aSz = ImplCalcSize( pWin );
2896 tools::Rectangle aDesktopRect(pWin->GetDesktopRectPixel());
2897 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() )
2899 vcl::Window* pDeskW = pWindow->GetWindow( GetWindowType::RealParent );
2900 if( ! pDeskW )
2901 pDeskW = pWindow;
2902 Point aDesktopTL( pDeskW->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) );
2903 aDesktopRect = Application::GetScreenPosSizePixel(
2904 Application::GetBestScreen( tools::Rectangle( aDesktopTL, aRect.GetSize() ) ));
2907 long nMaxHeight = aDesktopRect.GetHeight();
2909 //rhbz#1021915. If a menu won't fit in the desired location the default
2910 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2911 //some cases, e.g. menubars, it's desirable to limit the options to
2912 //above/below and force the menu to scroll if it won't fit
2913 if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
2915 vcl::Window* pRef = pWin;
2916 if ( pRef->GetParent() )
2917 pRef = pRef->GetParent();
2919 tools::Rectangle devRect( pRef->OutputToAbsoluteScreenPixel( aRect.TopLeft() ),
2920 pRef->OutputToAbsoluteScreenPixel( aRect.BottomRight() ) );
2922 long nHeightAbove = devRect.Top() - aDesktopRect.Top();
2923 long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
2924 nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
2927 if (pStartedFrom && pStartedFrom->IsMenuBar())
2928 nMaxHeight -= pW->GetSizePixel().Height();
2929 sal_Int32 nLeft, nTop, nRight, nBottom;
2930 pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
2931 nMaxHeight -= nTop+nBottom;
2932 if ( aSz.Height() > nMaxHeight )
2934 pWin->EnableScrollMenu( true );
2935 sal_uInt16 nStart = ImplGetFirstVisible();
2936 sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
2937 aSz.setHeight( ImplCalcHeight( nEntries ) );
2940 pWin->SetFocusId( xFocusId );
2941 pWin->SetOutputSizePixel( aSz );
2942 if ( GetItemCount() )
2944 SalMenu* pMenu = ImplGetSalMenu();
2945 if( pMenu && bRealExecute && pMenu->ShowNativePopupMenu( pWin, aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus ) )
2947 pWin->StopExecute();
2948 pWin->doShutdown();
2949 pWindow->doLazyDelete();
2950 pWindow = nullptr;
2951 ImplClosePopupToolBox(pW);
2952 ImplFlushPendingSelect();
2953 return nSelectedId;
2955 else
2957 pWin->StartPopupMode( aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus );
2959 if( pSFrom )
2961 sal_uInt16 aPos;
2962 if (pSFrom->IsMenuBar())
2963 aPos = static_cast<MenuBarWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
2964 else
2965 aPos = static_cast<MenuFloatingWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
2967 pWin->SetPosInParent( aPos ); // store position to be sent in SUBMENUDEACTIVATE
2968 pSFrom->ImplCallEventListeners( VclEventId::MenuSubmenuActivate, aPos );
2971 if ( bPreSelectFirst )
2973 size_t nCount = pItemList->size();
2974 for ( size_t n = 0; n < nCount; n++ )
2976 MenuItemData* pData = pItemList->GetDataFromPos( n );
2977 if ( ( pData->bEnabled
2978 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
2980 && ( pData->eType != MenuItemType::SEPARATOR )
2981 && ImplIsVisible( n )
2982 && ImplIsSelectable( n )
2985 pWin->ChangeHighlightItem( n, false );
2986 break;
2990 if ( bRealExecute )
2992 pWin->Execute();
2993 if (pWin->IsDisposed())
2994 return 0;
2996 xFocusId = pWin->GetFocusId();
2997 assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
2998 pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
3000 if ( nSelectedId ) // then clean up .. ( otherwise done by TH )
3002 PopupMenu* pSub = pWin->GetActivePopup();
3003 while ( pSub )
3005 pSub->ImplGetFloatingWindow()->EndPopupMode();
3006 pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
3009 pWin->doShutdown();
3010 pWindow->doLazyDelete();
3011 pWindow = nullptr;
3012 ImplClosePopupToolBox(pW);
3013 ImplFlushPendingSelect();
3016 return bRealExecute ? nSelectedId : 0;
3019 sal_uInt16 PopupMenu::ImplCalcVisEntries( long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
3021 nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3023 long nHeight = 0;
3024 size_t nEntries = pItemList->size();
3025 sal_uInt16 nVisEntries = 0;
3027 if ( pLastVisible )
3028 *pLastVisible = 0;
3030 for ( size_t n = nStartEntry; n < nEntries; n++ )
3032 if ( ImplIsVisible( n ) )
3034 MenuItemData* pData = pItemList->GetDataFromPos( n );
3035 nHeight += pData->aSz.Height();
3036 if ( nHeight > nMaxHeight )
3037 break;
3039 if ( pLastVisible )
3040 *pLastVisible = n;
3041 nVisEntries++;
3044 return nVisEntries;
3047 long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
3049 long nHeight = 0;
3051 sal_uInt16 nFound = 0;
3052 for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ )
3054 if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) )
3056 MenuItemData* pData = pItemList->GetDataFromPos( n );
3057 nHeight += pData->aSz.Height();
3058 nFound++;
3062 nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
3064 return nHeight;
3067 ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
3068 : mpNext( nullptr )
3069 , mpMenu( nullptr )
3071 if( pMenu )
3072 const_cast< Menu* >( pMenu )->ImplAddDel( *this );
3075 ImplMenuDelData::~ImplMenuDelData()
3077 if( mpMenu )
3078 const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
3081 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */