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