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 <comphelper/diagnose_ex.hxx>
21 #include <sal/log.hxx>
23 #include <comphelper/lok.hxx>
24 #include <vcl/dialoghelper.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/mnemonic.hxx>
27 #include <vcl/image.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/help.hxx>
30 #include <vcl/toolkit/floatwin.hxx>
31 #include <vcl/decoview.hxx>
32 #include <vcl/menu.hxx>
33 #include <vcl/taskpanelist.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>
52 #include <rtl/ustrbuf.hxx>
54 #include <configsettings.hxx>
57 #include <string_view>
63 struct MenuLayoutData
: public ControlLayoutData
65 std::vector
< sal_uInt16
> m_aLineItemIds
;
66 std::map
< sal_uInt16
, tools::Rectangle
> m_aVisibleItemBoundRects
;
73 #define EXTRAITEMHEIGHT 4
74 #define SPACE_AROUND_TITLE 4
76 static bool ImplAccelDisabled()
78 // display of accelerator strings may be suppressed via configuration
79 static int nAccelDisabled
= -1;
81 if( nAccelDisabled
== -1 )
84 vcl::SettingsConfigItem::get()->
85 getValue( "Menu", "SuppressAccelerators" );
86 nAccelDisabled
= aStr
.equalsIgnoreAsciiCase("true") ? 1 : 0;
88 return nAccelDisabled
== 1;
91 static void ImplSetMenuItemData( MenuItemData
* pData
)
95 pData
->eType
= MenuItemType::STRING
;
96 else if ( pData
->aText
.isEmpty() )
97 pData
->eType
= MenuItemType::IMAGE
;
99 pData
->eType
= MenuItemType::STRINGIMAGE
;
104 void ImplClosePopupToolBox( const VclPtr
<vcl::Window
>& pWin
)
106 if ( pWin
->GetType() == WindowType::TOOLBOX
&& ImplGetDockingManager()->IsInPopupMode( pWin
) )
108 SystemWindow
* pFloatingWindow
= ImplGetDockingManager()->GetFloatingWindow(pWin
);
110 static_cast<FloatingWindow
*>(pFloatingWindow
)->EndPopupMode( FloatWinPopupEndFlags::CloseAll
);
114 // TODO: Move to common code with the same function in toolbox
115 // Draw the ">>" - more indicator at the coordinates
116 void lclDrawMoreIndicator(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
118 rRenderContext
.Push(PushFlags::FILLCOLOR
| PushFlags::LINECOLOR
);
119 rRenderContext
.SetLineColor();
121 if (rRenderContext
.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
122 rRenderContext
.SetFillColor(COL_WHITE
);
124 rRenderContext
.SetFillColor(COL_BLACK
);
125 float fScaleFactor
= rRenderContext
.GetDPIScaleFactor();
127 int linewidth
= 1 * fScaleFactor
;
128 int space
= 4 * fScaleFactor
;
130 tools::Long width
= 8 * fScaleFactor
;
131 tools::Long height
= 5 * fScaleFactor
;
133 //Keep odd b/c drawing code works better
134 if ( height
% 2 == 0 )
137 tools::Long heightOrig
= height
;
139 tools::Long x
= rRect
.Left() + (rRect
.getOpenWidth() - width
)/2 + 1;
140 tools::Long y
= rRect
.Top() + (rRect
.getOpenHeight() - height
)/2 + 1;
143 rRenderContext
.DrawRect( tools::Rectangle( x
, y
, x
+ linewidth
, y
) );
145 rRenderContext
.DrawRect( tools::Rectangle( x
, y
, x
+ linewidth
, y
) );
148 if( height
<= heightOrig
/ 2 + 1) x
--;
152 rRenderContext
.Pop();
155 } // end anonymous namespace
159 : mpFirstDel(nullptr),
160 pItemList(new MenuItemList
),
161 pStartedFrom(nullptr),
165 mnHighlightedItemPos(ITEMPOS_INVALID
),
166 nMenuFlags(MenuFlags::NONE
),
183 ImplCallEventListeners( VclEventId::ObjectDying
, ITEMPOS_INVALID
);
185 // at the window free the reference to the accessible component
186 // and make sure the MenuFloatingWindow knows about our destruction
189 MenuFloatingWindow
* pFloat
= static_cast<MenuFloatingWindow
*>(pWindow
.get());
190 if( pFloat
->pMenu
.get() == this )
191 pFloat
->pMenu
.clear();
192 pWindow
->SetAccessible( css::uno::Reference
< css::accessibility::XAccessible
>() );
195 // dispose accessible components
196 if ( mxAccessible
.is() )
198 css::uno::Reference
< css::lang::XComponent
> xComponent( mxAccessible
, css::uno::UNO_QUERY
);
199 if ( xComponent
.is() )
200 xComponent
->dispose();
204 Application::RemoveUserEvent( nEventId
);
206 // Notify deletion of this menu
207 ImplMenuDelData
* pDelData
= mpFirstDel
;
210 pDelData
->mpMenu
= nullptr;
211 pDelData
= pDelData
->mpNext
;
216 // tdf#140225 when clearing pItemList, keep SalMenu in sync with
217 // their removal during menu teardown
218 for (size_t n
= pItemList
->size(); n
;)
222 mpSalMenu
->RemoveItem(n
);
223 pItemList
->Remove(n
);
226 assert(!pItemList
->size());
228 mpLayoutData
.reset();
230 // Native-support: destroy SalMenu
233 pStartedFrom
.clear();
235 VclReferenceBase::dispose();
238 void Menu::CreateAutoMnemonics()
240 MnemonicGenerator aMnemonicGenerator
;
242 for ( n
= 0; n
< pItemList
->size(); n
++ )
244 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
245 if ( ! (pData
->nBits
& MenuItemBits::NOSELECT
) )
246 aMnemonicGenerator
.RegisterMnemonic( pData
->aText
);
248 for ( n
= 0; n
< pItemList
->size(); n
++ )
250 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
251 if ( ! (pData
->nBits
& MenuItemBits::NOSELECT
) )
252 pData
->aText
= aMnemonicGenerator
.CreateMnemonic( pData
->aText
);
256 void Menu::Activate()
260 ImplMenuDelData
aDelData( this );
262 ImplCallEventListeners( VclEventId::MenuActivate
, ITEMPOS_INVALID
);
264 if( !aDelData
.isDeleted() )
266 if ( !aActivateHdl
.Call( this ) )
268 if( !aDelData
.isDeleted() )
270 Menu
* pStartMenu
= ImplGetStartMenu();
271 if ( pStartMenu
&& ( pStartMenu
!= this ) )
273 pStartMenu
->bInCallback
= true;
274 // MT 11/01: Call EventListener here? I don't know...
275 pStartMenu
->aActivateHdl
.Call( this );
276 pStartMenu
->bInCallback
= false;
283 if (!aDelData
.isDeleted() && !(nMenuFlags
& MenuFlags::NoAutoMnemonics
))
284 CreateAutoMnemonics();
287 void Menu::Deactivate()
289 for ( size_t n
= pItemList
->size(); n
; )
291 MenuItemData
* pData
= pItemList
->GetDataFromPos( --n
);
292 if ( pData
->bIsTemporary
)
294 if ( ImplGetSalMenu() )
295 ImplGetSalMenu()->RemoveItem( n
);
297 pItemList
->Remove( n
);
303 ImplMenuDelData
aDelData( this );
305 Menu
* pStartMenu
= ImplGetStartMenu();
306 ImplCallEventListeners( VclEventId::MenuDeactivate
, ITEMPOS_INVALID
);
308 if( !aDelData
.isDeleted() )
310 if ( !aDeactivateHdl
.Call( this ) )
312 if( !aDelData
.isDeleted() )
314 if ( pStartMenu
&& ( pStartMenu
!= this ) )
316 pStartMenu
->bInCallback
= true;
317 pStartMenu
->aDeactivateHdl
.Call( this );
318 pStartMenu
->bInCallback
= false;
324 if( !aDelData
.isDeleted() )
330 void Menu::ImplSelect()
332 MenuItemData
* pData
= GetItemList()->GetData( nSelectedId
);
333 if ( pData
&& (pData
->nBits
& MenuItemBits::AUTOCHECK
) )
335 bool bChecked
= IsItemChecked( nSelectedId
);
336 if ( pData
->nBits
& MenuItemBits::RADIOCHECK
)
339 CheckItem( nSelectedId
);
342 CheckItem( nSelectedId
, !bChecked
);
346 ImplSVData
* pSVData
= ImplGetSVData();
347 pSVData
->maAppData
.mpActivePopupMenu
= nullptr; // if new execute in select()
348 nEventId
= Application::PostUserEvent( LINK( this, Menu
, ImplCallSelect
) );
353 ImplMenuDelData
aDelData( this );
355 ImplCallEventListeners( VclEventId::MenuSelect
, GetItemPos( GetCurItemId() ) );
356 if (aDelData
.isDeleted())
358 if (aSelectHdl
.Call(this))
360 if (aDelData
.isDeleted())
362 Menu
* pStartMenu
= ImplGetStartMenu();
363 if (!pStartMenu
|| (pStartMenu
== this))
365 pStartMenu
->nSelectedId
= nSelectedId
;
366 pStartMenu
->sSelectedIdent
= sSelectedIdent
;
367 pStartMenu
->aSelectHdl
.Call( this );
371 void Menu::ImplSelectWithStart( Menu
* pSMenu
)
373 auto pOldStartedFrom
= pStartedFrom
;
374 pStartedFrom
= pSMenu
;
375 auto pOldStartedStarted
= pOldStartedFrom
? pOldStartedFrom
->pStartedFrom
: VclPtr
<Menu
>();
377 if( pOldStartedFrom
)
378 pOldStartedFrom
->pStartedFrom
= pOldStartedStarted
;
379 pStartedFrom
= pOldStartedFrom
;
383 void Menu::ImplCallEventListeners( VclEventId nEvent
, sal_uInt16 nPos
)
385 ImplMenuDelData
aDelData( this );
387 VclMenuEvent
aEvent( this, nEvent
, nPos
);
389 // This is needed by atk accessibility bridge
390 if ( nEvent
== VclEventId::MenuHighlight
)
392 Application::ImplCallEventListeners( aEvent
);
395 if ( !aDelData
.isDeleted() )
397 // Copy the list, because this can be destroyed when calling a Link...
398 std::list
<Link
<VclMenuEvent
&,void>> aCopy( maEventListeners
);
399 for ( const auto& rLink
: aCopy
)
401 if( std::find(maEventListeners
.begin(), maEventListeners
.end(), rLink
) != maEventListeners
.end() )
402 rLink
.Call( aEvent
);
407 void Menu::AddEventListener( const Link
<VclMenuEvent
&,void>& rEventListener
)
409 maEventListeners
.push_back( rEventListener
);
412 void Menu::RemoveEventListener( const Link
<VclMenuEvent
&,void>& rEventListener
)
414 maEventListeners
.remove( rEventListener
);
417 MenuItemData
* Menu::NbcInsertItem(sal_uInt16 nId
, MenuItemBits nBits
,
418 const OUString
& rStr
, Menu
* pMenu
,
419 size_t nPos
, const OUString
&rIdent
)
421 // put Item in MenuItemList
422 MenuItemData
* pData
= pItemList
->Insert(nId
, MenuItemType::STRING
,
423 nBits
, rStr
, pMenu
, nPos
, rIdent
);
425 // update native menu
426 if (ImplGetSalMenu() && pData
->pSalMenuItem
)
427 ImplGetSalMenu()->InsertItem(pData
->pSalMenuItem
.get(), nPos
);
432 void Menu::InsertItem(sal_uInt16 nItemId
, const OUString
& rStr
, MenuItemBits nItemBits
,
433 const OUString
&rIdent
, sal_uInt16 nPos
)
435 SAL_WARN_IF( !nItemId
, "vcl", "Menu::InsertItem(): ItemId == 0" );
436 SAL_WARN_IF( GetItemPos( nItemId
) != MENU_ITEM_NOTFOUND
, "vcl",
437 "Menu::InsertItem(): ItemId already exists" );
439 // if Position > ItemCount, append
440 if ( nPos
>= pItemList
->size() )
443 // put Item in MenuItemList
444 NbcInsertItem(nItemId
, nItemBits
, rStr
, this, nPos
, rIdent
);
446 vcl::Window
* pWin
= ImplGetWindow();
447 mpLayoutData
.reset();
450 ImplCalcSize( pWin
);
451 if ( pWin
->IsVisible() )
454 ImplCallEventListeners( VclEventId::MenuInsertItem
, nPos
);
457 void Menu::InsertItem(sal_uInt16 nItemId
, const Image
& rImage
,
458 MenuItemBits nItemBits
, const OUString
&rIdent
, sal_uInt16 nPos
)
460 InsertItem(nItemId
, OUString(), nItemBits
, rIdent
, nPos
);
461 SetItemImage( nItemId
, rImage
);
464 void Menu::InsertItem(sal_uInt16 nItemId
, const OUString
& rStr
,
465 const Image
& rImage
, MenuItemBits nItemBits
,
466 const OUString
&rIdent
, sal_uInt16 nPos
)
468 InsertItem(nItemId
, rStr
, nItemBits
, rIdent
, nPos
);
469 SetItemImage( nItemId
, rImage
);
472 void Menu::InsertSeparator(const OUString
&rIdent
, sal_uInt16 nPos
)
474 // do nothing if it's a menu bar
478 // if position > ItemCount, append
479 if ( nPos
>= pItemList
->size() )
482 // put separator in item list
483 pItemList
->InsertSeparator(rIdent
, nPos
);
485 // update native menu
486 size_t itemPos
= ( nPos
!= MENU_APPEND
) ? nPos
: pItemList
->size() - 1;
487 MenuItemData
*pData
= pItemList
->GetDataFromPos( itemPos
);
488 if( ImplGetSalMenu() && pData
&& pData
->pSalMenuItem
)
489 ImplGetSalMenu()->InsertItem( pData
->pSalMenuItem
.get(), nPos
);
491 mpLayoutData
.reset();
493 ImplCallEventListeners( VclEventId::MenuInsertItem
, nPos
);
496 void Menu::RemoveItem( sal_uInt16 nPos
)
498 bool bRemove
= false;
500 if ( nPos
< GetItemCount() )
502 // update native menu
503 if( ImplGetSalMenu() )
504 ImplGetSalMenu()->RemoveItem( nPos
);
506 pItemList
->Remove( nPos
);
510 vcl::Window
* pWin
= ImplGetWindow();
513 ImplCalcSize( pWin
);
514 if ( pWin
->IsVisible() )
517 mpLayoutData
.reset();
520 ImplCallEventListeners( VclEventId::MenuRemoveItem
, nPos
);
523 static void ImplCopyItem( Menu
* pThis
, const Menu
& rMenu
, sal_uInt16 nPos
, sal_uInt16 nNewPos
)
525 MenuItemType eType
= rMenu
.GetItemType( nPos
);
527 if ( eType
== MenuItemType::DONTKNOW
)
530 if ( eType
== MenuItemType::SEPARATOR
)
531 pThis
->InsertSeparator( {}, nNewPos
);
534 sal_uInt16 nId
= rMenu
.GetItemId( nPos
);
536 SAL_WARN_IF( pThis
->GetItemPos( nId
) != MENU_ITEM_NOTFOUND
, "vcl",
537 "Menu::CopyItem(): ItemId already exists" );
539 MenuItemData
* pData
= rMenu
.GetItemList()->GetData( nId
);
544 if ( eType
== MenuItemType::STRINGIMAGE
)
545 pThis
->InsertItem( nId
, pData
->aText
, pData
->aImage
, pData
->nBits
, pData
->sIdent
, nNewPos
);
546 else if ( eType
== MenuItemType::STRING
)
547 pThis
->InsertItem( nId
, pData
->aText
, pData
->nBits
, pData
->sIdent
, nNewPos
);
549 pThis
->InsertItem( nId
, pData
->aImage
, pData
->nBits
, pData
->sIdent
, nNewPos
);
551 if ( rMenu
.IsItemChecked( nId
) )
552 pThis
->CheckItem( nId
);
553 if ( !rMenu
.IsItemEnabled( nId
) )
554 pThis
->EnableItem( nId
, false );
555 pThis
->SetHelpId( nId
, pData
->aHelpId
);
556 pThis
->SetHelpText( nId
, pData
->aHelpText
);
557 pThis
->SetAccelKey( nId
, pData
->aAccelKey
);
558 pThis
->SetItemCommand( nId
, pData
->aCommandStr
);
559 pThis
->SetHelpCommand( nId
, pData
->aHelpCommandStr
);
561 PopupMenu
* pSubMenu
= rMenu
.GetPopupMenu( nId
);
565 VclPtr
<PopupMenu
> pNewMenu
= VclPtr
<PopupMenu
>::Create( *pSubMenu
);
566 pThis
->SetPopupMenu( nId
, pNewMenu
);
573 for ( sal_uInt16 i
= GetItemCount(); i
; i
-- )
577 sal_uInt16
Menu::GetItemCount() const
579 return static_cast<sal_uInt16
>(pItemList
->size());
582 sal_uInt16
Menu::ImplGetVisibleItemCount() const
584 sal_uInt16 nItems
= 0;
585 for ( size_t n
= pItemList
->size(); n
; )
587 if ( ImplIsVisible( --n
) )
593 sal_uInt16
Menu::ImplGetFirstVisible() const
595 for ( size_t n
= 0; n
< pItemList
->size(); n
++ )
597 if ( ImplIsVisible( n
) )
600 return ITEMPOS_INVALID
;
603 sal_uInt16
Menu::ImplGetPrevVisible( sal_uInt16 nPos
) const
605 for ( size_t n
= nPos
; n
; )
607 if (ImplIsVisible(--n
))
610 return ITEMPOS_INVALID
;
613 sal_uInt16
Menu::ImplGetNextVisible( sal_uInt16 nPos
) const
615 for ( size_t n
= nPos
+1; n
< pItemList
->size(); n
++ )
617 if ( ImplIsVisible( n
) )
620 return ITEMPOS_INVALID
;
623 sal_uInt16
Menu::GetItemId(sal_uInt16 nPos
) const
625 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
633 sal_uInt16
Menu::GetItemId(std::u16string_view rIdent
) const
635 for (size_t n
= 0; n
< pItemList
->size(); ++n
)
637 MenuItemData
* pData
= pItemList
->GetDataFromPos(n
);
638 if (pData
&& pData
->sIdent
== rIdent
)
641 return MENU_ITEM_NOTFOUND
;
644 sal_uInt16
Menu::GetItemPos( sal_uInt16 nItemId
) const
647 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
650 return static_cast<sal_uInt16
>(nPos
);
652 return MENU_ITEM_NOTFOUND
;
655 MenuItemType
Menu::GetItemType( sal_uInt16 nPos
) const
657 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
662 return MenuItemType::DONTKNOW
;
665 OUString
Menu::GetItemIdent(sal_uInt16 nId
) const
667 const MenuItemData
* pData
= pItemList
->GetData(nId
);
668 return pData
? pData
->sIdent
: OUString();
671 void Menu::SetItemBits( sal_uInt16 nItemId
, MenuItemBits nBits
)
674 MenuItemData
* pData
= pItemList
->GetData(nItemId
, nPos
);
676 if (pData
&& (pData
->nBits
!= nBits
))
678 pData
->nBits
= nBits
;
680 // update native menu
681 if (ImplGetSalMenu())
682 ImplGetSalMenu()->SetItemBits(nPos
, nBits
);
686 MenuItemBits
Menu::GetItemBits( sal_uInt16 nItemId
) const
688 MenuItemBits nBits
= MenuItemBits::NONE
;
689 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
691 nBits
= pData
->nBits
;
695 void Menu::SetUserValue(sal_uInt16 nItemId
, void* nUserValue
, MenuUserDataReleaseFunction aFunc
)
697 MenuItemData
* pData
= pItemList
->GetData(nItemId
);
700 if (pData
->aUserValueReleaseFunc
)
701 pData
->aUserValueReleaseFunc(pData
->nUserValue
);
702 pData
->aUserValueReleaseFunc
= aFunc
;
703 pData
->nUserValue
= nUserValue
;
707 void* Menu::GetUserValue( sal_uInt16 nItemId
) const
709 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
710 return pData
? pData
->nUserValue
: nullptr;
713 void Menu::SetPopupMenu( sal_uInt16 nItemId
, PopupMenu
* pMenu
)
716 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
718 // Item does not exist -> return NULL
722 // same menu, nothing to do
723 if ( static_cast<PopupMenu
*>(pData
->pSubMenu
.get()) == pMenu
)
727 auto oldSubMenu
= pData
->pSubMenu
;
730 pData
->pSubMenu
= pMenu
;
732 // #112023# Make sure pStartedFrom does not point to invalid (old) data
733 if ( pData
->pSubMenu
)
734 pData
->pSubMenu
->pStartedFrom
= nullptr;
736 // set native submenu
737 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
740 ImplGetSalMenu()->SetSubMenu( pData
->pSalMenuItem
.get(), pMenu
->ImplGetSalMenu(), nPos
);
742 ImplGetSalMenu()->SetSubMenu( pData
->pSalMenuItem
.get(), nullptr, nPos
);
745 oldSubMenu
.disposeAndClear();
747 ImplCallEventListeners( VclEventId::MenuSubmenuChanged
, nPos
);
750 PopupMenu
* Menu::GetPopupMenu( sal_uInt16 nItemId
) const
752 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
755 return static_cast<PopupMenu
*>(pData
->pSubMenu
.get());
760 void Menu::SetAccelKey( sal_uInt16 nItemId
, const KeyCode
& rKeyCode
)
763 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
768 if ( pData
->aAccelKey
== rKeyCode
)
771 pData
->aAccelKey
= rKeyCode
;
773 // update native menu
774 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
775 ImplGetSalMenu()->SetAccelerator( nPos
, pData
->pSalMenuItem
.get(), rKeyCode
, rKeyCode
.GetName() );
778 KeyCode
Menu::GetAccelKey( sal_uInt16 nItemId
) const
780 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
783 return pData
->aAccelKey
;
788 KeyEvent
Menu::GetActivationKey( sal_uInt16 nItemId
) const
791 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
794 sal_Int32 nPos
= pData
->aText
.indexOf( '~' );
795 if( nPos
!= -1 && nPos
< pData
->aText
.getLength()-1 )
797 sal_uInt16 nCode
= 0;
798 sal_Unicode cAccel
= pData
->aText
[nPos
+1];
799 if( cAccel
>= 'a' && cAccel
<= 'z' )
800 nCode
= KEY_A
+ (cAccel
-'a');
801 else if( cAccel
>= 'A' && cAccel
<= 'Z' )
802 nCode
= KEY_A
+ (cAccel
-'A');
803 else if( cAccel
>= '0' && cAccel
<= '9' )
804 nCode
= KEY_0
+ (cAccel
-'0');
806 aRet
= KeyEvent( cAccel
, KeyCode( nCode
, KEY_MOD2
) );
813 void Menu::CheckItem( sal_uInt16 nItemId
, bool bCheck
)
816 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
818 if ( !pData
|| pData
->bChecked
== bCheck
)
821 // if radio-check, then uncheck previous
822 if ( bCheck
&& (pData
->nBits
& MenuItemBits::AUTOCHECK
) &&
823 (pData
->nBits
& MenuItemBits::RADIOCHECK
) )
825 MenuItemData
* pGroupData
;
826 sal_uInt16 nGroupPos
;
827 sal_uInt16 nItemCount
= GetItemCount();
833 pGroupData
= pItemList
->GetDataFromPos( nGroupPos
-1 );
834 if ( pGroupData
->nBits
& MenuItemBits::RADIOCHECK
)
836 if ( IsItemChecked( pGroupData
->nId
) )
838 CheckItem( pGroupData
->nId
, false );
851 while ( nGroupPos
< nItemCount
)
853 pGroupData
= pItemList
->GetDataFromPos( nGroupPos
);
854 if ( pGroupData
->nBits
& MenuItemBits::RADIOCHECK
)
856 if ( IsItemChecked( pGroupData
->nId
) )
858 CheckItem( pGroupData
->nId
, false );
869 pData
->bChecked
= bCheck
;
871 // update native menu
872 if( ImplGetSalMenu() )
873 ImplGetSalMenu()->CheckItem( nPos
, bCheck
);
875 ImplCallEventListeners( bCheck
? VclEventId::MenuItemChecked
: VclEventId::MenuItemUnchecked
, nPos
);
878 void Menu::CheckItem( std::u16string_view rIdent
, bool bCheck
)
880 CheckItem( GetItemId( rIdent
), bCheck
);
883 bool Menu::IsItemChecked( sal_uInt16 nItemId
) const
886 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
891 return pData
->bChecked
;
894 void Menu::EnableItem( sal_uInt16 nItemId
, bool bEnable
)
897 MenuItemData
* pItemData
= pItemList
->GetData( nItemId
, nPos
);
899 if ( !(pItemData
&& ( pItemData
->bEnabled
!= bEnable
)) )
902 pItemData
->bEnabled
= bEnable
;
904 vcl::Window
* pWin
= ImplGetWindow();
905 if ( pWin
&& pWin
->IsVisible() )
907 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
909 size_t nCount
= pItemList
->size();
910 for ( size_t n
= 0; n
< nCount
; n
++ )
912 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
915 pWin
->Invalidate( tools::Rectangle( Point( nX
, 0 ), Size( pData
->aSz
.Width(), pData
->aSz
.Height() ) ) );
918 nX
+= pData
->aSz
.Width();
921 // update native menu
922 if( ImplGetSalMenu() )
923 ImplGetSalMenu()->EnableItem( nPos
, bEnable
);
925 ImplCallEventListeners( bEnable
? VclEventId::MenuEnable
: VclEventId::MenuDisable
, nPos
);
928 bool Menu::IsItemEnabled( sal_uInt16 nItemId
) const
931 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
936 return pData
->bEnabled
;
939 void Menu::ShowItem( sal_uInt16 nItemId
, bool bVisible
)
942 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
944 SAL_WARN_IF(IsMenuBar() && !bVisible
, "vcl", "Menu::ShowItem - ignored for menu bar entries!");
945 if (IsMenuBar() || !pData
|| (pData
->bVisible
== bVisible
))
948 vcl::Window
* pWin
= ImplGetWindow();
949 if ( pWin
&& pWin
->IsVisible() )
951 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
954 pData
->bVisible
= bVisible
;
956 // update native menu
957 if( ImplGetSalMenu() )
958 ImplGetSalMenu()->ShowItem( nPos
, bVisible
);
961 void Menu::SetItemText( sal_uInt16 nItemId
, const OUString
& rStr
)
964 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
969 if ( rStr
== pData
->aText
)
973 // Clear layout for aText.
974 pData
->aTextGlyphs
.Invalidate();
975 ImplSetMenuItemData( pData
);
976 // update native menu
977 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
978 ImplGetSalMenu()->SetItemText( nPos
, pData
->pSalMenuItem
.get(), rStr
);
980 vcl::Window
* pWin
= ImplGetWindow();
981 mpLayoutData
.reset();
982 if (pWin
&& IsMenuBar())
984 ImplCalcSize( pWin
);
985 if ( pWin
->IsVisible() )
989 ImplCallEventListeners( VclEventId::MenuItemTextChanged
, nPos
);
992 OUString
Menu::GetItemText( sal_uInt16 nItemId
) const
995 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1003 void Menu::SetItemImage( sal_uInt16 nItemId
, const Image
& rImage
)
1006 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1011 pData
->aImage
= rImage
;
1012 ImplSetMenuItemData( pData
);
1014 // update native menu
1015 if( ImplGetSalMenu() && pData
->pSalMenuItem
)
1016 ImplGetSalMenu()->SetItemImage( nPos
, pData
->pSalMenuItem
.get(), rImage
);
1019 Image
Menu::GetItemImage( sal_uInt16 nItemId
) const
1021 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1024 return pData
->aImage
;
1029 void Menu::SetItemCommand( sal_uInt16 nItemId
, const OUString
& rCommand
)
1032 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
1035 pData
->aCommandStr
= rCommand
;
1038 OUString
Menu::GetItemCommand( sal_uInt16 nItemId
) const
1040 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1043 return pData
->aCommandStr
;
1048 void Menu::SetHelpCommand( sal_uInt16 nItemId
, const OUString
& rStr
)
1050 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1053 pData
->aHelpCommandStr
= rStr
;
1056 OUString
Menu::GetHelpCommand( sal_uInt16 nItemId
) const
1058 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1061 return pData
->aHelpCommandStr
;
1066 void Menu::SetHelpText( sal_uInt16 nItemId
, const OUString
& rStr
)
1068 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1071 pData
->aHelpText
= rStr
;
1074 OUString
Menu::ImplGetHelpText( sal_uInt16 nItemId
) const
1076 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1081 if ( pData
->aHelpText
.isEmpty() &&
1082 (( !pData
->aHelpId
.isEmpty() ) || ( !pData
->aCommandStr
.isEmpty() )))
1084 Help
* pHelp
= Application::GetHelp();
1087 if (!pData
->aCommandStr
.isEmpty())
1088 pData
->aHelpText
= pHelp
->GetHelpText( pData
->aCommandStr
, static_cast<weld::Widget
*>(nullptr) );
1089 if (pData
->aHelpText
.isEmpty() && !pData
->aHelpId
.isEmpty())
1090 pData
->aHelpText
= pHelp
->GetHelpText( pData
->aHelpId
, static_cast<weld::Widget
*>(nullptr) );
1094 //Fallback to Menu::GetAccessibleDescription without reentry to GetHelpText()
1095 if (pData
->aHelpText
.isEmpty())
1096 return pData
->aAccessibleDescription
;
1097 return pData
->aHelpText
;
1100 OUString
Menu::GetHelpText( sal_uInt16 nItemId
) const
1102 return ImplGetHelpText( nItemId
);
1105 void Menu::SetTipHelpText( sal_uInt16 nItemId
, const OUString
& rStr
)
1107 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1110 pData
->aTipHelpText
= rStr
;
1113 OUString
Menu::GetTipHelpText( sal_uInt16 nItemId
) const
1115 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1118 return pData
->aTipHelpText
;
1123 void Menu::SetHelpId( sal_uInt16 nItemId
, const OUString
& rHelpId
)
1125 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1128 pData
->aHelpId
= rHelpId
;
1131 OUString
Menu::GetHelpId( sal_uInt16 nItemId
) const
1135 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
1139 if ( !pData
->aHelpId
.isEmpty() )
1140 aRet
= pData
->aHelpId
;
1142 aRet
= pData
->aCommandStr
;
1148 Menu
& Menu::operator=( const Menu
& rMenu
)
1157 sal_uInt16 nCount
= rMenu
.GetItemCount();
1158 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1159 ImplCopyItem( this, rMenu
, i
, MENU_APPEND
);
1161 aActivateHdl
= rMenu
.aActivateHdl
;
1162 aDeactivateHdl
= rMenu
.aDeactivateHdl
;
1163 aSelectHdl
= rMenu
.aSelectHdl
;
1164 aTitleText
= rMenu
.aTitleText
;
1165 nTitleHeight
= rMenu
.nTitleHeight
;
1170 // Returns true if the item is completely hidden on the GUI and shouldn't
1171 // be possible to interact with
1172 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos
) const
1174 MenuItemData
* pData
= pItemList
->GetDataFromPos(nPos
);
1177 MenuItemData
* pPreviousData
= pItemList
->GetDataFromPos( nPos
- 1 );
1178 if (pPreviousData
&& pPreviousData
->bHiddenOnGUI
)
1186 bool Menu::ImplIsVisible( sal_uInt16 nPos
) const
1188 bool bVisible
= true;
1190 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
1191 // check general visibility first
1192 if( pData
&& !pData
->bVisible
)
1195 if ( bVisible
&& pData
&& pData
->eType
== MenuItemType::SEPARATOR
)
1197 if( nPos
== 0 ) // no separator should be shown at the very beginning
1201 // always avoid adjacent separators
1202 size_t nCount
= pItemList
->size();
1204 MenuItemData
* pNextData
= nullptr;
1205 // search next visible item
1206 for( n
= nPos
+ 1; n
< nCount
; n
++ )
1208 pNextData
= pItemList
->GetDataFromPos( n
);
1209 if( pNextData
&& pNextData
->bVisible
)
1211 if( pNextData
->eType
== MenuItemType::SEPARATOR
|| ImplIsVisible(n
) )
1215 if( n
== nCount
) // no next visible item
1217 // check for separator
1218 if( pNextData
&& pNextData
->bVisible
&& pNextData
->eType
== MenuItemType::SEPARATOR
)
1223 for( n
= nPos
; n
> 0; n
-- )
1225 pNextData
= pItemList
->GetDataFromPos( n
-1 );
1226 if( pNextData
&& pNextData
->bVisible
)
1228 if( pNextData
->eType
!= MenuItemType::SEPARATOR
&& ImplIsVisible(n
-1) )
1232 if( n
== 0 ) // no previous visible item
1238 // not allowed for menubar, as I do not know
1239 // whether a menu-entry will disappear or will appear
1240 if (bVisible
&& !IsMenuBar() && (nMenuFlags
& MenuFlags::HideDisabledEntries
) &&
1241 !(nMenuFlags
& MenuFlags::AlwaysShowDisabledEntries
))
1243 if( !pData
) // e.g. nPos == ITEMPOS_INVALID
1245 else if ( pData
->eType
!= MenuItemType::SEPARATOR
) // separators handled above
1247 // tdf#86850 Always display clipboard functions
1248 if ( pData
->aCommandStr
== ".uno:Cut" || pData
->aCommandStr
== ".uno:Copy" || pData
->aCommandStr
== ".uno:Paste" ||
1249 pData
->sIdent
== ".uno:Cut" || pData
->sIdent
== ".uno:Copy" || pData
->sIdent
== ".uno:Paste" )
1252 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
1253 bVisible
= pData
->bEnabled
; // do not check submenus as they might be filled at Activate().
1260 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos
) const
1262 return IsMenuVisible() && ImplIsVisible( nItemPos
);
1265 bool Menu::IsMenuVisible() const
1267 return pWindow
&& pWindow
->IsReallyVisible();
1270 bool Menu::ImplIsSelectable( sal_uInt16 nPos
) const
1272 bool bSelectable
= true;
1274 MenuItemData
* pData
= pItemList
->GetDataFromPos( nPos
);
1275 // check general visibility first
1276 if ( pData
&& ( pData
->nBits
& MenuItemBits::NOSELECT
) )
1277 bSelectable
= false;
1282 css::uno::Reference
<css::accessibility::XAccessible
> Menu::GetAccessible()
1284 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
1285 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
1286 // mxAccessible member only for sub menus.
1287 if (pStartedFrom
&& pStartedFrom
!= this)
1289 for ( sal_uInt16 i
= 0, nCount
= pStartedFrom
->GetItemCount(); i
< nCount
; ++i
)
1291 sal_uInt16 nItemId
= pStartedFrom
->GetItemId( i
);
1292 if ( static_cast< Menu
* >( pStartedFrom
->GetPopupMenu( nItemId
) ) == this )
1294 css::uno::Reference
<css::accessibility::XAccessible
> xParent
= pStartedFrom
->GetAccessible();
1297 css::uno::Reference
<css::accessibility::XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
1298 if (xParentContext
.is())
1299 return xParentContext
->getAccessibleChild( i
);
1304 else if ( !mxAccessible
.is() )
1306 UnoWrapperBase
* pWrapper
= UnoWrapperBase::GetUnoWrapper();
1308 mxAccessible
= pWrapper
->CreateAccessible(this, IsMenuBar());
1311 return mxAccessible
;
1314 void Menu::SetAccessible(const css::uno::Reference
<css::accessibility::XAccessible
>& rxAccessible
)
1316 mxAccessible
= rxAccessible
;
1319 Size
Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext
const & rRenderContext
, tools::Long
& rCheckHeight
, tools::Long
& rRadioHeight
) const
1321 tools::Long nCheckWidth
= 0, nRadioWidth
= 0;
1322 rCheckHeight
= rRadioHeight
= 0;
1326 ImplControlValue aVal
;
1327 tools::Rectangle aNativeBounds
;
1328 tools::Rectangle aNativeContent
;
1330 tools::Rectangle
aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1331 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItemCheckMark
))
1333 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::MenuItemCheckMark
,
1334 aCtrlRegion
, ControlState::ENABLED
, aVal
,
1335 aNativeBounds
, aNativeContent
))
1337 rCheckHeight
= aNativeBounds
.GetHeight() - 1;
1338 nCheckWidth
= aNativeContent
.GetWidth() - 1;
1341 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItemRadioMark
))
1343 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::MenuItemRadioMark
,
1344 aCtrlRegion
, ControlState::ENABLED
, aVal
,
1345 aNativeBounds
, aNativeContent
))
1347 rRadioHeight
= aNativeBounds
.GetHeight() - 1;
1348 nRadioWidth
= aNativeContent
.GetWidth() - 1;
1352 return Size(std::max(nCheckWidth
, nRadioWidth
), std::max(rCheckHeight
, rRadioHeight
));
1355 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext
const & rRenderContext
, Size
& rArrowSize
, tools::Long
& rArrowSpacing
)
1357 ImplControlValue aVal
;
1358 tools::Rectangle
aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
1359 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::SubmenuArrow
))
1361 tools::Rectangle aNativeContent
;
1362 tools::Rectangle aNativeBounds
;
1363 if (rRenderContext
.GetNativeControlRegion(ControlType::MenuPopup
, ControlPart::SubmenuArrow
,
1364 aCtrlRegion
, ControlState::ENABLED
,
1365 aVal
, aNativeBounds
, aNativeContent
))
1367 Size
aSize(aNativeContent
.GetWidth(), aNativeContent
.GetHeight());
1369 rArrowSpacing
= aNativeBounds
.GetWidth() - aNativeContent
.GetWidth();
1376 void Menu::ImplAddDel( ImplMenuDelData
& rDel
)
1378 SAL_WARN_IF( rDel
.mpMenu
, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
1382 rDel
.mpNext
= mpFirstDel
;
1387 void Menu::ImplRemoveDel( ImplMenuDelData
& rDel
)
1389 rDel
.mpMenu
= nullptr;
1390 if ( mpFirstDel
== &rDel
)
1392 mpFirstDel
= rDel
.mpNext
;
1396 ImplMenuDelData
* pData
= mpFirstDel
;
1397 while ( pData
&& (pData
->mpNext
!= &rDel
) )
1398 pData
= pData
->mpNext
;
1400 SAL_WARN_IF( !pData
, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
1402 pData
->mpNext
= rDel
.mpNext
;
1406 Size
Menu::ImplCalcSize( vcl::Window
* pWin
)
1408 // | Check/Radio/Image| Text| Accel/Popup|
1410 // for symbols: nFontHeight x nFontHeight
1411 tools::Long nFontHeight
= pWin
->GetTextHeight();
1412 tools::Long nExtra
= nFontHeight
/4;
1414 tools::Long nMinMenuItemHeight
= nFontHeight
;
1415 tools::Long nCheckHeight
= 0, nRadioHeight
= 0;
1416 Size aMarkSize
= ImplGetNativeCheckAndRadioSize(*pWin
->GetOutDev(), nCheckHeight
, nRadioHeight
);
1417 if( aMarkSize
.Height() > nMinMenuItemHeight
)
1418 nMinMenuItemHeight
= aMarkSize
.Height();
1420 tools::Long aMaxImgWidth
= 0;
1422 const StyleSettings
& rSettings
= pWin
->GetSettings().GetStyleSettings();
1423 if ( rSettings
.GetUseImagesInMenus() )
1425 if ( 16 > nMinMenuItemHeight
)
1426 nMinMenuItemHeight
= 16;
1427 for ( size_t i
= pItemList
->size(); i
; )
1429 MenuItemData
* pData
= pItemList
->GetDataFromPos( --i
);
1430 if ( ImplIsVisible( i
)
1431 && ( ( pData
->eType
== MenuItemType::IMAGE
)
1432 || ( pData
->eType
== MenuItemType::STRINGIMAGE
)
1436 Size aImgSz
= pData
->aImage
.GetSizePixel();
1437 if ( aImgSz
.Width() > aMaxImgWidth
)
1438 aMaxImgWidth
= aImgSz
.Width();
1439 if ( aImgSz
.Height() > nMinMenuItemHeight
)
1440 nMinMenuItemHeight
= aImgSz
.Height();
1447 tools::Long nMaxWidth
= 0;
1449 for ( size_t n
= pItemList
->size(); n
; )
1451 MenuItemData
* pData
= pItemList
->GetDataFromPos( --n
);
1453 pData
->aSz
.setHeight( 0 );
1454 pData
->aSz
.setWidth( 0 );
1456 if ( ImplIsVisible( n
) )
1458 tools::Long nWidth
= 0;
1461 if (!IsMenuBar()&& (pData
->eType
== MenuItemType::SEPARATOR
))
1463 pData
->aSz
.setHeight( 4 );
1467 if (!IsMenuBar() && ((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1469 tools::Long aImgHeight
= pData
->aImage
.GetSizePixel().Height();
1471 aImgHeight
+= 4; // add a border for native marks
1472 if (aImgHeight
> pData
->aSz
.Height())
1473 pData
->aSz
.setHeight(aImgHeight
);
1477 if (!IsMenuBar() && pData
->HasCheck())
1479 // checks / images take the same place
1480 if( ( pData
->eType
!= MenuItemType::IMAGE
) && ( pData
->eType
!= MenuItemType::STRINGIMAGE
) )
1482 nWidth
+= aMarkSize
.Width() + nExtra
* 2;
1483 if (aMarkSize
.Height() > pData
->aSz
.Height())
1484 pData
->aSz
.setHeight(aMarkSize
.Height());
1489 if ( (pData
->eType
== MenuItemType::STRING
) || (pData
->eType
== MenuItemType::STRINGIMAGE
) )
1491 const SalLayoutGlyphs
* pGlyphs
= pData
->GetTextGlyphs(pWin
->GetOutDev());
1492 tools::Long nTextWidth
= pWin
->GetOutDev()->GetCtrlTextWidth(pData
->aText
, pGlyphs
);
1493 tools::Long nTextHeight
= pWin
->GetTextHeight() + EXTRAITEMHEIGHT
;
1497 if ( nTextHeight
> pData
->aSz
.Height() )
1498 pData
->aSz
.setHeight( nTextHeight
);
1500 pData
->aSz
.setWidth( nTextWidth
+ 4*nExtra
);
1501 aSz
.AdjustWidth(pData
->aSz
.Width() );
1504 pData
->aSz
.setHeight( std::max( std::max( nTextHeight
, pData
->aSz
.Height() ), nMinMenuItemHeight
) );
1506 nWidth
+= nTextWidth
;
1510 if (!IsMenuBar()&& pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1512 OUString aName
= pData
->aAccelKey
.GetName();
1513 tools::Long nAccWidth
= pWin
->GetTextWidth( aName
);
1514 nAccWidth
+= nExtra
;
1515 nWidth
+= nAccWidth
;
1519 if (!IsMenuBar() && pData
->pSubMenu
)
1521 if ( nFontHeight
> nWidth
)
1522 nWidth
+= nFontHeight
;
1524 pData
->aSz
.setHeight( std::max( std::max( nFontHeight
, pData
->aSz
.Height() ), nMinMenuItemHeight
) );
1528 aSz
.AdjustHeight(pData
->aSz
.Height() );
1530 if ( nWidth
> nMaxWidth
)
1536 // Additional space for title
1538 if (!IsMenuBar() && aTitleText
.getLength() > 0) {
1539 // Set expected font
1540 pWin
->GetOutDev()->Push(PushFlags::FONT
);
1541 vcl::Font aFont
= pWin
->GetFont();
1542 aFont
.SetWeight(WEIGHT_BOLD
);
1543 pWin
->SetFont(aFont
);
1545 // Compute text bounding box
1546 tools::Rectangle aTextBoundRect
;
1547 pWin
->GetOutDev()->GetTextBoundRect(aTextBoundRect
, aTitleText
);
1549 // Vertically, one height of char + extra space for decoration
1550 nTitleHeight
= aTextBoundRect
.GetSize().Height() + 4 * SPACE_AROUND_TITLE
;
1551 aSz
.AdjustHeight(nTitleHeight
);
1553 tools::Long nWidth
= aTextBoundRect
.GetSize().Width() + 4 * SPACE_AROUND_TITLE
;
1554 pWin
->GetOutDev()->Pop();
1555 if ( nWidth
> nMaxWidth
)
1561 // popup menus should not be wider than half the screen
1562 // except on rather small screens
1563 // TODO: move GetScreenNumber from SystemWindow to Window ?
1564 // currently we rely on internal privileges
1565 unsigned int nDisplayScreen
= pWin
->ImplGetWindowImpl()->mpFrame
->maGeometry
.screen();
1566 tools::Rectangle
aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen
) );
1567 tools::Long nScreenWidth
= aDispRect
.GetWidth() >= 800 ? aDispRect
.GetWidth() : 800;
1568 if( nMaxWidth
> nScreenWidth
/2 )
1569 nMaxWidth
= nScreenWidth
/2;
1571 sal_uInt16 gfxExtra
= static_cast<sal_uInt16
>(std::max( nExtra
, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
1572 nImgOrChkPos
= static_cast<sal_uInt16
>(nExtra
);
1573 tools::Long nImgOrChkWidth
= 0;
1574 if( aMarkSize
.Height() > 0 ) // NWF case
1575 nImgOrChkWidth
= aMarkSize
.Height() + nExtra
;
1576 else // non NWF case
1577 nImgOrChkWidth
= nFontHeight
/2 + gfxExtra
;
1578 nImgOrChkWidth
= std::max( nImgOrChkWidth
, aMaxImgWidth
+ gfxExtra
);
1579 nTextPos
= static_cast<sal_uInt16
>(nImgOrChkPos
+ nImgOrChkWidth
);
1580 nTextPos
= nTextPos
+ gfxExtra
;
1582 aSz
.setWidth( nTextPos
+ nMaxWidth
+ nExtra
);
1583 aSz
.AdjustWidth(4*nExtra
); // a _little_ more ...
1585 aSz
.AdjustWidth(2*ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
);
1586 aSz
.AdjustHeight(2*ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
);
1590 nTextPos
= static_cast<sal_uInt16
>(2*nExtra
);
1591 aSz
.setHeight( nFontHeight
+6 );
1593 // get menubar height from native methods if supported
1594 if( pWindow
->IsNativeControlSupported( ControlType::Menubar
, ControlPart::Entire
) )
1596 ImplControlValue aVal
;
1597 tools::Rectangle aNativeBounds
;
1598 tools::Rectangle aNativeContent
;
1600 tools::Rectangle
aCtrlRegion( tmp
, Size( 100, 15 ) );
1601 if( pWindow
->GetNativeControlRegion( ControlType::Menubar
,
1602 ControlPart::Entire
,
1604 ControlState::ENABLED
,
1610 int nNativeHeight
= aNativeBounds
.GetHeight();
1611 if( nNativeHeight
> aSz
.Height() )
1612 aSz
.setHeight( nNativeHeight
);
1616 // account for the size of the close button, which actually is a toolbox
1617 // due to NWF this is variable
1618 tools::Long nCloseButtonHeight
= static_cast<MenuBarWindow
*>(pWindow
.get())->MinCloseButtonSize().Height();
1619 if (aSz
.Height() < nCloseButtonHeight
)
1620 aSz
.setHeight( nCloseButtonHeight
);
1626 static void ImplPaintCheckBackground(vcl::RenderContext
& rRenderContext
, vcl::Window
const & rWindow
, const tools::Rectangle
& i_rRect
, bool i_bHighlight
)
1628 bool bNativeOk
= false;
1629 if (rRenderContext
.IsNativeControlSupported(ControlType::Toolbar
, ControlPart::Button
))
1631 ImplControlValue aControlValue
;
1632 aControlValue
.setTristateVal(ButtonValue::On
);
1633 tools::Rectangle r
= i_rRect
;
1636 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::Toolbar
, ControlPart::Button
,
1638 ControlState::PRESSED
| ControlState::ENABLED
,
1645 const StyleSettings
& rSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1646 Color
aColor( i_bHighlight
? rSettings
.GetMenuHighlightTextColor() : rSettings
.GetHighlightColor() );
1647 RenderTools::DrawSelectionBackground(rRenderContext
, rWindow
, i_rRect
, 0, i_bHighlight
, true, false, nullptr, 2, &aColor
);
1651 static OUString
getShortenedString( const OUString
& i_rLong
, vcl::RenderContext
const & rRenderContext
, tools::Long i_nMaxWidth
)
1653 sal_Int32 nPos
= -1;
1654 OUString
aNonMnem(removeMnemonicFromString(i_rLong
, nPos
));
1655 aNonMnem
= rRenderContext
.GetEllipsisString( aNonMnem
, i_nMaxWidth
, DrawTextFlags::CenterEllipsis
);
1656 // re-insert mnemonic
1659 if (nPos
< aNonMnem
.getLength() && i_rLong
[nPos
+1] == aNonMnem
[nPos
])
1661 OUString aTmp
= OUString::Concat(aNonMnem
.subView(0, nPos
)) + "~" + aNonMnem
.subView(nPos
);
1668 void Menu::ImplPaintMenuTitle(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
) const
1670 // Save previous graphical settings, set new one
1671 rRenderContext
.Push(PushFlags::FONT
| PushFlags::FILLCOLOR
);
1672 Wallpaper aOldBackground
= rRenderContext
.GetBackground();
1674 Color aBackgroundColor
= rRenderContext
.GetSettings().GetStyleSettings().GetMenuBarColor();
1675 rRenderContext
.SetBackground(Wallpaper(aBackgroundColor
));
1676 rRenderContext
.SetFillColor(aBackgroundColor
);
1677 vcl::Font aFont
= rRenderContext
.GetFont();
1678 aFont
.SetWeight(WEIGHT_BOLD
);
1679 rRenderContext
.SetFont(aFont
);
1681 // Draw background rectangle
1682 tools::Rectangle
aBgRect(rRect
);
1683 int nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
1684 aBgRect
.Move(SPACE_AROUND_TITLE
, SPACE_AROUND_TITLE
);
1685 aBgRect
.setWidth(aBgRect
.getOpenWidth() - 2 * SPACE_AROUND_TITLE
- 2 * nOuterSpaceX
);
1686 aBgRect
.setHeight(nTitleHeight
- 2 * SPACE_AROUND_TITLE
);
1687 rRenderContext
.DrawRect(aBgRect
);
1689 // Draw the text centered
1690 Point
aTextTopLeft(aBgRect
.TopLeft());
1691 tools::Rectangle aTextBoundRect
;
1692 rRenderContext
.GetTextBoundRect( aTextBoundRect
, aTitleText
);
1693 aTextTopLeft
.AdjustX((aBgRect
.getOpenWidth() - aTextBoundRect
.GetSize().Width()) / 2 );
1694 aTextTopLeft
.AdjustY((aBgRect
.GetHeight() - aTextBoundRect
.GetSize().Height()) / 2
1695 - aTextBoundRect
.Top() );
1696 rRenderContext
.DrawText(aTextTopLeft
, aTitleText
, 0, aTitleText
.getLength());
1699 rRenderContext
.Pop();
1700 rRenderContext
.SetBackground(aOldBackground
);
1703 void Menu::ImplPaint(vcl::RenderContext
& rRenderContext
, Size
const & rSize
,
1704 sal_uInt16 nBorder
, tools::Long nStartY
, MenuItemData
const * pThisItemOnly
,
1705 bool bHighlighted
, bool bLayout
, bool bRollover
) const
1707 // for symbols: nFontHeight x nFontHeight
1708 tools::Long nFontHeight
= rRenderContext
.GetTextHeight();
1709 tools::Long nExtra
= nFontHeight
/ 4;
1711 tools::Long nCheckHeight
= 0, nRadioHeight
= 0;
1712 ImplGetNativeCheckAndRadioSize(rRenderContext
, nCheckHeight
, nRadioHeight
);
1714 DecorationView
aDecoView(&rRenderContext
);
1715 const StyleSettings
& rSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1717 Point aTopLeft
, aTmpPos
;
1719 int nOuterSpaceX
= 0;
1722 nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
1723 aTopLeft
.AdjustX(nOuterSpaceX
);
1724 aTopLeft
.AdjustY(ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
);
1727 // for the computations, use size of the underlying window, not of RenderContext
1730 size_t nCount
= pItemList
->size();
1732 mpLayoutData
->m_aVisibleItemBoundRects
.clear();
1735 if (!pThisItemOnly
&& !IsMenuBar() && nTitleHeight
> 0)
1736 ImplPaintMenuTitle(rRenderContext
, tools::Rectangle(aTopLeft
, aOutSz
));
1738 bool bHiddenItems
= false; // are any items on the GUI hidden
1740 for (size_t n
= 0; n
< nCount
; n
++)
1742 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
1743 if (ImplIsVisible(n
) && (!pThisItemOnly
|| (pData
== pThisItemOnly
)))
1749 if (!ImplGetSVData()->maNWFData
.mbRolloverMenubar
)
1752 rRenderContext
.SetTextColor(rSettings
.GetMenuBarRolloverTextColor());
1753 else if (bHighlighted
)
1754 rRenderContext
.SetTextColor(rSettings
.GetMenuBarHighlightTextColor());
1759 rRenderContext
.SetTextColor(rSettings
.GetMenuBarHighlightTextColor());
1761 rRenderContext
.SetTextColor(rSettings
.GetMenuBarRolloverTextColor());
1763 if (!bRollover
&& !bHighlighted
)
1764 rRenderContext
.SetTextColor(rSettings
.GetMenuBarTextColor());
1766 else if (bHighlighted
)
1767 rRenderContext
.SetTextColor(rSettings
.GetMenuHighlightTextColor());
1770 Point
aPos(aTopLeft
);
1771 aPos
.AdjustY(nBorder
);
1772 aPos
.AdjustY(nStartY
);
1776 tools::Long nTextOffsetY
= (pData
->aSz
.Height() - nFontHeight
) / 2;
1778 nTextOffsetY
+= (aOutSz
.Height()-pData
->aSz
.Height()) / 2;
1779 DrawTextFlags nTextStyle
= DrawTextFlags::NONE
;
1780 DrawSymbolFlags nSymbolStyle
= DrawSymbolFlags::NONE
;
1781 DrawImageFlags nImageStyle
= DrawImageFlags::NONE
;
1783 // submenus without items are not disabled when no items are
1784 // contained. The application itself should check for this!
1785 // Otherwise it could happen entries are disabled due to
1786 // asynchronous loading
1787 if (!pData
->bEnabled
|| !pWindow
->IsEnabled())
1789 nTextStyle
|= DrawTextFlags::Disable
;
1790 nSymbolStyle
|= DrawSymbolFlags::Disable
;
1791 nImageStyle
|= DrawImageFlags::Disable
;
1795 if (!bLayout
&& !IsMenuBar() && (pData
->eType
== MenuItemType::SEPARATOR
))
1797 bool bNativeOk
= false;
1798 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Separator
))
1800 ControlState nState
= ControlState::NONE
;
1801 if (pData
->bEnabled
&& pWindow
->IsEnabled())
1802 nState
|= ControlState::ENABLED
;
1804 nState
|= ControlState::SELECTED
;
1805 Size
aSz(pData
->aSz
);
1806 aSz
.setWidth( aOutSz
.Width() - 2*nOuterSpaceX
);
1807 tools::Rectangle
aItemRect(aPos
, aSz
);
1808 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
1809 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::Separator
,
1810 aItemRect
, nState
, aVal
, OUString());
1814 aTmpPos
.setY( aPos
.Y() + ((pData
->aSz
.Height() - 2) / 2) );
1815 aTmpPos
.setX( aPos
.X() + 2 + nOuterSpaceX
);
1816 rRenderContext
.SetLineColor(rSettings
.GetShadowColor());
1817 rRenderContext
.DrawLine(aTmpPos
, Point(aOutSz
.Width() - 3 - 2 * nOuterSpaceX
, aTmpPos
.Y()));
1818 aTmpPos
.AdjustY( 1 );
1819 rRenderContext
.SetLineColor(rSettings
.GetLightColor());
1820 rRenderContext
.DrawLine(aTmpPos
, Point(aOutSz
.Width() - 3 - 2 * nOuterSpaceX
, aTmpPos
.Y()));
1821 rRenderContext
.SetLineColor();
1825 tools::Rectangle
aOuterCheckRect(Point(aPos
.X()+nImgOrChkPos
, aPos
.Y()),
1826 Size(pData
->aSz
.Height(), pData
->aSz
.Height()));
1829 if (!bLayout
&& !IsMenuBar() && pData
->HasCheck())
1831 // draw selection transparent marker if checked
1832 // onto that either a checkmark or the item image
1834 // however do not do this if native checks will be painted since
1835 // the selection color too often does not fit the theme's check and/or radio
1837 if( (pData
->eType
!= MenuItemType::IMAGE
) && (pData
->eType
!= MenuItemType::STRINGIMAGE
))
1839 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
,
1840 (pData
->nBits
& MenuItemBits::RADIOCHECK
)
1841 ? ControlPart::MenuItemCheckMark
1842 : ControlPart::MenuItemRadioMark
))
1844 ControlPart nPart
= ((pData
->nBits
& MenuItemBits::RADIOCHECK
)
1845 ? ControlPart::MenuItemRadioMark
1846 : ControlPart::MenuItemCheckMark
);
1848 ControlState nState
= ControlState::NONE
;
1850 if (pData
->bChecked
)
1851 nState
|= ControlState::PRESSED
;
1853 if (pData
->bEnabled
&& pWindow
->IsEnabled())
1854 nState
|= ControlState::ENABLED
;
1857 nState
|= ControlState::SELECTED
;
1859 tools::Long nCtrlHeight
= (pData
->nBits
& MenuItemBits::RADIOCHECK
) ? nCheckHeight
: nRadioHeight
;
1860 aTmpPos
.setX( aOuterCheckRect
.Left() + (aOuterCheckRect
.GetWidth() - nCtrlHeight
) / 2 );
1861 aTmpPos
.setY( aOuterCheckRect
.Top() + (aOuterCheckRect
.GetHeight() - nCtrlHeight
) / 2 );
1863 tools::Rectangle
aCheckRect(aTmpPos
, Size(nCtrlHeight
, nCtrlHeight
));
1864 Size
aSz(pData
->aSz
);
1865 aSz
.setWidth( aOutSz
.Width() - 2 * nOuterSpaceX
);
1866 tools::Rectangle
aItemRect(aPos
, aSz
);
1867 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
1868 rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, nPart
, aCheckRect
,
1869 nState
, aVal
, OUString());
1871 else if (pData
->bChecked
) // by default do nothing for unchecked items
1873 ImplPaintCheckBackground(rRenderContext
, *pWindow
, aOuterCheckRect
, pThisItemOnly
&& bHighlighted
);
1877 if (pData
->nBits
& MenuItemBits::RADIOCHECK
)
1879 eSymbol
= SymbolType::RADIOCHECKMARK
;
1880 aSymbolSize
= Size(nFontHeight
/ 2, nFontHeight
/ 2);
1884 eSymbol
= SymbolType::CHECKMARK
;
1885 aSymbolSize
= Size((nFontHeight
* 25) / 40, nFontHeight
/ 2);
1887 aTmpPos
.setX( aOuterCheckRect
.Left() + (aOuterCheckRect
.GetWidth() - aSymbolSize
.Width()) / 2 );
1888 aTmpPos
.setY( aOuterCheckRect
.Top() + (aOuterCheckRect
.GetHeight() - aSymbolSize
.Height()) / 2 );
1889 tools::Rectangle
aRect(aTmpPos
, aSymbolSize
);
1890 aDecoView
.DrawSymbol(aRect
, eSymbol
, rRenderContext
.GetTextColor(), nSymbolStyle
);
1896 if (!bLayout
&& !IsMenuBar() && ((pData
->eType
== MenuItemType::IMAGE
) || (pData
->eType
== MenuItemType::STRINGIMAGE
)))
1898 // Don't render an image for a check thing
1899 if (pData
->bChecked
)
1900 ImplPaintCheckBackground(rRenderContext
, *pWindow
, aOuterCheckRect
, pThisItemOnly
&& bHighlighted
);
1902 Image aImage
= pData
->aImage
;
1904 aTmpPos
= aOuterCheckRect
.TopLeft();
1905 aTmpPos
.AdjustX((aOuterCheckRect
.GetWidth() - aImage
.GetSizePixel().Width()) / 2 );
1906 aTmpPos
.AdjustY((aOuterCheckRect
.GetHeight() - aImage
.GetSizePixel().Height()) / 2 );
1907 rRenderContext
.DrawImage(aTmpPos
, aImage
, nImageStyle
);
1911 if ((pData
->eType
== MenuItemType::STRING
) || (pData
->eType
== MenuItemType::STRINGIMAGE
))
1913 aTmpPos
.setX( aPos
.X() + nTextPos
);
1914 aTmpPos
.setY( aPos
.Y() );
1915 aTmpPos
.AdjustY(nTextOffsetY
);
1916 DrawTextFlags nStyle
= nTextStyle
| DrawTextFlags::Mnemonic
;
1918 if (pData
->bIsTemporary
)
1919 nStyle
|= DrawTextFlags::Disable
;
1920 std::vector
< tools::Rectangle
>* pVector
= bLayout
? &mpLayoutData
->m_aUnicodeBoundRects
: nullptr;
1921 OUString
* pDisplayText
= bLayout
? &mpLayoutData
->m_aDisplayText
: nullptr;
1924 mpLayoutData
->m_aLineIndices
.push_back(mpLayoutData
->m_aDisplayText
.getLength());
1925 mpLayoutData
->m_aLineItemIds
.push_back(pData
->nId
);
1927 // #i47946# with NWF painted menus the background is transparent
1928 // since DrawCtrlText can depend on the background (e.g. for
1929 // DrawTextFlags::Disable), temporarily set a background which
1930 // hopefully matches the NWF background since it is read
1931 // from the system style settings
1932 bool bSetTmpBackground
= !rRenderContext
.IsBackground()
1933 && rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
);
1934 if (bSetTmpBackground
)
1936 Color aBg
= IsMenuBar() ? rRenderContext
.GetSettings().GetStyleSettings().GetMenuBarColor()
1937 : rRenderContext
.GetSettings().GetStyleSettings().GetMenuColor();
1938 rRenderContext
.SetBackground(Wallpaper(aBg
));
1940 // how much space is there for the text?
1941 tools::Long nMaxItemTextWidth
= aOutSz
.Width() - aTmpPos
.X() - nExtra
- nOuterSpaceX
;
1942 if (!IsMenuBar() && pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1944 OUString aAccText
= pData
->aAccelKey
.GetName();
1945 nMaxItemTextWidth
-= rRenderContext
.GetTextWidth(aAccText
) + 3 * nExtra
;
1947 if (!IsMenuBar() && pData
->pSubMenu
)
1949 nMaxItemTextWidth
-= nFontHeight
- nExtra
;
1952 OUString
aItemText(pData
->aText
);
1953 pData
->bHiddenOnGUI
= false;
1955 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
1957 if (nMaxItemTextWidth
< rRenderContext
.GetTextWidth(aItemText
))
1960 pData
->bHiddenOnGUI
= true;
1961 bHiddenItems
= true;
1966 aItemText
= getShortenedString(aItemText
, rRenderContext
, nMaxItemTextWidth
);
1967 pData
->bHiddenOnGUI
= false;
1970 const SalLayoutGlyphs
* pGlyphs
= pData
->GetTextGlyphs(&rRenderContext
);
1971 if (aItemText
!= pData
->aText
)
1972 // Can't use pre-computed glyphs, item text was
1975 rRenderContext
.DrawCtrlText(aTmpPos
, aItemText
, 0, aItemText
.getLength(),
1976 nStyle
, pVector
, pDisplayText
, pGlyphs
);
1977 if (bSetTmpBackground
)
1978 rRenderContext
.SetBackground();
1982 if (!bLayout
&& !IsMenuBar() && pData
->aAccelKey
.GetCode() && !ImplAccelDisabled())
1984 OUString aAccText
= pData
->aAccelKey
.GetName();
1985 aTmpPos
.setX( aOutSz
.Width() - rRenderContext
.GetTextWidth(aAccText
) );
1986 aTmpPos
.AdjustX( -(4 * nExtra
) );
1988 aTmpPos
.AdjustX( -nOuterSpaceX
);
1989 aTmpPos
.setY( aPos
.Y() );
1990 aTmpPos
.AdjustY(nTextOffsetY
);
1991 rRenderContext
.DrawCtrlText(aTmpPos
, aAccText
, 0, aAccText
.getLength(), nTextStyle
);
1995 if (!bLayout
&& !IsMenuBar() && pData
->pSubMenu
)
1997 bool bNativeOk
= false;
1998 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::SubmenuArrow
))
2000 ControlState nState
= ControlState::NONE
;
2002 tools::Long aSpacing
= 0;
2004 if (!ImplGetNativeSubmenuArrowSize(rRenderContext
, aTmpSz
, aSpacing
))
2006 aTmpSz
= Size(nFontHeight
, nFontHeight
);
2007 aSpacing
= nOuterSpaceX
;
2010 if (pData
->bEnabled
&& pWindow
->IsEnabled())
2011 nState
|= ControlState::ENABLED
;
2013 nState
|= ControlState::SELECTED
;
2015 aTmpPos
.setX( aOutSz
.Width() - aTmpSz
.Width() - aSpacing
- nOuterSpaceX
);
2016 aTmpPos
.setY( aPos
.Y() + ( pData
->aSz
.Height() - aTmpSz
.Height() ) / 2 );
2017 aTmpPos
.AdjustY(nExtra
/ 2 );
2019 tools::Rectangle
aItemRect(aTmpPos
, aTmpSz
);
2020 MenupopupValue
aVal(nTextPos
- GUTTERBORDER
, aItemRect
);
2021 bNativeOk
= rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::SubmenuArrow
,
2022 aItemRect
, nState
, aVal
, OUString());
2026 aTmpPos
.setX( aOutSz
.Width() - nFontHeight
+ nExtra
- nOuterSpaceX
);
2027 aTmpPos
.setY( aPos
.Y() );
2028 aTmpPos
.AdjustY(nExtra
/2 );
2029 aTmpPos
.AdjustY((pData
->aSz
.Height() / 2) - (nFontHeight
/ 4) );
2030 if (pData
->nBits
& MenuItemBits::POPUPSELECT
)
2032 rRenderContext
.SetTextColor(rSettings
.GetMenuTextColor());
2033 Point
aTmpPos2(aPos
);
2034 aTmpPos2
.setX( aOutSz
.Width() - nFontHeight
- nFontHeight
/4 );
2035 aDecoView
.DrawFrame(tools::Rectangle(aTmpPos2
, Size(nFontHeight
+ nFontHeight
/ 4,
2036 pData
->aSz
.Height())),
2037 DrawFrameStyle::Group
);
2039 aDecoView
.DrawSymbol(tools::Rectangle(aTmpPos
, Size(nFontHeight
/ 2, nFontHeight
/ 2)),
2040 SymbolType::SPIN_RIGHT
, rRenderContext
.GetTextColor(), nSymbolStyle
);
2044 if (pThisItemOnly
&& bHighlighted
)
2046 // This restores the normal menu or menu bar text
2047 // color for when it is no longer highlighted.
2049 rRenderContext
.SetTextColor(rSettings
.GetMenuBarTextColor());
2051 rRenderContext
.SetTextColor(rSettings
.GetMenuTextColor());
2057 mpLayoutData
->m_aVisibleItemBoundRects
[ n
] = tools::Rectangle(aTopLeft
, Size(aOutSz
.Width(), pData
->aSz
.Height()));
2059 mpLayoutData
->m_aVisibleItemBoundRects
[ n
] = tools::Rectangle(aTopLeft
, pData
->aSz
);
2064 aTopLeft
.AdjustY(pData
->aSz
.Height() );
2066 aTopLeft
.AdjustX(pData
->aSz
.Width() );
2069 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
2072 sal_Int32 nSize
= nFontHeight
;
2073 tools::Rectangle
aRectangle(Point(aOutSz
.Width() - nSize
, (aOutSz
.Height() / 2) - (nSize
/ 2)), Size(nSize
, nSize
));
2074 lclDrawMoreIndicator(rRenderContext
, aRectangle
);
2078 Menu
* Menu::ImplGetStartMenu()
2080 Menu
* pStart
= this;
2081 while ( pStart
&& pStart
->pStartedFrom
&& ( pStart
->pStartedFrom
!= pStart
) )
2082 pStart
= pStart
->pStartedFrom
;
2086 void Menu::ImplCallHighlight(sal_uInt16 nItem
)
2088 ImplMenuDelData
aDelData( this );
2091 sSelectedIdent
.clear();
2092 MenuItemData
* pData
= pItemList
->GetDataFromPos(nItem
);
2095 nSelectedId
= pData
->nId
;
2096 sSelectedIdent
= pData
->sIdent
;
2098 ImplCallEventListeners( VclEventId::MenuHighlight
, GetItemPos( GetCurItemId() ) );
2100 if( !aDelData
.isDeleted() )
2103 sSelectedIdent
.clear();
2107 IMPL_LINK_NOARG(Menu
, ImplCallSelect
, void*, void)
2113 Menu
* Menu::ImplFindSelectMenu()
2115 Menu
* pSelMenu
= nEventId
? this : nullptr;
2117 for ( size_t n
= GetItemList()->size(); n
&& !pSelMenu
; )
2119 MenuItemData
* pData
= GetItemList()->GetDataFromPos( --n
);
2121 if ( pData
->pSubMenu
)
2122 pSelMenu
= pData
->pSubMenu
->ImplFindSelectMenu();
2128 Menu
* Menu::ImplFindMenu( sal_uInt16 nItemId
)
2130 Menu
* pSelMenu
= nullptr;
2132 for ( size_t n
= GetItemList()->size(); n
&& !pSelMenu
; )
2134 MenuItemData
* pData
= GetItemList()->GetDataFromPos( --n
);
2136 if( pData
->nId
== nItemId
)
2138 else if ( pData
->pSubMenu
)
2139 pSelMenu
= pData
->pSubMenu
->ImplFindMenu( nItemId
);
2145 void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups
)
2147 for ( sal_uInt16 n
= 0; n
< GetItemCount(); n
++ )
2149 bool bRemove
= false;
2150 MenuItemData
* pItem
= pItemList
->GetDataFromPos( n
);
2151 if ( pItem
->eType
== MenuItemType::SEPARATOR
)
2153 if ( !n
|| ( GetItemType( n
-1 ) == MenuItemType::SEPARATOR
) )
2157 bRemove
= !pItem
->bEnabled
;
2159 if ( pItem
->pSubMenu
)
2161 pItem
->pSubMenu
->RemoveDisabledEntries();
2162 if ( bRemoveEmptyPopups
&& !pItem
->pSubMenu
->GetItemCount() )
2170 if ( GetItemCount() )
2172 sal_uInt16 nLast
= GetItemCount() - 1;
2173 MenuItemData
* pItem
= pItemList
->GetDataFromPos( nLast
);
2174 if ( pItem
->eType
== MenuItemType::SEPARATOR
)
2175 RemoveItem( nLast
);
2177 mpLayoutData
.reset();
2180 void Menu::UpdateNativeMenu()
2182 if ( ImplGetSalMenu() )
2183 ImplGetSalMenu()->Update();
2186 void Menu::MenuBarKeyInput(const KeyEvent
&)
2190 void Menu::ImplKillLayoutData() const
2192 mpLayoutData
.reset();
2195 void Menu::ImplFillLayoutData() const
2197 if (!(pWindow
&& pWindow
->IsReallyVisible()))
2200 mpLayoutData
.reset(new MenuLayoutData
);
2203 ImplPaint(*pWindow
->GetOutDev(), pWindow
->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
2207 MenuFloatingWindow
* pFloat
= static_cast<MenuFloatingWindow
*>(pWindow
.get());
2208 ImplPaint(*pWindow
->GetOutDev(), pWindow
->GetOutputSizePixel(), pFloat
->nScrollerHeight
, pFloat
->ImplGetStartY(),
2209 nullptr, false, true); //FIXME
2213 tools::Rectangle
Menu::GetCharacterBounds( sal_uInt16 nItemID
, tools::Long nIndex
) const
2215 tools::Long nItemIndex
= -1;
2216 if( ! mpLayoutData
)
2217 ImplFillLayoutData();
2220 for( size_t i
= 0; i
< mpLayoutData
->m_aLineItemIds
.size(); i
++ )
2222 if( mpLayoutData
->m_aLineItemIds
[i
] == nItemID
)
2224 nItemIndex
= mpLayoutData
->m_aLineIndices
[i
];
2229 return (mpLayoutData
&& nItemIndex
!= -1) ? mpLayoutData
->GetCharacterBounds( nItemIndex
+nIndex
) : tools::Rectangle();
2232 tools::Long
Menu::GetIndexForPoint( const Point
& rPoint
, sal_uInt16
& rItemID
) const
2234 tools::Long nIndex
= -1;
2236 if( ! mpLayoutData
)
2237 ImplFillLayoutData();
2240 nIndex
= mpLayoutData
->GetIndexForPoint( rPoint
);
2241 for( size_t i
= 0; i
< mpLayoutData
->m_aLineIndices
.size(); i
++ )
2243 if( mpLayoutData
->m_aLineIndices
[i
] <= nIndex
&&
2244 (i
== mpLayoutData
->m_aLineIndices
.size()-1 || mpLayoutData
->m_aLineIndices
[i
+1] > nIndex
) )
2246 // make index relative to item
2247 nIndex
-= mpLayoutData
->m_aLineIndices
[i
];
2248 rItemID
= mpLayoutData
->m_aLineItemIds
[i
];
2256 tools::Rectangle
Menu::GetBoundingRectangle( sal_uInt16 nPos
) const
2258 tools::Rectangle aRet
;
2261 ImplFillLayoutData();
2264 std::map
< sal_uInt16
, tools::Rectangle
>::const_iterator it
= mpLayoutData
->m_aVisibleItemBoundRects
.find( nPos
);
2265 if( it
!= mpLayoutData
->m_aVisibleItemBoundRects
.end() )
2271 void Menu::SetAccessibleName( sal_uInt16 nItemId
, const OUString
& rStr
)
2274 MenuItemData
* pData
= pItemList
->GetData( nItemId
, nPos
);
2276 if (pData
&& !rStr
.equals(pData
->aAccessibleName
))
2278 pData
->aAccessibleName
= rStr
;
2279 ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged
, nPos
);
2283 OUString
Menu::GetAccessibleName( sal_uInt16 nItemId
) const
2285 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2288 return pData
->aAccessibleName
;
2293 void Menu::SetAccessibleDescription( sal_uInt16 nItemId
, const OUString
& rStr
)
2295 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2298 pData
->aAccessibleDescription
= rStr
;
2301 OUString
Menu::GetAccessibleDescription( sal_uInt16 nItemId
) const
2303 MenuItemData
* pData
= pItemList
->GetData( nItemId
);
2305 if (pData
&& !pData
->aAccessibleDescription
.isEmpty())
2306 return pData
->aAccessibleDescription
;
2308 return GetHelpText(nItemId
);
2311 void Menu::GetSystemMenuData( SystemMenuData
* pData
) const
2313 Menu
* pMenu
= const_cast<Menu
*>(this);
2314 if( pData
&& pMenu
->ImplGetSalMenu() )
2316 pMenu
->ImplGetSalMenu()->GetSystemMenuData( pData
);
2320 bool Menu::IsHighlighted( sal_uInt16 nItemPos
) const
2327 bRet
= ( nItemPos
== static_cast< MenuBarWindow
* > (pWindow
.get())->GetHighlightedItem() );
2329 bRet
= ( nItemPos
== static_cast< MenuFloatingWindow
* > (pWindow
.get())->GetHighlightedItem() );
2335 void Menu::HighlightItem( sal_uInt16 nItemPos
)
2342 MenuBarWindow
* pMenuWin
= static_cast< MenuBarWindow
* >( pWindow
.get() );
2343 pMenuWin
->SetAutoPopup( false );
2344 pMenuWin
->ChangeHighlightItem( nItemPos
, false );
2348 static_cast< MenuFloatingWindow
* >( pWindow
.get() )->ChangeHighlightItem( nItemPos
, false );
2352 MenuBarWindow
* MenuBar::getMenuBarWindow()
2354 // so far just a dynamic_cast, hopefully to be turned into something saner
2356 MenuBarWindow
*pWin
= dynamic_cast<MenuBarWindow
*>(pWindow
.get());
2357 //either there is no window (fdo#87663) or it is a MenuBarWindow
2358 assert(!pWindow
|| pWin
);
2363 : mbCloseBtnVisible(false),
2364 mbFloatBtnVisible(false),
2365 mbHideBtnVisible(false),
2368 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(true, this);
2371 MenuBar::MenuBar( const MenuBar
& rMenu
)
2372 : mbCloseBtnVisible(false),
2373 mbFloatBtnVisible(false),
2374 mbHideBtnVisible(false),
2377 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(true, this);
2386 void MenuBar::dispose()
2388 ImplDestroy( this, true );
2392 void MenuBar::ClosePopup(Menu
*pMenu
)
2394 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2397 pMenuWin
->PopupClosed(pMenu
);
2400 void MenuBar::MenuBarKeyInput(const KeyEvent
& rEvent
)
2402 pWindow
->KeyInput(rEvent
);
2405 void MenuBar::ShowCloseButton(bool bShow
)
2407 ShowButtons( bShow
, mbFloatBtnVisible
, mbHideBtnVisible
);
2410 void MenuBar::ShowButtons( bool bClose
, bool bFloat
, bool bHide
)
2412 if ((bClose
!= mbCloseBtnVisible
) ||
2413 (bFloat
!= mbFloatBtnVisible
) ||
2414 (bHide
!= mbHideBtnVisible
))
2416 mbCloseBtnVisible
= bClose
;
2417 mbFloatBtnVisible
= bFloat
;
2418 mbHideBtnVisible
= bHide
;
2419 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2421 pMenuWin
->ShowButtons(bClose
, bFloat
, bHide
);
2425 void MenuBar::LayoutChanged()
2427 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2429 pMenuWin
->LayoutChanged();
2432 void MenuBar::SetDisplayable( bool bDisplayable
)
2434 if( bDisplayable
!= mbDisplayable
)
2436 if ( ImplGetSalMenu() )
2437 ImplGetSalMenu()->ShowMenuBar( bDisplayable
);
2439 mbDisplayable
= bDisplayable
;
2444 VclPtr
<vcl::Window
> MenuBar::ImplCreate(vcl::Window
* pParent
, vcl::Window
* pWindow
, MenuBar
* pMenu
)
2446 VclPtr
<MenuBarWindow
> pMenuBarWindow
= dynamic_cast<MenuBarWindow
*>(pWindow
);
2447 if (!pMenuBarWindow
)
2449 pWindow
= pMenuBarWindow
= VclPtr
<MenuBarWindow
>::Create( pParent
);
2452 pMenu
->pStartedFrom
= nullptr;
2453 pMenu
->pWindow
= pWindow
;
2454 pMenuBarWindow
->SetMenu(pMenu
);
2455 tools::Long nHeight
= pWindow
? pMenu
->ImplCalcSize(pWindow
).Height() : 0;
2457 // depending on the native implementation or the displayable flag
2458 // the menubar windows is suppressed (ie, height=0)
2459 if (!pMenu
->IsDisplayable() || (pMenu
->ImplGetSalMenu() && pMenu
->ImplGetSalMenu()->VisibleMenuBar()))
2464 pMenuBarWindow
->SetHeight(nHeight
);
2468 void MenuBar::ImplDestroy( MenuBar
* pMenu
, bool bDelete
)
2470 vcl::Window
*pWindow
= pMenu
->ImplGetWindow();
2471 if (pWindow
&& bDelete
)
2473 MenuBarWindow
* pMenuWin
= pMenu
->getMenuBarWindow();
2475 pMenuWin
->KillActivePopup();
2476 pWindow
->disposeOnce();
2478 pMenu
->pWindow
= nullptr;
2479 if (pMenu
->mpSalMenu
) {
2480 pMenu
->mpSalMenu
->ShowMenuBar(false);
2484 bool MenuBar::ImplHandleKeyEvent( const KeyEvent
& rKEvent
)
2486 // No keyboard processing when our menubar is invisible
2487 if (!IsDisplayable())
2490 // No keyboard processing when system handles the menu.
2491 SalMenu
*pNativeMenu
= ImplGetSalMenu();
2492 if (pNativeMenu
&& pNativeMenu
->VisibleMenuBar())
2494 // Except when the event is the F6 cycle pane event and we can put our
2495 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
2496 // where it's not part of the application window)
2497 if (!TaskPaneList::IsCycleKey(rKEvent
.GetKeyCode()))
2499 if (!pNativeMenu
->CanGetFocus())
2504 // check for enabled, if this method is called from another window...
2505 vcl::Window
* pWin
= ImplGetWindow();
2506 if (pWin
&& pWin
->IsEnabled() && pWin
->IsInputEnabled() && !pWin
->IsInModalMode())
2508 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2509 bDone
= pMenuWin
&& pMenuWin
->HandleKeyEvent(rKEvent
, false/*bFromMenu*/);
2514 void MenuBar::SelectItem(sal_uInt16 nId
)
2519 pWindow
->GrabFocus();
2520 nId
= GetItemPos( nId
);
2522 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2525 // #99705# popup the selected menu
2526 pMenuWin
->SetAutoPopup( true );
2527 if (ITEMPOS_INVALID
!= pMenuWin
->GetHighlightedItem())
2529 pMenuWin
->KillActivePopup();
2530 pMenuWin
->ChangeHighlightItem( ITEMPOS_INVALID
, false );
2532 if (nId
!= ITEMPOS_INVALID
)
2533 pMenuWin
->ChangeHighlightItem( nId
, false );
2537 // handler for native menu selection and command events
2538 bool Menu::HandleMenuActivateEvent( Menu
*pMenu
) const
2542 ImplMenuDelData
aDelData( this );
2544 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2545 pMenu
->bInCallback
= true;
2548 if( !aDelData
.isDeleted() )
2549 pMenu
->bInCallback
= false;
2554 bool Menu::HandleMenuDeActivateEvent( Menu
*pMenu
) const
2558 ImplMenuDelData
aDelData( this );
2560 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2561 pMenu
->bInCallback
= true;
2562 pMenu
->Deactivate();
2563 if( !aDelData
.isDeleted() )
2564 pMenu
->bInCallback
= false;
2569 bool MenuBar::HandleMenuHighlightEvent( Menu
*pMenu
, sal_uInt16 nHighlightEventId
) const
2572 pMenu
= const_cast<MenuBar
*>(this)->ImplFindMenu(nHighlightEventId
);
2575 ImplMenuDelData
aDelData( pMenu
);
2577 if( mnHighlightedItemPos
!= ITEMPOS_INVALID
)
2578 pMenu
->ImplCallEventListeners( VclEventId::MenuDehighlight
, mnHighlightedItemPos
);
2580 if( !aDelData
.isDeleted() )
2582 pMenu
->mnHighlightedItemPos
= pMenu
->GetItemPos( nHighlightEventId
);
2583 pMenu
->nSelectedId
= nHighlightEventId
;
2584 pMenu
->sSelectedIdent
= pMenu
->GetItemIdent( nHighlightEventId
);
2585 pMenu
->pStartedFrom
= const_cast<MenuBar
*>(this);
2586 pMenu
->ImplCallHighlight( pMenu
->mnHighlightedItemPos
);
2594 bool Menu::HandleMenuCommandEvent( Menu
*pMenu
, sal_uInt16 nCommandEventId
) const
2597 pMenu
= const_cast<Menu
*>(this)->ImplFindMenu(nCommandEventId
);
2600 pMenu
->nSelectedId
= nCommandEventId
;
2601 pMenu
->sSelectedIdent
= pMenu
->GetItemIdent(nCommandEventId
);
2602 pMenu
->pStartedFrom
= const_cast<Menu
*>(this);
2603 pMenu
->ImplSelect();
2610 sal_uInt16
MenuBar::AddMenuBarButton( const Image
& i_rImage
, const Link
<MenuBarButtonCallbackArg
&,bool>& i_rLink
, const OUString
& i_rToolTip
)
2612 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2613 return pMenuWin
? pMenuWin
->AddMenuBarButton(i_rImage
, i_rLink
, i_rToolTip
) : 0;
2616 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId
, const Link
<MenuBarButtonCallbackArg
&,bool>& rLink
)
2618 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2621 pMenuWin
->SetMenuBarButtonHighlightHdl(nId
, rLink
);
2624 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId
)
2626 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2629 pMenuWin
->RemoveMenuBarButton(nId
);
2632 tools::Rectangle
MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId
)
2634 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2635 return pMenuWin
? pMenuWin
->GetMenuBarButtonRectPixel(nId
) : tools::Rectangle();
2638 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId
)
2640 MenuBarWindow
* pMenuWin
= getMenuBarWindow();
2641 return pMenuWin
&& pMenuWin
->HandleMenuButtonEvent(i_nButtonId
);
2644 int MenuBar::GetMenuBarHeight() const
2646 MenuBar
* pMenuBar
= const_cast<MenuBar
*>(this);
2647 const SalMenu
*pNativeMenu
= pMenuBar
->ImplGetSalMenu();
2650 nMenubarHeight
= pNativeMenu
->GetMenuBarHeight();
2653 vcl::Window
* pMenubarWin
= GetWindow();
2654 nMenubarHeight
= pMenubarWin
? pMenubarWin
->GetOutputSizePixel().Height() : 0;
2656 return nMenubarHeight
;
2659 // bool PopupMenu::bAnyPopupInExecute = false;
2661 MenuFloatingWindow
* PopupMenu::ImplGetFloatingWindow() const {
2662 return static_cast<MenuFloatingWindow
*>(Menu::ImplGetWindow());
2665 PopupMenu::PopupMenu()
2667 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(false, this);
2670 PopupMenu::PopupMenu( const PopupMenu
& rMenu
)
2672 mpSalMenu
= ImplGetSVData()->mpDefInst
->CreateMenu(false, this);
2676 PopupMenu::~PopupMenu()
2681 void PopupMenu::ClosePopup(Menu
* pMenu
)
2683 MenuFloatingWindow
* p
= dynamic_cast<MenuFloatingWindow
*>(ImplGetWindow());
2684 PopupMenu
*pPopup
= dynamic_cast<PopupMenu
*>(pMenu
);
2686 p
->KillActivePopup(pPopup
);
2691 bool IsInPopupMenuExecute()
2693 return PopupMenu::GetActivePopupMenu() != nullptr;
2697 PopupMenu
* PopupMenu::GetActivePopupMenu()
2699 ImplSVData
* pSVData
= ImplGetSVData();
2700 return pSVData
->maAppData
.mpActivePopupMenu
;
2703 void PopupMenu::EndExecute()
2705 if ( ImplGetWindow() )
2706 ImplGetFloatingWindow()->EndExecute( 0 );
2709 void PopupMenu::SelectItem(sal_uInt16 nId
)
2711 if ( !ImplGetWindow() )
2714 if( nId
!= ITEMPOS_INVALID
)
2717 MenuItemData
* pData
= GetItemList()->GetData( nId
, nPos
);
2718 if (pData
&& pData
->pSubMenu
)
2719 ImplGetFloatingWindow()->ChangeHighlightItem( nPos
, true );
2721 ImplGetFloatingWindow()->EndExecute( nId
);
2725 MenuFloatingWindow
* pFloat
= ImplGetFloatingWindow();
2726 pFloat
->GrabFocus();
2728 for( size_t nPos
= 0; nPos
< GetItemList()->size(); nPos
++ )
2730 MenuItemData
* pData
= GetItemList()->GetDataFromPos( nPos
);
2731 if( pData
->pSubMenu
)
2733 pFloat
->KillActivePopup();
2736 pFloat
->ChangeHighlightItem( ITEMPOS_INVALID
, false );
2740 void PopupMenu::SetSelectedEntry( sal_uInt16 nId
)
2743 sSelectedIdent
= GetItemIdent(nId
);
2746 sal_uInt16
PopupMenu::Execute( vcl::Window
* pExecWindow
, const Point
& rPopupPos
)
2748 return Execute( pExecWindow
, tools::Rectangle( rPopupPos
, rPopupPos
), PopupMenuFlags::ExecuteDown
);
2751 static FloatWinPopupFlags
lcl_TranslateFlags(PopupMenuFlags nFlags
)
2753 FloatWinPopupFlags nPopupModeFlags
= FloatWinPopupFlags::NONE
;
2754 if ( nFlags
& PopupMenuFlags::ExecuteDown
)
2755 nPopupModeFlags
= FloatWinPopupFlags::Down
;
2756 else if ( nFlags
& PopupMenuFlags::ExecuteUp
)
2757 nPopupModeFlags
= FloatWinPopupFlags::Up
;
2758 else if ( nFlags
& PopupMenuFlags::ExecuteRight
)
2759 nPopupModeFlags
= FloatWinPopupFlags::Right
;
2761 nPopupModeFlags
= FloatWinPopupFlags::Down
;
2763 if (nFlags
& PopupMenuFlags::NoMouseUpClose
) // allow popup menus to stay open on mouse button up
2764 nPopupModeFlags
|= FloatWinPopupFlags::NoMouseUpClose
; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
2766 return nPopupModeFlags
;
2769 sal_uInt16
PopupMenu::Execute( vcl::Window
* pExecWindow
, const tools::Rectangle
& rRect
, PopupMenuFlags nFlags
)
2771 ENSURE_OR_RETURN( pExecWindow
, "PopupMenu::Execute: need a non-NULL window!", 0 );
2772 return ImplExecute( pExecWindow
, rRect
, lcl_TranslateFlags(nFlags
), nullptr, false );
2775 void PopupMenu::ImplFlushPendingSelect()
2777 // is there still Select?
2778 Menu
* pSelect
= ImplFindSelectMenu();
2781 // Select should be called prior to leaving execute in a popup menu!
2782 Application::RemoveUserEvent( pSelect
->nEventId
);
2783 pSelect
->nEventId
= nullptr;
2788 bool PopupMenu::PrepareRun(const VclPtr
<vcl::Window
>& pParentWin
, tools::Rectangle
& rRect
,
2789 FloatWinPopupFlags
& nPopupModeFlags
, Menu
* pSFrom
,
2790 bool& bRealExecute
, VclPtr
<MenuFloatingWindow
>& pWin
)
2792 bRealExecute
= false;
2793 const sal_uInt16 nItemCount
= GetItemCount();
2794 if (!pSFrom
&& (vcl::IsInPopupMenuExecute() || !nItemCount
))
2797 mpLayoutData
.reset();
2799 ImplSVData
* pSVData
= ImplGetSVData();
2801 pStartedFrom
= pSFrom
;
2803 sSelectedIdent
.clear();
2806 VclPtr
<vcl::Window
> xFocusId
;
2807 if ( !pStartedFrom
)
2809 pSVData
->mpWinData
->mbNoDeactivate
= true;
2810 xFocusId
= Window::SaveFocus();
2811 bRealExecute
= true;
2815 // assure that only one menu is open at a time
2816 if (pStartedFrom
->IsMenuBar() && pSVData
->mpWinData
->mpFirstFloat
)
2817 pSVData
->mpWinData
->mpFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
2818 | FloatWinPopupEndFlags::CloseAll
);
2821 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
2822 rRect
.SetPos(pParentWin
->OutputToScreenPixel(rRect
.TopLeft()));
2824 nPopupModeFlags
|= FloatWinPopupFlags::NoKeyClose
| FloatWinPopupFlags::AllMouseButtonClose
| FloatWinPopupFlags::GrabFocus
;
2826 nPopupModeFlags
|= FloatWinPopupFlags::NewLevel
;
2828 bInCallback
= true; // set it here, if Activate overridden
2830 bInCallback
= false;
2832 if (pParentWin
->isDisposed())
2835 if ( bCanceled
|| bKilled
)
2841 // The flag MenuFlags::HideDisabledEntries is inherited.
2844 if ( pSFrom
->nMenuFlags
& MenuFlags::HideDisabledEntries
)
2845 nMenuFlags
|= MenuFlags::HideDisabledEntries
;
2847 nMenuFlags
&= ~MenuFlags::HideDisabledEntries
;
2850 // #102790# context menus shall never show disabled entries
2851 nMenuFlags
|= MenuFlags::HideDisabledEntries
;
2853 sal_uInt16 nVisibleEntries
= ImplGetVisibleItemCount();
2854 if ( !nVisibleEntries
)
2856 OUString
aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE
));
2858 MenuItemData
* pData
= NbcInsertItem(0xFFFF, MenuItemBits::NONE
, aTmpEntryText
, nullptr, 0xFFFF, {});
2860 pData
= pItemList
->GetData( pData
->nId
, nPos
);
2864 pData
->bIsTemporary
= true;
2866 ImplCallEventListeners(VclEventId::MenuSubmenuChanged
, nPos
);
2869 pWin
= VclPtrInstance
<MenuFloatingWindow
>(this, pParentWin
, WB_BORDER
| WB_SYSTEMWINDOW
);
2870 if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu")
2872 VclPtr
<vcl::Window
> xNotifierParent
= pParentWin
->GetParentWithLOKNotifier();
2873 assert(xNotifierParent
&& xNotifierParent
->GetLOKNotifier() && "editview menu without LOKNotifier");
2874 pWin
->SetLOKNotifier(xNotifierParent
->GetLOKNotifier());
2877 if( pSVData
->maNWFData
.mbFlatMenu
)
2878 pWin
->SetBorderStyle( WindowBorderStyle::NOBORDER
);
2880 pWin
->SetBorderStyle( pWin
->GetBorderStyle() | WindowBorderStyle::MENU
);
2883 Size aSz
= ImplCalcSize( pWin
);
2885 tools::Rectangle
aDesktopRect(pWin
->GetDesktopRectPixel());
2886 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() )
2888 vcl::Window
* pDeskW
= pWindow
->GetWindow( GetWindowType::RealParent
);
2891 Point
aDesktopTL(pDeskW
->OutputToAbsoluteScreenPixel(rRect
.TopLeft()));
2892 aDesktopRect
= Application::GetScreenPosSizePixel(
2893 Application::GetBestScreen(tools::Rectangle(aDesktopTL
, rRect
.GetSize())));
2896 tools::Long nMaxHeight
= aDesktopRect
.GetHeight();
2898 //rhbz#1021915. If a menu won't fit in the desired location the default
2899 //mode is to place it somewhere it will fit. e.g. above, left, right. For
2900 //some cases, e.g. menubars, it's desirable to limit the options to
2901 //above/below and force the menu to scroll if it won't fit
2902 if (nPopupModeFlags
& FloatWinPopupFlags::NoHorzPlacement
)
2904 vcl::Window
* pRef
= pWin
;
2905 if ( pRef
->GetParent() )
2906 pRef
= pRef
->GetParent();
2908 tools::Rectangle
devRect(pRef
->OutputToAbsoluteScreenPixel(rRect
.TopLeft()),
2909 pRef
->OutputToAbsoluteScreenPixel(rRect
.BottomRight()));
2911 tools::Long nHeightAbove
= devRect
.Top() - aDesktopRect
.Top();
2912 tools::Long nHeightBelow
= aDesktopRect
.Bottom() - devRect
.Bottom();
2913 nMaxHeight
= std::min(nMaxHeight
, std::max(nHeightAbove
, nHeightBelow
));
2916 // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
2917 // So assume that the available screen size matches at least the system requirements
2918 SAL_WARN_IF(nMaxHeight
< 768, "vcl",
2919 "Available height misdetected as " << nMaxHeight
2920 << "px. Setting to 768px instead.");
2921 nMaxHeight
= std::max(nMaxHeight
, tools::Long(768));
2923 if (pStartedFrom
&& pStartedFrom
->IsMenuBar())
2924 nMaxHeight
-= pParentWin
->GetSizePixel().Height();
2925 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
2926 pWindow
->GetBorder( nLeft
, nTop
, nRight
, nBottom
);
2927 nMaxHeight
-= nTop
+nBottom
;
2928 if ( aSz
.Height() > nMaxHeight
)
2930 pWin
->EnableScrollMenu( true );
2931 sal_uInt16 nStart
= ImplGetFirstVisible();
2932 sal_uInt16 nEntries
= ImplCalcVisEntries( nMaxHeight
, nStart
);
2933 aSz
.setHeight( ImplCalcHeight( nEntries
) );
2936 pWin
->SetFocusId( xFocusId
);
2937 pWin
->SetOutputSizePixel( aSz
);
2941 bool PopupMenu::Run(const VclPtr
<MenuFloatingWindow
>& pWin
, const bool bRealExecute
, const bool bPreSelectFirst
,
2942 const FloatWinPopupFlags nPopupModeFlags
, Menu
* pSFrom
, const tools::Rectangle
& rRect
)
2944 SalMenu
* pMenu
= ImplGetSalMenu();
2945 if (pMenu
&& bRealExecute
&& pMenu
->ShowNativePopupMenu(pWin
, rRect
, nPopupModeFlags
))
2948 pWin
->StartPopupMode(rRect
, nPopupModeFlags
);
2952 if (pSFrom
->IsMenuBar())
2953 aPos
= static_cast<MenuBarWindow
*>(pSFrom
->pWindow
.get())->GetHighlightedItem();
2955 aPos
= static_cast<MenuFloatingWindow
*>(pSFrom
->pWindow
.get())->GetHighlightedItem();
2957 pWin
->SetPosInParent(aPos
); // store position to be sent in SUBMENUDEACTIVATE
2958 pSFrom
->ImplCallEventListeners(VclEventId::MenuSubmenuActivate
, aPos
);
2961 if ( bPreSelectFirst
)
2963 for (size_t n
= 0; n
< static_cast<size_t>(GetItemCount()); n
++)
2965 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
2966 if ( ( pData
->bEnabled
2967 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
2969 && ( pData
->eType
!= MenuItemType::SEPARATOR
)
2970 && ImplIsVisible( n
)
2971 && ImplIsSelectable( n
)
2974 pWin
->ChangeHighlightItem(n
, false);
2986 void PopupMenu::FinishRun(const VclPtr
<MenuFloatingWindow
>& pWin
, const VclPtr
<vcl::Window
>& pParentWin
, const bool bRealExecute
, const bool bIsNativeMenu
)
2988 if (!bRealExecute
|| pWin
->isDisposed())
2993 VclPtr
<vcl::Window
> xFocusId
= pWin
->GetFocusId();
2994 assert(xFocusId
== nullptr && "Focus should already be restored by MenuFloatingWindow::End");
2995 pWin
->ImplEndPopupMode(FloatWinPopupEndFlags::NONE
, xFocusId
);
2997 if (nSelectedId
) // then clean up .. ( otherwise done by TH )
2999 PopupMenu
* pSub
= pWin
->GetActivePopup();
3002 pSub
->ImplGetFloatingWindow()->EndPopupMode();
3003 pSub
= pSub
->ImplGetFloatingWindow()->GetActivePopup();
3008 pWin
->StopExecute();
3011 pWindow
.disposeAndClear();
3012 ImplClosePopupToolBox(pParentWin
);
3013 ImplFlushPendingSelect();
3016 sal_uInt16
PopupMenu::ImplExecute(const VclPtr
<vcl::Window
>& pParentWin
, const tools::Rectangle
& rRect
,
3017 FloatWinPopupFlags nPopupModeFlags
, Menu
* pSFrom
, bool bPreSelectFirst
)
3019 // tdf#126054 hold this until after function completes
3020 VclPtr
<PopupMenu
> xThis(this);
3021 bool bRealExecute
= false;
3022 tools::Rectangle
aRect(rRect
);
3023 VclPtr
<MenuFloatingWindow
> pWin
;
3024 if (!PrepareRun(pParentWin
, aRect
, nPopupModeFlags
, pSFrom
, bRealExecute
, pWin
))
3026 const bool bNative
= Run(pWin
, bRealExecute
, bPreSelectFirst
, nPopupModeFlags
, pSFrom
, aRect
);
3027 FinishRun(pWin
, pParentWin
, bRealExecute
, bNative
);
3031 sal_uInt16
PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight
, sal_uInt16 nStartEntry
, sal_uInt16
* pLastVisible
) const
3033 nMaxHeight
-= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
3035 tools::Long nHeight
= 0;
3036 size_t nEntries
= pItemList
->size();
3037 sal_uInt16 nVisEntries
= 0;
3042 for ( size_t n
= nStartEntry
; n
< nEntries
; n
++ )
3044 if ( ImplIsVisible( n
) )
3046 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3047 nHeight
+= pData
->aSz
.Height();
3048 if ( nHeight
> nMaxHeight
)
3059 tools::Long
PopupMenu::ImplCalcHeight( sal_uInt16 nEntries
) const
3061 tools::Long nHeight
= 0;
3063 sal_uInt16 nFound
= 0;
3064 for ( size_t n
= 0; ( nFound
< nEntries
) && ( n
< pItemList
->size() ); n
++ )
3066 if ( ImplIsVisible( static_cast<sal_uInt16
>(n
) ) )
3068 MenuItemData
* pData
= pItemList
->GetDataFromPos( n
);
3069 nHeight
+= pData
->aSz
.Height();
3074 nHeight
+= 2*ImplGetFloatingWindow()->GetScrollerHeight();
3079 css::uno::Reference
<css::awt::XPopupMenu
> PopupMenu::CreateMenuInterface()
3081 UnoWrapperBase
* pWrapper
= UnoWrapperBase::GetUnoWrapper();
3083 return pWrapper
->CreateMenuInterface(this);
3087 ImplMenuDelData::ImplMenuDelData( const Menu
* pMenu
)
3092 const_cast< Menu
* >( pMenu
)->ImplAddDel( *this );
3095 ImplMenuDelData::~ImplMenuDelData()
3098 const_cast< Menu
* >( mpMenu
.get() )->ImplRemoveDel( *this );
3101 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */