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 <accessibility/accessiblemenubasecomponent.hxx>
21 #include <accessibility/vclxaccessiblemenubar.hxx>
22 #include <accessibility/vclxaccessiblepopupmenu.hxx>
24 #include <comphelper/diagnose_ex.hxx>
25 #include <sal/log.hxx>
27 #include <comphelper/types.hxx>
28 #include <comphelper/lok.hxx>
29 #include <vcl/dialoghelper.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/mnemonic.hxx>
32 #include <vcl/image.hxx>
33 #include <vcl/event.hxx>
34 #include <vcl/help.hxx>
35 #include <vcl/toolkit/floatwin.hxx>
36 #include <vcl/decoview.hxx>
37 #include <vcl/menu.hxx>
38 #include <vcl/taskpanelist.hxx>
39 #include <vcl/settings.hxx>
40 #include <vcl/commandinfoprovider.hxx>
42 #include <salinst.hxx>
44 #include <strings.hrc>
46 #include <salmenu.hxx>
47 #include <salframe.hxx>
49 #include "menubarwindow.hxx"
50 #include "menufloatingwindow.hxx"
51 #include "menuitemlist.hxx"
53 #include <com/sun/star/uno/Reference.h>
54 #include <com/sun/star/lang/XComponent.hpp>
55 #include <com/sun/star/accessibility/XAccessible.hpp>
56 #include <vcl/toolkit/unowrap.hxx>
57 #include <rtl/ustrbuf.hxx>
59 #include <configsettings.hxx>
62 #include <string_view>
65 #include <officecfg/Office/Common.hxx>
70 struct MenuLayoutData
: public ControlLayoutData
72 std::vector
< sal_uInt16
> m_aLineItemIds
;
73 std::map
< sal_uInt16
, tools::Rectangle
> m_aVisibleItemBoundRects
;
80 #define EXTRAITEMHEIGHT 4
81 #define SPACE_AROUND_TITLE 4
83 static bool ImplAccelDisabled()
85 // display of accelerator strings may be suppressed via configuration
86 static int nAccelDisabled
= -1;
88 if( nAccelDisabled
== -1 )
91 vcl::SettingsConfigItem::get()->
92 getValue( u
"Menu"_ustr
, u
"SuppressAccelerators"_ustr
);
93 nAccelDisabled
= aStr
.equalsIgnoreAsciiCase("true") ? 1 : 0;
95 return nAccelDisabled
== 1;
98 static void ImplSetMenuItemData( MenuItemData
* pData
)
101 if ( !pData
->aImage
)
102 pData
->eType
= MenuItemType::STRING
;
103 else if ( pData
->aText
.isEmpty() )
104 pData
->eType
= MenuItemType::IMAGE
;
106 pData
->eType
= MenuItemType::STRINGIMAGE
;
111 void ImplClosePopupToolBox( const VclPtr
<vcl::Window
>& pWin
)
113 if ( pWin
->GetType() == WindowType::TOOLBOX
&& ImplGetDockingManager()->IsInPopupMode( pWin
) )
115 SystemWindow
* pFloatingWindow
= ImplGetDockingManager()->GetFloatingWindow(pWin
);
117 static_cast<FloatingWindow
*>(pFloatingWindow
)->EndPopupMode( FloatWinPopupEndFlags::CloseAll
);
121 // TODO: Move to common code with the same function in toolbox
122 // Draw the ">>" - more indicator at the coordinates
123 void lclDrawMoreIndicator(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
125 rRenderContext
.Push(PushFlags::FILLCOLOR
| PushFlags::LINECOLOR
);
126 rRenderContext
.SetLineColor();
128 if (rRenderContext
.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
129 rRenderContext
.SetFillColor(COL_WHITE
);
131 rRenderContext
.SetFillColor(COL_BLACK
);
132 float fScaleFactor
= rRenderContext
.GetDPIScaleFactor();
134 int linewidth
= 1 * fScaleFactor
;
135 int space
= 4 * fScaleFactor
;
137 tools::Long width
= 8 * fScaleFactor
;
138 tools::Long height
= 5 * fScaleFactor
;
140 //Keep odd b/c drawing code works better
141 if ( height
% 2 == 0 )
144 tools::Long heightOrig
= height
;
146 tools::Long x
= rRect
.Left() + (rRect
.getOpenWidth() - width
)/2 + 1;
147 tools::Long y
= rRect
.Top() + (rRect
.getOpenHeight() - height
)/2 + 1;
150 rRenderContext
.DrawRect( tools::Rectangle( x
, y
, x
+ linewidth
, y
) );
152 rRenderContext
.DrawRect( tools::Rectangle( x
, y
, x
+ linewidth
, y
) );
155 if( height
<= heightOrig
/ 2 + 1) x
--;
159 rRenderContext
.Pop();
162 } // end anonymous namespace
166 : mpFirstDel(nullptr),
167 pItemList(new MenuItemList
),
168 pStartedFrom(nullptr),
172 mnHighlightedItemPos(ITEMPOS_INVALID
),
173 nMenuFlags(MenuFlags::NONE
),
190 ImplCallEventListeners( VclEventId::ObjectDying
, ITEMPOS_INVALID
);
192 m_pWindow
.disposeAndClear();
194 // dispose accessible components
195 comphelper::disposeComponent(mxAccessible
);
198 Application::RemoveUserEvent( nEventId
);
200 // Notify deletion of this menu
201 ImplMenuDelData
* pDelData
= mpFirstDel
;
204 pDelData
->mpMenu
= nullptr;
205 pDelData
= pDelData
->mpNext
;
210 // tdf#140225 when clearing pItemList, keep SalMenu in sync with
211 // their removal during menu teardown
212 for (size_t n
= pItemList
->size(); n
;)
216 mpSalMenu
->RemoveItem(n
);
217 pItemList
->Remove(n
);
220 assert(!pItemList
->size());
222 mpLayoutData
.reset();
224 // Native-support: destroy SalMenu
227 pStartedFrom
.clear();
229 VclReferenceBase::dispose();
232 void Menu::CreateAutoMnemonics()
234 MnemonicGenerator aMnemonicGenerator
;
236 for ( n
= 0; n
< pItemList
->size(); n
++ )
238 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
239 if ( ! (pData
->nBits
& MenuItemBits::NOSELECT
) )
240 aMnemonicGenerator
.RegisterMnemonic( pData
->aText
);
242 for ( n
= 0; n
< pItemList
->size(); n
++ )
244 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
245 if ( ! (pData
->nBits
& MenuItemBits::NOSELECT
) )
246 pData
->aText
= aMnemonicGenerator
.CreateMnemonic( pData
->aText
);
250 void Menu::Activate()
254 ImplMenuDelData
aDelData( this );
256 ImplCallEventListeners( VclEventId::MenuActivate
, ITEMPOS_INVALID
);
258 if( !aDelData
.isDeleted() )
260 if ( !aActivateHdl
.Call( this ) )
262 if( !aDelData
.isDeleted() )
264 Menu
* pStartMenu
= ImplGetStartMenu();
265 if ( pStartMenu
&& ( pStartMenu
!= this ) )
267 pStartMenu
->bInCallback
= true;
268 // MT 11/01: Call EventListener here? I don't know...
269 pStartMenu
->aActivateHdl
.Call( this );
270 pStartMenu
->bInCallback
= false;
277 if (!aDelData
.isDeleted() && !(nMenuFlags
& MenuFlags::NoAutoMnemonics
))
278 CreateAutoMnemonics();
281 void Menu::Deactivate()
283 for ( size_t n
= pItemList
->size(); n
; )
285 MenuItemData
* pData
= pItemList
->GetDataFromPos( --n
);
286 if ( pData
->bIsTemporary
)
288 if ( ImplGetSalMenu() )
289 ImplGetSalMenu()->RemoveItem( n
);
291 pItemList
->Remove( n
);
297 ImplMenuDelData
aDelData( this );
299 Menu
* pStartMenu
= ImplGetStartMenu();
300 ImplCallEventListeners( VclEventId::MenuDeactivate
, ITEMPOS_INVALID
);
302 if( !aDelData
.isDeleted() )
304 if ( !aDeactivateHdl
.Call( this ) )
306 if( !aDelData
.isDeleted() )
308 if ( pStartMenu
&& ( pStartMenu
!= this ) )
310 pStartMenu
->bInCallback
= true;
311 pStartMenu
->aDeactivateHdl
.Call( this );
312 pStartMenu
->bInCallback
= false;
318 if( !aDelData
.isDeleted() )
324 void Menu::ImplSelect()
326 MenuItemData
* pData
= GetItemList()->GetData( nSelectedId
);
327 if ( pData
&& (pData
->nBits
& MenuItemBits::AUTOCHECK
) )
329 bool bChecked
= IsItemChecked( nSelectedId
);
330 if ( pData
->nBits
& MenuItemBits::RADIOCHECK
)
333 CheckItem( nSelectedId
);
336 CheckItem( nSelectedId
, !bChecked
);
340 ImplSVData
* pSVData
= ImplGetSVData();
341 pSVData
->maAppData
.mpActivePopupMenu
= nullptr; // if new execute in select()
342 nEventId
= Application::PostUserEvent( LINK( this, Menu
, ImplCallSelect
) );
347 ImplMenuDelData
aDelData( this );
349 ImplCallEventListeners( VclEventId::MenuSelect
, GetItemPos( GetCurItemId() ) );
350 if (aDelData
.isDeleted())
352 if (aSelectHdl
.Call(this))
354 if (aDelData
.isDeleted())
356 Menu
* pStartMenu
= ImplGetStartMenu();
357 if (!pStartMenu
|| (pStartMenu
== this))
359 pStartMenu
->nSelectedId
= nSelectedId
;
360 pStartMenu
->sSelectedIdent
= sSelectedIdent
;
361 pStartMenu
->aSelectHdl
.Call( this );
365 void Menu::ImplSelectWithStart( Menu
* pSMenu
)
367 auto pOldStartedFrom
= pStartedFrom
;
368 pStartedFrom
= pSMenu
;
369 auto pOldStartedStarted
= pOldStartedFrom
? pOldStartedFrom
->pStartedFrom
: VclPtr
<Menu
>();
371 if( pOldStartedFrom
)
372 pOldStartedFrom
->pStartedFrom
= pOldStartedStarted
;
373 pStartedFrom
= pOldStartedFrom
;
377 void Menu::ImplCallEventListeners( VclEventId nEvent
, sal_uInt16 nPos
)
379 ImplMenuDelData
aDelData( this );
381 VclMenuEvent
aEvent( this, nEvent
, nPos
);
383 // This is needed by atk accessibility bridge
384 if ( nEvent
== VclEventId::MenuHighlight
)
386 Application::ImplCallEventListeners( aEvent
);
389 if ( !aDelData
.isDeleted() )
391 // Copy the list, because this can be destroyed when calling a Link...
392 std::list
<Link
<VclMenuEvent
&,void>> aCopy( maEventListeners
);
393 for ( const auto& rLink
: aCopy
)
395 if( std::find(maEventListeners
.begin(), maEventListeners
.end(), rLink
) != maEventListeners
.end() )
396 rLink
.Call( aEvent
);
401 void Menu::AddEventListener( const Link
<VclMenuEvent
&,void>& rEventListener
)
403 maEventListeners
.push_back( rEventListener
);
406 void Menu::RemoveEventListener( const Link
<VclMenuEvent
&,void>& rEventListener
)
408 maEventListeners
.remove( rEventListener
);
411 MenuItemData
* Menu::NbcInsertItem(sal_uInt16 nId
, MenuItemBits nBits
,
412 const OUString
& rStr
, Menu
* pMenu
,
413 size_t nPos
, const OUString
&rIdent
)
415 // put Item in MenuItemList
416 MenuItemData
* pData
= pItemList
->Insert(nId
, MenuItemType::STRING
,
417 nBits
, rStr
, pMenu
, nPos
, rIdent
);
419 // update native menu
420 if (ImplGetSalMenu() && pData
->pSalMenuItem
)
421 ImplGetSalMenu()->InsertItem(pData
->pSalMenuItem
.get(), nPos
);
426 void Menu::InsertItem(sal_uInt16 nItemId
, const OUString
& rStr
, MenuItemBits nItemBits
,
427 const OUString
&rIdent
, sal_uInt16 nPos
)
429 SAL_WARN_IF( !nItemId
, "vcl", "Menu::InsertItem(): ItemId == 0" );
430 SAL_WARN_IF( GetItemPos( nItemId
) != MENU_ITEM_NOTFOUND
, "vcl",
431 "Menu::InsertItem(): ItemId already exists" );
433 // if Position > ItemCount, append
434 if ( nPos
>= pItemList
->size() )
437 // put Item in MenuItemList
438 NbcInsertItem(nItemId
, nItemBits
, rStr
, this, nPos
, rIdent
);
440 vcl::Window
* pWin
= GetWindow();
441 mpLayoutData
.reset();
444 ImplCalcSize( pWin
);
445 if ( pWin
->IsVisible() )
448 ImplCallEventListeners( VclEventId::MenuInsertItem
, nPos
);
451 void Menu::InsertItem(sal_uInt16 nItemId
, const Image
& rImage
,
452 MenuItemBits nItemBits
, const OUString
&rIdent
, sal_uInt16 nPos
)
454 InsertItem(nItemId
, OUString(), nItemBits
, rIdent
, nPos
);
455 SetItemImage( nItemId
, rImage
);
458 void Menu::InsertItem(sal_uInt16 nItemId
, const OUString
& rStr
,
459 const Image
& rImage
, MenuItemBits nItemBits
,
460 const OUString
&rIdent
, sal_uInt16 nPos
)
462 InsertItem(nItemId
, rStr
, nItemBits
, rIdent
, nPos
);
463 SetItemImage( nItemId
, rImage
);
466 void Menu::InsertSeparator(const OUString
&rIdent
, sal_uInt16 nPos
)
468 // do nothing if it's a menu bar
472 // if position > ItemCount, append
473 if ( nPos
>= pItemList
->size() )
476 // put separator in item list
477 pItemList
->InsertSeparator(rIdent
, nPos
);
479 // update native menu
480 size_t itemPos
= ( nPos
!= MENU_APPEND
) ? nPos
: pItemList
->size() - 1;
481 MenuItemData
*pData
= pItemList
->GetDataFromPos( itemPos
);
482 if( ImplGetSalMenu() && pData
&& pData
->pSalMenuItem
)
483 ImplGetSalMenu()->InsertItem( pData
->pSalMenuItem
.get(), nPos
);
485 mpLayoutData
.reset();
487 ImplCallEventListeners( VclEventId::MenuInsertItem
, nPos
);
490 void Menu::RemoveItem( sal_uInt16 nPos
)
492 bool bRemove
= false;
494 if ( nPos
< GetItemCount() )
496 // update native menu
497 if( ImplGetSalMenu() )
498 ImplGetSalMenu()->RemoveItem( nPos
);
500 pItemList
->Remove( nPos
);
504 vcl::Window
* pWin
= GetWindow();
507 ImplCalcSize( pWin
);
508 if ( pWin
->IsVisible() )
511 mpLayoutData
.reset();
514 ImplCallEventListeners( VclEventId::MenuRemoveItem
, nPos
);
517 static void ImplCopyItem( Menu
* pThis
, const Menu
& rMenu
, sal_uInt16 nPos
, sal_uInt16 nNewPos
)
519 MenuItemType eType
= rMenu
.GetItemType( nPos
);
521 if ( eType
== MenuItemType::DONTKNOW
)
524 if ( eType
== MenuItemType::SEPARATOR
)
525 pThis
->InsertSeparator( {}, nNewPos
);
528 sal_uInt16 nId
= rMenu
.GetItemId( nPos
);
530 SAL_WARN_IF( pThis
->GetItemPos( nId
) != MENU_ITEM_NOTFOUND
, "vcl",
531 "Menu::CopyItem(): ItemId already exists" );
533 MenuItemData
* pData
= rMenu
.GetItemList()->GetData( nId
);
538 if ( eType
== MenuItemType::STRINGIMAGE
)
539 pThis
->InsertItem( nId
, pData
->aText
, pData
->aImage
, pData
->nBits
, pData
->sIdent
, nNewPos
);
540 else if ( eType
== MenuItemType::STRING
)
541 pThis
->InsertItem( nId
, pData
->aText
, pData
->nBits
, pData
->sIdent
, nNewPos
);
543 pThis
->InsertItem( nId
, pData
->aImage
, pData
->nBits
, pData
->sIdent
, nNewPos
);
545 if ( rMenu
.IsItemChecked( nId
) )
546 pThis
->CheckItem( nId
);
547 if ( !rMenu
.IsItemEnabled( nId
) )
548 pThis
->EnableItem( nId
, false );
549 pThis
->SetHelpId( nId
, pData
->aHelpId
);
550 pThis
->SetHelpText( nId
, pData
->aHelpText
);
551 pThis
->SetAccelKey( nId
, pData
->aAccelKey
);
552 pThis
->SetItemCommand( nId
, pData
->aCommandStr
);
553 pThis
->SetHelpCommand( nId
, pData
->aHelpCommandStr
);
555 PopupMenu
* pSubMenu
= rMenu
.GetPopupMenu( nId
);
559 VclPtr
<PopupMenu
> pNewMenu
= VclPtr
<PopupMenu
>::Create( *pSubMenu
);
560 pThis
->SetPopupMenu( nId
, pNewMenu
);
567 for ( sal_uInt16 i
= GetItemCount(); i
; i
-- )
571 sal_uInt16
Menu::GetItemCount() const
573 return static_cast<sal_uInt16
>(pItemList
->size());
576 bool Menu::HasValidEntries(bool bCheckPopups
) const
578 bool bValidEntries
= false;
579 sal_uInt16 nCount
= GetItemCount();
580 for (sal_uInt16 n
= 0; !bValidEntries
&& (n
< nCount
); n
++)
582 MenuItemData
* pItem
= pItemList
->GetDataFromPos(n
);
583 if (pItem
->bEnabled
&& (pItem
->eType
!= MenuItemType::SEPARATOR
))
585 if (bCheckPopups
&& pItem
->pSubMenu
)
586 bValidEntries
= pItem
->pSubMenu
->HasValidEntries(true);
588 bValidEntries
= true;
591 return bValidEntries
;
594 sal_uInt16
Menu::ImplGetVisibleItemCount() const
596 sal_uInt16 nItems
= 0;
597 for ( size_t n
= pItemList
->size(); n
; )
599 if ( ImplIsVisible( --n
) )
605 sal_uInt16
Menu::ImplGetFirstVisible() const
607 for ( size_t n
= 0; n
< pItemList
->size(); n
++ )
609 if ( ImplIsVisible( n
) )
612 return ITEMPOS_INVALID
;
615 sal_uInt16
Menu::ImplGetPrevVisible( sal_uInt16 nPos
) const
617 for ( size_t n
= nPos
; n
; )
619 if (ImplIsVisible(--n
))
622 return ITEMPOS_INVALID
;
625 sal_uInt16
Menu::ImplGetNextVisible( sal_uInt16 nPos
) const
627 for ( size_t n
= nPos
+1; n
< pItemList
->size(); n
++ )
629 if ( ImplIsVisible( n
) )
632 return ITEMPOS_INVALID
;
635 sal_uInt16
Menu::GetItemId(sal_uInt16 nPos
) const
637 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
645 sal_uInt16
Menu::GetItemId(std::u16string_view rIdent
) const
647 for (size_t n
= 0; n
< pItemList
->size(); ++n
)
649 MenuItemData
* pData
= pItemList
->GetDataFromPos(n
);
650 if (pData
&& pData
->sIdent
== rIdent
)
653 return MENU_ITEM_NOTFOUND
;
656 sal_uInt16
Menu::GetItemPos( sal_uInt16 nItemId
) const
659 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
662 return static_cast<sal_uInt16
>(nPos
);
664 return MENU_ITEM_NOTFOUND
;
667 MenuItemType
Menu::GetItemType( sal_uInt16 nPos
) const
669 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
674 return MenuItemType::DONTKNOW
;
677 OUString
Menu::GetItemIdent(sal_uInt16 nId
) const
679 const MenuItemData
* pData
= pItemList
->GetData(nId
);
680 return pData
? pData
->sIdent
: OUString();
683 void Menu::SetItemBits( sal_uInt16 nItemId
, MenuItemBits nBits
)
686 MenuItemData
* pData
= pItemList
->GetData(nItemId
, nPos
);
688 if (pData
&& (pData
->nBits
!= nBits
))
690 // these two menu item bits are relevant for (accessible) role
691 const MenuItemBits nRoleMask
= MenuItemBits::CHECKABLE
| MenuItemBits::RADIOCHECK
;
692 const bool bRoleBitsChanged
= (pData
->nBits
& nRoleMask
) != (nBits
& nRoleMask
);
694 pData
->nBits
= nBits
;
696 // update native menu
697 if (ImplGetSalMenu())
698 ImplGetSalMenu()->SetItemBits(nPos
, nBits
);
700 if (bRoleBitsChanged
)
701 ImplCallEventListeners(VclEventId::MenuItemRoleChanged
, nPos
);
705 MenuItemBits
Menu::GetItemBits( sal_uInt16 nItemId
) const
707 MenuItemBits nBits
= MenuItemBits::NONE
;
708 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
710 nBits
= pData
->nBits
;
714 void Menu::SetUserValue(sal_uInt16 nItemId
, void* nUserValue
, MenuUserDataReleaseFunction aFunc
)
716 MenuItemData
* pData
= pItemList
->GetData(nItemId
);
719 if (pData
->aUserValueReleaseFunc
)
720 pData
->aUserValueReleaseFunc(pData
->nUserValue
);
721 pData
->aUserValueReleaseFunc
= aFunc
;
722 pData
->nUserValue
= nUserValue
;
726 void* Menu::GetUserValue( sal_uInt16 nItemId
) const
728 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
729 return pData
? pData
->nUserValue
: nullptr;
732 void Menu::SetPopupMenu( sal_uInt16 nItemId
, PopupMenu
* pMenu
)
735 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
737 // Item does not exist -> return NULL
741 // same menu, nothing to do
742 if ( pData
->pSubMenu
.get() == pMenu
)
746 auto oldSubMenu
= pData
->pSubMenu
;
749 pData
->pSubMenu
= pMenu
;
751 // #112023# Make sure pStartedFrom does not point to invalid (old) data
752 if ( pData
->pSubMenu
)
753 pData
->pSubMenu
->pStartedFrom
= nullptr;
755 // set native submenu
756 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
759 ImplGetSalMenu()->SetSubMenu( pData
->pSalMenuItem
.get(), pMenu
->ImplGetSalMenu(), nPos
);
761 ImplGetSalMenu()->SetSubMenu( pData
->pSalMenuItem
.get(), nullptr, nPos
);
764 oldSubMenu
.disposeAndClear();
766 ImplCallEventListeners( VclEventId::MenuSubmenuChanged
, nPos
);
769 PopupMenu
* Menu::GetPopupMenu( sal_uInt16 nItemId
) const
771 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
774 return pData
->pSubMenu
.get();
779 void Menu::SetAccelKey( sal_uInt16 nItemId
, const KeyCode
& rKeyCode
)
782 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
787 if ( pData
->aAccelKey
== rKeyCode
)
790 pData
->aAccelKey
= rKeyCode
;
792 // update native menu
793 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
794 ImplGetSalMenu()->SetAccelerator( nPos
, pData
->pSalMenuItem
.get(), rKeyCode
, rKeyCode
.GetName() );
797 KeyCode
Menu::GetAccelKey( sal_uInt16 nItemId
) const
799 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
802 return pData
->aAccelKey
;
807 KeyEvent
Menu::GetActivationKey( sal_uInt16 nItemId
) const
810 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
813 sal_Int32 nPos
= pData
->aText
.indexOf( '~' );
814 if( nPos
!= -1 && nPos
< pData
->aText
.getLength()-1 )
816 sal_uInt16 nCode
= 0;
817 sal_Unicode cAccel
= pData
->aText
[nPos
+1];
818 if( cAccel
>= 'a' && cAccel
<= 'z' )
819 nCode
= KEY_A
+ (cAccel
-'a');
820 else if( cAccel
>= 'A' && cAccel
<= 'Z' )
821 nCode
= KEY_A
+ (cAccel
-'A');
822 else if( cAccel
>= '0' && cAccel
<= '9' )
823 nCode
= KEY_0
+ (cAccel
-'0');
825 aRet
= KeyEvent( cAccel
, KeyCode( nCode
, KEY_MOD2
) );
832 void Menu::CheckItem( sal_uInt16 nItemId
, bool bCheck
)
835 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
837 if ( !pData
|| pData
->bChecked
== bCheck
)
840 // if radio-check, then uncheck previous
841 if ( bCheck
&& (pData
->nBits
& MenuItemBits::AUTOCHECK
) &&
842 (pData
->nBits
& MenuItemBits::RADIOCHECK
) )
844 MenuItemData
* pGroupData
;
845 sal_uInt16 nGroupPos
;
846 sal_uInt16 nItemCount
= GetItemCount();
852 pGroupData
= pItemList
->GetDataFromPos( nGroupPos
-1 );
853 if ( pGroupData
->nBits
& MenuItemBits::RADIOCHECK
)
855 if ( IsItemChecked( pGroupData
->nId
) )
857 CheckItem( pGroupData
->nId
, false );
870 while ( nGroupPos
< nItemCount
)
872 pGroupData
= pItemList
->GetDataFromPos( nGroupPos
);
873 if ( pGroupData
->nBits
& MenuItemBits::RADIOCHECK
)
875 if ( IsItemChecked( pGroupData
->nId
) )
877 CheckItem( pGroupData
->nId
, false );
888 pData
->bChecked
= bCheck
;
890 // update native menu
891 if( ImplGetSalMenu() )
892 ImplGetSalMenu()->CheckItem( nPos
, bCheck
);
894 ImplCallEventListeners( bCheck
? VclEventId::MenuItemChecked
: VclEventId::MenuItemUnchecked
, nPos
);
897 void Menu::CheckItem( std::u16string_view rIdent
, bool bCheck
)
899 CheckItem( GetItemId( rIdent
), bCheck
);
902 bool Menu::IsItemCheckable(sal_uInt16 nItemId
) const
905 MenuItemData
* pData
= pItemList
->GetData(nItemId
, nPos
);
910 return pData
->HasCheck();
913 bool Menu::IsItemChecked( sal_uInt16 nItemId
) const
916 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
921 return pData
->bChecked
;
924 void Menu::EnableItem( sal_uInt16 nItemId
, bool bEnable
)
927 MenuItemData
* pItemData
= pItemList
->GetData( nItemId
, nPos
);
929 if ( !(pItemData
&& ( pItemData
->bEnabled
!= bEnable
)) )
932 pItemData
->bEnabled
= bEnable
;
934 vcl::Window
* pWin
= GetWindow();
935 if ( pWin
&& pWin
->IsVisible() )
937 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
939 size_t nCount
= pItemList
->size();
940 for ( size_t n
= 0; n
< nCount
; n
++ )
942 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
945 pWin
->Invalidate( tools::Rectangle( Point( nX
, 0 ), Size( pData
->aSz
.Width(), pData
->aSz
.Height() ) ) );
948 nX
+= pData
->aSz
.Width();
951 // update native menu
952 if( ImplGetSalMenu() )
953 ImplGetSalMenu()->EnableItem( nPos
, bEnable
);
955 ImplCallEventListeners( bEnable
? VclEventId::MenuEnable
: VclEventId::MenuDisable
, nPos
);
958 bool Menu::IsItemEnabled( sal_uInt16 nItemId
) const
961 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
966 return pData
->bEnabled
;
969 void Menu::ShowItem( sal_uInt16 nItemId
, bool bVisible
)
972 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
974 SAL_WARN_IF(IsMenuBar() && !bVisible
, "vcl", "Menu::ShowItem - ignored for menu bar entries!");
975 if (IsMenuBar() || !pData
|| (pData
->bVisible
== bVisible
))
978 vcl::Window
* pWin
= GetWindow();
979 if ( pWin
&& pWin
->IsVisible() )
981 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
984 pData
->bVisible
= bVisible
;
986 // update native menu
987 if( ImplGetSalMenu() )
988 ImplGetSalMenu()->ShowItem( nPos
, bVisible
);
991 void Menu::SetItemText( sal_uInt16 nItemId
, const OUString
& rStr
)
994 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
999 if ( rStr
== pData
->aText
)
1002 pData
->aText
= rStr
;
1003 // Clear layout for aText.
1004 pData
->aTextGlyphs
.Invalidate();
1005 ImplSetMenuItemData( pData
);
1006 // update native menu
1007 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
1008 ImplGetSalMenu()->SetItemText( nPos
, pData
->pSalMenuItem
.get(), rStr
);
1010 vcl::Window
* pWin
= GetWindow();
1011 mpLayoutData
.reset();
1012 if (pWin
&& IsMenuBar())
1014 ImplCalcSize( pWin
);
1015 if ( pWin
->IsVisible() )
1019 ImplCallEventListeners( VclEventId::MenuItemTextChanged
, nPos
);
1022 OUString
Menu::GetItemText( sal_uInt16 nItemId
) const
1025 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1028 return pData
->aText
;
1033 void Menu::SetItemImage( sal_uInt16 nItemId
, const Image
& rImage
)
1036 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1041 pData
->aImage
= rImage
;
1042 ImplSetMenuItemData( pData
);
1044 // update native menu
1045 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
1046 ImplGetSalMenu()->SetItemImage( nPos
, pData
->pSalMenuItem
.get(), rImage
);
1049 Image
Menu::GetItemImage( sal_uInt16 nItemId
) const
1051 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1054 return pData
->aImage
;
1059 void Menu::SetItemCommand( sal_uInt16 nItemId
, const OUString
& rCommand
)
1062 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1065 pData
->aCommandStr
= rCommand
;
1068 OUString
Menu::GetItemCommand( sal_uInt16 nItemId
) const
1070 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1073 return pData
->aCommandStr
;
1078 void Menu::SetHelpCommand( sal_uInt16 nItemId
, const OUString
& rStr
)
1080 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1083 pData
->aHelpCommandStr
= rStr
;
1086 OUString
Menu::GetHelpCommand( sal_uInt16 nItemId
) const
1088 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1091 return pData
->aHelpCommandStr
;
1096 void Menu::SetHelpText( sal_uInt16 nItemId
, const OUString
& rStr
)
1098 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1101 pData
->aHelpText
= rStr
;
1104 OUString
Menu::ImplGetHelpText( sal_uInt16 nItemId
) const
1106 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1111 if ( pData
->aHelpText
.isEmpty() &&
1112 (( !pData
->aHelpId
.isEmpty() ) || ( !pData
->aCommandStr
.isEmpty() )))
1114 Help
* pHelp
= Application::GetHelp();
1117 if (!pData
->aCommandStr
.isEmpty())
1118 pData
->aHelpText
= pHelp
->GetHelpText( pData
->aCommandStr
);
1119 if (pData
->aHelpText
.isEmpty() && !pData
->aHelpId
.isEmpty())
1120 pData
->aHelpText
= pHelp
->GetHelpText( pData
->aHelpId
);
1124 //Fallback to Menu::GetAccessibleDescription without reentry to GetHelpText()
1125 if (pData
->aHelpText
.isEmpty())
1126 return pData
->aAccessibleDescription
;
1127 return pData
->aHelpText
;
1130 OUString
Menu::GetHelpText( sal_uInt16 nItemId
) const
1132 return ImplGetHelpText( nItemId
);
1135 void Menu::SetTipHelpText( sal_uInt16 nItemId
, const OUString
& rStr
)
1137 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1140 pData
->aTipHelpText
= rStr
;
1143 OUString
Menu::GetTipHelpText( sal_uInt16 nItemId
) const
1145 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1148 return pData
->aTipHelpText
;
1153 void Menu::SetHelpId( sal_uInt16 nItemId
, const OUString
& rHelpId
)
1155 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1158 pData
->aHelpId
= rHelpId
;
1161 OUString
Menu::GetHelpId( sal_uInt16 nItemId
) const
1165 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1169 if ( !pData
->aHelpId
.isEmpty() )
1170 aRet
= pData
->aHelpId
;
1172 aRet
= pData
->aCommandStr
;
1178 Menu
& Menu::operator=( const Menu
& rMenu
)
1187 sal_uInt16 nCount
= rMenu
.GetItemCount();
1188 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1189 ImplCopyItem( this, rMenu
, i
, MENU_APPEND
);
1191 aActivateHdl
= rMenu
.aActivateHdl
;
1192 aDeactivateHdl
= rMenu
.aDeactivateHdl
;
1193 aSelectHdl
= rMenu
.aSelectHdl
;
1194 aTitleText
= rMenu
.aTitleText
;
1195 nTitleHeight
= rMenu
.nTitleHeight
;
1200 // Returns true if the item is completely hidden on the GUI and shouldn't
1201 // be possible to interact with
1202 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos
) const
1204 MenuItemData
* pData
= pItemList
->GetDataFromPos(nPos
);
1207 MenuItemData
* pPreviousData
= pItemList
->GetDataFromPos( nPos
- 1 );
1208 if (pPreviousData
&& pPreviousData
->bHiddenOnGUI
)
1216 bool Menu::ImplIsVisible( sal_uInt16 nPos
) const
1218 bool bVisible
= true;
1220 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
1221 // check general visibility first
1222 if( pData
&& !pData
->bVisible
)
1225 if ( bVisible
&& pData
&& pData
->eType
== MenuItemType::SEPARATOR
)
1227 if( nPos
== 0 ) // no separator should be shown at the very beginning
1231 // always avoid adjacent separators
1232 size_t nCount
= pItemList
->size();
1234 MenuItemData
* pNextData
= nullptr;
1235 // search next visible item
1236 for( n
= nPos
+ 1; n
< nCount
; n
++ )
1238 pNextData
= pItemList
->GetDataFromPos( n
);
1239 if( pNextData
&& pNextData
->bVisible
)
1241 if( pNextData
->eType
== MenuItemType::SEPARATOR
|| ImplIsVisible(n
) )
1245 if( n
== nCount
) // no next visible item
1247 // check for separator
1248 if( pNextData
&& pNextData
->bVisible
&& pNextData
->eType
== MenuItemType::SEPARATOR
)
1253 for( n
= nPos
; n
> 0; n
-- )
1255 pNextData
= pItemList
->GetDataFromPos( n
-1 );
1256 if( pNextData
&& pNextData
->bVisible
)
1258 if( pNextData
->eType
!= MenuItemType::SEPARATOR
&& ImplIsVisible(n
-1) )
1262 if( n
== 0 ) // no previous visible item
1268 // not allowed for menubar, as I do not know
1269 // whether a menu-entry will disappear or will appear
1270 if (bVisible
&& !IsMenuBar() && (nMenuFlags
& MenuFlags::HideDisabledEntries
) &&
1271 !(nMenuFlags
& MenuFlags::AlwaysShowDisabledEntries
))
1273 if( !pData
) // e.g. nPos == ITEMPOS_INVALID
1275 else if ( pData
->eType
!= MenuItemType::SEPARATOR
) // separators handled above
1277 // tdf#86850 Always display clipboard functions
1278 if ( pData
->aCommandStr
== ".uno:Cut" || pData
->aCommandStr
== ".uno:Copy" || pData
->aCommandStr
== ".uno:Paste" ||
1279 pData
->sIdent
== ".uno:Cut" || pData
->sIdent
== ".uno:Copy" || pData
->sIdent
== ".uno:Paste" )
1282 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1283 bVisible
= pData
->bEnabled
; // do not check submenus as they might be filled at Activate().
1290 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos
) const
1292 return IsMenuVisible() && ImplIsVisible( nItemPos
);
1295 bool Menu::IsMenuVisible() const
1297 return m_pWindow
&& m_pWindow
->IsReallyVisible();
1300 bool Menu::ImplIsSelectable( sal_uInt16 nPos
) const
1302 bool bSelectable
= true;
1304 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
1305 // check general visibility first
1306 if ( pData
&& ( pData
->nBits
& MenuItemBits::NOSELECT
) )
1307 bSelectable
= false;
1312 css::uno::Reference
<css::accessibility::XAccessible
> Menu::CreateAccessible()
1314 rtl::Reference
<OAccessibleMenuBaseComponent
> xAccessible
;
1316 xAccessible
= new VCLXAccessibleMenuBar(this);
1318 xAccessible
= new VCLXAccessiblePopupMenu(this);
1319 xAccessible
->SetStates();
1323 css::uno::Reference
<css::accessibility::XAccessible
> Menu::GetAccessible()
1325 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1326 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1327 // mxAccessible member only for sub menus.
1328 if (pStartedFrom
&& pStartedFrom
!= this)
1330 for ( sal_uInt16 i
= 0, nCount
= pStartedFrom
->GetItemCount(); i
< nCount
; ++i
)
1332 sal_uInt16 nItemId
= pStartedFrom
->GetItemId( i
);
1333 if ( static_cast< Menu
* >( pStartedFrom
->GetPopupMenu( nItemId
) ) == this )
1335 css::uno::Reference
<css::accessibility::XAccessible
> xParent
= pStartedFrom
->GetAccessible();
1338 css::uno::Reference
<css::accessibility::XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
1339 if (xParentContext
.is())
1340 return xParentContext
->getAccessibleChild( i
);
1345 else if ( !mxAccessible
.is() )
1346 mxAccessible
= CreateAccessible();
1348 return mxAccessible
;
1351 void Menu::SetAccessible(const css::uno::Reference
<css::accessibility::XAccessible
>& rxAccessible
)
1353 mxAccessible
= rxAccessible
;
1356 Size
Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext
const & rRenderContext
, tools::Long
& rCheckHeight
, tools::Long
& rRadioHeight
) const
1358 tools::Long nCheckWidth
= 0, nRadioWidth
= 0;
1359 rCheckHeight
= rRadioHeight
= 0;
1363 ImplControlValue aVal
;
1364 tools::Rectangle aNativeBounds
;
1365 tools::Rectangle aNativeContent
;
1367 tools::Rectangle
aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1368 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItemCheckMark
))
1370 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::MenuItemCheckMark
,
1371 aCtrlRegion
, ControlState::ENABLED
, aVal
,
1372 aNativeBounds
, aNativeContent
))
1374 rCheckHeight
= aNativeBounds
.GetHeight() - 1;
1375 nCheckWidth
= aNativeContent
.GetWidth() - 1;
1378 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItemRadioMark
))
1380 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::MenuItemRadioMark
,
1381 aCtrlRegion
, ControlState::ENABLED
, aVal
,
1382 aNativeBounds
, aNativeContent
))
1384 rRadioHeight
= aNativeBounds
.GetHeight() - 1;
1385 nRadioWidth
= aNativeContent
.GetWidth() - 1;
1389 return Size(std::max(nCheckWidth
, nRadioWidth
), std::max(rCheckHeight
, rRadioHeight
));
1392 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext
const & rRenderContext
, Size
& rArrowSize
, tools::Long
& rArrowSpacing
)
1394 ImplControlValue aVal
;
1395 tools::Rectangle
aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1396 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::SubmenuArrow
))
1398 tools::Rectangle aNativeContent
;
1399 tools::Rectangle aNativeBounds
;
1400 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::SubmenuArrow
,
1401 aCtrlRegion
, ControlState::ENABLED
,
1402 aVal
, aNativeBounds
, aNativeContent
))
1404 Size
aSize(aNativeContent
.GetWidth(), aNativeContent
.GetHeight());
1406 rArrowSpacing
= aNativeBounds
.GetWidth() - aNativeContent
.GetWidth();
1413 void Menu::ImplAddDel( ImplMenuDelData
& rDel
)
1415 SAL_WARN_IF( rDel
.mpMenu
, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1419 rDel
.mpNext
= mpFirstDel
;
1424 void Menu::ImplRemoveDel( ImplMenuDelData
& rDel
)
1426 rDel
.mpMenu
= nullptr;
1427 if ( mpFirstDel
== &rDel
)
1429 mpFirstDel
= rDel
.mpNext
;
1433 ImplMenuDelData
* pData
= mpFirstDel
;
1434 while ( pData
&& (pData
->mpNext
!= &rDel
) )
1435 pData
= pData
->mpNext
;
1437 SAL_WARN_IF( !pData
, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1439 pData
->mpNext
= rDel
.mpNext
;
1443 Size
Menu::ImplCalcSize( vcl::Window
* pWin
)
1445 // | Check/Radio/Image| Text| Accel/Popup|
1447 // for symbols: nFontHeight x nFontHeight
1448 tools::Long nFontHeight
= pWin
->GetTextHeight();
1449 tools::Long nExtra
= nFontHeight
/4;
1451 tools::Long nMinMenuItemHeight
= nFontHeight
;
1452 tools::Long nCheckHeight
= 0, nRadioHeight
= 0;
1453 Size aMarkSize
= ImplGetNativeCheckAndRadioSize(*pWin
->GetOutDev(), nCheckHeight
, nRadioHeight
);
1454 if( aMarkSize
.Height() > nMinMenuItemHeight
)
1455 nMinMenuItemHeight
= aMarkSize
.Height();
1457 tools::Long aMaxImgWidth
= 0;
1459 const StyleSettings
& rSettings
= pWin
->GetSettings().GetStyleSettings();
1460 if ( rSettings
.GetUseImagesInMenus() )
1462 if ( 16 > nMinMenuItemHeight
)
1463 nMinMenuItemHeight
= 16;
1464 for ( size_t i
= pItemList
->size(); i
; )
1466 MenuItemData
* pData
= pItemList
->GetDataFromPos( --i
);
1467 if ( ImplIsVisible( i
)
1468 && ( ( pData
->eType
== MenuItemType::IMAGE
)
1469 || ( pData
->eType
== MenuItemType::STRINGIMAGE
)
1473 Size aImgSz
= pData
->aImage
.GetSizePixel();
1474 if ( aImgSz
.Width() > aMaxImgWidth
)
1475 aMaxImgWidth
= aImgSz
.Width();
1476 if ( aImgSz
.Height() > nMinMenuItemHeight
)
1477 nMinMenuItemHeight
= aImgSz
.Height();
1484 tools::Long nMaxWidth
= 0;
1486 for ( size_t n
= pItemList
->size(); n
; )
1488 MenuItemData
* pData
= pItemList
->GetDataFromPos( --n
);
1490 pData
->aSz
.setHeight( 0 );
1491 pData
->aSz
.setWidth( 0 );
1493 if ( ImplIsVisible( n
) )
1495 tools::Long nWidth
= 0;
1498 if (!IsMenuBar()&& (pData
->eType
== MenuItemType::SEPARATOR
))
1500 pData
->aSz
.setHeight( 4 );
1504 if (!IsMenuBar() && ((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1506 tools::Long aImgHeight
= pData
->aImage
.GetSizePixel().Height();
1508 aImgHeight
+= 4; // add a border for native marks
1509 if (aImgHeight
> pData
->aSz
.Height())
1510 pData
->aSz
.setHeight(aImgHeight
);
1514 if (!IsMenuBar() && pData
->HasCheck())
1516 // checks / images take the same place
1517 if( ( pData
->eType
!= MenuItemType::IMAGE
) && ( pData
->eType
!= MenuItemType::STRINGIMAGE
) )
1519 nWidth
+= aMarkSize
.Width() + nExtra
* 2;
1520 if (aMarkSize
.Height() > pData
->aSz
.Height())
1521 pData
->aSz
.setHeight(aMarkSize
.Height());
1526 if ( (pData
->eType
== MenuItemType::STRING
) || (pData
->eType
== MenuItemType::STRINGIMAGE
) )
1528 const SalLayoutGlyphs
* pGlyphs
= pData
->GetTextGlyphs(pWin
->GetOutDev());
1529 tools::Long nTextWidth
= pWin
->GetOutDev()->GetCtrlTextWidth(pData
->aText
, pGlyphs
);
1530 tools::Long nTextHeight
= pWin
->GetTextHeight() + EXTRAITEMHEIGHT
;
1534 if ( nTextHeight
> pData
->aSz
.Height() )
1535 pData
->aSz
.setHeight( nTextHeight
);
1537 pData
->aSz
.setWidth( nTextWidth
+ 4*nExtra
);
1538 aSz
.AdjustWidth(pData
->aSz
.Width() );
1541 pData
->aSz
.setHeight( std::max( std::max( nTextHeight
, pData
->aSz
.Height() ), nMinMenuItemHeight
) );
1543 nWidth
+= nTextWidth
;
1547 if (!IsMenuBar()&& pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1549 OUString aName
= pData
->aAccelKey
.GetName();
1550 tools::Long nAccWidth
= pWin
->GetTextWidth( aName
);
1551 nAccWidth
+= nExtra
;
1552 nWidth
+= nAccWidth
;
1556 if (!IsMenuBar() && pData
->pSubMenu
)
1558 if ( nFontHeight
> nWidth
)
1559 nWidth
+= nFontHeight
;
1561 pData
->aSz
.setHeight( std::max( std::max( nFontHeight
, pData
->aSz
.Height() ), nMinMenuItemHeight
) );
1565 aSz
.AdjustHeight(pData
->aSz
.Height() );
1567 if ( nWidth
> nMaxWidth
)
1573 // Additional space for title
1575 if (!IsMenuBar() && aTitleText
.getLength() > 0) {
1576 // Set expected font
1577 pWin
->GetOutDev()->Push(PushFlags::FONT
);
1578 vcl::Font aFont
= pWin
->GetFont();
1579 aFont
.SetWeight(WEIGHT_BOLD
);
1580 pWin
->SetFont(aFont
);
1582 // Compute text bounding box
1583 tools::Rectangle aTextBoundRect
;
1584 pWin
->GetOutDev()->GetTextBoundRect(aTextBoundRect
, aTitleText
);
1586 // Vertically, one height of char + extra space for decoration
1587 nTitleHeight
= aTextBoundRect
.GetSize().Height() + 4 * SPACE_AROUND_TITLE
;
1588 aSz
.AdjustHeight(nTitleHeight
);
1590 tools::Long nWidth
= aTextBoundRect
.GetSize().Width() + 4 * SPACE_AROUND_TITLE
;
1591 pWin
->GetOutDev()->Pop();
1592 if ( nWidth
> nMaxWidth
)
1598 // popup menus should not be wider than half the screen
1599 // except on rather small screens
1600 // TODO: move GetScreenNumber from SystemWindow to Window ?
1601 // currently we rely on internal privileges
1602 unsigned int nDisplayScreen
= pWin
->ImplGetWindowImpl()->mpFrame
->GetUnmirroredGeometry().screen();
1603 tools::Rectangle
aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen
) );
1604 tools::Long nScreenWidth
= aDispRect
.GetWidth() >= 800 ? aDispRect
.GetWidth() : 800;
1605 if( nMaxWidth
> nScreenWidth
/2 )
1606 nMaxWidth
= nScreenWidth
/2;
1608 sal_uInt16 gfxExtra
= static_cast<sal_uInt16
>(std::max( nExtra
, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
1609 nImgOrChkPos
= static_cast<sal_uInt16
>(nExtra
);
1610 tools::Long nImgOrChkWidth
= 0;
1611 if( aMarkSize
.Height() > 0 ) // NWF case
1612 nImgOrChkWidth
= aMarkSize
.Height() + nExtra
;
1613 else // non NWF case
1614 nImgOrChkWidth
= nFontHeight
/2 + gfxExtra
;
1615 nImgOrChkWidth
= std::max( nImgOrChkWidth
, aMaxImgWidth
+ gfxExtra
);
1616 nTextPos
= static_cast<sal_uInt16
>(nImgOrChkPos
+ nImgOrChkWidth
);
1617 nTextPos
= nTextPos
+ gfxExtra
;
1619 aSz
.setWidth( nTextPos
+ nMaxWidth
+ nExtra
);
1620 aSz
.AdjustWidth(4*nExtra
); // a _little_ more ...
1622 aSz
.AdjustWidth(2*ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
);
1623 aSz
.AdjustHeight(2*ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
);
1627 nTextPos
= static_cast<sal_uInt16
>(2*nExtra
);
1628 aSz
.setHeight( nFontHeight
+6 );
1630 // get menubar height from native methods if supported
1631 if (m_pWindow
->IsNativeControlSupported(ControlType::Menubar
, ControlPart::Entire
))
1633 ImplControlValue aVal
;
1634 tools::Rectangle aNativeBounds
;
1635 tools::Rectangle aNativeContent
;
1637 tools::Rectangle
aCtrlRegion( tmp
, Size( 100, 15 ) );
1638 if (m_pWindow
->GetNativeControlRegion(ControlType::Menubar
,
1639 ControlPart::Entire
,
1641 ControlState::ENABLED
,
1647 int nNativeHeight
= aNativeBounds
.GetHeight();
1648 if( nNativeHeight
> aSz
.Height() )
1649 aSz
.setHeight( nNativeHeight
);
1653 // account for the size of the close button, which actually is a toolbox
1654 // due to NWF this is variable
1655 tools::Long nCloseButtonHeight
= static_cast<MenuBarWindow
*>(m_pWindow
.get())->MinCloseButtonSize().Height();
1656 if (aSz
.Height() < nCloseButtonHeight
)
1657 aSz
.setHeight( nCloseButtonHeight
);
1663 static void ImplPaintCheckBackground(vcl::RenderContext
& rRenderContext
, vcl::Window
const & rWindow
, const tools::Rectangle
& i_rRect
, bool i_bHighlight
)
1665 bool bNativeOk
= false;
1666 if (rRenderContext
.IsNativeControlSupported(ControlType::Toolbar
, ControlPart::Button
))
1668 ImplControlValue aControlValue
;
1669 aControlValue
.setTristateVal(ButtonValue::On
);
1670 tools::Rectangle r
= i_rRect
;
1673 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::Toolbar
, ControlPart::Button
,
1675 ControlState::PRESSED
| ControlState::ENABLED
,
1682 const StyleSettings
& rSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1683 Color
aColor( i_bHighlight
? rSettings
.GetMenuHighlightTextColor() : rSettings
.GetHighlightColor() );
1684 RenderTools::DrawSelectionBackground(rRenderContext
, rWindow
, i_rRect
, 0, i_bHighlight
, true, false, nullptr, 2, &aColor
);
1688 static OUString
getShortenedString( const OUString
& i_rLong
, vcl::RenderContext
const & rRenderContext
, tools::Long i_nMaxWidth
)
1690 sal_Int32 nPos
= -1;
1691 OUString
aNonMnem(removeMnemonicFromString(i_rLong
, nPos
));
1692 aNonMnem
= rRenderContext
.GetEllipsisString( aNonMnem
, i_nMaxWidth
, DrawTextFlags::CenterEllipsis
);
1693 // re-insert mnemonic
1696 if (nPos
< aNonMnem
.getLength() && i_rLong
[nPos
+1] == aNonMnem
[nPos
])
1697 aNonMnem
= OUString::Concat(aNonMnem
.subView(0, nPos
)) + "~" + aNonMnem
.subView(nPos
);
1702 void Menu::ImplPaintMenuTitle(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
) const
1704 // Save previous graphical settings, set new one
1705 rRenderContext
.Push(PushFlags::FONT
| PushFlags::FILLCOLOR
);
1706 Wallpaper aOldBackground
= rRenderContext
.GetBackground();
1708 Color aBackgroundColor
= rRenderContext
.GetSettings().GetStyleSettings().GetMenuBarColor();
1709 rRenderContext
.SetBackground(Wallpaper(aBackgroundColor
));
1710 rRenderContext
.SetFillColor(aBackgroundColor
);
1711 vcl::Font aFont
= rRenderContext
.GetFont();
1712 aFont
.SetWeight(WEIGHT_BOLD
);
1713 rRenderContext
.SetFont(aFont
);
1715 // Draw background rectangle
1716 tools::Rectangle
aBgRect(rRect
);
1717 int nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
1718 aBgRect
.Move(SPACE_AROUND_TITLE
, SPACE_AROUND_TITLE
);
1719 aBgRect
.setWidth(aBgRect
.getOpenWidth() - 2 * SPACE_AROUND_TITLE
- 2 * nOuterSpaceX
);
1720 aBgRect
.setHeight(nTitleHeight
- 2 * SPACE_AROUND_TITLE
);
1721 rRenderContext
.DrawRect(aBgRect
);
1723 // Draw the text centered
1724 Point
aTextTopLeft(aBgRect
.TopLeft());
1725 tools::Rectangle aTextBoundRect
;
1726 rRenderContext
.GetTextBoundRect( aTextBoundRect
, aTitleText
);
1727 aTextTopLeft
.AdjustX((aBgRect
.getOpenWidth() - aTextBoundRect
.GetSize().Width()) / 2 );
1728 aTextTopLeft
.AdjustY((aBgRect
.GetHeight() - aTextBoundRect
.GetSize().Height()) / 2
1729 - aTextBoundRect
.Top() );
1730 rRenderContext
.DrawText(aTextTopLeft
, aTitleText
, 0, aTitleText
.getLength());
1733 rRenderContext
.Pop();
1734 rRenderContext
.SetBackground(aOldBackground
);
1737 void Menu::ImplPaint(vcl::RenderContext
& rRenderContext
, Size
const & rSize
,
1738 sal_uInt16 nBorder
, tools::Long nStartY
, MenuItemData
const * pThisItemOnly
,
1739 bool bHighlighted
, bool bLayout
, bool bRollover
) const
1741 // for symbols: nFontHeight x nFontHeight
1742 tools::Long nFontHeight
= rRenderContext
.GetTextHeight();
1743 tools::Long nExtra
= nFontHeight
/ 4;
1745 tools::Long nCheckHeight
= 0, nRadioHeight
= 0;
1746 ImplGetNativeCheckAndRadioSize(rRenderContext
, nCheckHeight
, nRadioHeight
);
1748 DecorationView
aDecoView(&rRenderContext
);
1749 const StyleSettings
& rSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1751 Point aTopLeft
, aTmpPos
;
1753 int nOuterSpaceX
= 0;
1756 nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
1757 aTopLeft
.AdjustX(nOuterSpaceX
);
1758 aTopLeft
.AdjustY(ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
);
1761 // for the computations, use size of the underlying window, not of RenderContext
1764 size_t nCount
= pItemList
->size();
1766 mpLayoutData
->m_aVisibleItemBoundRects
.clear();
1769 if (!pThisItemOnly
&& !IsMenuBar() && nTitleHeight
> 0)
1770 ImplPaintMenuTitle(rRenderContext
, tools::Rectangle(aTopLeft
, aOutSz
));
1772 bool bHiddenItems
= false; // are any items on the GUI hidden
1774 for (size_t n
= 0; n
< nCount
; n
++)
1776 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
1777 if (ImplIsVisible(n
) && (!pThisItemOnly
|| (pData
== pThisItemOnly
)))
1783 if (!ImplGetSVData()->maNWFData
.mbRolloverMenubar
)
1786 rRenderContext
.SetTextColor(rSettings
.GetMenuBarRolloverTextColor());
1787 else if (bHighlighted
)
1788 rRenderContext
.SetTextColor(rSettings
.GetMenuBarHighlightTextColor());
1793 rRenderContext
.SetTextColor(rSettings
.GetMenuBarHighlightTextColor());
1795 rRenderContext
.SetTextColor(rSettings
.GetMenuBarRolloverTextColor());
1797 if (!bRollover
&& !bHighlighted
)
1798 rRenderContext
.SetTextColor(rSettings
.GetMenuBarTextColor());
1800 else if (bHighlighted
)
1801 rRenderContext
.SetTextColor(rSettings
.GetMenuHighlightTextColor());
1804 Point
aPos(aTopLeft
);
1805 aPos
.AdjustY(nBorder
);
1806 aPos
.AdjustY(nStartY
);
1810 tools::Long nTextOffsetY
= (pData
->aSz
.Height() - nFontHeight
) / 2;
1812 nTextOffsetY
+= (aOutSz
.Height()-pData
->aSz
.Height()) / 2;
1813 DrawTextFlags nTextStyle
= DrawTextFlags::NONE
;
1814 DrawSymbolFlags nSymbolStyle
= DrawSymbolFlags::NONE
;
1815 DrawImageFlags nImageStyle
= DrawImageFlags::NONE
;
1817 // submenus without items are not disabled when no items are
1818 // contained. The application itself should check for this!
1819 // Otherwise it could happen entries are disabled due to
1820 // asynchronous loading
1821 if (!pData
->bEnabled
|| !m_pWindow
->IsEnabled())
1823 nTextStyle
|= DrawTextFlags::Disable
;
1824 nSymbolStyle
|= DrawSymbolFlags::Disable
;
1825 nImageStyle
|= DrawImageFlags::Disable
;
1829 if (!bLayout
&& !IsMenuBar() && (pData
->eType
== MenuItemType::SEPARATOR
))
1831 bool bNativeOk
= false;
1832 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Separator
))
1834 ControlState nState
= ControlState::NONE
;
1835 if (pData
->bEnabled
&& m_pWindow
->IsEnabled())
1836 nState
|= ControlState::ENABLED
;
1838 nState
|= ControlState::SELECTED
;
1839 Size
aSz(pData
->aSz
);
1840 aSz
.setWidth( aOutSz
.Width() - 2*nOuterSpaceX
);
1841 tools::Rectangle
aItemRect(aPos
, aSz
);
1842 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
1843 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::Separator
,
1844 aItemRect
, nState
, aVal
, OUString());
1848 aTmpPos
.setY( aPos
.Y() + ((pData
->aSz
.Height() - 2) / 2) );
1849 aTmpPos
.setX( aPos
.X() + 2 + nOuterSpaceX
);
1850 rRenderContext
.SetLineColor(rSettings
.GetShadowColor());
1851 rRenderContext
.DrawLine(aTmpPos
, Point(aOutSz
.Width() - 3 - 2 * nOuterSpaceX
, aTmpPos
.Y()));
1852 aTmpPos
.AdjustY( 1 );
1853 rRenderContext
.SetLineColor(rSettings
.GetLightColor());
1854 rRenderContext
.DrawLine(aTmpPos
, Point(aOutSz
.Width() - 3 - 2 * nOuterSpaceX
, aTmpPos
.Y()));
1855 rRenderContext
.SetLineColor();
1859 tools::Rectangle
aOuterCheckRect(Point(aPos
.X()+nImgOrChkPos
, aPos
.Y()),
1860 Size(pData
->aSz
.Height(), pData
->aSz
.Height()));
1863 if (!bLayout
&& !IsMenuBar() && pData
->HasCheck())
1865 // draw selection transparent marker if checked
1866 // onto that either a checkmark or the item image
1868 // however do not do this if native checks will be painted since
1869 // the selection color too often does not fit the theme's check and/or radio
1871 if( (pData
->eType
!= MenuItemType::IMAGE
) && (pData
->eType
!= MenuItemType::STRINGIMAGE
))
1873 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
,
1874 (pData
->nBits
& MenuItemBits::RADIOCHECK
)
1875 ? ControlPart::MenuItemCheckMark
1876 : ControlPart::MenuItemRadioMark
))
1878 ControlPart nPart
= ((pData
->nBits
& MenuItemBits::RADIOCHECK
)
1879 ? ControlPart::MenuItemRadioMark
1880 : ControlPart::MenuItemCheckMark
);
1882 ControlState nState
= ControlState::NONE
;
1884 if (pData
->bChecked
)
1885 nState
|= ControlState::PRESSED
;
1887 if (pData
->bEnabled
&& m_pWindow
->IsEnabled())
1888 nState
|= ControlState::ENABLED
;
1891 nState
|= ControlState::SELECTED
;
1893 tools::Long nCtrlHeight
= (pData
->nBits
& MenuItemBits::RADIOCHECK
) ? nCheckHeight
: nRadioHeight
;
1894 aTmpPos
.setX( aOuterCheckRect
.Left() + (aOuterCheckRect
.GetWidth() - nCtrlHeight
) / 2 );
1895 aTmpPos
.setY( aOuterCheckRect
.Top() + (aOuterCheckRect
.GetHeight() - nCtrlHeight
) / 2 );
1897 tools::Rectangle
aCheckRect(aTmpPos
, Size(nCtrlHeight
, nCtrlHeight
));
1898 Size
aSz(pData
->aSz
);
1899 aSz
.setWidth( aOutSz
.Width() - 2 * nOuterSpaceX
);
1900 tools::Rectangle
aItemRect(aPos
, aSz
);
1901 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
1902 rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, nPart
, aCheckRect
,
1903 nState
, aVal
, OUString());
1905 else if (pData
->bChecked
) // by default do nothing for unchecked items
1907 ImplPaintCheckBackground(rRenderContext
, *m_pWindow
, aOuterCheckRect
, pThisItemOnly
&& bHighlighted
);
1911 if (pData
->nBits
& MenuItemBits::RADIOCHECK
)
1913 eSymbol
= SymbolType::RADIOCHECKMARK
;
1914 aSymbolSize
= Size(nFontHeight
/ 2, nFontHeight
/ 2);
1918 eSymbol
= SymbolType::CHECKMARK
;
1919 aSymbolSize
= Size((nFontHeight
* 25) / 40, nFontHeight
/ 2);
1921 aTmpPos
.setX( aOuterCheckRect
.Left() + (aOuterCheckRect
.GetWidth() - aSymbolSize
.Width()) / 2 );
1922 aTmpPos
.setY( aOuterCheckRect
.Top() + (aOuterCheckRect
.GetHeight() - aSymbolSize
.Height()) / 2 );
1923 tools::Rectangle
aRect(aTmpPos
, aSymbolSize
);
1924 aDecoView
.DrawSymbol(aRect
, eSymbol
, rRenderContext
.GetTextColor(), nSymbolStyle
);
1930 if (!bLayout
&& !IsMenuBar() && ((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1932 // Don't render an image for a check thing
1933 if (pData
->bChecked
)
1934 ImplPaintCheckBackground(rRenderContext
, *m_pWindow
, aOuterCheckRect
, pThisItemOnly
&& bHighlighted
);
1936 Image aImage
= pData
->aImage
;
1938 aTmpPos
= aOuterCheckRect
.TopLeft();
1939 aTmpPos
.AdjustX((aOuterCheckRect
.GetWidth() - aImage
.GetSizePixel().Width()) / 2 );
1940 aTmpPos
.AdjustY((aOuterCheckRect
.GetHeight() - aImage
.GetSizePixel().Height()) / 2 );
1941 rRenderContext
.DrawImage(aTmpPos
, aImage
, nImageStyle
);
1945 if ((pData
->eType
== MenuItemType::STRING
) || (pData
->eType
== MenuItemType::STRINGIMAGE
))
1947 aTmpPos
.setX( aPos
.X() + nTextPos
);
1948 aTmpPos
.setY( aPos
.Y() );
1949 aTmpPos
.AdjustY(nTextOffsetY
);
1950 DrawTextFlags nStyle
= nTextStyle
;
1952 const Menu
*pMenu
= this;
1953 while (!pMenu
->IsMenuBar() && pMenu
->pStartedFrom
)
1954 pMenu
= pMenu
->pStartedFrom
;
1955 if (!pMenu
->IsMenuBar() || !static_cast<MenuBarWindow
*>(pMenu
->m_pWindow
.get())->GetMBWHideAccel())
1956 nStyle
|= DrawTextFlags::Mnemonic
;
1958 if (pData
->bIsTemporary
)
1959 nStyle
|= DrawTextFlags::Disable
;
1960 std::vector
< tools::Rectangle
>* pVector
= bLayout
? &mpLayoutData
->m_aUnicodeBoundRects
: nullptr;
1961 OUString
* pDisplayText
= bLayout
? &mpLayoutData
->m_aDisplayText
: nullptr;
1964 mpLayoutData
->m_aLineIndices
.push_back(mpLayoutData
->m_aDisplayText
.getLength());
1965 mpLayoutData
->m_aLineItemIds
.push_back(pData
->nId
);
1967 // #i47946# with NWF painted menus the background is transparent
1968 // since DrawCtrlText can depend on the background (e.g. for
1969 // DrawTextFlags::Disable), temporarily set a background which
1970 // hopefully matches the NWF background since it is read
1971 // from the system style settings
1972 bool bSetTmpBackground
= !rRenderContext
.IsBackground()
1973 && rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
);
1974 if (bSetTmpBackground
)
1976 Color aBg
= IsMenuBar() ? rRenderContext
.GetSettings().GetStyleSettings().GetMenuBarColor()
1977 : rRenderContext
.GetSettings().GetStyleSettings().GetMenuColor();
1978 rRenderContext
.SetBackground(Wallpaper(aBg
));
1980 // how much space is there for the text?
1981 tools::Long nMaxItemTextWidth
= aOutSz
.Width() - aTmpPos
.X() - nExtra
- nOuterSpaceX
;
1982 if (!IsMenuBar() && pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1984 OUString aAccText
= pData
->aAccelKey
.GetName();
1985 nMaxItemTextWidth
-= rRenderContext
.GetTextWidth(aAccText
) + 3 * nExtra
;
1987 if (!IsMenuBar() && pData
->pSubMenu
)
1989 nMaxItemTextWidth
-= nFontHeight
- nExtra
;
1992 OUString
aItemText(pData
->aText
);
1993 pData
->bHiddenOnGUI
= false;
1995 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1997 if (nMaxItemTextWidth
< rRenderContext
.GetTextWidth(aItemText
))
2000 pData
->bHiddenOnGUI
= true;
2001 bHiddenItems
= true;
2006 aItemText
= getShortenedString(aItemText
, rRenderContext
, nMaxItemTextWidth
);
2007 pData
->bHiddenOnGUI
= false;
2010 const SalLayoutGlyphs
* pGlyphs
= pData
->GetTextGlyphs(&rRenderContext
);
2011 if (aItemText
!= pData
->aText
)
2012 // Can't use pre-computed glyphs, item text was
2015 rRenderContext
.DrawCtrlText(aTmpPos
, aItemText
, 0, aItemText
.getLength(),
2016 nStyle
, pVector
, pDisplayText
, pGlyphs
);
2017 if (bSetTmpBackground
)
2018 rRenderContext
.SetBackground();
2022 if (!bLayout
&& !IsMenuBar() && pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
2024 OUString aAccText
= pData
->aAccelKey
.GetName();
2025 aTmpPos
.setX( aOutSz
.Width() - rRenderContext
.GetTextWidth(aAccText
) );
2026 aTmpPos
.AdjustX( -(4 * nExtra
) );
2028 aTmpPos
.AdjustX( -nOuterSpaceX
);
2029 aTmpPos
.setY( aPos
.Y() );
2030 aTmpPos
.AdjustY(nTextOffsetY
);
2031 rRenderContext
.DrawCtrlText(aTmpPos
, aAccText
, 0, aAccText
.getLength(), nTextStyle
);
2035 if (!bLayout
&& !IsMenuBar() && pData
->pSubMenu
)
2037 bool bNativeOk
= false;
2038 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::SubmenuArrow
))
2040 ControlState nState
= ControlState::NONE
;
2042 tools::Long aSpacing
= 0;
2044 if (!ImplGetNativeSubmenuArrowSize(rRenderContext
, aTmpSz
, aSpacing
))
2046 aTmpSz
= Size(nFontHeight
, nFontHeight
);
2047 aSpacing
= nOuterSpaceX
;
2050 if (pData
->bEnabled
&& m_pWindow
->IsEnabled())
2051 nState
|= ControlState::ENABLED
;
2053 nState
|= ControlState::SELECTED
;
2055 aTmpPos
.setX( aOutSz
.Width() - aTmpSz
.Width() - aSpacing
- nOuterSpaceX
);
2056 aTmpPos
.setY( aPos
.Y() + ( pData
->aSz
.Height() - aTmpSz
.Height() ) / 2 );
2057 aTmpPos
.AdjustY(nExtra
/ 2 );
2059 tools::Rectangle
aItemRect(aTmpPos
, aTmpSz
);
2060 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
2061 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::SubmenuArrow
,
2062 aItemRect
, nState
, aVal
, OUString());
2066 aTmpPos
.setX( aOutSz
.Width() - nFontHeight
+ nExtra
- nOuterSpaceX
);
2067 aTmpPos
.setY( aPos
.Y() );
2068 aTmpPos
.AdjustY(nExtra
/2 );
2069 aTmpPos
.AdjustY((pData
->aSz
.Height() / 2) - (nFontHeight
/ 4) );
2070 if (pData
->nBits
& MenuItemBits::POPUPSELECT
)
2072 rRenderContext
.SetTextColor(rSettings
.GetMenuTextColor());
2073 Point
aTmpPos2(aPos
);
2074 aTmpPos2
.setX( aOutSz
.Width() - nFontHeight
- nFontHeight
/4 );
2075 aDecoView
.DrawFrame(tools::Rectangle(aTmpPos2
, Size(nFontHeight
+ nFontHeight
/ 4,
2076 pData
->aSz
.Height())),
2077 DrawFrameStyle::Group
);
2079 aDecoView
.DrawSymbol(tools::Rectangle(aTmpPos
, Size(nFontHeight
/ 2, nFontHeight
/ 2)),
2080 SymbolType::SPIN_RIGHT
, rRenderContext
.GetTextColor(), nSymbolStyle
);
2084 if (pThisItemOnly
&& bHighlighted
)
2086 // This restores the normal menu or menu bar text
2087 // color for when it is no longer highlighted.
2089 rRenderContext
.SetTextColor(rSettings
.GetMenuBarTextColor());
2091 rRenderContext
.SetTextColor(rSettings
.GetMenuTextColor());
2097 mpLayoutData
->m_aVisibleItemBoundRects
[ n
] = tools::Rectangle(aTopLeft
, Size(aOutSz
.Width(), pData
->aSz
.Height()));
2099 mpLayoutData
->m_aVisibleItemBoundRects
[ n
] = tools::Rectangle(aTopLeft
, pData
->aSz
);
2104 aTopLeft
.AdjustY(pData
->aSz
.Height() );
2106 aTopLeft
.AdjustX(pData
->aSz
.Width() );
2109 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2112 sal_Int32 nSize
= nFontHeight
;
2113 tools::Rectangle
aRectangle(Point(aOutSz
.Width() - nSize
, (aOutSz
.Height() / 2) - (nSize
/ 2)), Size(nSize
, nSize
));
2114 lclDrawMoreIndicator(rRenderContext
, aRectangle
);
2118 Menu
* Menu::ImplGetStartMenu()
2120 Menu
* pStart
= this;
2121 while ( pStart
&& pStart
->pStartedFrom
&& ( pStart
->pStartedFrom
!= pStart
) )
2122 pStart
= pStart
->pStartedFrom
;
2126 void Menu::ImplCallHighlight(sal_uInt16 nItem
)
2128 ImplMenuDelData
aDelData( this );
2131 sSelectedIdent
.clear();
2132 MenuItemData
* pData
= pItemList
->GetDataFromPos(nItem
);
2135 nSelectedId
= pData
->nId
;
2136 sSelectedIdent
= pData
->sIdent
;
2138 ImplCallEventListeners( VclEventId::MenuHighlight
, GetItemPos( GetCurItemId() ) );
2140 if( !aDelData
.isDeleted() )
2143 sSelectedIdent
.clear();
2147 IMPL_LINK_NOARG(Menu
, ImplCallSelect
, void*, void)
2153 Menu
* Menu::ImplFindSelectMenu()
2155 Menu
* pSelMenu
= nEventId
? this : nullptr;
2157 for ( size_t n
= GetItemList()->size(); n
&& !pSelMenu
; )
2159 MenuItemData
* pData
= GetItemList()->GetDataFromPos( --n
);
2161 if ( pData
->pSubMenu
)
2162 pSelMenu
= pData
->pSubMenu
->ImplFindSelectMenu();
2168 Menu
* Menu::ImplFindMenu( sal_uInt16 nItemId
)
2170 Menu
* pSelMenu
= nullptr;
2172 for ( size_t n
= GetItemList()->size(); n
&& !pSelMenu
; )
2174 MenuItemData
* pData
= GetItemList()->GetDataFromPos( --n
);
2176 if( pData
->nId
== nItemId
)
2178 else if ( pData
->pSubMenu
)
2179 pSelMenu
= pData
->pSubMenu
->ImplFindMenu( nItemId
);
2185 void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups
)
2188 while (n
< GetItemCount())
2190 bool bRemove
= false;
2191 MenuItemData
* pItem
= pItemList
->GetDataFromPos( n
);
2192 if ( pItem
->eType
== MenuItemType::SEPARATOR
)
2194 if ( !n
|| ( GetItemType( n
-1 ) == MenuItemType::SEPARATOR
) )
2198 bRemove
= !pItem
->bEnabled
;
2200 if ( pItem
->pSubMenu
)
2202 pItem
->pSubMenu
->RemoveDisabledEntries();
2203 if ( bRemoveEmptyPopups
&& !pItem
->pSubMenu
->GetItemCount() )
2213 if ( GetItemCount() )
2215 sal_uInt16 nLast
= GetItemCount() - 1;
2216 MenuItemData
* pItem
= pItemList
->GetDataFromPos( nLast
);
2217 if ( pItem
->eType
== MenuItemType::SEPARATOR
)
2218 RemoveItem( nLast
);
2220 mpLayoutData
.reset();
2223 void Menu::UpdateNativeMenu()
2225 if ( ImplGetSalMenu() )
2226 ImplGetSalMenu()->Update();
2229 void Menu::MenuBarKeyInput(const KeyEvent
&)
2233 void Menu::ImplKillLayoutData() const
2235 mpLayoutData
.reset();
2238 void Menu::ImplFillLayoutData() const
2240 if (!(m_pWindow
&& m_pWindow
->IsReallyVisible()))
2243 mpLayoutData
.reset(new MenuLayoutData
);
2246 ImplPaint(*m_pWindow
->GetOutDev(), m_pWindow
->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2250 MenuFloatingWindow
* pFloat
= static_cast<MenuFloatingWindow
*>(m_pWindow
.get());
2251 ImplPaint(*m_pWindow
->GetOutDev(), m_pWindow
->GetOutputSizePixel(), pFloat
->nScrollerHeight
, pFloat
->ImplGetStartY(),
2252 nullptr, false, true); //FIXME
2256 tools::Rectangle
Menu::GetCharacterBounds( sal_uInt16 nItemID
, tools::Long nIndex
) const
2258 tools::Long nItemIndex
= -1;
2259 if( ! mpLayoutData
)
2260 ImplFillLayoutData();
2263 for( size_t i
= 0; i
< mpLayoutData
->m_aLineItemIds
.size(); i
++ )
2265 if( mpLayoutData
->m_aLineItemIds
[i
] == nItemID
)
2267 nItemIndex
= mpLayoutData
->m_aLineIndices
[i
];
2272 return (mpLayoutData
&& nItemIndex
!= -1) ? mpLayoutData
->GetCharacterBounds( nItemIndex
+nIndex
) : tools::Rectangle();
2275 tools::Long
Menu::GetIndexForPoint( const Point
& rPoint
, sal_uInt16
& rItemID
) const
2278 if( ! mpLayoutData
)
2279 ImplFillLayoutData();
2282 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
2283 tools::Long nIndex
= mpLayoutData
->GetIndexForPoint( rPoint
);
2284 for( size_t i
= 0; i
< mpLayoutData
->m_aLineIndices
.size(); i
++ )
2286 if( mpLayoutData
->m_aLineIndices
[i
] <= nIndex
&&
2287 (i
== mpLayoutData
->m_aLineIndices
.size()-1 || mpLayoutData
->m_aLineIndices
[i
+1] > nIndex
) )
2289 rItemID
= mpLayoutData
->m_aLineItemIds
[i
];
2290 // return index relative to item
2291 return nIndex
- mpLayoutData
->m_aLineIndices
[i
];
2298 tools::Rectangle
Menu::GetBoundingRectangle( sal_uInt16 nPos
) const
2300 tools::Rectangle aRet
;
2303 ImplFillLayoutData();
2306 std::map
< sal_uInt16
, tools::Rectangle
>::const_iterator it
= mpLayoutData
->m_aVisibleItemBoundRects
.find( nPos
);
2307 if( it
!= mpLayoutData
->m_aVisibleItemBoundRects
.end() )
2313 void Menu::SetAccessibleName( sal_uInt16 nItemId
, const OUString
& rStr
)
2316 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
2318 if (pData
&& !rStr
.equals(pData
->aAccessibleName
))
2320 pData
->aAccessibleName
= rStr
;
2321 ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged
, nPos
);
2325 OUString
Menu::GetAccessibleName( sal_uInt16 nItemId
) const
2327 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2330 return pData
->aAccessibleName
;
2335 void Menu::SetAccessibleDescription( sal_uInt16 nItemId
, const OUString
& rStr
)
2337 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2340 pData
->aAccessibleDescription
= rStr
;
2343 OUString
Menu::GetAccessibleDescription( sal_uInt16 nItemId
) const
2345 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2347 if (pData
&& !pData
->aAccessibleDescription
.isEmpty())
2348 return pData
->aAccessibleDescription
;
2350 return GetHelpText(nItemId
);
2353 void Menu::GetSystemMenuData(SystemMenuData
& rData
) const
2355 if (ImplGetSalMenu())
2357 ImplGetSalMenu()->GetSystemMenuData(rData
);
2361 bool Menu::IsHighlighted( sal_uInt16 nItemPos
) const
2368 bRet
= ( nItemPos
== static_cast< MenuBarWindow
* > (m_pWindow
.get())->GetHighlightedItem() );
2370 bRet
= ( nItemPos
== static_cast< MenuFloatingWindow
* > (m_pWindow
.get())->GetHighlightedItem() );
2376 void Menu::HighlightItem( sal_uInt16 nItemPos
)
2383 MenuBarWindow
* pMenuWin
= static_cast< MenuBarWindow
* >( m_pWindow
.get() );
2384 pMenuWin
->SetAutoPopup( false );
2385 pMenuWin
->ChangeHighlightItem( nItemPos
, false );
2389 static_cast< MenuFloatingWindow
* >( m_pWindow
.get() )->ChangeHighlightItem( nItemPos
, false );
2393 void Menu::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
) const
2395 rJsonWriter
.put("id", "__MENU__"); // we have single instance of menu at the time per session
2396 rJsonWriter
.put("type", "menu");
2397 rJsonWriter
.put("count", GetItemCount());
2399 auto aEntries
= rJsonWriter
.startArray("entries");
2400 for (size_t i
= 0; i
< GetItemCount(); i
++)
2402 auto aEntry
= rJsonWriter
.startStruct();
2403 sal_uInt16 nId
= GetItemId(i
);
2404 rJsonWriter
.put("row", GetItemIdent(nId
));
2406 auto aColumns
= rJsonWriter
.startArray("columns");
2407 auto aColumn
= rJsonWriter
.startStruct();
2408 rJsonWriter
.put("text", GetItemText(nId
));
2414 MenuBarWindow
* MenuBar::getMenuBarWindow()
2416 // so far just a dynamic_cast, hopefully to be turned into something saner
2418 MenuBarWindow
*pWin
= dynamic_cast<MenuBarWindow
*>(m_pWindow
.get());
2419 //either there is no window (fdo#87663) or it is a MenuBarWindow
2420 assert(!m_pWindow
|| pWin
);
2425 : mbCloseBtnVisible(false),
2426 mbFloatBtnVisible(false),
2427 mbHideBtnVisible(false),
2430 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(true, this);
2433 MenuBar::MenuBar( const MenuBar
& rMenu
)
2434 : mbCloseBtnVisible(false),
2435 mbFloatBtnVisible(false),
2436 mbHideBtnVisible(false),
2439 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(true, this);
2448 void MenuBar::dispose()
2450 ImplDestroy( this, true );
2454 void MenuBar::ClosePopup(PopupMenu
*pPopupMenu
)
2456 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2459 pMenuWin
->PopupClosed(pPopupMenu
);
2462 void MenuBar::MenuBarKeyInput(const KeyEvent
& rEvent
)
2464 m_pWindow
->KeyInput(rEvent
);
2467 void MenuBar::ShowCloseButton(bool bShow
)
2469 ShowButtons( bShow
, mbFloatBtnVisible
, mbHideBtnVisible
);
2472 void MenuBar::ShowButtons( bool bClose
, bool bFloat
, bool bHide
)
2474 if ((bClose
!= mbCloseBtnVisible
) ||
2475 (bFloat
!= mbFloatBtnVisible
) ||
2476 (bHide
!= mbHideBtnVisible
))
2478 mbCloseBtnVisible
= bClose
;
2479 mbFloatBtnVisible
= bFloat
;
2480 mbHideBtnVisible
= bHide
;
2481 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2483 pMenuWin
->ShowButtons(bClose
, bFloat
, bHide
);
2487 void MenuBar::LayoutChanged()
2489 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2491 pMenuWin
->LayoutChanged();
2494 void MenuBar::SetDisplayable( bool bDisplayable
)
2496 if( bDisplayable
!= mbDisplayable
)
2498 if ( ImplGetSalMenu() )
2499 ImplGetSalMenu()->ShowMenuBar( bDisplayable
);
2501 mbDisplayable
= bDisplayable
;
2506 VclPtr
<MenuBarWindow
> MenuBar::ImplCreate(vcl::Window
* pParent
, MenuBarWindow
* pWindow
, MenuBar
* pMenu
)
2508 VclPtr
<MenuBarWindow
> pMenuBarWindow
= pWindow
;
2509 if (!pMenuBarWindow
)
2511 pMenuBarWindow
= VclPtr
<MenuBarWindow
>::Create(pParent
);
2514 pMenu
->pStartedFrom
= nullptr;
2515 pMenu
->m_pWindow
= pMenuBarWindow
;
2516 pMenuBarWindow
->SetMenu(pMenu
);
2517 tools::Long nHeight
= pMenu
->ImplCalcSize(pMenuBarWindow
).Height();
2519 // depending on the native implementation or the displayable flag
2520 // the menubar windows is suppressed (ie, height=0)
2521 if (!pMenu
->IsDisplayable() || (pMenu
->ImplGetSalMenu() && pMenu
->ImplGetSalMenu()->VisibleMenuBar()))
2526 pMenuBarWindow
->SetHeight(nHeight
);
2527 return pMenuBarWindow
;
2530 void MenuBar::ImplDestroy( MenuBar
* pMenu
, bool bDelete
)
2532 MenuBarWindow
* pMenuWin
= pMenu
->getMenuBarWindow();
2533 if (pMenuWin
&& bDelete
)
2535 pMenuWin
->KillActivePopup();
2536 pMenuWin
->disposeOnce();
2538 pMenu
->m_pWindow
= nullptr;
2539 if (pMenu
->mpSalMenu
) {
2540 pMenu
->mpSalMenu
->ShowMenuBar(false);
2544 bool MenuBar::ImplHandleKeyEvent( const KeyEvent
& rKEvent
)
2546 // No keyboard processing when our menubar is invisible
2547 if (!IsDisplayable())
2550 // No keyboard processing when system handles the menu.
2551 SalMenu
*pNativeMenu
= ImplGetSalMenu();
2552 if (pNativeMenu
&& pNativeMenu
->VisibleMenuBar())
2554 // Except when the event is the F6 cycle pane event and we can put our
2555 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2556 // where it's not part of the application window)
2557 if (!TaskPaneList::IsCycleKey(rKEvent
.GetKeyCode()))
2559 if (!pNativeMenu
->CanGetFocus())
2564 // check for enabled, if this method is called from another window...
2565 vcl::Window
* pWin
= GetWindow();
2566 if (pWin
&& pWin
->IsEnabled() && pWin
->IsInputEnabled() && !pWin
->IsInModalMode())
2568 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2569 bDone
= pMenuWin
&& pMenuWin
->HandleKeyEvent(rKEvent
, false/*bFromMenu*/);
2574 bool MenuBar::ImplHandleCmdEvent( const CommandEvent
& rCEvent
)
2576 // No keyboard processing when system handles the menu or our menubar is invisible
2577 if( !IsDisplayable() ||
2578 ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
2581 // check for enabled, if this method is called from another window...
2582 MenuBarWindow
* pWin
= static_cast<MenuBarWindow
*>(GetWindow());
2583 if ( pWin
&& pWin
->IsEnabled() && pWin
->IsInputEnabled() && ! pWin
->IsInModalMode() )
2585 if (rCEvent
.GetCommand() == CommandEventId::ModKeyChange
&& ImplGetSVData()->maNWFData
.mbAutoAccel
)
2587 const CommandModKeyData
* pCData
= rCEvent
.GetModKeyData ();
2588 if (pWin
->m_nHighlightedItem
== ITEMPOS_INVALID
)
2590 if (pCData
&& pCData
->IsMod2() && pCData
->IsDown())
2591 pWin
->SetMBWHideAccel(false);
2592 pWin
->Invalidate(InvalidateFlags::Update
);
2600 void MenuBar::SelectItem(sal_uInt16 nId
)
2605 m_pWindow
->GrabFocus();
2606 nId
= GetItemPos( nId
);
2608 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2611 // #99705# popup the selected menu
2612 pMenuWin
->SetAutoPopup( true );
2613 if (ITEMPOS_INVALID
!= pMenuWin
->GetHighlightedItem())
2615 pMenuWin
->KillActivePopup();
2616 pMenuWin
->ChangeHighlightItem( ITEMPOS_INVALID
, false );
2618 if (nId
!= ITEMPOS_INVALID
)
2619 pMenuWin
->ChangeHighlightItem( nId
, false );
2623 // handler for native menu selection and command events
2624 bool Menu::HandleMenuActivateEvent( Menu
*pMenu
) const
2628 ImplMenuDelData
aDelData( this );
2630 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2631 pMenu
->bInCallback
= true;
2634 if( !aDelData
.isDeleted() )
2635 pMenu
->bInCallback
= false;
2640 bool Menu::HandleMenuDeActivateEvent( Menu
*pMenu
) const
2644 ImplMenuDelData
aDelData( this );
2646 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2647 pMenu
->bInCallback
= true;
2648 pMenu
->Deactivate();
2649 if( !aDelData
.isDeleted() )
2650 pMenu
->bInCallback
= false;
2655 bool MenuBar::HandleMenuHighlightEvent( Menu
*pMenu
, sal_uInt16 nHighlightEventId
) const
2658 pMenu
= const_cast<MenuBar
*>(this)->ImplFindMenu(nHighlightEventId
);
2661 ImplMenuDelData
aDelData( pMenu
);
2663 if( mnHighlightedItemPos
!= ITEMPOS_INVALID
)
2664 pMenu
->ImplCallEventListeners( VclEventId::MenuDehighlight
, mnHighlightedItemPos
);
2666 if( !aDelData
.isDeleted() )
2668 pMenu
->mnHighlightedItemPos
= pMenu
->GetItemPos( nHighlightEventId
);
2669 pMenu
->nSelectedId
= nHighlightEventId
;
2670 pMenu
->sSelectedIdent
= pMenu
->GetItemIdent( nHighlightEventId
);
2671 pMenu
->pStartedFrom
= const_cast<MenuBar
*>(this);
2672 pMenu
->ImplCallHighlight( pMenu
->mnHighlightedItemPos
);
2680 bool Menu::HandleMenuCommandEvent( Menu
*pMenu
, sal_uInt16 nCommandEventId
) const
2683 pMenu
= const_cast<Menu
*>(this)->ImplFindMenu(nCommandEventId
);
2686 pMenu
->nSelectedId
= nCommandEventId
;
2687 pMenu
->sSelectedIdent
= pMenu
->GetItemIdent(nCommandEventId
);
2688 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2689 pMenu
->ImplSelect();
2696 sal_uInt16
MenuBar::AddMenuBarButton( const Image
& i_rImage
, const Link
<MenuBarButtonCallbackArg
&,bool>& i_rLink
, const OUString
& i_rToolTip
)
2698 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2699 return pMenuWin
? pMenuWin
->AddMenuBarButton(i_rImage
, i_rLink
, i_rToolTip
) : 0;
2702 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId
, const Link
<MenuBarButtonCallbackArg
&,bool>& rLink
)
2704 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2707 pMenuWin
->SetMenuBarButtonHighlightHdl(nId
, rLink
);
2710 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId
)
2712 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2715 pMenuWin
->RemoveMenuBarButton(nId
);
2718 tools::Rectangle
MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId
)
2720 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2721 return pMenuWin
? pMenuWin
->GetMenuBarButtonRectPixel(nId
) : tools::Rectangle();
2724 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId
)
2726 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2727 return pMenuWin
&& pMenuWin
->HandleMenuButtonEvent(i_nButtonId
);
2730 int MenuBar::GetMenuBarHeight() const
2732 MenuBar
* pMenuBar
= const_cast<MenuBar
*>(this);
2733 const SalMenu
*pNativeMenu
= pMenuBar
->ImplGetSalMenu();
2736 nMenubarHeight
= pNativeMenu
->GetMenuBarHeight();
2739 vcl::Window
* pMenubarWin
= GetWindow();
2740 nMenubarHeight
= pMenubarWin
? pMenubarWin
->GetOutputSizePixel().Height() : 0;
2742 return nMenubarHeight
;
2745 MenuFloatingWindow
* PopupMenu::ImplGetFloatingWindow() const {
2746 return static_cast<MenuFloatingWindow
*>(GetWindow());
2749 PopupMenu::PopupMenu()
2751 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(false, this);
2754 PopupMenu::PopupMenu( const PopupMenu
& rMenu
)
2756 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(false, this);
2760 PopupMenu::~PopupMenu()
2765 void PopupMenu::ClosePopup(PopupMenu
* pPopupMenu
)
2767 MenuFloatingWindow
* p
= ImplGetFloatingWindow();
2768 if (p
&& pPopupMenu
&& p
->GetActivePopup() == pPopupMenu
)
2769 p
->KillActivePopup();
2774 bool IsInPopupMenuExecute()
2776 return PopupMenu::GetActivePopupMenu() != nullptr;
2780 PopupMenu
* PopupMenu::GetActivePopupMenu()
2782 ImplSVData
* pSVData
= ImplGetSVData();
2783 return pSVData
->maAppData
.mpActivePopupMenu
;
2786 void PopupMenu::EndExecute()
2789 ImplGetFloatingWindow()->EndExecute( 0 );
2792 void PopupMenu::SelectItem(sal_uInt16 nId
)
2797 if( nId
!= ITEMPOS_INVALID
)
2800 MenuItemData
* pData
= GetItemList()->GetData( nId
, nPos
);
2801 if (pData
&& pData
->pSubMenu
)
2802 ImplGetFloatingWindow()->ChangeHighlightItem( nPos
, true );
2804 ImplGetFloatingWindow()->EndExecute( nId
);
2808 MenuFloatingWindow
* pFloat
= ImplGetFloatingWindow();
2809 pFloat
->GrabFocus();
2811 for( size_t nPos
= 0; nPos
< GetItemList()->size(); nPos
++ )
2813 MenuItemData
* pData
= GetItemList()->GetDataFromPos( nPos
);
2814 if( pData
->pSubMenu
)
2816 pFloat
->KillActivePopup();
2819 pFloat
->ChangeHighlightItem( ITEMPOS_INVALID
, false );
2823 void PopupMenu::SetSelectedEntry( sal_uInt16 nId
)
2826 sSelectedIdent
= GetItemIdent(nId
);
2829 sal_uInt16
PopupMenu::Execute( vcl::Window
* pExecWindow
, const Point
& rPopupPos
)
2831 return Execute( pExecWindow
, tools::Rectangle( rPopupPos
, rPopupPos
), PopupMenuFlags::ExecuteDown
);
2834 static FloatWinPopupFlags
lcl_TranslateFlags(PopupMenuFlags nFlags
)
2836 FloatWinPopupFlags nPopupModeFlags
= FloatWinPopupFlags::NONE
;
2837 if ( nFlags
& PopupMenuFlags::ExecuteDown
)
2838 nPopupModeFlags
= FloatWinPopupFlags::Down
;
2839 else if ( nFlags
& PopupMenuFlags::ExecuteUp
)
2840 nPopupModeFlags
= FloatWinPopupFlags::Up
;
2841 else if ( nFlags
& PopupMenuFlags::ExecuteRight
)
2842 nPopupModeFlags
= FloatWinPopupFlags::Right
;
2844 nPopupModeFlags
= FloatWinPopupFlags::Down
;
2846 if (nFlags
& PopupMenuFlags::NoMouseUpClose
) // allow popup menus to stay open on mouse button up
2847 nPopupModeFlags
|= FloatWinPopupFlags::NoMouseUpClose
; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2849 return nPopupModeFlags
;
2852 sal_uInt16
PopupMenu::Execute( vcl::Window
* pExecWindow
, const tools::Rectangle
& rRect
, PopupMenuFlags nFlags
)
2854 ENSURE_OR_RETURN( pExecWindow
, "PopupMenu::Execute: need a non-NULL window!", 0 );
2855 return ImplExecute( pExecWindow
, rRect
, lcl_TranslateFlags(nFlags
), nullptr, false );
2858 void PopupMenu::ImplFlushPendingSelect()
2860 // is there still Select?
2861 Menu
* pSelect
= ImplFindSelectMenu();
2864 // Select should be called prior to leaving execute in a popup menu!
2865 Application::RemoveUserEvent( pSelect
->nEventId
);
2866 pSelect
->nEventId
= nullptr;
2871 sal_uInt16
PopupMenu::ImplExecute(const VclPtr
<vcl::Window
>& pParentWin
, const tools::Rectangle
& rRect
,
2872 FloatWinPopupFlags nPopupModeFlags
, Menu
* pSFrom
, bool bPreSelectFirst
)
2874 // tdf#126054 hold this until after function completes
2875 VclPtr
<PopupMenu
> xThis(this);
2877 bool bRealExecute
= false;
2878 const sal_uInt16 nItemCount
= GetItemCount();
2879 if (!pSFrom
&& (vcl::IsInPopupMenuExecute() || !nItemCount
))
2882 mpLayoutData
.reset();
2884 ImplSVData
* pSVData
= ImplGetSVData();
2886 pStartedFrom
= pSFrom
;
2888 sSelectedIdent
.clear();
2891 VclPtr
<vcl::Window
> xFocusId
;
2892 if ( !pStartedFrom
)
2894 pSVData
->mpWinData
->mbNoDeactivate
= true;
2895 xFocusId
= Window::SaveFocus();
2896 bRealExecute
= true;
2900 // assure that only one menu is open at a time
2901 if (pStartedFrom
->IsMenuBar() && pSVData
->mpWinData
->mpFirstFloat
)
2902 pSVData
->mpWinData
->mpFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
2903 | FloatWinPopupEndFlags::CloseAll
);
2906 tools::Rectangle
aRect(rRect
);
2907 aRect
.SetPos(pParentWin
->OutputToScreenPixel(aRect
.TopLeft()));
2909 nPopupModeFlags
|= FloatWinPopupFlags::NoKeyClose
| FloatWinPopupFlags::AllMouseButtonClose
| FloatWinPopupFlags::GrabFocus
;
2911 nPopupModeFlags
|= FloatWinPopupFlags::NewLevel
;
2913 // MenuFlags get clobbered in the Activate function. Restore them after calling.
2914 MenuFlags nMenuFlagsSaved
= GetMenuFlags();
2915 bInCallback
= true; // set it here, if Activate overridden
2917 bInCallback
= false;
2918 SetMenuFlags(nMenuFlagsSaved
);
2920 if (pParentWin
->isDisposed())
2923 if ( bCanceled
|| bKilled
)
2929 // The flag MenuFlags::HideDisabledEntries is inherited.
2932 if ( pSFrom
->nMenuFlags
& MenuFlags::HideDisabledEntries
)
2933 nMenuFlags
|= MenuFlags::HideDisabledEntries
;
2935 nMenuFlags
&= ~MenuFlags::HideDisabledEntries
;
2938 sal_uInt16 nVisibleEntries
= ImplGetVisibleItemCount();
2939 if ( !nVisibleEntries
)
2941 OUString
aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE
));
2943 MenuItemData
* pData
= NbcInsertItem(0xFFFF, MenuItemBits::NONE
, aTmpEntryText
, nullptr, 0xFFFF, {});
2945 pData
= pItemList
->GetData( pData
->nId
, nPos
);
2949 pData
->bIsTemporary
= true;
2951 ImplCallEventListeners(VclEventId::MenuSubmenuChanged
, nPos
);
2954 VclPtr
<MenuFloatingWindow
> pWin
= VclPtrInstance
<MenuFloatingWindow
>(this, pParentWin
, WB_BORDER
| WB_SYSTEMWINDOW
);
2955 if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu")
2957 VclPtr
<vcl::Window
> xNotifierParent
= pParentWin
->GetParentWithLOKNotifier();
2958 assert(xNotifierParent
&& xNotifierParent
->GetLOKNotifier() && "editview menu without LOKNotifier");
2959 pWin
->SetLOKNotifier(xNotifierParent
->GetLOKNotifier());
2962 if( pSVData
->maNWFData
.mbFlatMenu
)
2963 pWin
->SetBorderStyle( WindowBorderStyle::NOBORDER
);
2965 pWin
->SetBorderStyle( pWin
->GetBorderStyle() | WindowBorderStyle::MENU
);
2967 m_pWindow
.disposeAndClear();
2970 Size aSz
= ImplCalcSize( pWin
);
2972 AbsoluteScreenPixelRectangle
aDesktopRect(pWin
->GetDesktopRectPixel());
2973 if( Application::GetScreenCount() > 1 )
2975 vcl::Window
* pDeskW
= m_pWindow
->GetWindow( GetWindowType::RealParent
);
2978 AbsoluteScreenPixelPoint
aDesktopTL(pDeskW
->OutputToAbsoluteScreenPixel(aRect
.TopLeft()));
2979 aDesktopRect
= Application::GetScreenPosSizePixel(
2980 Application::GetBestScreen(AbsoluteScreenPixelRectangle(aDesktopTL
, aRect
.GetSize())));
2983 tools::Long nMaxHeight
= aDesktopRect
.GetHeight();
2985 //rhbz#1021915. If a menu won't fit in the desired location the default
2986 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2987 //some cases, e.g. menubars, it's desirable to limit the options to
2988 //above/below and force the menu to scroll if it won't fit
2989 if (nPopupModeFlags
& FloatWinPopupFlags::NoHorzPlacement
)
2991 vcl::Window
* pRef
= pWin
;
2992 if ( pRef
->GetParent() )
2993 pRef
= pRef
->GetParent();
2995 AbsoluteScreenPixelRectangle
devRect(pRef
->OutputToAbsoluteScreenPixel(aRect
.TopLeft()),
2996 pRef
->OutputToAbsoluteScreenPixel(aRect
.BottomRight()));
2998 tools::Long nHeightAbove
= devRect
.Top() - aDesktopRect
.Top();
2999 tools::Long nHeightBelow
= aDesktopRect
.Bottom() - devRect
.Bottom();
3000 nMaxHeight
= std::min(nMaxHeight
, std::max(nHeightAbove
, nHeightBelow
));
3003 // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
3004 // So assume that the available screen size matches at least the system requirements. With menu origin
3005 // in the middle, nMaxHeight will be at least half of screen height.
3006 SAL_WARN_IF(nMaxHeight
< 768 / 2, "vcl",
3007 "Available height misdetected as " << nMaxHeight
3008 << "px. Setting to 384px instead.");
3009 nMaxHeight
= std::max(nMaxHeight
, tools::Long(768 / 2));
3011 if (pStartedFrom
&& pStartedFrom
->IsMenuBar())
3012 nMaxHeight
-= pParentWin
->GetSizePixel().Height();
3013 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
3014 m_pWindow
->GetBorder( nLeft
, nTop
, nRight
, nBottom
);
3015 nMaxHeight
-= nTop
+nBottom
;
3016 if ( aSz
.Height() > nMaxHeight
)
3018 pWin
->EnableScrollMenu( true );
3019 sal_uInt16 nStart
= ImplGetFirstVisible();
3020 sal_uInt16 nEntries
= ImplCalcVisEntries( nMaxHeight
, nStart
);
3021 aSz
.setHeight( ImplCalcHeight( nEntries
) );
3024 pWin
->SetFocusId( xFocusId
);
3025 pWin
->SetOutputSizePixel( aSz
);
3027 const bool bNative
= Run(pWin
, bRealExecute
, bPreSelectFirst
, nPopupModeFlags
, pSFrom
, aRect
);
3028 FinishRun(pWin
, pParentWin
, bRealExecute
, bNative
);
3032 bool PopupMenu::Run(const VclPtr
<MenuFloatingWindow
>& pWin
, const bool bRealExecute
, const bool bPreSelectFirst
,
3033 const FloatWinPopupFlags nPopupModeFlags
, Menu
* pSFrom
, const tools::Rectangle
& rRect
)
3035 SalMenu
* pMenu
= ImplGetSalMenu();
3036 if (pMenu
&& bRealExecute
&& pMenu
->ShowNativePopupMenu(pWin
, rRect
, nPopupModeFlags
))
3039 pWin
->StartPopupMode(rRect
, nPopupModeFlags
);
3043 if (pSFrom
->IsMenuBar())
3044 aPos
= static_cast<MenuBarWindow
*>(pSFrom
->m_pWindow
.get())->GetHighlightedItem();
3046 aPos
= static_cast<MenuFloatingWindow
*>(pSFrom
->m_pWindow
.get())->GetHighlightedItem();
3048 pWin
->SetPosInParent(aPos
); // store position to be sent in SUBMENUDEACTIVATE
3049 pSFrom
->ImplCallEventListeners(VclEventId::MenuSubmenuActivate
, aPos
);
3052 if ( bPreSelectFirst
)
3054 for (size_t n
= 0; n
< static_cast<size_t>(GetItemCount()); n
++)
3056 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3057 if ( ( pData
->bEnabled
3058 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
3060 && ( pData
->eType
!= MenuItemType::SEPARATOR
)
3061 && ImplIsVisible( n
)
3062 && ImplIsSelectable( n
)
3065 pWin
->ChangeHighlightItem(n
, false);
3077 void PopupMenu::FinishRun(const VclPtr
<MenuFloatingWindow
>& pWin
, const VclPtr
<vcl::Window
>& pParentWin
, const bool bRealExecute
, const bool bIsNativeMenu
)
3079 if (!bRealExecute
|| pWin
->isDisposed())
3084 VclPtr
<vcl::Window
> xFocusId
= pWin
->GetFocusId();
3085 assert(xFocusId
== nullptr && "Focus should already be restored by MenuFloatingWindow::End");
3086 pWin
->ImplEndPopupMode(FloatWinPopupEndFlags::NONE
, xFocusId
);
3088 if (nSelectedId
) // then clean up .. ( otherwise done by TH )
3090 PopupMenu
* pSub
= pWin
->GetActivePopup();
3093 pSub
->ImplGetFloatingWindow()->EndPopupMode();
3094 pSub
= pSub
->ImplGetFloatingWindow()->GetActivePopup();
3099 pWin
->StopExecute();
3102 ImplClosePopupToolBox(pParentWin
);
3103 ImplFlushPendingSelect();
3106 sal_uInt16
PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight
, sal_uInt16 nStartEntry
, sal_uInt16
* pLastVisible
) const
3108 nMaxHeight
-= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3110 tools::Long nHeight
= 0;
3111 size_t nEntries
= pItemList
->size();
3112 sal_uInt16 nVisEntries
= 0;
3117 for ( size_t n
= nStartEntry
; n
< nEntries
; n
++ )
3119 if ( ImplIsVisible( n
) )
3121 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3122 nHeight
+= pData
->aSz
.Height();
3123 if ( nHeight
> nMaxHeight
)
3134 tools::Long
PopupMenu::ImplCalcHeight( sal_uInt16 nEntries
) const
3136 tools::Long nHeight
= 0;
3138 sal_uInt16 nFound
= 0;
3139 for ( size_t n
= 0; ( nFound
< nEntries
) && ( n
< pItemList
->size() ); n
++ )
3141 if ( ImplIsVisible( static_cast<sal_uInt16
>(n
) ) )
3143 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3144 nHeight
+= pData
->aSz
.Height();
3149 nHeight
+= 2*ImplGetFloatingWindow()->GetScrollerHeight();
3154 css::uno::Reference
<css::awt::XPopupMenu
> PopupMenu::CreateMenuInterface()
3156 UnoWrapperBase
* pWrapper
= UnoWrapperBase::GetUnoWrapper();
3158 return pWrapper
->CreateMenuInterface(this);
3162 ImplMenuDelData::ImplMenuDelData( const Menu
* pMenu
)
3167 const_cast< Menu
* >( pMenu
)->ImplAddDel( *this );
3170 ImplMenuDelData::~ImplMenuDelData()
3173 const_cast< Menu
* >( mpMenu
.get() )->ImplRemoveDel( *this );
3176 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */