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 "menufloatingwindow.hxx"
21 #include "menuitemlist.hxx"
22 #include "menubarwindow.hxx"
23 #include "bufferdevice.hxx"
25 #include <sal/log.hxx>
26 #include <salframe.hxx>
28 #include <vcl/decoview.hxx>
29 #include <vcl/settings.hxx>
32 MenuFloatingWindow::MenuFloatingWindow( Menu
* pMen
, vcl::Window
* pParent
, WinBits nStyle
) :
33 FloatingWindow( pParent
, nStyle
),
35 nHighlightedItem(ITEMPOS_INVALID
),
36 nMBDownPos(ITEMPOS_INVALID
),
39 nPosInParent(ITEMPOS_INVALID
),
44 bIgnoreFirstMove(true),
47 mpWindowImpl
->mbMenuFloatingWindow
= true;
51 SetPopupModeEndHdl( LINK( this, MenuFloatingWindow
, PopupEnd
) );
53 aHighlightChangedTimer
.SetInvokeHandler( LINK( this, MenuFloatingWindow
, HighlightChanged
) );
54 aHighlightChangedTimer
.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
55 aHighlightChangedTimer
.SetDebugName( "vcl::MenuFloatingWindow aHighlightChangedTimer" );
57 aSubmenuCloseTimer
.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
58 aSubmenuCloseTimer
.SetInvokeHandler( LINK( this, MenuFloatingWindow
, SubmenuClose
) );
59 aSubmenuCloseTimer
.SetDebugName( "vcl::MenuFloatingWindow aSubmenuCloseTimer" );
61 aScrollTimer
.SetInvokeHandler( LINK( this, MenuFloatingWindow
, AutoScroll
) );
62 aScrollTimer
.SetDebugName( "vcl::MenuFloatingWindow aScrollTimer" );
64 AddEventListener( LINK( this, MenuFloatingWindow
, ShowHideListener
) );
67 void MenuFloatingWindow::doShutdown()
72 // #105373# notify toolkit that highlight was removed
73 // otherwise the entry will not be read when the menu is opened again
74 if( nHighlightedItem
!= ITEMPOS_INVALID
)
75 pMenu
->ImplCallEventListeners( VclEventId::MenuDehighlight
, nHighlightedItem
);
76 if (!bKeyInput
&& pMenu
&& pMenu
->pStartedFrom
&& !pMenu
->pStartedFrom
->IsMenuBar())
78 // #102461# remove highlight in parent
79 size_t i
, nCount
= pMenu
->pStartedFrom
->pItemList
->size();
80 for(i
= 0; i
< nCount
; i
++)
82 MenuItemData
* pData
= pMenu
->pStartedFrom
->pItemList
->GetDataFromPos( i
);
83 if( pData
&& ( pData
->pSubMenu
== pMenu
) )
88 MenuFloatingWindow
* pPWin
= static_cast<MenuFloatingWindow
*>(pMenu
->pStartedFrom
->ImplGetWindow());
90 pPWin
->InvalidateItem(i
);
94 // free the reference to the accessible component
95 SetAccessible( css::uno::Reference
< css::accessibility::XAccessible
>() );
97 aHighlightChangedTimer
.Stop();
99 // #95056# invalidate screen area covered by system window
100 // so this can be taken into account if the commandhandler performs a scroll operation
103 tools::Rectangle
aInvRect( GetWindowExtentsRelative( GetParent() ) );
104 GetParent()->Invalidate( aInvRect
);
107 RemoveEventListener( LINK( this, MenuFloatingWindow
, ShowHideListener
) );
110 aSubmenuCloseTimer
.Stop();
111 aSubmenuCloseTimer
.Stop();
112 aHighlightChangedTimer
.Stop();
113 aHighlightChangedTimer
.Stop();
117 MenuFloatingWindow::~MenuFloatingWindow()
122 void MenuFloatingWindow::dispose()
126 pActivePopup
.clear();
127 xSaveFocusId
.clear();
128 FloatingWindow::dispose();
131 void MenuFloatingWindow::Resize()
133 InitMenuClipRegion(*this); // FIXME
136 void MenuFloatingWindow::ApplySettings(vcl::RenderContext
& rRenderContext
)
138 FloatingWindow::ApplySettings(rRenderContext
);
140 if (IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItem
) &&
141 IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
143 AllSettings
aSettings(GetSettings());
144 ImplGetFrame()->UpdateSettings(aSettings
); // Update theme colors.
145 StyleSettings
aStyle(aSettings
.GetStyleSettings());
146 Color aHighlightTextColor
= ImplGetSVData()->maNWFData
.maMenuBarHighlightTextColor
;
147 if (aHighlightTextColor
!= COL_TRANSPARENT
)
149 aStyle
.SetMenuHighlightTextColor(aHighlightTextColor
);
151 aSettings
.SetStyleSettings(aStyle
);
152 OutputDevice::SetSettings(aSettings
);
155 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
156 SetPointFont(rRenderContext
, rStyleSettings
.GetMenuFont());
158 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
160 rRenderContext
.SetBackground(); // background will be drawn by NWF
163 rRenderContext
.SetBackground(Wallpaper(rStyleSettings
.GetMenuColor()));
165 rRenderContext
.SetTextColor(rStyleSettings
.GetMenuTextColor());
166 rRenderContext
.SetTextFillColor();
167 rRenderContext
.SetLineColor();
170 /// Get a negative pixel offset for an offset menu
171 long MenuFloatingWindow::ImplGetStartY() const
176 // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
177 if ( nFirstEntry
> 0 && !pMenu
->GetItemList()->GetDataFromPos(nFirstEntry
- 1) )
182 for ( sal_uInt16 n
= 0; n
< nFirstEntry
; n
++ )
183 nY
+= pMenu
->GetItemList()->GetDataFromPos( n
)->aSz
.Height();
184 nY
-= pMenu
->GetTitleHeight();
189 vcl::Region
MenuFloatingWindow::ImplCalcClipRegion() const
191 Size aOutSz
= GetOutputSizePixel();
192 tools::Rectangle
aRect( Point(), aOutSz
);
193 aRect
.AdjustTop(nScrollerHeight
);
194 aRect
.AdjustBottom( -nScrollerHeight
);
196 vcl::Region
aRegion(aRect
);
201 void MenuFloatingWindow::InitMenuClipRegion(vcl::RenderContext
& rRenderContext
)
205 rRenderContext
.SetClipRegion(ImplCalcClipRegion());
209 rRenderContext
.SetClipRegion();
213 void MenuFloatingWindow::ImplHighlightItem( const MouseEvent
& rMEvt
, bool bMBDown
)
218 long nY
= GetInitialItemY();
219 long nMouseY
= rMEvt
.GetPosPixel().Y();
220 Size aOutSz
= GetOutputSizePixel();
221 if ( ( nMouseY
>= nY
) && ( nMouseY
< aOutSz
.Height() ) )
223 bool bHighlighted
= false;
224 size_t nCount
= pMenu
->pItemList
->size();
225 for ( size_t n
= 0; !bHighlighted
&& ( n
< nCount
); n
++ )
227 if ( pMenu
->ImplIsVisible( n
) )
229 MenuItemData
* pItemData
= pMenu
->pItemList
->GetDataFromPos( n
);
231 nY
+= pItemData
->aSz
.Height();
232 if ( ( nOldY
<= nMouseY
) && ( nY
> nMouseY
) && pMenu
->ImplIsSelectable( n
) )
234 bool bPopupArea
= true;
235 if ( pItemData
->nBits
& MenuItemBits::POPUPSELECT
)
237 // only when clicked over the arrow...
238 Size aSz
= GetOutputSizePixel();
239 long nFontHeight
= GetTextHeight();
240 bPopupArea
= ( rMEvt
.GetPosPixel().X() >= ( aSz
.Width() - nFontHeight
- nFontHeight
/4 ) );
245 if ( n
!= nHighlightedItem
)
247 ChangeHighlightItem( static_cast<sal_uInt16
>(n
), false );
250 bool bAllowNewPopup
= true;
253 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
254 bAllowNewPopup
= pData
&& ( pData
->pSubMenu
!= pActivePopup
);
255 if ( bAllowNewPopup
)
259 if ( bPopupArea
&& bAllowNewPopup
)
261 HighlightChanged( nullptr );
266 if ( n
!= nHighlightedItem
)
268 ChangeHighlightItem( static_cast<sal_uInt16
>(n
), true );
270 else if ( pItemData
->nBits
& MenuItemBits::POPUPSELECT
)
272 if ( bPopupArea
&& ( pActivePopup
!= pItemData
->pSubMenu
) )
273 HighlightChanged( nullptr );
281 ChangeHighlightItem( ITEMPOS_INVALID
, true );
285 ImplScroll( rMEvt
.GetPosPixel() );
286 ChangeHighlightItem( ITEMPOS_INVALID
, true );
290 IMPL_LINK_NOARG(MenuFloatingWindow
, PopupEnd
, FloatingWindow
*, void)
292 // "this" will be deleted before the end of this method!
299 KillActivePopup(); // should be ok to just remove it
300 //pActivePopup->bCanceled = true;
302 pMenu
->bInCallback
= true;
304 pMenu
->bInCallback
= false;
308 if (pMenu
&& pMenu
->pStartedFrom
)
309 pMenu
->pStartedFrom
->ClosePopup(pMenu
);
313 pM
->pStartedFrom
= nullptr;
316 IMPL_LINK_NOARG(MenuFloatingWindow
, AutoScroll
, Timer
*, void)
318 ImplScroll( GetPointerPosPixel() );
321 IMPL_LINK( MenuFloatingWindow
, HighlightChanged
, Timer
*, pTimer
, void )
326 MenuItemData
* pItemData
= pMenu
->pItemList
->GetDataFromPos( nHighlightedItem
);
329 if ( pActivePopup
&& ( pActivePopup
!= pItemData
->pSubMenu
) )
331 FloatWinPopupFlags nOldFlags
= GetPopupModeFlags();
332 SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose
);
334 SetPopupModeFlags( nOldFlags
);
336 if ( pItemData
->bEnabled
&& pItemData
->pSubMenu
&& pItemData
->pSubMenu
->GetItemCount() && ( pItemData
->pSubMenu
!= pActivePopup
) )
338 pActivePopup
= static_cast<PopupMenu
*>(pItemData
->pSubMenu
.get());
339 long nY
= nScrollerHeight
+ImplGetStartY();
340 MenuItemData
* pData
= nullptr;
341 for ( sal_uLong n
= 0; n
< nHighlightedItem
; n
++ )
343 pData
= pMenu
->pItemList
->GetDataFromPos( n
);
344 nY
+= pData
->aSz
.Height();
346 pData
= pMenu
->pItemList
->GetDataFromPos( nHighlightedItem
);
347 Size MySize
= GetOutputSizePixel();
348 Point
aItemTopLeft( 0, nY
);
349 Point
aItemBottomRight( aItemTopLeft
);
350 aItemBottomRight
.AdjustX(MySize
.Width() );
351 aItemBottomRight
.AdjustY(pData
->aSz
.Height() );
353 // shift the popups a little
354 aItemTopLeft
.AdjustX(2 );
355 aItemBottomRight
.AdjustX( -2 );
356 if ( nHighlightedItem
)
357 aItemTopLeft
.AdjustY( -2 );
360 sal_Int32 nL
, nT
, nR
, nB
;
361 GetBorder( nL
, nT
, nR
, nB
);
362 aItemTopLeft
.AdjustY( -nT
);
365 // pTest: crash due to Reschedule() in call of Activate()
366 // Also it is prevented that submenus are displayed which
367 // were for long in Activate Rescheduled and which should not be
369 Menu
* pTest
= pActivePopup
;
370 FloatWinPopupFlags nOldFlags
= GetPopupModeFlags();
371 SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose
);
372 sal_uInt16 nRet
= pActivePopup
->ImplExecute( this, tools::Rectangle( aItemTopLeft
, aItemBottomRight
), FloatWinPopupFlags::Right
, pMenu
, pTimer
== nullptr );
373 SetPopupModeFlags( nOldFlags
);
375 // nRet != 0, if it was stopped during Activate()...
376 if ( !nRet
&& ( pActivePopup
== pTest
) && pActivePopup
->ImplGetWindow() )
377 pActivePopup
->ImplGetFloatingWindow()->AddPopupModeWindow( this );
382 IMPL_LINK_NOARG(MenuFloatingWindow
, SubmenuClose
, Timer
*, void)
384 if( pMenu
&& pMenu
->pStartedFrom
)
386 MenuFloatingWindow
* pWin
= static_cast<MenuFloatingWindow
*>(pMenu
->pStartedFrom
->GetWindow());
388 pWin
->KillActivePopup();
392 IMPL_LINK( MenuFloatingWindow
, ShowHideListener
, VclWindowEvent
&, rEvent
, void )
397 if( rEvent
.GetId() == VclEventId::WindowShow
)
398 pMenu
->ImplCallEventListeners( VclEventId::MenuShow
, ITEMPOS_INVALID
);
399 else if( rEvent
.GetId() == VclEventId::WindowHide
)
400 pMenu
->ImplCallEventListeners( VclEventId::MenuHide
, ITEMPOS_INVALID
);
403 void MenuFloatingWindow::EnableScrollMenu( bool b
)
406 nScrollerHeight
= b
? static_cast<sal_uInt16
>(GetSettings().GetStyleSettings().GetScrollBarSize()) /2 : 0;
408 InitMenuClipRegion(*this);
411 void MenuFloatingWindow::Start()
417 GetParent()->IncModalCount();
420 bool MenuFloatingWindow::MenuInHierarchyHasFocus() const
422 if (HasChildPathFocus())
424 PopupMenu
* pSub
= GetActivePopup();
427 return pSub
->ImplGetFloatingWindow()->HasChildPathFocus();
430 void MenuFloatingWindow::End()
435 if (GetParent() && !GetParent()->IsDisposed())
436 GetParent()->DecModalCount();
438 // restore focus to previous window if we still have the focus
439 VclPtr
<vcl::Window
> xFocusId(xSaveFocusId
);
440 xSaveFocusId
= nullptr;
441 if (xFocusId
!= nullptr && MenuInHierarchyHasFocus())
443 ImplGetSVData()->maWinData
.mbNoDeactivate
= false;
444 Window::EndSaveFocus(xFocusId
);
450 void MenuFloatingWindow::Execute()
452 ImplSVData
* pSVData
= ImplGetSVData();
454 pSVData
->maAppData
.mpActivePopupMenu
= static_cast<PopupMenu
*>(pMenu
.get());
459 Application::Yield();
461 pSVData
->maAppData
.mpActivePopupMenu
= nullptr;
464 void MenuFloatingWindow::StopExecute()
468 ImplEndPopupMode(FloatWinPopupEndFlags::NONE
, xSaveFocusId
);
470 aHighlightChangedTimer
.Stop();
475 // notify parent, needed for accessibility
476 if( pMenu
&& pMenu
->pStartedFrom
)
477 pMenu
->pStartedFrom
->ImplCallEventListeners( VclEventId::MenuSubmenuDeactivate
, nPosInParent
);
480 void MenuFloatingWindow::KillActivePopup( PopupMenu
* pThisOnly
)
482 if ( pActivePopup
&& ( !pThisOnly
|| ( pThisOnly
== pActivePopup
) ) )
484 if( pActivePopup
->pWindow
)
485 if( static_cast<FloatingWindow
*>(pActivePopup
->pWindow
.get())->IsInCleanUp() )
486 return; // kill it later
487 if ( pActivePopup
->bInCallback
)
488 pActivePopup
->bCanceled
= true;
490 // For all actions pActivePopup = 0, if e.g.
491 // PopupModeEndHdl the popups to destroy were called synchronous
492 PopupMenu
* pPopup
= pActivePopup
;
493 pActivePopup
= nullptr;
494 pPopup
->bInCallback
= true;
495 pPopup
->Deactivate();
496 pPopup
->bInCallback
= false;
497 if ( pPopup
->ImplGetWindow() )
499 pPopup
->ImplGetFloatingWindow()->StopExecute();
500 pPopup
->ImplGetFloatingWindow()->doShutdown();
501 pPopup
->pWindow
->SetParentToDefaultWindow();
502 pPopup
->pWindow
.disposeAndClear();
509 void MenuFloatingWindow::EndExecute()
511 Menu
* pStart
= pMenu
? pMenu
->ImplGetStartMenu() : nullptr;
513 // if started elsewhere, cleanup there as well
514 MenuFloatingWindow
* pCleanUpFrom
= this;
515 MenuFloatingWindow
* pWin
= this;
516 while (pWin
&& !pWin
->bInExecute
&&
517 pWin
->pMenu
->pStartedFrom
&& !pWin
->pMenu
->pStartedFrom
->IsMenuBar())
519 pWin
= static_cast<PopupMenu
*>(pWin
->pMenu
->pStartedFrom
.get())->ImplGetFloatingWindow();
524 // this window will be destroyed => store date locally...
526 sal_uInt16 nItem
= nHighlightedItem
;
528 pCleanUpFrom
->StopExecute();
530 if ( nItem
!= ITEMPOS_INVALID
&& pM
)
532 MenuItemData
* pItemData
= pM
->GetItemList()->GetDataFromPos( nItem
);
533 if ( pItemData
&& !pItemData
->bIsTemporary
)
535 pM
->nSelectedId
= pItemData
->nId
;
536 pM
->sSelectedIdent
= pItemData
->sIdent
;
539 pStart
->nSelectedId
= pItemData
->nId
;
540 pStart
->sSelectedIdent
= pItemData
->sIdent
;
548 void MenuFloatingWindow::EndExecute( sal_uInt16 nId
)
551 if ( pMenu
&& pMenu
->GetItemList()->GetData( nId
, nPos
) )
552 nHighlightedItem
= nPos
;
554 nHighlightedItem
= ITEMPOS_INVALID
;
559 void MenuFloatingWindow::MouseButtonDown( const MouseEvent
& rMEvt
)
561 // TH creates a ToTop on this window, but the active popup
562 // should stay on top...
563 // due to focus change this would close all menus -> don't do it (#94123)
564 //if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup )
565 // pActivePopup->ImplGetFloatingWindow()->ToTop( ToTopFlags::NoGrabFocus );
567 ImplHighlightItem( rMEvt
, true );
569 nMBDownPos
= nHighlightedItem
;
572 void MenuFloatingWindow::MouseButtonUp( const MouseEvent
& rMEvt
)
574 MenuItemData
* pData
= pMenu
? pMenu
->GetItemList()->GetDataFromPos( nHighlightedItem
) : nullptr;
575 // nMBDownPos store in local variable and reset immediately,
576 // as it will be too late after EndExecute
577 sal_uInt16 _nMBDownPos
= nMBDownPos
;
578 nMBDownPos
= ITEMPOS_INVALID
;
579 if ( pData
&& pData
->bEnabled
&& ( pData
->eType
!= MenuItemType::SEPARATOR
) )
581 if ( !pData
->pSubMenu
)
585 else if ( ( pData
->nBits
& MenuItemBits::POPUPSELECT
) && ( nHighlightedItem
== _nMBDownPos
) && ( rMEvt
.GetClicks() == 2 ) )
587 // not when clicked over the arrow...
588 Size aSz
= GetOutputSizePixel();
589 long nFontHeight
= GetTextHeight();
590 if ( rMEvt
.GetPosPixel().X() < ( aSz
.Width() - nFontHeight
- nFontHeight
/4 ) )
597 void MenuFloatingWindow::MouseMove( const MouseEvent
& rMEvt
)
599 if ( !IsVisible() || rMEvt
.IsSynthetic() || rMEvt
.IsEnterWindow() )
602 if ( rMEvt
.IsLeaveWindow() )
604 // #102461# do not remove highlight if a popup menu is open at this position
605 MenuItemData
* pData
= pMenu
? pMenu
->pItemList
->GetDataFromPos( nHighlightedItem
) : nullptr;
606 // close popup with some delayed if we leave somewhere else
607 if( pActivePopup
&& pData
&& pData
->pSubMenu
!= pActivePopup
)
608 pActivePopup
->ImplGetFloatingWindow()->aSubmenuCloseTimer
.Start();
610 if( !pActivePopup
|| (pData
&& pData
->pSubMenu
!= pActivePopup
) )
611 ChangeHighlightItem( ITEMPOS_INVALID
, false );
613 if ( IsScrollMenu() )
614 ImplScroll( rMEvt
.GetPosPixel() );
618 aSubmenuCloseTimer
.Stop();
619 if( bIgnoreFirstMove
)
620 bIgnoreFirstMove
= false;
622 ImplHighlightItem( rMEvt
, false );
626 void MenuFloatingWindow::ImplScroll( bool bUp
)
636 pMenu
->ImplKillLayoutData();
638 if ( bScrollUp
&& bUp
)
640 nFirstEntry
= pMenu
->ImplGetPrevVisible( nFirstEntry
);
641 SAL_WARN_IF( nFirstEntry
== ITEMPOS_INVALID
, "vcl", "Scroll?!" );
643 // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
644 const auto pItemData
= pMenu
->GetItemList()->GetDataFromPos( nFirstEntry
);
647 long nScrollEntryHeight
= pItemData
->aSz
.Height();
655 if ( pMenu
->ImplGetPrevVisible( nFirstEntry
) == ITEMPOS_INVALID
)
661 Scroll( 0, nScrollEntryHeight
, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip
);
664 else if ( bScrollDown
&& !bUp
)
666 // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
667 const auto pItemData
= pMenu
->GetItemList()->GetDataFromPos( nFirstEntry
);
670 long nScrollEntryHeight
= pItemData
->aSz
.Height();
672 nFirstEntry
= pMenu
->ImplGetNextVisible( nFirstEntry
);
673 SAL_WARN_IF( nFirstEntry
== ITEMPOS_INVALID
, "vcl", "Scroll?!" );
681 long nHeight
= GetOutputSizePixel().Height();
682 sal_uInt16 nLastVisible
;
683 static_cast<PopupMenu
*>(pMenu
.get())->ImplCalcVisEntries( nHeight
, nFirstEntry
, &nLastVisible
);
684 if ( pMenu
->ImplGetNextVisible( nLastVisible
) == ITEMPOS_INVALID
)
690 Scroll( 0, -nScrollEntryHeight
, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip
);
697 void MenuFloatingWindow::ImplScroll( const Point
& rMousePos
)
699 Size aOutSz
= GetOutputSizePixel();
701 long nY
= nScrollerHeight
;
702 long nMouseY
= rMousePos
.Y();
705 if ( bScrollUp
&& ( nMouseY
< nY
) )
708 nDelta
= nY
- nMouseY
;
710 else if ( bScrollDown
&& ( nMouseY
> ( aOutSz
.Height() - nY
) ) )
713 nDelta
= nMouseY
- ( aOutSz
.Height() - nY
);
718 aScrollTimer
.Stop(); // if scrolled through MouseMove.
722 else if ( nDelta
< 5 )
724 else if ( nDelta
< 8 )
726 else if ( nDelta
< 12 )
730 aScrollTimer
.SetTimeout( nTimeout
);
731 aScrollTimer
.Start();
734 void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n
, bool bStartPopupTimer
)
736 // #57934# if necessary, immediately close the active, as TH's backgroundstorage works.
737 // #65750# we prefer to refrain from the background storage of small lines.
738 // otherwise the menus are difficult to operate.
739 // MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
740 // if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) )
741 // KillActivePopup();
743 aSubmenuCloseTimer
.Stop();
747 if ( nHighlightedItem
!= ITEMPOS_INVALID
)
749 InvalidateItem(nHighlightedItem
);
750 pMenu
->ImplCallEventListeners( VclEventId::MenuDehighlight
, nHighlightedItem
);
753 nHighlightedItem
= n
;
754 SAL_WARN_IF( !pMenu
->ImplIsVisible( nHighlightedItem
) && nHighlightedItem
!= ITEMPOS_INVALID
, "vcl", "ChangeHighlightItem: Not visible!" );
755 if( nHighlightedItem
!= ITEMPOS_INVALID
)
757 if (pMenu
->pStartedFrom
&& !pMenu
->pStartedFrom
->IsMenuBar())
759 // #102461# make sure parent entry is highlighted as well
760 size_t i
, nCount
= pMenu
->pStartedFrom
->pItemList
->size();
761 for(i
= 0; i
< nCount
; i
++)
763 MenuItemData
* pData
= pMenu
->pStartedFrom
->pItemList
->GetDataFromPos( i
);
764 if( pData
&& ( pData
->pSubMenu
== pMenu
) )
769 MenuFloatingWindow
* pPWin
= static_cast<MenuFloatingWindow
*>(pMenu
->pStartedFrom
->ImplGetWindow());
770 if( pPWin
&& pPWin
->nHighlightedItem
!= i
)
772 pPWin
->InvalidateItem(i
);
773 pPWin
->nHighlightedItem
= i
;
777 InvalidateItem(nHighlightedItem
);
778 pMenu
->ImplCallHighlight( nHighlightedItem
);
782 pMenu
->nSelectedId
= 0;
783 pMenu
->sSelectedIdent
.clear();
786 if ( bStartPopupTimer
)
788 // #102438# Menu items are not selectable
789 // If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue
790 // or XAccessibleSelection interface, and the parent popup menus are not executed yet,
791 // the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected.
792 if ( GetSettings().GetMouseSettings().GetMenuDelay() )
793 aHighlightChangedTimer
.Start();
795 HighlightChanged( &aHighlightChangedTimer
);
799 /// Calculate the initial vertical pixel offset of the first item.
800 /// May be negative for scrolled windows.
801 long MenuFloatingWindow::GetInitialItemY(long *pStartY
) const
803 long nStartY
= ImplGetStartY();
806 return nScrollerHeight
+ nStartY
+
807 ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
;
810 /// Emit an Invalidate just for this item's area
811 void MenuFloatingWindow::InvalidateItem(sal_uInt16 nPos
)
816 long nY
= GetInitialItemY();
817 size_t nCount
= pMenu
->pItemList
->size();
818 for (size_t n
= 0; n
< nCount
; n
++)
820 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
821 long nHeight
= pData
->aSz
.Height();
824 Size
aWidth( GetSizePixel() );
825 tools::Rectangle
aRect(Point(0, nY
), Size(aWidth
.Width(), nHeight
));
832 void MenuFloatingWindow::RenderHighlightItem(vcl::RenderContext
& rRenderContext
, sal_uInt16 nPos
)
837 Size
aSz(GetOutputSizePixel());
841 long nY
= GetInitialItemY(&nStartY
);
843 int nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
845 size_t nCount
= pMenu
->pItemList
->size();
846 for (size_t n
= 0; n
< nCount
; n
++)
848 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
851 SAL_WARN_IF(!pMenu
->ImplIsVisible(n
), "vcl", "Highlight: Item not visible!");
852 if (pData
->eType
!= MenuItemType::SEPARATOR
)
854 bool bRestoreLineColor
= false;
856 bool bDrawItemRect
= true;
858 tools::Rectangle
aItemRect(Point(nX
+ nOuterSpaceX
, nY
), Size(aSz
.Width() - 2 * nOuterSpaceX
, pData
->aSz
.Height()));
859 if (pData
->nBits
& MenuItemBits::POPUPSELECT
)
861 long nFontHeight
= GetTextHeight();
862 aItemRect
.AdjustRight( -(nFontHeight
+ nFontHeight
/ 4) );
865 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
867 Size
aPxSize(GetOutputSizePixel());
868 rRenderContext
.Push(PushFlags::CLIPREGION
);
869 rRenderContext
.IntersectClipRegion(tools::Rectangle(Point(nX
, nY
), Size(aSz
.Width(), pData
->aSz
.Height())));
870 tools::Rectangle
aCtrlRect(Point(nX
, 0), Size(aPxSize
.Width()-nX
, aPxSize
.Height()));
871 MenupopupValue
aVal(pMenu
->nTextPos
-GUTTERBORDER
, aItemRect
);
872 rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::Entire
,
873 aCtrlRect
, ControlState::ENABLED
, aVal
, OUString());
874 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItem
))
876 bDrawItemRect
= false;
877 if (!rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::MenuItem
, aItemRect
,
878 ControlState::SELECTED
| (pData
->bEnabled
879 ? ControlState::ENABLED
880 : ControlState::NONE
),
883 bDrawItemRect
= true;
887 bDrawItemRect
= true;
888 rRenderContext
.Pop();
893 rRenderContext
.SetFillColor(rRenderContext
.GetSettings().GetStyleSettings().GetMenuHighlightColor());
896 rRenderContext
.SetFillColor();
897 oldLineColor
= rRenderContext
.GetLineColor();
898 rRenderContext
.SetLineColor(rRenderContext
.GetSettings().GetStyleSettings().GetMenuHighlightColor());
899 bRestoreLineColor
= true;
902 rRenderContext
.DrawRect(aItemRect
);
904 pMenu
->ImplPaint(rRenderContext
, GetOutputSizePixel(), nScrollerHeight
, nStartY
, pData
, true/*bHighlight*/);
905 if (bRestoreLineColor
)
906 rRenderContext
.SetLineColor(oldLineColor
);
911 nY
+= pData
->aSz
.Height();
915 tools::Rectangle
MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos
)
918 return tools::Rectangle();
920 tools::Rectangle aRect
;
921 Size aSz
= GetOutputSizePixel();
922 long nStartY
= ImplGetStartY();
923 long nY
= nScrollerHeight
+nStartY
;
925 size_t nCount
= pMenu
->pItemList
->size();
926 for ( size_t n
= 0; n
< nCount
; n
++ )
928 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
931 SAL_WARN_IF( !pMenu
->ImplIsVisible( n
), "vcl", "ImplGetItemRect: Item not visible!" );
932 if ( pData
->eType
!= MenuItemType::SEPARATOR
)
934 aRect
= tools::Rectangle( Point( 0, nY
), Size( aSz
.Width(), pData
->aSz
.Height() ) );
935 if ( pData
->nBits
& MenuItemBits::POPUPSELECT
)
937 long nFontHeight
= GetTextHeight();
938 aRect
.AdjustRight( -(nFontHeight
+ nFontHeight
/4) );
943 nY
+= pData
->aSz
.Height();
948 void MenuFloatingWindow::ImplCursorUpDown( bool bUp
, bool bHomeEnd
)
953 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
955 sal_uInt16 n
= nHighlightedItem
;
956 if ( n
== ITEMPOS_INVALID
)
961 n
= pMenu
->GetItemCount()-1;
964 sal_uInt16 nLoop
= n
;
968 // absolute positioning
971 n
= pMenu
->GetItemCount();
988 if ( !IsScrollMenu() || ( nHighlightedItem
== ITEMPOS_INVALID
) )
989 n
= pMenu
->GetItemCount()-1;
996 if ( n
>= pMenu
->GetItemCount() )
998 if ( !IsScrollMenu() || ( nHighlightedItem
== ITEMPOS_INVALID
) )
1005 MenuItemData
* pData
= pMenu
->GetItemList()->GetDataFromPos( n
);
1006 if ( ( pData
->bEnabled
|| !rSettings
.GetSkipDisabledInMenus() )
1007 && ( pData
->eType
!= MenuItemType::SEPARATOR
) && pMenu
->ImplIsVisible( n
) && pMenu
->ImplIsSelectable( n
) )
1009 // Is selection in visible area?
1010 if ( IsScrollMenu() )
1012 ChangeHighlightItem( ITEMPOS_INVALID
, false );
1014 while ( n
< nFirstEntry
)
1017 Size aOutSz
= GetOutputSizePixel();
1018 sal_uInt16 nLastVisible
;
1019 static_cast<PopupMenu
*>(pMenu
.get())->ImplCalcVisEntries( aOutSz
.Height(), nFirstEntry
, &nLastVisible
);
1020 while ( n
> nLastVisible
)
1022 ImplScroll( false );
1023 static_cast<PopupMenu
*>(pMenu
.get())->ImplCalcVisEntries( aOutSz
.Height(), nFirstEntry
, &nLastVisible
);
1026 ChangeHighlightItem( n
, false );
1029 } while ( n
!= nLoop
);
1032 void MenuFloatingWindow::KeyInput( const KeyEvent
& rKEvent
)
1034 VclPtr
<vcl::Window
> xWindow
= this;
1036 bool autoacc
= ImplGetSVData()->maNWFData
.mbAutoAccel
;
1037 sal_uInt16 nCode
= rKEvent
.GetKeyCode().GetCode();
1044 ImplCursorUpDown( nCode
== KEY_UP
);
1050 ImplCursorUpDown( nCode
== KEY_END
, true );
1056 // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document
1057 if( nCode
== KEY_F6
&& !rKEvent
.GetKeyCode().IsMod1() )
1061 if ( !pMenu
->pStartedFrom
)
1066 else if (pMenu
->pStartedFrom
->IsMenuBar())
1068 pMenu
->pStartedFrom
->MenuBarKeyInput(rKEvent
);
1073 PopupMenu
* pPopupMenu
= static_cast<PopupMenu
*>(pMenu
->pStartedFrom
.get());
1074 MenuFloatingWindow
* pFloat
= pPopupMenu
->ImplGetFloatingWindow();
1075 pFloat
->GrabFocus();
1076 pFloat
->KillActivePopup();
1077 pPopupMenu
->ImplCallHighlight(pFloat
->nHighlightedItem
);
1084 if ( pMenu
&& pMenu
->pStartedFrom
)
1087 if (pMenu
->pStartedFrom
->IsMenuBar())
1089 pMenu
->pStartedFrom
->MenuBarKeyInput(rKEvent
);
1093 MenuFloatingWindow
* pFloat
= static_cast<PopupMenu
*>(pMenu
->pStartedFrom
.get())->ImplGetFloatingWindow();
1094 pFloat
->GrabFocus();
1095 pFloat
->KillActivePopup();
1096 sal_uInt16 highlightItem
= pFloat
->GetHighlightedItem();
1097 pFloat
->ChangeHighlightItem(highlightItem
, false);
1107 if ( nHighlightedItem
!= ITEMPOS_INVALID
)
1109 MenuItemData
* pData
= pMenu
->GetItemList()->GetDataFromPos( nHighlightedItem
);
1110 if ( pData
&& pData
->pSubMenu
)
1112 HighlightChanged( nullptr );
1118 Menu
* pStart
= pMenu
->ImplGetStartMenu();
1119 if (pStart
&& pStart
->IsMenuBar())
1122 pStart
->ImplGetWindow()->KeyInput( rKEvent
);
1132 MenuItemData
* pData
= pMenu
->GetItemList()->GetDataFromPos( nHighlightedItem
);
1133 if ( pData
&& pData
->bEnabled
)
1135 if ( pData
->pSubMenu
)
1136 HighlightChanged( nullptr );
1149 Menu
* pStart
= pMenu
->ImplGetStartMenu();
1150 if (pStart
&& pStart
->IsMenuBar())
1153 pStart
->ImplGetWindow()->KeyInput( rKEvent
);
1160 sal_Unicode nCharCode
= rKEvent
.GetCharCode();
1162 size_t nDuplicates
= 0;
1163 MenuItemData
* pData
= (nCharCode
&& pMenu
) ?
1164 pMenu
->GetItemList()->SearchItem(nCharCode
, rKEvent
.GetKeyCode(), nPos
, nDuplicates
, nHighlightedItem
) : nullptr;
1167 if ( pData
->pSubMenu
|| nDuplicates
> 1 )
1169 ChangeHighlightItem( nPos
, false );
1170 HighlightChanged( nullptr );
1174 nHighlightedItem
= nPos
;
1179 FloatingWindow::KeyInput( rKEvent
);
1183 if (pMenu
&& pMenu
->pStartedFrom
&& pMenu
->pStartedFrom
->IsMenuBar())
1185 MenuBar
*pMenuBar
= static_cast<MenuBar
*>(pMenu
->pStartedFrom
.get());
1186 const bool bShowAccels
= nCode
!= KEY_ESCAPE
;
1187 if (pMenuBar
->getMenuBarWindow()->GetMBWMenuKey() != bShowAccels
)
1189 pMenuBar
->getMenuBarWindow()->SetMBWMenuKey(bShowAccels
);
1190 pMenuBar
->getMenuBarWindow()->SetMBWHideAccel(!bShowAccels
);
1192 Invalidate(InvalidateFlags::Update
);
1196 // #105474# check if menu window was not destroyed
1197 if ( !xWindow
->IsDisposed() )
1203 void MenuFloatingWindow::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
&rPaintRect
)
1208 // Make sure that all actual rendering happens in one go to avoid flicker.
1209 vcl::BufferDevice
pBuffer(this, rRenderContext
);
1211 pBuffer
->Push(PushFlags::CLIPREGION
);
1212 pBuffer
->SetClipRegion(vcl::Region(rPaintRect
));
1214 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
1216 pBuffer
->SetClipRegion();
1218 Size
aPxSize(GetOutputSizePixel());
1219 aPxSize
.AdjustWidth( -nX
);
1220 ImplControlValue
aVal(pMenu
->nTextPos
- GUTTERBORDER
);
1221 pBuffer
->DrawNativeControl(ControlType::MenuPopup
, ControlPart::Entire
,
1222 tools::Rectangle(Point(nX
, 0), aPxSize
), ControlState::ENABLED
,
1224 InitMenuClipRegion(*pBuffer
);
1228 ImplDrawScroller(*pBuffer
, true);
1229 ImplDrawScroller(*pBuffer
, false);
1231 pBuffer
->SetFillColor(rRenderContext
.GetSettings().GetStyleSettings().GetMenuColor());
1232 pMenu
->ImplPaint(*pBuffer
, GetOutputSizePixel(), nScrollerHeight
, ImplGetStartY());
1233 if (nHighlightedItem
!= ITEMPOS_INVALID
)
1234 RenderHighlightItem(*pBuffer
, nHighlightedItem
);
1239 void MenuFloatingWindow::ImplDrawScroller(vcl::RenderContext
& rRenderContext
, bool bUp
)
1244 rRenderContext
.SetClipRegion();
1246 Size
aOutSz(GetOutputSizePixel());
1247 long nY
= bUp
? 0 : (aOutSz
.Height() - nScrollerHeight
);
1249 tools::Rectangle
aRect(Point(nX
, nY
), Size(aOutSz
.Width() - nX
, nScrollerHeight
));
1251 DecorationView
aDecoView(&rRenderContext
);
1252 SymbolType eSymbol
= bUp
? SymbolType::SPIN_UP
: SymbolType::SPIN_DOWN
;
1254 DrawSymbolFlags nStyle
= DrawSymbolFlags::NONE
;
1255 if ((bUp
&& !bScrollUp
) || (!bUp
&& !bScrollDown
))
1256 nStyle
|= DrawSymbolFlags::Disable
;
1258 aDecoView
.DrawSymbol(aRect
, eSymbol
, rRenderContext
.GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle
);
1260 InitMenuClipRegion(rRenderContext
);
1263 void MenuFloatingWindow::RequestHelp( const HelpEvent
& rHEvt
)
1265 sal_uInt16 nId
= nHighlightedItem
;
1267 vcl::Window
* pW
= this;
1269 // #102618# Get item rect before destroying the window in EndExecute() call
1270 tools::Rectangle
aHighlightRect( ImplGetItemRect( nHighlightedItem
) );
1272 if ( rHEvt
.GetMode() & HelpEventMode::CONTEXT
)
1274 nHighlightedItem
= ITEMPOS_INVALID
;
1279 if( !ImplHandleHelpEvent( pW
, pM
, nId
, rHEvt
, aHighlightRect
) )
1280 Window::RequestHelp( rHEvt
);
1283 void MenuFloatingWindow::StateChanged( StateChangedType nType
)
1285 FloatingWindow::StateChanged( nType
);
1287 if ( ( nType
== StateChangedType::ControlForeground
) || ( nType
== StateChangedType::ControlBackground
) )
1289 ApplySettings(*this);
1294 void MenuFloatingWindow::DataChanged( const DataChangedEvent
& rDCEvt
)
1296 FloatingWindow::DataChanged( rDCEvt
);
1298 if ( (rDCEvt
.GetType() == DataChangedEventType::FONTS
) ||
1299 (rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
) ||
1300 ((rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
1301 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
)) )
1303 ApplySettings(*this);
1308 void MenuFloatingWindow::Command( const CommandEvent
& rCEvt
)
1310 if ( rCEvt
.GetCommand() == CommandEventId::Wheel
)
1312 const CommandWheelData
* pData
= rCEvt
.GetWheelData();
1313 if( !pData
->GetModifier() && ( pData
->GetMode() == CommandWheelMode::SCROLL
) )
1315 ImplScroll( pData
->GetDelta() > 0 );
1316 MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
1321 css::uno::Reference
<css::accessibility::XAccessible
> MenuFloatingWindow::CreateAccessible()
1323 css::uno::Reference
<css::accessibility::XAccessible
> xAcc
;
1325 if (pMenu
&& !pMenu
->pStartedFrom
)
1326 xAcc
= pMenu
->GetAccessible();
1331 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */