1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/diagnose_ex.h>
21 #include <sal/log.hxx>
23 #include <comphelper/lok.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/mnemonic.hxx>
26 #include <vcl/image.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/help.hxx>
29 #include <vcl/floatwin.hxx>
30 #include <vcl/decoview.hxx>
31 #include <vcl/menu.hxx>
32 #include <vcl/taskpanelist.hxx>
33 #include <vcl/controllayout.hxx>
34 #include <vcl/settings.hxx>
35 #include <vcl/commandinfoprovider.hxx>
37 #include <salinst.hxx>
39 #include <strings.hrc>
41 #include <salmenu.hxx>
42 #include <salframe.hxx>
44 #include "menubarwindow.hxx"
45 #include "menufloatingwindow.hxx"
46 #include "menuitemlist.hxx"
48 #include <com/sun/star/uno/Reference.h>
49 #include <com/sun/star/lang/XComponent.hpp>
50 #include <com/sun/star/accessibility/XAccessible.hpp>
51 #include <vcl/toolkit/unowrap.hxx>
53 #include <vcl/configsettings.hxx>
56 #include <string_view>
62 struct MenuLayoutData
: public ControlLayoutData
64 std::vector
< sal_uInt16
> m_aLineItemIds
;
65 std::map
< sal_uInt16
, tools::Rectangle
> m_aVisibleItemBoundRects
;
72 #define EXTRAITEMHEIGHT 4
73 #define SPACE_AROUND_TITLE 4
75 static bool ImplAccelDisabled()
77 // display of accelerator strings may be suppressed via configuration
78 static int nAccelDisabled
= -1;
80 if( nAccelDisabled
== -1 )
83 vcl::SettingsConfigItem::get()->
84 getValue( "Menu", "SuppressAccelerators" );
85 nAccelDisabled
= aStr
.equalsIgnoreAsciiCase("true") ? 1 : 0;
87 return nAccelDisabled
== 1;
90 static void ImplSetMenuItemData( MenuItemData
* pData
)
94 pData
->eType
= MenuItemType::STRING
;
95 else if ( pData
->aText
.isEmpty() )
96 pData
->eType
= MenuItemType::IMAGE
;
98 pData
->eType
= MenuItemType::STRINGIMAGE
;
103 void ImplClosePopupToolBox( const VclPtr
<vcl::Window
>& pWin
)
105 if ( pWin
->GetType() == WindowType::TOOLBOX
&& ImplGetDockingManager()->IsInPopupMode( pWin
) )
107 ImplDockingWindowWrapper
* pWrapper
= ImplGetDockingManager()->GetDockingWindowWrapper( pWin
);
108 if ( pWrapper
&& pWrapper
->GetFloatingWindow() )
109 pWrapper
->GetFloatingWindow()->EndPopupMode( FloatWinPopupEndFlags::CloseAll
);
113 // TODO: Move to common code with the same function in toolbox
114 // Draw the ">>" - more indicator at the coordinates
115 void lclDrawMoreIndicator(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
117 rRenderContext
.Push(PushFlags::FILLCOLOR
| PushFlags::LINECOLOR
);
118 rRenderContext
.SetLineColor();
120 if (rRenderContext
.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
121 rRenderContext
.SetFillColor(COL_WHITE
);
123 rRenderContext
.SetFillColor(COL_BLACK
);
124 float fScaleFactor
= rRenderContext
.GetDPIScaleFactor();
126 int linewidth
= 1 * fScaleFactor
;
127 int space
= 4 * fScaleFactor
;
129 long width
= 8 * fScaleFactor
;
130 long height
= 5 * fScaleFactor
;
132 //Keep odd b/c drawing code works better
133 if ( height
% 2 == 0 )
136 long heightOrig
= height
;
138 long x
= rRect
.Left() + (rRect
.getWidth() - width
)/2 + 1;
139 long y
= rRect
.Top() + (rRect
.getHeight() - height
)/2 + 1;
142 rRenderContext
.DrawRect( tools::Rectangle( x
, y
, x
+ linewidth
, y
) );
144 rRenderContext
.DrawRect( tools::Rectangle( x
, y
, x
+ linewidth
, y
) );
147 if( height
<= heightOrig
/ 2 + 1) x
--;
151 rRenderContext
.Pop();
154 } // end anonymous namespace
158 : mpFirstDel(nullptr),
159 pItemList(new MenuItemList
),
160 pStartedFrom(nullptr),
164 mnHighlightedItemPos(ITEMPOS_INVALID
),
165 nMenuFlags(MenuFlags::NONE
),
182 ImplCallEventListeners( VclEventId::ObjectDying
, ITEMPOS_INVALID
);
184 // at the window free the reference to the accessible component
185 // and make sure the MenuFloatingWindow knows about our destruction
188 MenuFloatingWindow
* pFloat
= static_cast<MenuFloatingWindow
*>(pWindow
.get());
189 if( pFloat
->pMenu
.get() == this )
190 pFloat
->pMenu
.clear();
191 pWindow
->SetAccessible( css::uno::Reference
< css::accessibility::XAccessible
>() );
194 // dispose accessible components
195 if ( mxAccessible
.is() )
197 css::uno::Reference
< css::lang::XComponent
> xComponent( mxAccessible
, css::uno::UNO_QUERY
);
198 if ( xComponent
.is() )
199 xComponent
->dispose();
203 Application::RemoveUserEvent( nEventId
);
205 // Notify deletion of this menu
206 ImplMenuDelData
* pDelData
= mpFirstDel
;
209 pDelData
->mpMenu
= nullptr;
210 pDelData
= pDelData
->mpNext
;
216 mpLayoutData
.reset();
218 // Native-support: destroy SalMenu
221 pStartedFrom
.clear();
223 VclReferenceBase::dispose();
226 void Menu::CreateAutoMnemonics()
228 MnemonicGenerator aMnemonicGenerator
;
230 for ( n
= 0; n
< pItemList
->size(); n
++ )
232 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
233 if ( ! (pData
->nBits
& MenuItemBits::NOSELECT
) )
234 aMnemonicGenerator
.RegisterMnemonic( pData
->aText
);
236 for ( n
= 0; n
< pItemList
->size(); n
++ )
238 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
239 if ( ! (pData
->nBits
& MenuItemBits::NOSELECT
) )
240 pData
->aText
= aMnemonicGenerator
.CreateMnemonic( pData
->aText
);
244 void Menu::Activate()
248 ImplMenuDelData
aDelData( this );
250 ImplCallEventListeners( VclEventId::MenuActivate
, ITEMPOS_INVALID
);
252 if( !aDelData
.isDeleted() )
254 if ( !aActivateHdl
.Call( this ) )
256 if( !aDelData
.isDeleted() )
258 Menu
* pStartMenu
= ImplGetStartMenu();
259 if ( pStartMenu
&& ( pStartMenu
!= this ) )
261 pStartMenu
->bInCallback
= true;
262 // MT 11/01: Call EventListener here? I don't know...
263 pStartMenu
->aActivateHdl
.Call( this );
264 pStartMenu
->bInCallback
= false;
271 if (!aDelData
.isDeleted() && !(nMenuFlags
& MenuFlags::NoAutoMnemonics
))
272 CreateAutoMnemonics();
275 void Menu::Deactivate()
277 for ( size_t n
= pItemList
->size(); n
; )
279 MenuItemData
* pData
= pItemList
->GetDataFromPos( --n
);
280 if ( pData
->bIsTemporary
)
282 if ( ImplGetSalMenu() )
283 ImplGetSalMenu()->RemoveItem( n
);
285 pItemList
->Remove( n
);
291 ImplMenuDelData
aDelData( this );
293 Menu
* pStartMenu
= ImplGetStartMenu();
294 ImplCallEventListeners( VclEventId::MenuDeactivate
, ITEMPOS_INVALID
);
296 if( !aDelData
.isDeleted() )
298 if ( !aDeactivateHdl
.Call( this ) )
300 if( !aDelData
.isDeleted() )
302 if ( pStartMenu
&& ( pStartMenu
!= this ) )
304 pStartMenu
->bInCallback
= true;
305 pStartMenu
->aDeactivateHdl
.Call( this );
306 pStartMenu
->bInCallback
= false;
312 if( !aDelData
.isDeleted() )
318 void Menu::ImplSelect()
320 MenuItemData
* pData
= GetItemList()->GetData( nSelectedId
);
321 if ( pData
&& (pData
->nBits
& MenuItemBits::AUTOCHECK
) )
323 bool bChecked
= IsItemChecked( nSelectedId
);
324 if ( pData
->nBits
& MenuItemBits::RADIOCHECK
)
327 CheckItem( nSelectedId
);
330 CheckItem( nSelectedId
, !bChecked
);
334 ImplSVData
* pSVData
= ImplGetSVData();
335 pSVData
->maAppData
.mpActivePopupMenu
= nullptr; // if new execute in select()
336 nEventId
= Application::PostUserEvent( LINK( this, Menu
, ImplCallSelect
) );
341 ImplMenuDelData
aDelData( this );
343 ImplCallEventListeners( VclEventId::MenuSelect
, GetItemPos( GetCurItemId() ) );
344 if (aDelData
.isDeleted())
346 if (aSelectHdl
.Call(this))
348 if (aDelData
.isDeleted())
350 Menu
* pStartMenu
= ImplGetStartMenu();
351 if (!pStartMenu
|| (pStartMenu
== this))
353 pStartMenu
->nSelectedId
= nSelectedId
;
354 pStartMenu
->sSelectedIdent
= sSelectedIdent
;
355 pStartMenu
->aSelectHdl
.Call( this );
359 void Menu::ImplSelectWithStart( Menu
* pSMenu
)
361 auto pOldStartedFrom
= pStartedFrom
;
362 pStartedFrom
= pSMenu
;
363 auto pOldStartedStarted
= pOldStartedFrom
? pOldStartedFrom
->pStartedFrom
: VclPtr
<Menu
>();
365 if( pOldStartedFrom
)
366 pOldStartedFrom
->pStartedFrom
= pOldStartedStarted
;
367 pStartedFrom
= pOldStartedFrom
;
371 void Menu::ImplCallEventListeners( VclEventId nEvent
, sal_uInt16 nPos
)
373 ImplMenuDelData
aDelData( this );
375 VclMenuEvent
aEvent( this, nEvent
, nPos
);
377 // This is needed by atk accessibility bridge
378 if ( nEvent
== VclEventId::MenuHighlight
)
380 Application::ImplCallEventListeners( aEvent
);
383 if ( !aDelData
.isDeleted() )
385 // Copy the list, because this can be destroyed when calling a Link...
386 std::list
<Link
<VclMenuEvent
&,void>> aCopy( maEventListeners
);
387 for ( const auto& rLink
: aCopy
)
389 if( std::find(maEventListeners
.begin(), maEventListeners
.end(), rLink
) != maEventListeners
.end() )
390 rLink
.Call( aEvent
);
395 void Menu::AddEventListener( const Link
<VclMenuEvent
&,void>& rEventListener
)
397 maEventListeners
.push_back( rEventListener
);
400 void Menu::RemoveEventListener( const Link
<VclMenuEvent
&,void>& rEventListener
)
402 maEventListeners
.remove( rEventListener
);
405 MenuItemData
* Menu::NbcInsertItem(sal_uInt16 nId
, MenuItemBits nBits
,
406 const OUString
& rStr
, Menu
* pMenu
,
407 size_t nPos
, const OString
&rIdent
)
409 // put Item in MenuItemList
410 MenuItemData
* pData
= pItemList
->Insert(nId
, MenuItemType::STRING
,
411 nBits
, rStr
, pMenu
, nPos
, rIdent
);
413 // update native menu
414 if (ImplGetSalMenu() && pData
->pSalMenuItem
)
415 ImplGetSalMenu()->InsertItem(pData
->pSalMenuItem
.get(), nPos
);
420 void Menu::InsertItem(sal_uInt16 nItemId
, const OUString
& rStr
, MenuItemBits nItemBits
,
421 const OString
&rIdent
, sal_uInt16 nPos
)
423 SAL_WARN_IF( !nItemId
, "vcl", "Menu::InsertItem(): ItemId == 0" );
424 SAL_WARN_IF( GetItemPos( nItemId
) != MENU_ITEM_NOTFOUND
, "vcl",
425 "Menu::InsertItem(): ItemId already exists" );
427 // if Position > ItemCount, append
428 if ( nPos
>= pItemList
->size() )
431 // put Item in MenuItemList
432 NbcInsertItem(nItemId
, nItemBits
, rStr
, this, nPos
, rIdent
);
434 vcl::Window
* pWin
= ImplGetWindow();
435 mpLayoutData
.reset();
438 ImplCalcSize( pWin
);
439 if ( pWin
->IsVisible() )
442 ImplCallEventListeners( VclEventId::MenuInsertItem
, nPos
);
445 void Menu::InsertItem(sal_uInt16 nItemId
, const Image
& rImage
,
446 MenuItemBits nItemBits
, const OString
&rIdent
, sal_uInt16 nPos
)
448 InsertItem(nItemId
, OUString(), nItemBits
, rIdent
, nPos
);
449 SetItemImage( nItemId
, rImage
);
452 void Menu::InsertItem(sal_uInt16 nItemId
, const OUString
& rStr
,
453 const Image
& rImage
, MenuItemBits nItemBits
,
454 const OString
&rIdent
, sal_uInt16 nPos
)
456 InsertItem(nItemId
, rStr
, nItemBits
, rIdent
, nPos
);
457 SetItemImage( nItemId
, rImage
);
460 void Menu::InsertItem(const OUString
& rCommand
, const css::uno::Reference
<css::frame::XFrame
>& rFrame
)
462 sal_uInt16 nItemId
= GetItemCount() + 1;
466 OUString
aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame
));
467 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(rCommand
, aModuleName
);
468 OUString
aLabel(CommandInfoProvider::GetPopupLabelForCommand(aProperties
));
469 OUString
aTooltip(CommandInfoProvider::GetTooltipForCommand(rCommand
, aProperties
, rFrame
));
470 Image
aImage(CommandInfoProvider::GetImageForCommand(rCommand
, rFrame
));
472 InsertItem(nItemId
, aLabel
, aImage
);
473 SetHelpText(nItemId
, aTooltip
);
476 InsertItem(nItemId
, OUString());
478 SetItemCommand(nItemId
, rCommand
);
482 void Menu::InsertSeparator(const OString
&rIdent
, sal_uInt16 nPos
)
484 // do nothing if it's a menu bar
488 // if position > ItemCount, append
489 if ( nPos
>= pItemList
->size() )
492 // put separator in item list
493 pItemList
->InsertSeparator(rIdent
, nPos
);
495 // update native menu
496 size_t itemPos
= ( nPos
!= MENU_APPEND
) ? nPos
: pItemList
->size() - 1;
497 MenuItemData
*pData
= pItemList
->GetDataFromPos( itemPos
);
498 if( ImplGetSalMenu() && pData
&& pData
->pSalMenuItem
)
499 ImplGetSalMenu()->InsertItem( pData
->pSalMenuItem
.get(), nPos
);
501 mpLayoutData
.reset();
503 ImplCallEventListeners( VclEventId::MenuInsertItem
, nPos
);
506 void Menu::RemoveItem( sal_uInt16 nPos
)
508 bool bRemove
= false;
510 if ( nPos
< GetItemCount() )
512 // update native menu
513 if( ImplGetSalMenu() )
514 ImplGetSalMenu()->RemoveItem( nPos
);
516 pItemList
->Remove( nPos
);
520 vcl::Window
* pWin
= ImplGetWindow();
523 ImplCalcSize( pWin
);
524 if ( pWin
->IsVisible() )
527 mpLayoutData
.reset();
530 ImplCallEventListeners( VclEventId::MenuRemoveItem
, nPos
);
533 static void ImplCopyItem( Menu
* pThis
, const Menu
& rMenu
, sal_uInt16 nPos
, sal_uInt16 nNewPos
)
535 MenuItemType eType
= rMenu
.GetItemType( nPos
);
537 if ( eType
== MenuItemType::DONTKNOW
)
540 if ( eType
== MenuItemType::SEPARATOR
)
541 pThis
->InsertSeparator( OString(), nNewPos
);
544 sal_uInt16 nId
= rMenu
.GetItemId( nPos
);
546 SAL_WARN_IF( pThis
->GetItemPos( nId
) != MENU_ITEM_NOTFOUND
, "vcl",
547 "Menu::CopyItem(): ItemId already exists" );
549 MenuItemData
* pData
= rMenu
.GetItemList()->GetData( nId
);
554 if ( eType
== MenuItemType::STRINGIMAGE
)
555 pThis
->InsertItem( nId
, pData
->aText
, pData
->aImage
, pData
->nBits
, pData
->sIdent
, nNewPos
);
556 else if ( eType
== MenuItemType::STRING
)
557 pThis
->InsertItem( nId
, pData
->aText
, pData
->nBits
, pData
->sIdent
, nNewPos
);
559 pThis
->InsertItem( nId
, pData
->aImage
, pData
->nBits
, pData
->sIdent
, nNewPos
);
561 if ( rMenu
.IsItemChecked( nId
) )
562 pThis
->CheckItem( nId
);
563 if ( !rMenu
.IsItemEnabled( nId
) )
564 pThis
->EnableItem( nId
, false );
565 pThis
->SetHelpId( nId
, pData
->aHelpId
);
566 pThis
->SetHelpText( nId
, pData
->aHelpText
);
567 pThis
->SetAccelKey( nId
, pData
->aAccelKey
);
568 pThis
->SetItemCommand( nId
, pData
->aCommandStr
);
569 pThis
->SetHelpCommand( nId
, pData
->aHelpCommandStr
);
571 PopupMenu
* pSubMenu
= rMenu
.GetPopupMenu( nId
);
575 VclPtr
<PopupMenu
> pNewMenu
= VclPtr
<PopupMenu
>::Create( *pSubMenu
);
576 pThis
->SetPopupMenu( nId
, pNewMenu
);
583 for ( sal_uInt16 i
= GetItemCount(); i
; i
-- )
587 sal_uInt16
Menu::GetItemCount() const
589 return static_cast<sal_uInt16
>(pItemList
->size());
592 sal_uInt16
Menu::ImplGetVisibleItemCount() const
594 sal_uInt16 nItems
= 0;
595 for ( size_t n
= pItemList
->size(); n
; )
597 if ( ImplIsVisible( --n
) )
603 sal_uInt16
Menu::ImplGetFirstVisible() const
605 for ( size_t n
= 0; n
< pItemList
->size(); n
++ )
607 if ( ImplIsVisible( n
) )
610 return ITEMPOS_INVALID
;
613 sal_uInt16
Menu::ImplGetPrevVisible( sal_uInt16 nPos
) const
615 for ( size_t n
= nPos
; n
; )
617 if (ImplIsVisible(--n
))
620 return ITEMPOS_INVALID
;
623 sal_uInt16
Menu::ImplGetNextVisible( sal_uInt16 nPos
) const
625 for ( size_t n
= nPos
+1; n
< pItemList
->size(); n
++ )
627 if ( ImplIsVisible( n
) )
630 return ITEMPOS_INVALID
;
633 sal_uInt16
Menu::GetItemId(sal_uInt16 nPos
) const
635 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
643 sal_uInt16
Menu::GetItemId(const OString
&rIdent
) const
645 for (size_t n
= 0; n
< pItemList
->size(); ++n
)
647 MenuItemData
* pData
= pItemList
->GetDataFromPos(n
);
648 if (pData
&& pData
->sIdent
== rIdent
)
651 return MENU_ITEM_NOTFOUND
;
654 sal_uInt16
Menu::GetItemPos( sal_uInt16 nItemId
) const
657 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
660 return static_cast<sal_uInt16
>(nPos
);
662 return MENU_ITEM_NOTFOUND
;
665 MenuItemType
Menu::GetItemType( sal_uInt16 nPos
) const
667 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
672 return MenuItemType::DONTKNOW
;
675 OString
Menu::GetItemIdent(sal_uInt16 nId
) const
677 const MenuItemData
* pData
= pItemList
->GetData(nId
);
678 return pData
? pData
->sIdent
: OString();
681 void Menu::SetItemBits( sal_uInt16 nItemId
, MenuItemBits nBits
)
684 MenuItemData
* pData
= pItemList
->GetData(nItemId
, nPos
);
686 if (pData
&& (pData
->nBits
!= nBits
))
688 pData
->nBits
= nBits
;
690 // update native menu
691 if (ImplGetSalMenu())
692 ImplGetSalMenu()->SetItemBits(nPos
, nBits
);
696 MenuItemBits
Menu::GetItemBits( sal_uInt16 nItemId
) const
698 MenuItemBits nBits
= MenuItemBits::NONE
;
699 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
701 nBits
= pData
->nBits
;
705 void Menu::SetUserValue(sal_uInt16 nItemId
, void* nUserValue
, MenuUserDataReleaseFunction aFunc
)
707 MenuItemData
* pData
= pItemList
->GetData(nItemId
);
710 if (pData
->aUserValueReleaseFunc
)
711 pData
->aUserValueReleaseFunc(pData
->nUserValue
);
712 pData
->aUserValueReleaseFunc
= aFunc
;
713 pData
->nUserValue
= nUserValue
;
717 void* Menu::GetUserValue( sal_uInt16 nItemId
) const
719 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
720 return pData
? pData
->nUserValue
: nullptr;
723 void Menu::SetPopupMenu( sal_uInt16 nItemId
, PopupMenu
* pMenu
)
726 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
728 // Item does not exist -> return NULL
732 // same menu, nothing to do
733 if ( static_cast<PopupMenu
*>(pData
->pSubMenu
.get()) == pMenu
)
737 auto oldSubMenu
= pData
->pSubMenu
;
740 pData
->pSubMenu
= pMenu
;
742 // #112023# Make sure pStartedFrom does not point to invalid (old) data
743 if ( pData
->pSubMenu
)
744 pData
->pSubMenu
->pStartedFrom
= nullptr;
746 // set native submenu
747 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
750 ImplGetSalMenu()->SetSubMenu( pData
->pSalMenuItem
.get(), pMenu
->ImplGetSalMenu(), nPos
);
752 ImplGetSalMenu()->SetSubMenu( pData
->pSalMenuItem
.get(), nullptr, nPos
);
755 oldSubMenu
.disposeAndClear();
757 ImplCallEventListeners( VclEventId::MenuSubmenuChanged
, nPos
);
760 PopupMenu
* Menu::GetPopupMenu( sal_uInt16 nItemId
) const
762 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
765 return static_cast<PopupMenu
*>(pData
->pSubMenu
.get());
770 void Menu::SetAccelKey( sal_uInt16 nItemId
, const KeyCode
& rKeyCode
)
773 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
778 if ( pData
->aAccelKey
== rKeyCode
)
781 pData
->aAccelKey
= rKeyCode
;
783 // update native menu
784 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
785 ImplGetSalMenu()->SetAccelerator( nPos
, pData
->pSalMenuItem
.get(), rKeyCode
, rKeyCode
.GetName() );
788 KeyCode
Menu::GetAccelKey( sal_uInt16 nItemId
) const
790 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
793 return pData
->aAccelKey
;
798 KeyEvent
Menu::GetActivationKey( sal_uInt16 nItemId
) const
801 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
804 sal_Int32 nPos
= pData
->aText
.indexOf( '~' );
805 if( nPos
!= -1 && nPos
< pData
->aText
.getLength()-1 )
807 sal_uInt16 nCode
= 0;
808 sal_Unicode cAccel
= pData
->aText
[nPos
+1];
809 if( cAccel
>= 'a' && cAccel
<= 'z' )
810 nCode
= KEY_A
+ (cAccel
-'a');
811 else if( cAccel
>= 'A' && cAccel
<= 'Z' )
812 nCode
= KEY_A
+ (cAccel
-'A');
813 else if( cAccel
>= '0' && cAccel
<= '9' )
814 nCode
= KEY_0
+ (cAccel
-'0');
816 aRet
= KeyEvent( cAccel
, KeyCode( nCode
, KEY_MOD2
) );
823 void Menu::CheckItem( sal_uInt16 nItemId
, bool bCheck
)
826 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
828 if ( !pData
|| pData
->bChecked
== bCheck
)
831 // if radio-check, then uncheck previous
832 if ( bCheck
&& (pData
->nBits
& MenuItemBits::AUTOCHECK
) &&
833 (pData
->nBits
& MenuItemBits::RADIOCHECK
) )
835 MenuItemData
* pGroupData
;
836 sal_uInt16 nGroupPos
;
837 sal_uInt16 nItemCount
= GetItemCount();
843 pGroupData
= pItemList
->GetDataFromPos( nGroupPos
-1 );
844 if ( pGroupData
->nBits
& MenuItemBits::RADIOCHECK
)
846 if ( IsItemChecked( pGroupData
->nId
) )
848 CheckItem( pGroupData
->nId
, false );
861 while ( nGroupPos
< nItemCount
)
863 pGroupData
= pItemList
->GetDataFromPos( nGroupPos
);
864 if ( pGroupData
->nBits
& MenuItemBits::RADIOCHECK
)
866 if ( IsItemChecked( pGroupData
->nId
) )
868 CheckItem( pGroupData
->nId
, false );
879 pData
->bChecked
= bCheck
;
881 // update native menu
882 if( ImplGetSalMenu() )
883 ImplGetSalMenu()->CheckItem( nPos
, bCheck
);
885 ImplCallEventListeners( bCheck
? VclEventId::MenuItemChecked
: VclEventId::MenuItemUnchecked
, nPos
);
888 void Menu::CheckItem( const OString
&rIdent
, bool bCheck
)
890 CheckItem( GetItemId( rIdent
), bCheck
);
893 bool Menu::IsItemChecked( sal_uInt16 nItemId
) const
896 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
901 return pData
->bChecked
;
904 void Menu::EnableItem( sal_uInt16 nItemId
, bool bEnable
)
907 MenuItemData
* pItemData
= pItemList
->GetData( nItemId
, nPos
);
909 if ( pItemData
&& ( pItemData
->bEnabled
!= bEnable
) )
911 pItemData
->bEnabled
= bEnable
;
913 vcl::Window
* pWin
= ImplGetWindow();
914 if ( pWin
&& pWin
->IsVisible() )
916 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
918 size_t nCount
= pItemList
->size();
919 for ( size_t n
= 0; n
< nCount
; n
++ )
921 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
924 pWin
->Invalidate( tools::Rectangle( Point( nX
, 0 ), Size( pData
->aSz
.Width(), pData
->aSz
.Height() ) ) );
927 nX
+= pData
->aSz
.Width();
930 // update native menu
931 if( ImplGetSalMenu() )
932 ImplGetSalMenu()->EnableItem( nPos
, bEnable
);
934 ImplCallEventListeners( bEnable
? VclEventId::MenuEnable
: VclEventId::MenuDisable
, nPos
);
938 bool Menu::IsItemEnabled( sal_uInt16 nItemId
) const
941 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
946 return pData
->bEnabled
;
949 void Menu::ShowItem( sal_uInt16 nItemId
, bool bVisible
)
952 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
954 SAL_WARN_IF(IsMenuBar() && !bVisible
, "vcl", "Menu::ShowItem - ignored for menu bar entries!");
955 if (!IsMenuBar()&& pData
&& (pData
->bVisible
!= bVisible
))
957 vcl::Window
* pWin
= ImplGetWindow();
958 if ( pWin
&& pWin
->IsVisible() )
960 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
963 pData
->bVisible
= bVisible
;
965 // update native menu
966 if( ImplGetSalMenu() )
967 ImplGetSalMenu()->ShowItem( nPos
, bVisible
);
971 void Menu::SetItemText( sal_uInt16 nItemId
, const OUString
& rStr
)
974 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
979 if ( rStr
!= pData
->aText
)
982 // Clear layout for aText.
983 pData
->aTextGlyphs
.Invalidate();
984 ImplSetMenuItemData( pData
);
985 // update native menu
986 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
987 ImplGetSalMenu()->SetItemText( nPos
, pData
->pSalMenuItem
.get(), rStr
);
989 vcl::Window
* pWin
= ImplGetWindow();
990 mpLayoutData
.reset();
991 if (pWin
&& IsMenuBar())
993 ImplCalcSize( pWin
);
994 if ( pWin
->IsVisible() )
998 ImplCallEventListeners( VclEventId::MenuItemTextChanged
, nPos
);
1002 OUString
Menu::GetItemText( sal_uInt16 nItemId
) const
1005 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1008 return pData
->aText
;
1013 void Menu::SetItemImage( sal_uInt16 nItemId
, const Image
& rImage
)
1016 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1021 pData
->aImage
= rImage
;
1022 ImplSetMenuItemData( pData
);
1024 // update native menu
1025 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
1026 ImplGetSalMenu()->SetItemImage( nPos
, pData
->pSalMenuItem
.get(), rImage
);
1029 Image
Menu::GetItemImage( sal_uInt16 nItemId
) const
1031 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1034 return pData
->aImage
;
1039 void Menu::SetItemCommand( sal_uInt16 nItemId
, const OUString
& rCommand
)
1042 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1045 pData
->aCommandStr
= rCommand
;
1048 OUString
Menu::GetItemCommand( sal_uInt16 nItemId
) const
1050 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1053 return pData
->aCommandStr
;
1058 void Menu::SetHelpCommand( sal_uInt16 nItemId
, const OUString
& rStr
)
1060 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1063 pData
->aHelpCommandStr
= rStr
;
1066 OUString
Menu::GetHelpCommand( sal_uInt16 nItemId
) const
1068 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1071 return pData
->aHelpCommandStr
;
1076 void Menu::SetHelpText( sal_uInt16 nItemId
, const OUString
& rStr
)
1078 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1081 pData
->aHelpText
= rStr
;
1084 OUString
Menu::ImplGetHelpText( sal_uInt16 nItemId
) const
1086 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1088 if ( pData
&& pData
->aHelpText
.isEmpty() &&
1089 (( !pData
->aHelpId
.isEmpty() ) || ( !pData
->aCommandStr
.isEmpty() )))
1091 Help
* pHelp
= Application::GetHelp();
1094 if (!pData
->aCommandStr
.isEmpty())
1095 pData
->aHelpText
= pHelp
->GetHelpText( pData
->aCommandStr
, static_cast<weld::Widget
*>(nullptr) );
1096 if (pData
->aHelpText
.isEmpty() && !pData
->aHelpId
.isEmpty())
1097 pData
->aHelpText
= pHelp
->GetHelpText( OStringToOUString( pData
->aHelpId
, RTL_TEXTENCODING_UTF8
), static_cast<weld::Widget
*>(nullptr) );
1104 OUString
Menu::GetHelpText( sal_uInt16 nItemId
) const
1106 return ImplGetHelpText( nItemId
);
1109 void Menu::SetTipHelpText( sal_uInt16 nItemId
, const OUString
& rStr
)
1111 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1114 pData
->aTipHelpText
= rStr
;
1117 OUString
Menu::GetTipHelpText( sal_uInt16 nItemId
) const
1119 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1122 return pData
->aTipHelpText
;
1127 void Menu::SetHelpId( sal_uInt16 nItemId
, const OString
& rHelpId
)
1129 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1132 pData
->aHelpId
= rHelpId
;
1135 OString
Menu::GetHelpId( sal_uInt16 nItemId
) const
1139 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1143 if ( !pData
->aHelpId
.isEmpty() )
1144 aRet
= pData
->aHelpId
;
1146 aRet
= OUStringToOString( pData
->aCommandStr
, RTL_TEXTENCODING_UTF8
);
1152 Menu
& Menu::operator=( const Menu
& rMenu
)
1161 sal_uInt16 nCount
= rMenu
.GetItemCount();
1162 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1163 ImplCopyItem( this, rMenu
, i
, MENU_APPEND
);
1165 aActivateHdl
= rMenu
.aActivateHdl
;
1166 aDeactivateHdl
= rMenu
.aDeactivateHdl
;
1167 aSelectHdl
= rMenu
.aSelectHdl
;
1168 aTitleText
= rMenu
.aTitleText
;
1169 nTitleHeight
= rMenu
.nTitleHeight
;
1174 // Returns true if the item is completely hidden on the GUI and shouldn't
1175 // be possible to interact with
1176 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos
) const
1178 MenuItemData
* pData
= pItemList
->GetDataFromPos(nPos
);
1181 MenuItemData
* pPreviousData
= pItemList
->GetDataFromPos( nPos
- 1 );
1182 if (pPreviousData
&& pPreviousData
->bHiddenOnGUI
)
1190 bool Menu::ImplIsVisible( sal_uInt16 nPos
) const
1192 bool bVisible
= true;
1194 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
1195 // check general visibility first
1196 if( pData
&& !pData
->bVisible
)
1199 if ( bVisible
&& pData
&& pData
->eType
== MenuItemType::SEPARATOR
)
1201 if( nPos
== 0 ) // no separator should be shown at the very beginning
1205 // always avoid adjacent separators
1206 size_t nCount
= pItemList
->size();
1208 MenuItemData
* pNextData
= nullptr;
1209 // search next visible item
1210 for( n
= nPos
+ 1; n
< nCount
; n
++ )
1212 pNextData
= pItemList
->GetDataFromPos( n
);
1213 if( pNextData
&& pNextData
->bVisible
)
1215 if( pNextData
->eType
== MenuItemType::SEPARATOR
|| ImplIsVisible(n
) )
1219 if( n
== nCount
) // no next visible item
1221 // check for separator
1222 if( pNextData
&& pNextData
->bVisible
&& pNextData
->eType
== MenuItemType::SEPARATOR
)
1227 for( n
= nPos
; n
> 0; n
-- )
1229 pNextData
= pItemList
->GetDataFromPos( n
-1 );
1230 if( pNextData
&& pNextData
->bVisible
)
1232 if( pNextData
->eType
!= MenuItemType::SEPARATOR
&& ImplIsVisible(n
-1) )
1236 if( n
== 0 ) // no previous visible item
1242 // not allowed for menubar, as I do not know
1243 // whether a menu-entry will disappear or will appear
1244 if (bVisible
&& !IsMenuBar() && (nMenuFlags
& MenuFlags::HideDisabledEntries
) &&
1245 !(nMenuFlags
& MenuFlags::AlwaysShowDisabledEntries
))
1247 if( !pData
) // e.g. nPos == ITEMPOS_INVALID
1249 else if ( pData
->eType
!= MenuItemType::SEPARATOR
) // separators handled above
1251 // tdf#86850 Always display clipboard functions
1252 if ( pData
->aCommandStr
== ".uno:Cut" || pData
->aCommandStr
== ".uno:Copy" || pData
->aCommandStr
== ".uno:Paste" )
1255 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1256 bVisible
= pData
->bEnabled
; // do not check submenus as they might be filled at Activate().
1263 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos
) const
1265 return IsMenuVisible() && ImplIsVisible( nItemPos
);
1268 bool Menu::IsMenuVisible() const
1270 return pWindow
&& pWindow
->IsReallyVisible();
1273 bool Menu::ImplIsSelectable( sal_uInt16 nPos
) const
1275 bool bSelectable
= true;
1277 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
1278 // check general visibility first
1279 if ( pData
&& ( pData
->nBits
& MenuItemBits::NOSELECT
) )
1280 bSelectable
= false;
1285 css::uno::Reference
<css::accessibility::XAccessible
> Menu::GetAccessible()
1287 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1288 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1289 // mxAccessible member only for sub menus.
1292 for ( sal_uInt16 i
= 0, nCount
= pStartedFrom
->GetItemCount(); i
< nCount
; ++i
)
1294 sal_uInt16 nItemId
= pStartedFrom
->GetItemId( i
);
1295 if ( static_cast< Menu
* >( pStartedFrom
->GetPopupMenu( nItemId
) ) == this )
1297 css::uno::Reference
<css::accessibility::XAccessible
> xParent
= pStartedFrom
->GetAccessible();
1300 css::uno::Reference
<css::accessibility::XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
1301 if (xParentContext
.is())
1302 return xParentContext
->getAccessibleChild( i
);
1307 else if ( !mxAccessible
.is() )
1309 UnoWrapperBase
* pWrapper
= UnoWrapperBase::GetUnoWrapper();
1311 mxAccessible
= pWrapper
->CreateAccessible(this, IsMenuBar());
1314 return mxAccessible
;
1317 void Menu::SetAccessible(const css::uno::Reference
<css::accessibility::XAccessible
>& rxAccessible
)
1319 mxAccessible
= rxAccessible
;
1322 Size
Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext
const & rRenderContext
, long& rCheckHeight
, long& rRadioHeight
) const
1324 long nCheckWidth
= 0, nRadioWidth
= 0;
1325 rCheckHeight
= rRadioHeight
= 0;
1329 ImplControlValue aVal
;
1330 tools::Rectangle aNativeBounds
;
1331 tools::Rectangle aNativeContent
;
1333 tools::Rectangle
aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1334 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItemCheckMark
))
1336 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::MenuItemCheckMark
,
1337 aCtrlRegion
, ControlState::ENABLED
, aVal
,
1338 aNativeBounds
, aNativeContent
))
1340 rCheckHeight
= aNativeBounds
.GetHeight();
1341 nCheckWidth
= aNativeContent
.GetWidth();
1344 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItemRadioMark
))
1346 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::MenuItemRadioMark
,
1347 aCtrlRegion
, ControlState::ENABLED
, aVal
,
1348 aNativeBounds
, aNativeContent
))
1350 rRadioHeight
= aNativeBounds
.GetHeight();
1351 nRadioWidth
= aNativeContent
.GetWidth();
1355 return Size(std::max(nCheckWidth
, nRadioWidth
), std::max(rCheckHeight
, rRadioHeight
));
1358 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext
const & rRenderContext
, Size
& rArrowSize
, long& rArrowSpacing
)
1360 ImplControlValue aVal
;
1361 tools::Rectangle aNativeBounds
;
1362 tools::Rectangle aNativeContent
;
1363 tools::Rectangle
aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1364 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::SubmenuArrow
))
1366 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::SubmenuArrow
,
1367 aCtrlRegion
, ControlState::ENABLED
,
1368 aVal
, aNativeBounds
, aNativeContent
))
1370 Size
aSize(aNativeContent
.GetWidth(), aNativeContent
.GetHeight());
1372 rArrowSpacing
= aNativeBounds
.GetWidth() - aNativeContent
.GetWidth();
1379 void Menu::ImplAddDel( ImplMenuDelData
& rDel
)
1381 SAL_WARN_IF( rDel
.mpMenu
, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1385 rDel
.mpNext
= mpFirstDel
;
1390 void Menu::ImplRemoveDel( ImplMenuDelData
& rDel
)
1392 rDel
.mpMenu
= nullptr;
1393 if ( mpFirstDel
== &rDel
)
1395 mpFirstDel
= rDel
.mpNext
;
1399 ImplMenuDelData
* pData
= mpFirstDel
;
1400 while ( pData
&& (pData
->mpNext
!= &rDel
) )
1401 pData
= pData
->mpNext
;
1403 SAL_WARN_IF( !pData
, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1405 pData
->mpNext
= rDel
.mpNext
;
1409 Size
Menu::ImplCalcSize( vcl::Window
* pWin
)
1411 // | Check/Radio/Image| Text| Accel/Popup|
1413 // for symbols: nFontHeight x nFontHeight
1414 long nFontHeight
= pWin
->GetTextHeight();
1415 long nExtra
= nFontHeight
/4;
1417 long nMinMenuItemHeight
= nFontHeight
;
1418 long nCheckHeight
= 0, nRadioHeight
= 0;
1419 Size aMaxSize
= ImplGetNativeCheckAndRadioSize(*pWin
, nCheckHeight
, nRadioHeight
); // FIXME
1420 if( aMaxSize
.Height() > nMinMenuItemHeight
)
1421 nMinMenuItemHeight
= aMaxSize
.Height();
1425 const StyleSettings
& rSettings
= pWin
->GetSettings().GetStyleSettings();
1426 if ( rSettings
.GetUseImagesInMenus() )
1428 if ( 16 > nMinMenuItemHeight
)
1429 nMinMenuItemHeight
= 16;
1430 for ( size_t i
= pItemList
->size(); i
; )
1432 MenuItemData
* pData
= pItemList
->GetDataFromPos( --i
);
1433 if ( ImplIsVisible( i
)
1434 && ( ( pData
->eType
== MenuItemType::IMAGE
)
1435 || ( pData
->eType
== MenuItemType::STRINGIMAGE
)
1439 Size aImgSz
= pData
->aImage
.GetSizePixel();
1440 if ( aImgSz
.Height() > aMaxImgSz
.Height() )
1441 aMaxImgSz
.setHeight( aImgSz
.Height() );
1442 if ( aImgSz
.Height() > nMinMenuItemHeight
)
1443 nMinMenuItemHeight
= aImgSz
.Height();
1450 long nCheckWidth
= 0;
1453 for ( size_t n
= pItemList
->size(); n
; )
1455 MenuItemData
* pData
= pItemList
->GetDataFromPos( --n
);
1457 pData
->aSz
.setHeight( 0 );
1458 pData
->aSz
.setWidth( 0 );
1460 if ( ImplIsVisible( n
) )
1465 if (!IsMenuBar()&& (pData
->eType
== MenuItemType::SEPARATOR
))
1467 pData
->aSz
.setHeight( 4 );
1471 if (!IsMenuBar() && ((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1473 Size aImgSz
= pData
->aImage
.GetSizePixel();
1475 aImgSz
.AdjustHeight(4 ); // add a border for native marks
1476 aImgSz
.AdjustWidth(4 ); // add a border for native marks
1477 if ( aImgSz
.Width() > aMaxImgSz
.Width() )
1478 aMaxImgSz
.setWidth( aImgSz
.Width() );
1479 if ( aImgSz
.Height() > aMaxImgSz
.Height() )
1480 aMaxImgSz
.setHeight( aImgSz
.Height() );
1481 if ( aImgSz
.Height() > pData
->aSz
.Height() )
1482 pData
->aSz
.setHeight( aImgSz
.Height() );
1486 if (!IsMenuBar() && pData
->HasCheck())
1488 nCheckWidth
= aMaxSize
.Width();
1489 // checks / images take the same place
1490 if( ! ( ( pData
->eType
== MenuItemType::IMAGE
) || ( pData
->eType
== MenuItemType::STRINGIMAGE
) ) )
1491 nWidth
+= nCheckWidth
+ nExtra
* 2;
1495 if ( (pData
->eType
== MenuItemType::STRING
) || (pData
->eType
== MenuItemType::STRINGIMAGE
) )
1497 const SalLayoutGlyphs
* pGlyphs
= pData
->GetTextGlyphs(pWin
);
1498 long nTextWidth
= pWin
->GetCtrlTextWidth(pData
->aText
, pGlyphs
);
1499 long nTextHeight
= pWin
->GetTextHeight();
1503 if ( nTextHeight
> pData
->aSz
.Height() )
1504 pData
->aSz
.setHeight( nTextHeight
);
1506 pData
->aSz
.setWidth( nTextWidth
+ 4*nExtra
);
1507 aSz
.AdjustWidth(pData
->aSz
.Width() );
1510 pData
->aSz
.setHeight( std::max( std::max( nTextHeight
, pData
->aSz
.Height() ), nMinMenuItemHeight
) );
1512 nWidth
+= nTextWidth
;
1516 if (!IsMenuBar()&& pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1518 OUString aName
= pData
->aAccelKey
.GetName();
1519 long nAccWidth
= pWin
->GetTextWidth( aName
);
1520 nAccWidth
+= nExtra
;
1521 nWidth
+= nAccWidth
;
1525 if (!IsMenuBar() && pData
->pSubMenu
)
1527 if ( nFontHeight
> nWidth
)
1528 nWidth
+= nFontHeight
;
1530 pData
->aSz
.setHeight( std::max( std::max( nFontHeight
, pData
->aSz
.Height() ), nMinMenuItemHeight
) );
1533 pData
->aSz
.AdjustHeight(EXTRAITEMHEIGHT
); // little bit more distance
1536 aSz
.AdjustHeight(pData
->aSz
.Height() );
1538 if ( nWidth
> nMaxWidth
)
1544 // Additional space for title
1546 if (!IsMenuBar() && aTitleText
.getLength() > 0) {
1547 // Set expected font
1548 pWin
->Push(PushFlags::FONT
);
1549 vcl::Font aFont
= pWin
->GetFont();
1550 aFont
.SetWeight(WEIGHT_BOLD
);
1551 pWin
->SetFont(aFont
);
1553 // Compute text bounding box
1554 tools::Rectangle aTextBoundRect
;
1555 pWin
->GetTextBoundRect(aTextBoundRect
, aTitleText
);
1557 // Vertically, one height of char + extra space for decoration
1558 nTitleHeight
= aTextBoundRect
.GetSize().Height() + 4 * SPACE_AROUND_TITLE
;
1559 aSz
.AdjustHeight(nTitleHeight
);
1561 long nWidth
= aTextBoundRect
.GetSize().Width() + 4 * SPACE_AROUND_TITLE
;
1563 if ( nWidth
> nMaxWidth
)
1569 // popup menus should not be wider than half the screen
1570 // except on rather small screens
1571 // TODO: move GetScreenNumber from SystemWindow to Window ?
1572 // currently we rely on internal privileges
1573 unsigned int nDisplayScreen
= pWin
->ImplGetWindowImpl()->mpFrame
->maGeometry
.nDisplayScreenNumber
;
1574 tools::Rectangle
aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen
) );
1575 long nScreenWidth
= aDispRect
.GetWidth() >= 800 ? aDispRect
.GetWidth() : 800;
1576 if( nMaxWidth
> nScreenWidth
/2 )
1577 nMaxWidth
= nScreenWidth
/2;
1579 sal_uInt16 gfxExtra
= static_cast<sal_uInt16
>(std::max( nExtra
, 7L )); // #107710# increase space between checkmarks/images/text
1580 nImgOrChkPos
= static_cast<sal_uInt16
>(nExtra
);
1581 long nImgOrChkWidth
= 0;
1582 if( aMaxSize
.Height() > 0 ) // NWF case
1583 nImgOrChkWidth
= aMaxSize
.Height() + nExtra
;
1584 else // non NWF case
1585 nImgOrChkWidth
= nFontHeight
/2 + gfxExtra
;
1586 nImgOrChkWidth
= std::max( nImgOrChkWidth
, aMaxImgSz
.Width() + gfxExtra
);
1587 nTextPos
= static_cast<sal_uInt16
>(nImgOrChkPos
+ nImgOrChkWidth
);
1588 nTextPos
= nTextPos
+ gfxExtra
;
1590 aSz
.setWidth( nTextPos
+ nMaxWidth
+ nExtra
);
1591 aSz
.AdjustWidth(4*nExtra
); // a _little_ more ...
1593 aSz
.AdjustWidth(2*ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
);
1594 aSz
.AdjustHeight(2*ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
);
1598 nTextPos
= static_cast<sal_uInt16
>(2*nExtra
);
1599 aSz
.setHeight( nFontHeight
+6 );
1601 // get menubar height from native methods if supported
1602 if( pWindow
->IsNativeControlSupported( ControlType::Menubar
, ControlPart::Entire
) )
1604 ImplControlValue aVal
;
1605 tools::Rectangle aNativeBounds
;
1606 tools::Rectangle aNativeContent
;
1608 tools::Rectangle
aCtrlRegion( tmp
, Size( 100, 15 ) );
1609 if( pWindow
->GetNativeControlRegion( ControlType::Menubar
,
1610 ControlPart::Entire
,
1612 ControlState::ENABLED
,
1618 int nNativeHeight
= aNativeBounds
.GetHeight();
1619 if( nNativeHeight
> aSz
.Height() )
1620 aSz
.setHeight( nNativeHeight
);
1624 // account for the size of the close button, which actually is a toolbox
1625 // due to NWF this is variable
1626 long nCloseButtonHeight
= static_cast<MenuBarWindow
*>(pWindow
.get())->MinCloseButtonSize().Height();
1627 if (aSz
.Height() < nCloseButtonHeight
)
1628 aSz
.setHeight( nCloseButtonHeight
);
1634 static void ImplPaintCheckBackground(vcl::RenderContext
& rRenderContext
, vcl::Window
const & rWindow
, const tools::Rectangle
& i_rRect
, bool i_bHighlight
)
1636 bool bNativeOk
= false;
1637 if (rRenderContext
.IsNativeControlSupported(ControlType::Toolbar
, ControlPart::Button
))
1639 ImplControlValue aControlValue
;
1640 aControlValue
.setTristateVal(ButtonValue::On
);
1642 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::Toolbar
, ControlPart::Button
,
1644 ControlState::PRESSED
| ControlState::ENABLED
,
1651 const StyleSettings
& rSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1652 Color
aColor( i_bHighlight
? rSettings
.GetMenuHighlightTextColor() : rSettings
.GetHighlightColor() );
1653 RenderTools::DrawSelectionBackground(rRenderContext
, rWindow
, i_rRect
, 0, i_bHighlight
, true, false, nullptr, 2, &aColor
);
1657 static OUString
getShortenedString( const OUString
& i_rLong
, vcl::RenderContext
const & rRenderContext
, long i_nMaxWidth
)
1659 sal_Int32 nPos
= -1;
1660 OUString
aNonMnem(OutputDevice::GetNonMnemonicString(i_rLong
, nPos
));
1661 aNonMnem
= rRenderContext
.GetEllipsisString( aNonMnem
, i_nMaxWidth
, DrawTextFlags::CenterEllipsis
);
1662 // re-insert mnemonic
1665 if (nPos
< aNonMnem
.getLength() && i_rLong
[nPos
+1] == aNonMnem
[nPos
])
1667 OUStringBuffer
aBuf( i_rLong
.getLength() );
1668 aBuf
.append( std::u16string_view(aNonMnem
).substr(0, nPos
) );
1670 aBuf
.append( std::u16string_view(aNonMnem
).substr(nPos
) );
1671 aNonMnem
= aBuf
.makeStringAndClear();
1677 void Menu::ImplPaintMenuTitle(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
) const
1679 // Save previous graphical settings, set new one
1680 rRenderContext
.Push(PushFlags::FONT
| PushFlags::FILLCOLOR
);
1681 Wallpaper aOldBackground
= rRenderContext
.GetBackground();
1683 Color aBackgroundColor
= rRenderContext
.GetSettings().GetStyleSettings().GetMenuBarColor();
1684 rRenderContext
.SetBackground(Wallpaper(aBackgroundColor
));
1685 rRenderContext
.SetFillColor(aBackgroundColor
);
1686 vcl::Font aFont
= rRenderContext
.GetFont();
1687 aFont
.SetWeight(WEIGHT_BOLD
);
1688 rRenderContext
.SetFont(aFont
);
1690 // Draw background rectangle
1691 tools::Rectangle
aBgRect(rRect
);
1692 int nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
1693 aBgRect
.setX(aBgRect
.getX() + SPACE_AROUND_TITLE
);
1694 aBgRect
.setWidth(aBgRect
.getWidth() - 2 * SPACE_AROUND_TITLE
- 2 * nOuterSpaceX
);
1695 aBgRect
.setY(aBgRect
.getY() + SPACE_AROUND_TITLE
);
1696 aBgRect
.setHeight(nTitleHeight
- 2 * SPACE_AROUND_TITLE
);
1697 rRenderContext
.DrawRect(aBgRect
);
1699 // Draw the text centered
1700 Point
aTextTopLeft(aBgRect
.TopLeft());
1701 tools::Rectangle aTextBoundRect
;
1702 rRenderContext
.GetTextBoundRect( aTextBoundRect
, aTitleText
);
1703 aTextTopLeft
.AdjustX((aBgRect
.getWidth() - aTextBoundRect
.GetSize().Width()) / 2 );
1704 aTextTopLeft
.AdjustY((aBgRect
.GetHeight() - aTextBoundRect
.GetSize().Height()) / 2
1705 - aTextBoundRect
.TopLeft().Y() );
1706 rRenderContext
.DrawText(aTextTopLeft
, aTitleText
, 0, aTitleText
.getLength());
1709 rRenderContext
.Pop();
1710 rRenderContext
.SetBackground(aOldBackground
);
1713 void Menu::ImplPaint(vcl::RenderContext
& rRenderContext
, Size
const & rSize
,
1714 sal_uInt16 nBorder
, long nStartY
, MenuItemData
const * pThisItemOnly
,
1715 bool bHighlighted
, bool bLayout
, bool bRollover
) const
1717 // for symbols: nFontHeight x nFontHeight
1718 long nFontHeight
= rRenderContext
.GetTextHeight();
1719 long nExtra
= nFontHeight
/ 4;
1721 long nCheckHeight
= 0, nRadioHeight
= 0;
1722 ImplGetNativeCheckAndRadioSize(rRenderContext
, nCheckHeight
, nRadioHeight
);
1724 DecorationView
aDecoView(&rRenderContext
);
1725 const StyleSettings
& rSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1727 Point aTopLeft
, aTmpPos
;
1729 int nOuterSpaceX
= 0;
1732 nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
1733 aTopLeft
.AdjustX(nOuterSpaceX
);
1734 aTopLeft
.AdjustY(ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
);
1737 // for the computations, use size of the underlying window, not of RenderContext
1740 size_t nCount
= pItemList
->size();
1742 mpLayoutData
->m_aVisibleItemBoundRects
.clear();
1745 if (!pThisItemOnly
&& !IsMenuBar() && nTitleHeight
> 0)
1746 ImplPaintMenuTitle(rRenderContext
, tools::Rectangle(aTopLeft
, aOutSz
));
1748 bool bHiddenItems
= false; // are any items on the GUI hidden
1750 for (size_t n
= 0; n
< nCount
; n
++)
1752 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
1753 if (ImplIsVisible(n
) && (!pThisItemOnly
|| (pData
== pThisItemOnly
)))
1759 if (!ImplGetSVData()->maNWFData
.mbRolloverMenubar
)
1762 rRenderContext
.SetTextColor(rSettings
.GetMenuBarRolloverTextColor());
1763 else if (bHighlighted
)
1764 rRenderContext
.SetTextColor(rSettings
.GetMenuBarHighlightTextColor());
1769 rRenderContext
.SetTextColor(rSettings
.GetMenuBarHighlightTextColor());
1771 rRenderContext
.SetTextColor(rSettings
.GetMenuBarRolloverTextColor());
1773 if (!bRollover
&& !bHighlighted
)
1774 rRenderContext
.SetTextColor(rSettings
.GetMenuBarTextColor());
1776 else if (bHighlighted
)
1777 rRenderContext
.SetTextColor(rSettings
.GetMenuHighlightTextColor());
1780 Point
aPos(aTopLeft
);
1781 aPos
.AdjustY(nBorder
);
1782 aPos
.AdjustY(nStartY
);
1786 long nTextOffsetY
= (pData
->aSz
.Height() - nFontHeight
) / 2;
1788 nTextOffsetY
+= (aOutSz
.Height()-pData
->aSz
.Height()) / 2;
1789 DrawTextFlags nTextStyle
= DrawTextFlags::NONE
;
1790 DrawSymbolFlags nSymbolStyle
= DrawSymbolFlags::NONE
;
1791 DrawImageFlags nImageStyle
= DrawImageFlags::NONE
;
1793 // submenus without items are not disabled when no items are
1794 // contained. The application itself should check for this!
1795 // Otherwise it could happen entries are disabled due to
1796 // asynchronous loading
1797 if (!pData
->bEnabled
|| !pWindow
->IsEnabled())
1799 nTextStyle
|= DrawTextFlags::Disable
;
1800 nSymbolStyle
|= DrawSymbolFlags::Disable
;
1801 nImageStyle
|= DrawImageFlags::Disable
;
1805 if (!bLayout
&& !IsMenuBar() && (pData
->eType
== MenuItemType::SEPARATOR
))
1807 bool bNativeOk
= false;
1808 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Separator
))
1810 ControlState nState
= ControlState::NONE
;
1811 if (pData
->bEnabled
&& pWindow
->IsEnabled())
1812 nState
|= ControlState::ENABLED
;
1814 nState
|= ControlState::SELECTED
;
1815 Size
aSz(pData
->aSz
);
1816 aSz
.setWidth( aOutSz
.Width() - 2*nOuterSpaceX
);
1817 tools::Rectangle
aItemRect(aPos
, aSz
);
1818 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
1819 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::Separator
,
1820 aItemRect
, nState
, aVal
, OUString());
1824 aTmpPos
.setY( aPos
.Y() + ((pData
->aSz
.Height() - 2) / 2) );
1825 aTmpPos
.setX( aPos
.X() + 2 + nOuterSpaceX
);
1826 rRenderContext
.SetLineColor(rSettings
.GetShadowColor());
1827 rRenderContext
.DrawLine(aTmpPos
, Point(aOutSz
.Width() - 3 - 2 * nOuterSpaceX
, aTmpPos
.Y()));
1828 aTmpPos
.AdjustY( 1 );
1829 rRenderContext
.SetLineColor(rSettings
.GetLightColor());
1830 rRenderContext
.DrawLine(aTmpPos
, Point(aOutSz
.Width() - 3 - 2 * nOuterSpaceX
, aTmpPos
.Y()));
1831 rRenderContext
.SetLineColor();
1835 tools::Rectangle
aOuterCheckRect(Point(aPos
.X()+nImgOrChkPos
, aPos
.Y()),
1836 Size(pData
->aSz
.Height(), pData
->aSz
.Height()));
1837 aOuterCheckRect
.AdjustLeft(1 );
1838 aOuterCheckRect
.AdjustRight( -1 );
1839 aOuterCheckRect
.AdjustTop(1 );
1840 aOuterCheckRect
.AdjustBottom( -1 );
1843 if (!bLayout
&& !IsMenuBar() && pData
->HasCheck())
1845 // draw selection transparent marker if checked
1846 // onto that either a checkmark or the item image
1848 // however do not do this if native checks will be painted since
1849 // the selection color too often does not fit the theme's check and/or radio
1851 if( !((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1853 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
,
1854 (pData
->nBits
& MenuItemBits::RADIOCHECK
)
1855 ? ControlPart::MenuItemCheckMark
1856 : ControlPart::MenuItemRadioMark
))
1858 ControlPart nPart
= ((pData
->nBits
& MenuItemBits::RADIOCHECK
)
1859 ? ControlPart::MenuItemRadioMark
1860 : ControlPart::MenuItemCheckMark
);
1862 ControlState nState
= ControlState::NONE
;
1864 if (pData
->bChecked
)
1865 nState
|= ControlState::PRESSED
;
1867 if (pData
->bEnabled
&& pWindow
->IsEnabled())
1868 nState
|= ControlState::ENABLED
;
1871 nState
|= ControlState::SELECTED
;
1873 long nCtrlHeight
= (pData
->nBits
& MenuItemBits::RADIOCHECK
) ? nCheckHeight
: nRadioHeight
;
1874 aTmpPos
.setX( aOuterCheckRect
.Left() + (aOuterCheckRect
.GetWidth() - nCtrlHeight
) / 2 );
1875 aTmpPos
.setY( aOuterCheckRect
.Top() + (aOuterCheckRect
.GetHeight() - nCtrlHeight
) / 2 );
1877 tools::Rectangle
aCheckRect(aTmpPos
, Size(nCtrlHeight
, nCtrlHeight
));
1878 Size
aSz(pData
->aSz
);
1879 aSz
.setWidth( aOutSz
.Width() - 2 * nOuterSpaceX
);
1880 tools::Rectangle
aItemRect(aPos
, aSz
);
1881 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
1882 rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, nPart
, aCheckRect
,
1883 nState
, aVal
, OUString());
1885 else if (pData
->bChecked
) // by default do nothing for unchecked items
1887 ImplPaintCheckBackground(rRenderContext
, *pWindow
, aOuterCheckRect
, pThisItemOnly
&& bHighlighted
);
1891 if (pData
->nBits
& MenuItemBits::RADIOCHECK
)
1893 eSymbol
= SymbolType::RADIOCHECKMARK
;
1894 aSymbolSize
= Size(nFontHeight
/ 2, nFontHeight
/ 2);
1898 eSymbol
= SymbolType::CHECKMARK
;
1899 aSymbolSize
= Size((nFontHeight
* 25) / 40, nFontHeight
/ 2);
1901 aTmpPos
.setX( aOuterCheckRect
.Left() + (aOuterCheckRect
.GetWidth() - aSymbolSize
.Width()) / 2 );
1902 aTmpPos
.setY( aOuterCheckRect
.Top() + (aOuterCheckRect
.GetHeight() - aSymbolSize
.Height()) / 2 );
1903 tools::Rectangle
aRect(aTmpPos
, aSymbolSize
);
1904 aDecoView
.DrawSymbol(aRect
, eSymbol
, rRenderContext
.GetTextColor(), nSymbolStyle
);
1910 if (!bLayout
&& !IsMenuBar() && ((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1912 // Don't render an image for a check thing
1913 if (pData
->bChecked
)
1914 ImplPaintCheckBackground(rRenderContext
, *pWindow
, aOuterCheckRect
, pThisItemOnly
&& bHighlighted
);
1916 Image aImage
= pData
->aImage
;
1918 aTmpPos
= aOuterCheckRect
.TopLeft();
1919 aTmpPos
.AdjustX((aOuterCheckRect
.GetWidth() - aImage
.GetSizePixel().Width()) / 2 );
1920 aTmpPos
.AdjustY((aOuterCheckRect
.GetHeight() - aImage
.GetSizePixel().Height()) / 2 );
1921 rRenderContext
.DrawImage(aTmpPos
, aImage
, nImageStyle
);
1925 if ((pData
->eType
== MenuItemType::STRING
) || (pData
->eType
== MenuItemType::STRINGIMAGE
))
1927 aTmpPos
.setX( aPos
.X() + nTextPos
);
1928 aTmpPos
.setY( aPos
.Y() );
1929 aTmpPos
.AdjustY(nTextOffsetY
);
1930 DrawTextFlags nStyle
= nTextStyle
| DrawTextFlags::Mnemonic
;
1932 const Menu
*pMenu
= this;
1933 while (!pMenu
->IsMenuBar() && pMenu
->pStartedFrom
)
1934 pMenu
= pMenu
->pStartedFrom
;
1935 if (pMenu
->IsMenuBar() && static_cast<MenuBarWindow
*>(pMenu
->pWindow
.get())->GetMBWHideAccel())
1936 nStyle
|= DrawTextFlags::HideMnemonic
;
1938 if (pData
->bIsTemporary
)
1939 nStyle
|= DrawTextFlags::Disable
;
1940 MetricVector
* pVector
= bLayout
? &mpLayoutData
->m_aUnicodeBoundRects
: nullptr;
1941 OUString
* pDisplayText
= bLayout
? &mpLayoutData
->m_aDisplayText
: nullptr;
1944 mpLayoutData
->m_aLineIndices
.push_back(mpLayoutData
->m_aDisplayText
.getLength());
1945 mpLayoutData
->m_aLineItemIds
.push_back(pData
->nId
);
1947 // #i47946# with NWF painted menus the background is transparent
1948 // since DrawCtrlText can depend on the background (e.g. for
1949 // DrawTextFlags::Disable), temporarily set a background which
1950 // hopefully matches the NWF background since it is read
1951 // from the system style settings
1952 bool bSetTmpBackground
= !rRenderContext
.IsBackground()
1953 && rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
);
1954 if (bSetTmpBackground
)
1956 Color aBg
= IsMenuBar() ? rRenderContext
.GetSettings().GetStyleSettings().GetMenuBarColor()
1957 : rRenderContext
.GetSettings().GetStyleSettings().GetMenuColor();
1958 rRenderContext
.SetBackground(Wallpaper(aBg
));
1960 // how much space is there for the text?
1961 long nMaxItemTextWidth
= aOutSz
.Width() - aTmpPos
.X() - nExtra
- nOuterSpaceX
;
1962 if (!IsMenuBar() && pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1964 OUString aAccText
= pData
->aAccelKey
.GetName();
1965 nMaxItemTextWidth
-= rRenderContext
.GetTextWidth(aAccText
) + 3 * nExtra
;
1967 if (!IsMenuBar() && pData
->pSubMenu
)
1969 nMaxItemTextWidth
-= nFontHeight
- nExtra
;
1972 OUString
aItemText(pData
->aText
);
1973 pData
->bHiddenOnGUI
= false;
1975 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1977 if (nMaxItemTextWidth
< rRenderContext
.GetTextWidth(aItemText
))
1980 pData
->bHiddenOnGUI
= true;
1981 bHiddenItems
= true;
1986 aItemText
= getShortenedString(aItemText
, rRenderContext
, nMaxItemTextWidth
);
1987 pData
->bHiddenOnGUI
= false;
1990 const SalLayoutGlyphs
* pGlyphs
= pData
->GetTextGlyphs(&rRenderContext
);
1991 if (aItemText
!= pData
->aText
)
1992 // Can't use pre-computed glyphs, item text was
1995 rRenderContext
.DrawCtrlText(aTmpPos
, aItemText
, 0, aItemText
.getLength(),
1996 nStyle
, pVector
, pDisplayText
, pGlyphs
);
1997 if (bSetTmpBackground
)
1998 rRenderContext
.SetBackground();
2002 if (!bLayout
&& !IsMenuBar() && pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
2004 OUString aAccText
= pData
->aAccelKey
.GetName();
2005 aTmpPos
.setX( aOutSz
.Width() - rRenderContext
.GetTextWidth(aAccText
) );
2006 aTmpPos
.AdjustX( -(4 * nExtra
) );
2008 aTmpPos
.AdjustX( -nOuterSpaceX
);
2009 aTmpPos
.setY( aPos
.Y() );
2010 aTmpPos
.AdjustY(nTextOffsetY
);
2011 rRenderContext
.DrawCtrlText(aTmpPos
, aAccText
, 0, aAccText
.getLength(), nTextStyle
);
2015 if (!bLayout
&& !IsMenuBar() && pData
->pSubMenu
)
2017 bool bNativeOk
= false;
2018 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::SubmenuArrow
))
2020 ControlState nState
= ControlState::NONE
;
2024 if (!ImplGetNativeSubmenuArrowSize(rRenderContext
, aTmpSz
, aSpacing
))
2026 aTmpSz
= Size(nFontHeight
, nFontHeight
);
2027 aSpacing
= nOuterSpaceX
;
2030 if (pData
->bEnabled
&& pWindow
->IsEnabled())
2031 nState
|= ControlState::ENABLED
;
2033 nState
|= ControlState::SELECTED
;
2035 aTmpPos
.setX( aOutSz
.Width() - aTmpSz
.Width() - aSpacing
- nOuterSpaceX
);
2036 aTmpPos
.setY( aPos
.Y() + ( pData
->aSz
.Height() - aTmpSz
.Height() ) / 2 );
2037 aTmpPos
.AdjustY(nExtra
/ 2 );
2039 tools::Rectangle
aItemRect(aTmpPos
, aTmpSz
);
2040 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
2041 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::SubmenuArrow
,
2042 aItemRect
, nState
, aVal
, OUString());
2046 aTmpPos
.setX( aOutSz
.Width() - nFontHeight
+ nExtra
- nOuterSpaceX
);
2047 aTmpPos
.setY( aPos
.Y() );
2048 aTmpPos
.AdjustY(nExtra
/2 );
2049 aTmpPos
.AdjustY((pData
->aSz
.Height() / 2) - (nFontHeight
/ 4) );
2050 if (pData
->nBits
& MenuItemBits::POPUPSELECT
)
2052 rRenderContext
.SetTextColor(rSettings
.GetMenuTextColor());
2053 Point
aTmpPos2(aPos
);
2054 aTmpPos2
.setX( aOutSz
.Width() - nFontHeight
- nFontHeight
/4 );
2055 aDecoView
.DrawFrame(tools::Rectangle(aTmpPos2
, Size(nFontHeight
+ nFontHeight
/ 4,
2056 pData
->aSz
.Height())),
2057 DrawFrameStyle::Group
);
2059 aDecoView
.DrawSymbol(tools::Rectangle(aTmpPos
, Size(nFontHeight
/ 2, nFontHeight
/ 2)),
2060 SymbolType::SPIN_RIGHT
, rRenderContext
.GetTextColor(), nSymbolStyle
);
2064 if (pThisItemOnly
&& bHighlighted
)
2066 // This restores the normal menu or menu bar text
2067 // color for when it is no longer highlighted.
2069 rRenderContext
.SetTextColor(rSettings
.GetMenuBarTextColor());
2071 rRenderContext
.SetTextColor(rSettings
.GetMenuTextColor());
2077 mpLayoutData
->m_aVisibleItemBoundRects
[ n
] = tools::Rectangle(aTopLeft
, Size(aOutSz
.Width(), pData
->aSz
.Height()));
2079 mpLayoutData
->m_aVisibleItemBoundRects
[ n
] = tools::Rectangle(aTopLeft
, pData
->aSz
);
2084 aTopLeft
.AdjustY(pData
->aSz
.Height() );
2086 aTopLeft
.AdjustX(pData
->aSz
.Width() );
2089 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2092 sal_Int32 nSize
= nFontHeight
;
2093 tools::Rectangle
aRectangle(Point(aOutSz
.Width() - nSize
, (aOutSz
.Height() / 2) - (nSize
/ 2)), Size(nSize
, nSize
));
2094 lclDrawMoreIndicator(rRenderContext
, aRectangle
);
2098 Menu
* Menu::ImplGetStartMenu()
2100 Menu
* pStart
= this;
2101 while ( pStart
&& pStart
->pStartedFrom
&& ( pStart
->pStartedFrom
!= pStart
) )
2102 pStart
= pStart
->pStartedFrom
;
2106 void Menu::ImplCallHighlight(sal_uInt16 nItem
)
2108 ImplMenuDelData
aDelData( this );
2111 sSelectedIdent
.clear();
2112 MenuItemData
* pData
= pItemList
->GetDataFromPos(nItem
);
2115 nSelectedId
= pData
->nId
;
2116 sSelectedIdent
= pData
->sIdent
;
2118 ImplCallEventListeners( VclEventId::MenuHighlight
, GetItemPos( GetCurItemId() ) );
2120 if( !aDelData
.isDeleted() )
2123 sSelectedIdent
.clear();
2127 IMPL_LINK_NOARG(Menu
, ImplCallSelect
, void*, void)
2133 Menu
* Menu::ImplFindSelectMenu()
2135 Menu
* pSelMenu
= nEventId
? this : nullptr;
2137 for ( size_t n
= GetItemList()->size(); n
&& !pSelMenu
; )
2139 MenuItemData
* pData
= GetItemList()->GetDataFromPos( --n
);
2141 if ( pData
->pSubMenu
)
2142 pSelMenu
= pData
->pSubMenu
->ImplFindSelectMenu();
2148 Menu
* Menu::ImplFindMenu( sal_uInt16 nItemId
)
2150 Menu
* pSelMenu
= nullptr;
2152 for ( size_t n
= GetItemList()->size(); n
&& !pSelMenu
; )
2154 MenuItemData
* pData
= GetItemList()->GetDataFromPos( --n
);
2156 if( pData
->nId
== nItemId
)
2158 else if ( pData
->pSubMenu
)
2159 pSelMenu
= pData
->pSubMenu
->ImplFindMenu( nItemId
);
2165 void Menu::RemoveDisabledEntries( bool bCheckPopups
, bool bRemoveEmptyPopups
)
2167 for ( sal_uInt16 n
= 0; n
< GetItemCount(); n
++ )
2169 bool bRemove
= false;
2170 MenuItemData
* pItem
= pItemList
->GetDataFromPos( n
);
2171 if ( pItem
->eType
== MenuItemType::SEPARATOR
)
2173 if ( !n
|| ( GetItemType( n
-1 ) == MenuItemType::SEPARATOR
) )
2177 bRemove
= !pItem
->bEnabled
;
2179 if ( bCheckPopups
&& pItem
->pSubMenu
)
2181 pItem
->pSubMenu
->RemoveDisabledEntries();
2182 if ( bRemoveEmptyPopups
&& !pItem
->pSubMenu
->GetItemCount() )
2190 if ( GetItemCount() )
2192 sal_uInt16 nLast
= GetItemCount() - 1;
2193 MenuItemData
* pItem
= pItemList
->GetDataFromPos( nLast
);
2194 if ( pItem
->eType
== MenuItemType::SEPARATOR
)
2195 RemoveItem( nLast
);
2197 mpLayoutData
.reset();
2200 void Menu::UpdateNativeMenu()
2202 if ( ImplGetSalMenu() )
2203 ImplGetSalMenu()->Update();
2206 void Menu::MenuBarKeyInput(const KeyEvent
&)
2210 void Menu::ImplKillLayoutData() const
2212 mpLayoutData
.reset();
2215 void Menu::ImplFillLayoutData() const
2217 if (pWindow
&& pWindow
->IsReallyVisible())
2219 mpLayoutData
.reset(new MenuLayoutData
);
2222 ImplPaint(*pWindow
, pWindow
->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2226 MenuFloatingWindow
* pFloat
= static_cast<MenuFloatingWindow
*>(pWindow
.get());
2227 ImplPaint(*pWindow
, pWindow
->GetOutputSizePixel(), pFloat
->nScrollerHeight
, pFloat
->ImplGetStartY(),
2228 nullptr, false, true); //FIXME
2233 tools::Rectangle
Menu::GetCharacterBounds( sal_uInt16 nItemID
, long nIndex
) const
2235 long nItemIndex
= -1;
2236 if( ! mpLayoutData
)
2237 ImplFillLayoutData();
2240 for( size_t i
= 0; i
< mpLayoutData
->m_aLineItemIds
.size(); i
++ )
2242 if( mpLayoutData
->m_aLineItemIds
[i
] == nItemID
)
2244 nItemIndex
= mpLayoutData
->m_aLineIndices
[i
];
2249 return (mpLayoutData
&& nItemIndex
!= -1) ? mpLayoutData
->GetCharacterBounds( nItemIndex
+nIndex
) : tools::Rectangle();
2252 long Menu::GetIndexForPoint( const Point
& rPoint
, sal_uInt16
& rItemID
) const
2256 if( ! mpLayoutData
)
2257 ImplFillLayoutData();
2260 nIndex
= mpLayoutData
->GetIndexForPoint( rPoint
);
2261 for( size_t i
= 0; i
< mpLayoutData
->m_aLineIndices
.size(); i
++ )
2263 if( mpLayoutData
->m_aLineIndices
[i
] <= nIndex
&&
2264 (i
== mpLayoutData
->m_aLineIndices
.size()-1 || mpLayoutData
->m_aLineIndices
[i
+1] > nIndex
) )
2266 // make index relative to item
2267 nIndex
-= mpLayoutData
->m_aLineIndices
[i
];
2268 rItemID
= mpLayoutData
->m_aLineItemIds
[i
];
2276 tools::Rectangle
Menu::GetBoundingRectangle( sal_uInt16 nPos
) const
2278 tools::Rectangle aRet
;
2281 ImplFillLayoutData();
2284 std::map
< sal_uInt16
, tools::Rectangle
>::const_iterator it
= mpLayoutData
->m_aVisibleItemBoundRects
.find( nPos
);
2285 if( it
!= mpLayoutData
->m_aVisibleItemBoundRects
.end() )
2291 OUString
Menu::GetAccessibleName( sal_uInt16 nItemId
) const
2293 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2296 return pData
->aAccessibleName
;
2301 void Menu::ImplClearSalMenu()
2306 void Menu::GetSystemMenuData( SystemMenuData
* pData
) const
2308 Menu
* pMenu
= const_cast<Menu
*>(this);
2309 if( pData
&& pMenu
->ImplGetSalMenu() )
2311 pMenu
->ImplGetSalMenu()->GetSystemMenuData( pData
);
2315 bool Menu::IsHighlighted( sal_uInt16 nItemPos
) const
2322 bRet
= ( nItemPos
== static_cast< MenuBarWindow
* > (pWindow
.get())->GetHighlightedItem() );
2324 bRet
= ( nItemPos
== static_cast< MenuFloatingWindow
* > (pWindow
.get())->GetHighlightedItem() );
2330 void Menu::HighlightItem( sal_uInt16 nItemPos
)
2336 MenuBarWindow
* pMenuWin
= static_cast< MenuBarWindow
* >( pWindow
.get() );
2337 pMenuWin
->SetAutoPopup( false );
2338 pMenuWin
->ChangeHighlightItem( nItemPos
, false );
2342 static_cast< MenuFloatingWindow
* >( pWindow
.get() )->ChangeHighlightItem( nItemPos
, false );
2347 MenuBarWindow
* MenuBar::getMenuBarWindow()
2349 // so far just a dynamic_cast, hopefully to be turned into something saner
2351 MenuBarWindow
*pWin
= dynamic_cast<MenuBarWindow
*>(pWindow
.get());
2352 //either there is no window (fdo#87663) or it is a MenuBarWindow
2353 assert(!pWindow
|| pWin
);
2359 mbCloseBtnVisible(false),
2360 mbFloatBtnVisible(false),
2361 mbHideBtnVisible(false),
2364 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(true, this);
2367 MenuBar::MenuBar( const MenuBar
& rMenu
)
2369 mbCloseBtnVisible(false),
2370 mbFloatBtnVisible(false),
2371 mbHideBtnVisible(false),
2374 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(true, this);
2383 void MenuBar::dispose()
2385 ImplDestroy( this, true );
2389 void MenuBar::ClosePopup(Menu
*pMenu
)
2391 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2394 pMenuWin
->PopupClosed(pMenu
);
2397 void MenuBar::MenuBarKeyInput(const KeyEvent
& rEvent
)
2399 pWindow
->KeyInput(rEvent
);
2402 void MenuBar::ShowCloseButton(bool bShow
)
2404 ShowButtons( bShow
, mbFloatBtnVisible
, mbHideBtnVisible
);
2407 void MenuBar::ShowButtons( bool bClose
, bool bFloat
, bool bHide
)
2409 if ((bClose
!= mbCloseBtnVisible
) ||
2410 (bFloat
!= mbFloatBtnVisible
) ||
2411 (bHide
!= mbHideBtnVisible
))
2413 mbCloseBtnVisible
= bClose
;
2414 mbFloatBtnVisible
= bFloat
;
2415 mbHideBtnVisible
= bHide
;
2416 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2418 pMenuWin
->ShowButtons(bClose
, bFloat
, bHide
);
2422 void MenuBar::LayoutChanged()
2424 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2426 pMenuWin
->LayoutChanged();
2429 void MenuBar::SetDisplayable( bool bDisplayable
)
2431 if( bDisplayable
!= mbDisplayable
)
2433 if ( ImplGetSalMenu() )
2434 ImplGetSalMenu()->ShowMenuBar( bDisplayable
);
2436 mbDisplayable
= bDisplayable
;
2441 VclPtr
<vcl::Window
> MenuBar::ImplCreate(vcl::Window
* pParent
, vcl::Window
* pWindow
, MenuBar
* pMenu
)
2443 VclPtr
<MenuBarWindow
> pMenuBarWindow
= dynamic_cast<MenuBarWindow
*>(pWindow
);
2444 if (!pMenuBarWindow
)
2446 pWindow
= pMenuBarWindow
= VclPtr
<MenuBarWindow
>::Create( pParent
);
2449 pMenu
->pStartedFrom
= nullptr;
2450 pMenu
->pWindow
= pWindow
;
2451 pMenuBarWindow
->SetMenu(pMenu
);
2452 long nHeight
= pWindow
? pMenu
->ImplCalcSize(pWindow
).Height() : 0;
2454 // depending on the native implementation or the displayable flag
2455 // the menubar windows is suppressed (ie, height=0)
2456 if (!pMenu
->IsDisplayable() || (pMenu
->ImplGetSalMenu() && pMenu
->ImplGetSalMenu()->VisibleMenuBar()))
2461 pMenuBarWindow
->SetHeight(nHeight
);
2465 void MenuBar::ImplDestroy( MenuBar
* pMenu
, bool bDelete
)
2467 vcl::Window
*pWindow
= pMenu
->ImplGetWindow();
2468 if (pWindow
&& bDelete
)
2470 MenuBarWindow
* pMenuWin
= pMenu
->getMenuBarWindow();
2472 pMenuWin
->KillActivePopup();
2473 pWindow
->disposeOnce();
2475 pMenu
->pWindow
= nullptr;
2478 bool MenuBar::ImplHandleKeyEvent( const KeyEvent
& rKEvent
)
2480 // No keyboard processing when our menubar is invisible
2481 if (!IsDisplayable())
2484 // No keyboard processing when system handles the menu.
2485 SalMenu
*pNativeMenu
= ImplGetSalMenu();
2486 if (pNativeMenu
&& pNativeMenu
->VisibleMenuBar())
2488 // Except when the event is the F6 cycle pane event and we can put our
2489 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2490 // where it's not part of the application window)
2491 if (!TaskPaneList::IsCycleKey(rKEvent
.GetKeyCode()))
2493 if (!pNativeMenu
->CanGetFocus())
2498 // check for enabled, if this method is called from another window...
2499 vcl::Window
* pWin
= ImplGetWindow();
2500 if (pWin
&& pWin
->IsEnabled() && pWin
->IsInputEnabled() && !pWin
->IsInModalMode())
2502 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2503 bDone
= pMenuWin
&& pMenuWin
->HandleKeyEvent(rKEvent
, false/*bFromMenu*/);
2508 bool MenuBar::ImplHandleCmdEvent( const CommandEvent
& rCEvent
)
2510 // No keyboard processing when system handles the menu or our menubar is invisible
2511 if( !IsDisplayable() ||
2512 ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2515 // check for enabled, if this method is called from another window...
2516 MenuBarWindow
* pWin
= static_cast<MenuBarWindow
*>(ImplGetWindow());
2517 if ( pWin
&& pWin
->IsEnabled() && pWin
->IsInputEnabled() && ! pWin
->IsInModalMode() )
2519 if (rCEvent
.GetCommand() == CommandEventId::ModKeyChange
&& ImplGetSVData()->maNWFData
.mbAutoAccel
)
2521 const CommandModKeyData
* pCData
= rCEvent
.GetModKeyData ();
2522 if (pWin
->m_nHighlightedItem
== ITEMPOS_INVALID
)
2524 if (pCData
&& pCData
->IsMod2() && pCData
->IsDown())
2525 pWin
->SetMBWHideAccel(false);
2527 pWin
->SetMBWHideAccel(true);
2528 pWin
->Invalidate(InvalidateFlags::Update
);
2536 void MenuBar::SelectItem(sal_uInt16 nId
)
2540 pWindow
->GrabFocus();
2541 nId
= GetItemPos( nId
);
2543 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2546 // #99705# popup the selected menu
2547 pMenuWin
->SetAutoPopup( true );
2548 if (ITEMPOS_INVALID
!= pMenuWin
->GetHighlightedItem())
2550 pMenuWin
->KillActivePopup();
2551 pMenuWin
->ChangeHighlightItem( ITEMPOS_INVALID
, false );
2553 if (nId
!= ITEMPOS_INVALID
)
2554 pMenuWin
->ChangeHighlightItem( nId
, false );
2559 // handler for native menu selection and command events
2560 bool Menu::HandleMenuActivateEvent( Menu
*pMenu
) const
2564 ImplMenuDelData
aDelData( this );
2566 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2567 pMenu
->bInCallback
= true;
2570 if( !aDelData
.isDeleted() )
2571 pMenu
->bInCallback
= false;
2576 bool Menu::HandleMenuDeActivateEvent( Menu
*pMenu
) const
2580 ImplMenuDelData
aDelData( this );
2582 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2583 pMenu
->bInCallback
= true;
2584 pMenu
->Deactivate();
2585 if( !aDelData
.isDeleted() )
2586 pMenu
->bInCallback
= false;
2591 bool MenuBar::HandleMenuHighlightEvent( Menu
*pMenu
, sal_uInt16 nHighlightEventId
) const
2594 pMenu
= const_cast<MenuBar
*>(this)->ImplFindMenu(nHighlightEventId
);
2597 ImplMenuDelData
aDelData( pMenu
);
2599 if( mnHighlightedItemPos
!= ITEMPOS_INVALID
)
2600 pMenu
->ImplCallEventListeners( VclEventId::MenuDehighlight
, mnHighlightedItemPos
);
2602 if( !aDelData
.isDeleted() )
2604 pMenu
->mnHighlightedItemPos
= pMenu
->GetItemPos( nHighlightEventId
);
2605 pMenu
->nSelectedId
= nHighlightEventId
;
2606 pMenu
->sSelectedIdent
= pMenu
->GetItemIdent( nHighlightEventId
);
2607 pMenu
->pStartedFrom
= const_cast<MenuBar
*>(this);
2608 pMenu
->ImplCallHighlight( pMenu
->mnHighlightedItemPos
);
2616 bool Menu::HandleMenuCommandEvent( Menu
*pMenu
, sal_uInt16 nCommandEventId
) const
2619 pMenu
= const_cast<Menu
*>(this)->ImplFindMenu(nCommandEventId
);
2622 pMenu
->nSelectedId
= nCommandEventId
;
2623 pMenu
->sSelectedIdent
= pMenu
->GetItemIdent(nCommandEventId
);
2624 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2625 pMenu
->ImplSelect();
2632 sal_uInt16
MenuBar::AddMenuBarButton( const Image
& i_rImage
, const Link
<MenuBar::MenuBarButtonCallbackArg
&,bool>& i_rLink
, const OUString
& i_rToolTip
)
2634 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2635 return pMenuWin
? pMenuWin
->AddMenuBarButton(i_rImage
, i_rLink
, i_rToolTip
) : 0;
2638 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId
, const Link
<MenuBar::MenuBarButtonCallbackArg
&,bool>& rLink
)
2640 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2643 pMenuWin
->SetMenuBarButtonHighlightHdl(nId
, rLink
);
2646 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId
)
2648 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2651 pMenuWin
->RemoveMenuBarButton(nId
);
2654 tools::Rectangle
MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId
)
2656 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2657 return pMenuWin
? pMenuWin
->GetMenuBarButtonRectPixel(nId
) : tools::Rectangle();
2660 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId
)
2662 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2663 return pMenuWin
&& pMenuWin
->HandleMenuButtonEvent(i_nButtonId
);
2666 int MenuBar::GetMenuBarHeight() const
2668 MenuBar
* pMenuBar
= const_cast<MenuBar
*>(this);
2669 const SalMenu
*pNativeMenu
= pMenuBar
->ImplGetSalMenu();
2672 nMenubarHeight
= pNativeMenu
->GetMenuBarHeight();
2675 vcl::Window
* pMenubarWin
= GetWindow();
2676 nMenubarHeight
= pMenubarWin
? pMenubarWin
->GetOutputHeightPixel() : 0;
2678 return nMenubarHeight
;
2681 // bool PopupMenu::bAnyPopupInExecute = false;
2683 MenuFloatingWindow
* PopupMenu::ImplGetFloatingWindow() const {
2684 return static_cast<MenuFloatingWindow
*>(Menu::ImplGetWindow());
2687 PopupMenu::PopupMenu()
2688 : mpLOKNotifier(nullptr)
2690 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(false, this);
2693 PopupMenu::PopupMenu( const PopupMenu
& rMenu
)
2695 mpLOKNotifier(nullptr)
2697 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(false, this);
2701 PopupMenu::~PopupMenu()
2706 void PopupMenu::ClosePopup(Menu
* pMenu
)
2708 MenuFloatingWindow
* p
= dynamic_cast<MenuFloatingWindow
*>(ImplGetWindow());
2709 PopupMenu
*pPopup
= dynamic_cast<PopupMenu
*>(pMenu
);
2711 p
->KillActivePopup(pPopup
);
2714 bool PopupMenu::IsInExecute()
2716 return GetActivePopupMenu() != nullptr;
2719 PopupMenu
* PopupMenu::GetActivePopupMenu()
2721 ImplSVData
* pSVData
= ImplGetSVData();
2722 return pSVData
->maAppData
.mpActivePopupMenu
;
2725 void PopupMenu::EndExecute()
2727 if ( ImplGetWindow() )
2728 ImplGetFloatingWindow()->EndExecute( 0 );
2731 void PopupMenu::SelectItem(sal_uInt16 nId
)
2733 if ( ImplGetWindow() )
2735 if( nId
!= ITEMPOS_INVALID
)
2738 MenuItemData
* pData
= GetItemList()->GetData( nId
, nPos
);
2739 if (pData
&& pData
->pSubMenu
)
2740 ImplGetFloatingWindow()->ChangeHighlightItem( nPos
, true );
2742 ImplGetFloatingWindow()->EndExecute( nId
);
2746 MenuFloatingWindow
* pFloat
= ImplGetFloatingWindow();
2747 pFloat
->GrabFocus();
2749 for( size_t nPos
= 0; nPos
< GetItemList()->size(); nPos
++ )
2751 MenuItemData
* pData
= GetItemList()->GetDataFromPos( nPos
);
2752 if( pData
->pSubMenu
)
2754 pFloat
->KillActivePopup();
2757 pFloat
->ChangeHighlightItem( ITEMPOS_INVALID
, false );
2762 void PopupMenu::SetSelectedEntry( sal_uInt16 nId
)
2765 sSelectedIdent
= GetItemIdent(nId
);
2768 sal_uInt16
PopupMenu::Execute( vcl::Window
* pExecWindow
, const Point
& rPopupPos
)
2770 return Execute( pExecWindow
, tools::Rectangle( rPopupPos
, rPopupPos
), PopupMenuFlags::ExecuteDown
);
2773 sal_uInt16
PopupMenu::Execute( vcl::Window
* pExecWindow
, const tools::Rectangle
& rRect
, PopupMenuFlags nFlags
)
2775 ENSURE_OR_RETURN( pExecWindow
, "PopupMenu::Execute: need a non-NULL window!", 0 );
2777 FloatWinPopupFlags nPopupModeFlags
= FloatWinPopupFlags::NONE
;
2778 if ( nFlags
& PopupMenuFlags::ExecuteDown
)
2779 nPopupModeFlags
= FloatWinPopupFlags::Down
;
2780 else if ( nFlags
& PopupMenuFlags::ExecuteUp
)
2781 nPopupModeFlags
= FloatWinPopupFlags::Up
;
2782 else if ( nFlags
& PopupMenuFlags::ExecuteLeft
)
2783 nPopupModeFlags
= FloatWinPopupFlags::Left
;
2784 else if ( nFlags
& PopupMenuFlags::ExecuteRight
)
2785 nPopupModeFlags
= FloatWinPopupFlags::Right
;
2787 nPopupModeFlags
= FloatWinPopupFlags::Down
;
2789 if (nFlags
& PopupMenuFlags::NoMouseUpClose
) // allow popup menus to stay open on mouse button up
2790 nPopupModeFlags
|= FloatWinPopupFlags::NoMouseUpClose
; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2792 if (nFlags
& PopupMenuFlags::NoHorzPlacement
)
2793 nPopupModeFlags
|= FloatWinPopupFlags::NoHorzPlacement
;
2795 return ImplExecute( pExecWindow
, rRect
, nPopupModeFlags
, nullptr, false );
2798 void PopupMenu::ImplFlushPendingSelect()
2800 // is there still Select?
2801 Menu
* pSelect
= ImplFindSelectMenu();
2804 // Select should be called prior to leaving execute in a popup menu!
2805 Application::RemoveUserEvent( pSelect
->nEventId
);
2806 pSelect
->nEventId
= nullptr;
2811 sal_uInt16
PopupMenu::ImplExecute( const VclPtr
<vcl::Window
>& pW
, const tools::Rectangle
& rRect
, FloatWinPopupFlags nPopupModeFlags
, Menu
* pSFrom
, bool bPreSelectFirst
)
2813 if ( !pSFrom
&& ( PopupMenu::IsInExecute() || !GetItemCount() ) )
2816 // set the flag to hide or show accelerators in the menu depending on whether the menu was launched by mouse or keyboard shortcut
2817 if( pSFrom
&& pSFrom
->IsMenuBar())
2819 auto pMenuBarWindow
= static_cast<MenuBarWindow
*>(pSFrom
->pWindow
.get());
2820 pMenuBarWindow
->SetMBWHideAccel( !(pMenuBarWindow
->GetMBWMenuKey()) );
2823 mpLayoutData
.reset();
2825 ImplSVData
* pSVData
= ImplGetSVData();
2827 pStartedFrom
= pSFrom
;
2829 sSelectedIdent
.clear();
2832 VclPtr
<vcl::Window
> xFocusId
;
2833 bool bRealExecute
= false;
2834 if ( !pStartedFrom
)
2836 pSVData
->maWinData
.mbNoDeactivate
= true;
2837 xFocusId
= Window::SaveFocus();
2838 bRealExecute
= true;
2842 // assure that only one menu is open at a time
2843 if (pStartedFrom
->IsMenuBar() && pSVData
->maWinData
.mpFirstFloat
)
2844 pSVData
->maWinData
.mpFirstFloat
->EndPopupMode( FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
2847 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
2848 tools::Rectangle
aRect( rRect
);
2849 aRect
.SetPos( pW
->OutputToScreenPixel( aRect
.TopLeft() ) );
2852 nPopupModeFlags
|= FloatWinPopupFlags::NewLevel
;
2853 nPopupModeFlags
|= FloatWinPopupFlags::NoKeyClose
| FloatWinPopupFlags::AllMouseButtonClose
;
2855 bInCallback
= true; // set it here, if Activate overridden
2857 bInCallback
= false;
2859 if ( pW
->IsDisposed() )
2862 if ( bCanceled
|| bKilled
)
2865 if ( !GetItemCount() )
2868 // The flag MenuFlags::HideDisabledEntries is inherited.
2871 if ( pSFrom
->nMenuFlags
& MenuFlags::HideDisabledEntries
)
2872 nMenuFlags
|= MenuFlags::HideDisabledEntries
;
2874 nMenuFlags
&= ~MenuFlags::HideDisabledEntries
;
2877 // #102790# context menus shall never show disabled entries
2878 nMenuFlags
|= MenuFlags::HideDisabledEntries
;
2880 sal_uInt16 nVisibleEntries
= ImplGetVisibleItemCount();
2881 if ( !nVisibleEntries
)
2883 OUString
aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE
));
2885 MenuItemData
* pData
= NbcInsertItem(0xFFFF, MenuItemBits::NONE
, aTmpEntryText
, nullptr, 0xFFFF, OString());
2887 pData
= pItemList
->GetData( pData
->nId
, nPos
);
2891 pData
->bIsTemporary
= true;
2893 ImplCallEventListeners(VclEventId::MenuSubmenuChanged
, nPos
);
2896 VclPtrInstance
<MenuFloatingWindow
> pWin( this, pW
, WB_BORDER
| WB_SYSTEMWINDOW
);
2897 if (comphelper::LibreOfficeKit::isActive() && mpLOKNotifier
)
2898 pWin
->SetLOKNotifier(mpLOKNotifier
);
2900 if( pSVData
->maNWFData
.mbFlatMenu
)
2901 pWin
->SetBorderStyle( WindowBorderStyle::NOBORDER
);
2903 pWin
->SetBorderStyle( pWin
->GetBorderStyle() | WindowBorderStyle::MENU
);
2906 Size aSz
= ImplCalcSize( pWin
);
2908 tools::Rectangle
aDesktopRect(pWin
->GetDesktopRectPixel());
2909 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() )
2911 vcl::Window
* pDeskW
= pWindow
->GetWindow( GetWindowType::RealParent
);
2914 Point
aDesktopTL( pDeskW
->OutputToAbsoluteScreenPixel( aRect
.TopLeft() ) );
2915 aDesktopRect
= Application::GetScreenPosSizePixel(
2916 Application::GetBestScreen( tools::Rectangle( aDesktopTL
, aRect
.GetSize() ) ));
2919 long nMaxHeight
= aDesktopRect
.GetHeight();
2921 //rhbz#1021915. If a menu won't fit in the desired location the default
2922 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2923 //some cases, e.g. menubars, it's desirable to limit the options to
2924 //above/below and force the menu to scroll if it won't fit
2925 if (nPopupModeFlags
& FloatWinPopupFlags::NoHorzPlacement
)
2927 vcl::Window
* pRef
= pWin
;
2928 if ( pRef
->GetParent() )
2929 pRef
= pRef
->GetParent();
2931 tools::Rectangle
devRect( pRef
->OutputToAbsoluteScreenPixel( aRect
.TopLeft() ),
2932 pRef
->OutputToAbsoluteScreenPixel( aRect
.BottomRight() ) );
2934 long nHeightAbove
= devRect
.Top() - aDesktopRect
.Top();
2935 long nHeightBelow
= aDesktopRect
.Bottom() - devRect
.Bottom();
2936 nMaxHeight
= std::min(nMaxHeight
, std::max(nHeightAbove
, nHeightBelow
));
2939 if (pStartedFrom
&& pStartedFrom
->IsMenuBar())
2940 nMaxHeight
-= pW
->GetSizePixel().Height();
2941 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
2942 pWindow
->GetBorder( nLeft
, nTop
, nRight
, nBottom
);
2943 nMaxHeight
-= nTop
+nBottom
;
2944 if ( aSz
.Height() > nMaxHeight
)
2946 pWin
->EnableScrollMenu( true );
2947 sal_uInt16 nStart
= ImplGetFirstVisible();
2948 sal_uInt16 nEntries
= ImplCalcVisEntries( nMaxHeight
, nStart
);
2949 aSz
.setHeight( ImplCalcHeight( nEntries
) );
2952 // tdf#126054 hold this until after function completes
2953 VclPtr
<PopupMenu
> m_xThis(this);
2955 pWin
->SetFocusId( xFocusId
);
2956 pWin
->SetOutputSizePixel( aSz
);
2957 if ( GetItemCount() )
2959 SalMenu
* pMenu
= ImplGetSalMenu();
2960 if( pMenu
&& bRealExecute
&& pMenu
->ShowNativePopupMenu( pWin
, aRect
, nPopupModeFlags
| FloatWinPopupFlags::GrabFocus
) )
2962 pWin
->StopExecute();
2964 pWindow
->SetParentToDefaultWindow();
2965 pWindow
.disposeAndClear();
2966 ImplClosePopupToolBox(pW
);
2967 ImplFlushPendingSelect();
2972 pWin
->StartPopupMode( aRect
, nPopupModeFlags
| FloatWinPopupFlags::GrabFocus
);
2977 if (pSFrom
->IsMenuBar())
2978 aPos
= static_cast<MenuBarWindow
*>(pSFrom
->pWindow
.get())->GetHighlightedItem();
2980 aPos
= static_cast<MenuFloatingWindow
*>(pSFrom
->pWindow
.get())->GetHighlightedItem();
2982 pWin
->SetPosInParent( aPos
); // store position to be sent in SUBMENUDEACTIVATE
2983 pSFrom
->ImplCallEventListeners( VclEventId::MenuSubmenuActivate
, aPos
);
2986 if ( bPreSelectFirst
)
2988 size_t nCount
= pItemList
->size();
2989 for ( size_t n
= 0; n
< nCount
; n
++ )
2991 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
2992 if ( ( pData
->bEnabled
2993 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
2995 && ( pData
->eType
!= MenuItemType::SEPARATOR
)
2996 && ImplIsVisible( n
)
2997 && ImplIsSelectable( n
)
3000 pWin
->ChangeHighlightItem( n
, false );
3008 if (pWin
->IsDisposed())
3011 xFocusId
= pWin
->GetFocusId();
3012 assert(xFocusId
== nullptr && "Focus should already be restored by MenuFloatingWindow::End");
3013 pWin
->ImplEndPopupMode(FloatWinPopupEndFlags::NONE
, xFocusId
);
3015 if ( nSelectedId
) // then clean up .. ( otherwise done by TH )
3017 PopupMenu
* pSub
= pWin
->GetActivePopup();
3020 pSub
->ImplGetFloatingWindow()->EndPopupMode();
3021 pSub
= pSub
->ImplGetFloatingWindow()->GetActivePopup();
3025 pWindow
->SetParentToDefaultWindow();
3026 pWindow
.disposeAndClear();
3027 ImplClosePopupToolBox(pW
);
3028 ImplFlushPendingSelect();
3031 return bRealExecute
? nSelectedId
: 0;
3034 sal_uInt16
PopupMenu::ImplCalcVisEntries( long nMaxHeight
, sal_uInt16 nStartEntry
, sal_uInt16
* pLastVisible
) const
3036 nMaxHeight
-= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3039 size_t nEntries
= pItemList
->size();
3040 sal_uInt16 nVisEntries
= 0;
3045 for ( size_t n
= nStartEntry
; n
< nEntries
; n
++ )
3047 if ( ImplIsVisible( n
) )
3049 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3050 nHeight
+= pData
->aSz
.Height();
3051 if ( nHeight
> nMaxHeight
)
3062 long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries
) const
3066 sal_uInt16 nFound
= 0;
3067 for ( size_t n
= 0; ( nFound
< nEntries
) && ( n
< pItemList
->size() ); n
++ )
3069 if ( ImplIsVisible( static_cast<sal_uInt16
>(n
) ) )
3071 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3072 nHeight
+= pData
->aSz
.Height();
3077 nHeight
+= 2*ImplGetFloatingWindow()->GetScrollerHeight();
3082 ImplMenuDelData::ImplMenuDelData( const Menu
* pMenu
)
3087 const_cast< Menu
* >( pMenu
)->ImplAddDel( *this );
3090 ImplMenuDelData::~ImplMenuDelData()
3093 const_cast< Menu
* >( mpMenu
.get() )->ImplRemoveDel( *this );
3096 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */