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"
25 #include <vcl/decoview.hxx>
26 #include <vcl/settings.hxx>
29 MenuFloatingWindow::MenuFloatingWindow( Menu
* pMen
, vcl::Window
* pParent
, WinBits nStyle
) :
30 FloatingWindow( pParent
, nStyle
)
32 mpWindowImpl
->mbMenuFloatingWindow
= true;
34 pActivePopup
= nullptr;
37 nHighlightedItem
= ITEMPOS_INVALID
;
38 nMBDownPos
= ITEMPOS_INVALID
;
39 nPosInParent
= ITEMPOS_INVALID
;
41 nBorder
= EXTRASPACEY
;
45 bIgnoreFirstMove
= true;
50 SetPopupModeEndHdl( LINK( this, MenuFloatingWindow
, PopupEnd
) );
52 aHighlightChangedTimer
.SetTimeoutHdl( LINK( this, MenuFloatingWindow
, HighlightChanged
) );
53 aHighlightChangedTimer
.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
54 aHighlightChangedTimer
.SetDebugName( "vcl::MenuFloatingWindow aHighlightChangedTimer" );
56 aSubmenuCloseTimer
.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
57 aSubmenuCloseTimer
.SetTimeoutHdl( LINK( this, MenuFloatingWindow
, SubmenuClose
) );
58 aSubmenuCloseTimer
.SetDebugName( "vcl::MenuFloatingWindow aSubmenuCloseTimer" );
60 aScrollTimer
.SetTimeoutHdl( LINK( this, MenuFloatingWindow
, AutoScroll
) );
61 aScrollTimer
.SetDebugName( "vcl::MenuFloatingWindow aScrollTimer" );
63 AddEventListener( LINK( this, MenuFloatingWindow
, ShowHideListener
) );
66 void MenuFloatingWindow::doShutdown()
70 // #105373# notify toolkit that highlight was removed
71 // otherwise the entry will not be read when the menu is opened again
72 if( nHighlightedItem
!= ITEMPOS_INVALID
)
73 pMenu
->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT
, nHighlightedItem
);
74 if (!bKeyInput
&& pMenu
&& pMenu
->pStartedFrom
&& !pMenu
->pStartedFrom
->IsMenuBar())
76 // #102461# remove highlight in parent
77 size_t i
, nCount
= pMenu
->pStartedFrom
->pItemList
->size();
78 for(i
= 0; i
< nCount
; i
++)
80 MenuItemData
* pData
= pMenu
->pStartedFrom
->pItemList
->GetDataFromPos( i
);
81 if( pData
&& ( pData
->pSubMenu
== pMenu
) )
86 MenuFloatingWindow
* pPWin
= static_cast<MenuFloatingWindow
*>(pMenu
->pStartedFrom
->ImplGetWindow());
88 pPWin
->InvalidateItem(i
);
92 // free the reference to the accessible component
93 SetAccessible( css::uno::Reference
< css::accessibility::XAccessible
>() );
95 aHighlightChangedTimer
.Stop();
97 // #95056# invalidate screen area covered by system window
98 // so this can be taken into account if the commandhandler performs a scroll operation
101 Rectangle
aInvRect( GetWindowExtentsRelative( GetParent() ) );
102 GetParent()->Invalidate( aInvRect
);
105 RemoveEventListener( LINK( this, MenuFloatingWindow
, ShowHideListener
) );
108 aSubmenuCloseTimer
.Stop();
109 aSubmenuCloseTimer
.Stop();
110 aHighlightChangedTimer
.Stop();
111 aHighlightChangedTimer
.Stop();
115 MenuFloatingWindow::~MenuFloatingWindow()
120 void MenuFloatingWindow::dispose()
124 pActivePopup
.clear();
125 xSaveFocusId
.clear();
126 FloatingWindow::dispose();
129 void MenuFloatingWindow::Resize()
131 InitMenuClipRegion(*this); // FIXME
134 void MenuFloatingWindow::ApplySettings(vcl::RenderContext
& rRenderContext
)
136 FloatingWindow::ApplySettings(rRenderContext
);
138 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
140 SetPointFont(rRenderContext
, rStyleSettings
.GetMenuFont());
142 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
144 rRenderContext
.SetBackground(); // background will be drawn by NWF
147 rRenderContext
.SetBackground(Wallpaper(rStyleSettings
.GetMenuColor()));
149 rRenderContext
.SetTextColor(rStyleSettings
.GetMenuTextColor());
150 rRenderContext
.SetTextFillColor();
151 rRenderContext
.SetLineColor();
154 /// Get a negative pixel offset for an offset menu
155 long MenuFloatingWindow::ImplGetStartY() const
160 // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
161 if ( nFirstEntry
> 0 && !pMenu
->GetItemList()->GetDataFromPos(nFirstEntry
- 1) )
166 for ( sal_uInt16 n
= 0; n
< nFirstEntry
; n
++ )
167 nY
+= pMenu
->GetItemList()->GetDataFromPos( n
)->aSz
.Height();
168 nY
-= pMenu
->GetTitleHeight();
173 vcl::Region
MenuFloatingWindow::ImplCalcClipRegion( bool bIncludeLogo
) const
175 Size aOutSz
= GetOutputSizePixel();
177 Rectangle
aRect( aPos
, aOutSz
);
178 aRect
.Top() += nScrollerHeight
;
179 aRect
.Bottom() -= nScrollerHeight
;
181 if ( pMenu
&& pMenu
->pLogo
&& !bIncludeLogo
)
182 aRect
.Left() += pMenu
->pLogo
->aBitmap
.GetSizePixel().Width();
184 vcl::Region
aRegion(aRect
);
185 if ( pMenu
&& pMenu
->pLogo
&& bIncludeLogo
&& nScrollerHeight
)
186 aRegion
.Union( Rectangle( Point(), Size( pMenu
->pLogo
->aBitmap
.GetSizePixel().Width(), aOutSz
.Height() ) ) );
191 void MenuFloatingWindow::InitMenuClipRegion(vcl::RenderContext
& rRenderContext
)
195 rRenderContext
.SetClipRegion(ImplCalcClipRegion());
199 rRenderContext
.SetClipRegion();
203 void MenuFloatingWindow::ImplHighlightItem( const MouseEvent
& rMEvt
, bool bMBDown
)
208 long nY
= GetInitialItemY();
209 long nMouseY
= rMEvt
.GetPosPixel().Y();
210 Size aOutSz
= GetOutputSizePixel();
211 if ( ( nMouseY
>= nY
) && ( nMouseY
< aOutSz
.Height() ) )
213 bool bHighlighted
= false;
214 size_t nCount
= pMenu
->pItemList
->size();
215 for ( size_t n
= 0; !bHighlighted
&& ( n
< nCount
); n
++ )
217 if ( pMenu
->ImplIsVisible( n
) )
219 MenuItemData
* pItemData
= pMenu
->pItemList
->GetDataFromPos( n
);
221 nY
+= pItemData
->aSz
.Height();
222 if ( ( nOldY
<= nMouseY
) && ( nY
> nMouseY
) && pMenu
->ImplIsSelectable( n
) )
224 bool bPopupArea
= true;
225 if ( pItemData
->nBits
& MenuItemBits::POPUPSELECT
)
227 // only when clicked over the arrow...
228 Size aSz
= GetOutputSizePixel();
229 long nFontHeight
= GetTextHeight();
230 bPopupArea
= ( rMEvt
.GetPosPixel().X() >= ( aSz
.Width() - nFontHeight
- nFontHeight
/4 ) );
235 if ( n
!= nHighlightedItem
)
237 ChangeHighlightItem( (sal_uInt16
)n
, false );
240 bool bAllowNewPopup
= true;
243 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
244 bAllowNewPopup
= pData
&& ( pData
->pSubMenu
!= pActivePopup
);
245 if ( bAllowNewPopup
)
249 if ( bPopupArea
&& bAllowNewPopup
)
251 HighlightChanged( nullptr );
256 if ( n
!= nHighlightedItem
)
258 ChangeHighlightItem( (sal_uInt16
)n
, true );
260 else if ( pItemData
->nBits
& MenuItemBits::POPUPSELECT
)
262 if ( bPopupArea
&& ( pActivePopup
!= pItemData
->pSubMenu
) )
263 HighlightChanged( nullptr );
271 ChangeHighlightItem( ITEMPOS_INVALID
, true );
275 ImplScroll( rMEvt
.GetPosPixel() );
276 ChangeHighlightItem( ITEMPOS_INVALID
, true );
280 IMPL_LINK_NOARG(MenuFloatingWindow
, PopupEnd
, FloatingWindow
*, void)
282 // "this" will be deleted before the end of this method!
289 //SAL_WARN_IF( pActivePopup->ImplGetWindow(), "vcl", "PopupEnd, obwohl pActivePopup MIT Window!" );
290 KillActivePopup(); // should be ok to just remove it
291 //pActivePopup->bCanceled = true;
293 pMenu
->bInCallback
= true;
295 pMenu
->bInCallback
= false;
299 if (pMenu
&& pMenu
->pStartedFrom
)
300 pMenu
->pStartedFrom
->ClosePopup(pMenu
);
304 pM
->pStartedFrom
= nullptr;
307 IMPL_LINK_NOARG(MenuFloatingWindow
, AutoScroll
, Timer
*, void)
309 ImplScroll( GetPointerPosPixel() );
312 IMPL_LINK( MenuFloatingWindow
, HighlightChanged
, Timer
*, pTimer
, void )
317 MenuItemData
* pItemData
= pMenu
->pItemList
->GetDataFromPos( nHighlightedItem
);
320 if ( pActivePopup
&& ( pActivePopup
!= pItemData
->pSubMenu
) )
322 FloatWinPopupFlags nOldFlags
= GetPopupModeFlags();
323 SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose
);
325 SetPopupModeFlags( nOldFlags
);
327 if ( pItemData
->bEnabled
&& pItemData
->pSubMenu
&& pItemData
->pSubMenu
->GetItemCount() && ( pItemData
->pSubMenu
!= pActivePopup
) )
329 pActivePopup
= static_cast<PopupMenu
*>(pItemData
->pSubMenu
.get());
330 long nY
= nScrollerHeight
+ImplGetStartY();
331 MenuItemData
* pData
= nullptr;
332 for ( sal_uLong n
= 0; n
< nHighlightedItem
; n
++ )
334 pData
= pMenu
->pItemList
->GetDataFromPos( n
);
335 nY
+= pData
->aSz
.Height();
337 pData
= pMenu
->pItemList
->GetDataFromPos( nHighlightedItem
);
338 Size MySize
= GetOutputSizePixel();
339 Point
aItemTopLeft( 0, nY
);
340 Point
aItemBottomRight( aItemTopLeft
);
341 aItemBottomRight
.X() += MySize
.Width();
342 aItemBottomRight
.Y() += pData
->aSz
.Height();
344 // shift the popups a little
345 aItemTopLeft
.X() += 2;
346 aItemBottomRight
.X() -= 2;
347 if ( nHighlightedItem
)
348 aItemTopLeft
.Y() -= 2;
351 sal_Int32 nL
, nT
, nR
, nB
;
352 GetBorder( nL
, nT
, nR
, nB
);
353 aItemTopLeft
.Y() -= nT
;
356 // pTest: crash due to Reschedule() in call of Activate()
357 // Also it is prevented that submenus are displayed which
358 // were for long in Activate Rescheduled and which should not be
360 Menu
* pTest
= pActivePopup
;
361 FloatWinPopupFlags nOldFlags
= GetPopupModeFlags();
362 SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose
);
363 sal_uInt16 nRet
= pActivePopup
->ImplExecute( this, Rectangle( aItemTopLeft
, aItemBottomRight
), FloatWinPopupFlags::Right
, pMenu
, pTimer
== nullptr );
364 SetPopupModeFlags( nOldFlags
);
366 // nRet != 0, wenn es waerend Activate() abgeschossen wurde...
367 if ( !nRet
&& ( pActivePopup
== pTest
) && pActivePopup
->ImplGetWindow() )
368 pActivePopup
->ImplGetFloatingWindow()->AddPopupModeWindow( this );
373 IMPL_LINK_NOARG(MenuFloatingWindow
, SubmenuClose
, Timer
*, void)
375 if( pMenu
&& pMenu
->pStartedFrom
)
377 MenuFloatingWindow
* pWin
= static_cast<MenuFloatingWindow
*>(pMenu
->pStartedFrom
->GetWindow());
379 pWin
->KillActivePopup();
383 IMPL_LINK( MenuFloatingWindow
, ShowHideListener
, VclWindowEvent
&, rEvent
, void )
388 if( rEvent
.GetId() == VCLEVENT_WINDOW_SHOW
)
389 pMenu
->ImplCallEventListeners( VCLEVENT_MENU_SHOW
, ITEMPOS_INVALID
);
390 else if( rEvent
.GetId() == VCLEVENT_WINDOW_HIDE
)
391 pMenu
->ImplCallEventListeners( VCLEVENT_MENU_HIDE
, ITEMPOS_INVALID
);
394 void MenuFloatingWindow::EnableScrollMenu( bool b
)
397 nScrollerHeight
= b
? (sal_uInt16
) GetSettings().GetStyleSettings().GetScrollBarSize() /2 : 0;
399 InitMenuClipRegion(*this);
402 void MenuFloatingWindow::Start()
408 GetParent()->ImplIncModalCount();
411 void MenuFloatingWindow::End()
416 if (GetParent() && !GetParent()->IsDisposed())
417 GetParent()->ImplDecModalCount();
419 // restore focus to previous window if we still have the focus
420 VclPtr
<vcl::Window
> xFocusId(xSaveFocusId
);
421 xSaveFocusId
= nullptr;
422 if (HasChildPathFocus() && xFocusId
!= nullptr)
424 ImplGetSVData()->maWinData
.mbNoDeactivate
= false;
425 Window::EndSaveFocus(xFocusId
);
431 void MenuFloatingWindow::Execute()
433 ImplSVData
* pSVData
= ImplGetSVData();
435 pSVData
->maAppData
.mpActivePopupMenu
= static_cast<PopupMenu
*>(pMenu
.get());
440 Application::Yield();
442 pSVData
->maAppData
.mpActivePopupMenu
= nullptr;
445 void MenuFloatingWindow::StopExecute()
449 ImplEndPopupMode(FloatWinPopupEndFlags::NONE
, xSaveFocusId
);
451 aHighlightChangedTimer
.Stop();
456 // notify parent, needed for accessibility
457 if( pMenu
&& pMenu
->pStartedFrom
)
458 pMenu
->pStartedFrom
->ImplCallEventListeners( VCLEVENT_MENU_SUBMENUDEACTIVATE
, nPosInParent
);
461 void MenuFloatingWindow::KillActivePopup( PopupMenu
* pThisOnly
)
463 if ( pActivePopup
&& ( !pThisOnly
|| ( pThisOnly
== pActivePopup
) ) )
465 if( pActivePopup
->pWindow
)
466 if( static_cast<FloatingWindow
*>(pActivePopup
->pWindow
.get())->IsInCleanUp() )
467 return; // kill it later
468 if ( pActivePopup
->bInCallback
)
469 pActivePopup
->bCanceled
= true;
471 // For all actions pActivePopup = 0, if e.g.
472 // PopupModeEndHdl the popups to destroy were called synchronous
473 PopupMenu
* pPopup
= pActivePopup
;
474 pActivePopup
= nullptr;
475 pPopup
->bInCallback
= true;
476 pPopup
->Deactivate();
477 pPopup
->bInCallback
= false;
478 if ( pPopup
->ImplGetWindow() )
480 pPopup
->ImplGetFloatingWindow()->StopExecute();
481 pPopup
->ImplGetFloatingWindow()->doShutdown();
482 pPopup
->pWindow
->doLazyDelete();
483 pPopup
->pWindow
= nullptr;
490 void MenuFloatingWindow::EndExecute()
492 Menu
* pStart
= pMenu
? pMenu
->ImplGetStartMenu() : nullptr;
494 // if started elsewhere, cleanup there as well
495 MenuFloatingWindow
* pCleanUpFrom
= this;
496 MenuFloatingWindow
* pWin
= this;
497 while (pWin
&& !pWin
->bInExecute
&&
498 pWin
->pMenu
->pStartedFrom
&& !pWin
->pMenu
->pStartedFrom
->IsMenuBar())
500 pWin
= static_cast<PopupMenu
*>(pWin
->pMenu
->pStartedFrom
.get())->ImplGetFloatingWindow();
505 // this window will be destroyed => store date locally...
507 sal_uInt16 nItem
= nHighlightedItem
;
509 pCleanUpFrom
->StopExecute();
511 if ( nItem
!= ITEMPOS_INVALID
&& pM
)
513 MenuItemData
* pItemData
= pM
->GetItemList()->GetDataFromPos( nItem
);
514 if ( pItemData
&& !pItemData
->bIsTemporary
)
516 pM
->nSelectedId
= pItemData
->nId
;
518 pStart
->nSelectedId
= pItemData
->nId
;
525 void MenuFloatingWindow::EndExecute( sal_uInt16 nId
)
528 if ( pMenu
&& pMenu
->GetItemList()->GetData( nId
, nPos
) )
529 nHighlightedItem
= nPos
;
531 nHighlightedItem
= ITEMPOS_INVALID
;
536 void MenuFloatingWindow::MouseButtonDown( const MouseEvent
& rMEvt
)
538 // TH creates a ToTop on this window, but the active popup
539 // should stay on top...
540 // due to focus change this would close all menus -> don't do it (#94123)
541 //if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup )
542 // pActivePopup->ImplGetFloatingWindow()->ToTop( ToTopFlags::NoGrabFocus );
544 ImplHighlightItem( rMEvt
, true );
546 nMBDownPos
= nHighlightedItem
;
549 void MenuFloatingWindow::MouseButtonUp( const MouseEvent
& rMEvt
)
551 MenuItemData
* pData
= pMenu
? pMenu
->GetItemList()->GetDataFromPos( nHighlightedItem
) : nullptr;
552 // nMBDownPos store in local variable and reset immediately,
553 // as it will be too late after EndExecute
554 sal_uInt16 _nMBDownPos
= nMBDownPos
;
555 nMBDownPos
= ITEMPOS_INVALID
;
556 if ( pData
&& pData
->bEnabled
&& ( pData
->eType
!= MenuItemType::SEPARATOR
) )
558 if ( !pData
->pSubMenu
)
562 else if ( ( pData
->nBits
& MenuItemBits::POPUPSELECT
) && ( nHighlightedItem
== _nMBDownPos
) && ( rMEvt
.GetClicks() == 2 ) )
564 // not when clicked over the arrow...
565 Size aSz
= GetOutputSizePixel();
566 long nFontHeight
= GetTextHeight();
567 if ( rMEvt
.GetPosPixel().X() < ( aSz
.Width() - nFontHeight
- nFontHeight
/4 ) )
574 void MenuFloatingWindow::MouseMove( const MouseEvent
& rMEvt
)
576 if ( !IsVisible() || rMEvt
.IsSynthetic() || rMEvt
.IsEnterWindow() )
579 if ( rMEvt
.IsLeaveWindow() )
581 // #102461# do not remove highlight if a popup menu is open at this position
582 MenuItemData
* pData
= pMenu
? pMenu
->pItemList
->GetDataFromPos( nHighlightedItem
) : nullptr;
583 // close popup with some delayed if we leave somewhere else
584 if( pActivePopup
&& pData
&& pData
->pSubMenu
!= pActivePopup
)
585 pActivePopup
->ImplGetFloatingWindow()->aSubmenuCloseTimer
.Start();
587 if( !pActivePopup
|| (pData
&& pData
->pSubMenu
!= pActivePopup
) )
588 ChangeHighlightItem( ITEMPOS_INVALID
, false );
590 if ( IsScrollMenu() )
591 ImplScroll( rMEvt
.GetPosPixel() );
595 aSubmenuCloseTimer
.Stop();
596 if( bIgnoreFirstMove
)
597 bIgnoreFirstMove
= false;
599 ImplHighlightItem( rMEvt
, false );
603 void MenuFloatingWindow::ImplScroll( bool bUp
)
613 pMenu
->ImplKillLayoutData();
615 if ( bScrollUp
&& bUp
)
617 nFirstEntry
= pMenu
->ImplGetPrevVisible( nFirstEntry
);
618 SAL_WARN_IF( nFirstEntry
== ITEMPOS_INVALID
, "vcl", "Scroll?!" );
620 // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
621 const auto pItemData
= pMenu
->GetItemList()->GetDataFromPos( nFirstEntry
);
624 long nScrollEntryHeight
= pItemData
->aSz
.Height();
632 if ( pMenu
->ImplGetPrevVisible( nFirstEntry
) == ITEMPOS_INVALID
)
638 Scroll( 0, nScrollEntryHeight
, ImplCalcClipRegion( false ).GetBoundRect(), ScrollFlags::Clip
);
641 else if ( bScrollDown
&& !bUp
)
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();
649 nFirstEntry
= pMenu
->ImplGetNextVisible( nFirstEntry
);
650 SAL_WARN_IF( nFirstEntry
== ITEMPOS_INVALID
, "vcl", "Scroll?!" );
658 long nHeight
= GetOutputSizePixel().Height();
659 sal_uInt16 nLastVisible
;
660 static_cast<PopupMenu
*>(pMenu
.get())->ImplCalcVisEntries( nHeight
, nFirstEntry
, &nLastVisible
);
661 if ( pMenu
->ImplGetNextVisible( nLastVisible
) == ITEMPOS_INVALID
)
667 Scroll( 0, -nScrollEntryHeight
, ImplCalcClipRegion( false ).GetBoundRect(), ScrollFlags::Clip
);
674 void MenuFloatingWindow::ImplScroll( const Point
& rMousePos
)
676 Size aOutSz
= GetOutputSizePixel();
678 long nY
= nScrollerHeight
;
679 long nMouseY
= rMousePos
.Y();
682 if ( bScrollUp
&& ( nMouseY
< nY
) )
685 nDelta
= nY
- nMouseY
;
687 else if ( bScrollDown
&& ( nMouseY
> ( aOutSz
.Height() - nY
) ) )
690 nDelta
= nMouseY
- ( aOutSz
.Height() - nY
);
695 aScrollTimer
.Stop(); // if scrolled through MouseMove.
699 else if ( nDelta
< 5 )
701 else if ( nDelta
< 8 )
703 else if ( nDelta
< 12 )
707 aScrollTimer
.SetTimeout( nTimeout
);
708 aScrollTimer
.Start();
711 void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n
, bool bStartPopupTimer
)
713 // #57934# if necessary, immediately close the active, as TH's backgroundstorage works.
714 // #65750# we prefer to refrain from the background storage of small lines.
715 // otherwise the menus are difficult to operate.
716 // MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
717 // if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) )
718 // KillActivePopup();
720 aSubmenuCloseTimer
.Stop();
724 if ( nHighlightedItem
!= ITEMPOS_INVALID
)
726 InvalidateItem(nHighlightedItem
);
727 pMenu
->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT
, nHighlightedItem
);
730 nHighlightedItem
= (sal_uInt16
)n
;
731 SAL_WARN_IF( !pMenu
->ImplIsVisible( nHighlightedItem
) && nHighlightedItem
!= ITEMPOS_INVALID
, "vcl", "ChangeHighlightItem: Not visible!" );
732 if( nHighlightedItem
!= ITEMPOS_INVALID
)
734 if (pMenu
->pStartedFrom
&& !pMenu
->pStartedFrom
->IsMenuBar())
736 // #102461# make sure parent entry is highlighted as well
737 size_t i
, nCount
= pMenu
->pStartedFrom
->pItemList
->size();
738 for(i
= 0; i
< nCount
; i
++)
740 MenuItemData
* pData
= pMenu
->pStartedFrom
->pItemList
->GetDataFromPos( i
);
741 if( pData
&& ( pData
->pSubMenu
== pMenu
) )
746 MenuFloatingWindow
* pPWin
= static_cast<MenuFloatingWindow
*>(pMenu
->pStartedFrom
->ImplGetWindow());
747 if( pPWin
&& pPWin
->nHighlightedItem
!= i
)
749 pPWin
->InvalidateItem(i
);
750 pPWin
->nHighlightedItem
= i
;
754 InvalidateItem(nHighlightedItem
);
755 pMenu
->ImplCallHighlight( nHighlightedItem
);
758 pMenu
->nSelectedId
= 0;
760 if ( bStartPopupTimer
)
762 // #102438# Menu items are not selectable
763 // If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue
764 // or XAccessibleSelection interface, and the parent popup menus are not executed yet,
765 // the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected.
766 if ( GetSettings().GetMouseSettings().GetMenuDelay() )
767 aHighlightChangedTimer
.Start();
769 HighlightChanged( &aHighlightChangedTimer
);
773 /// Calculate the initial vertical pixel offset of the first item.
774 /// May be negative for scrolled windows.
775 long MenuFloatingWindow::GetInitialItemY(long *pStartY
) const
777 long nStartY
= ImplGetStartY();
780 return nScrollerHeight
+ nStartY
+
781 ImplGetSVData()->maNWFData
.mnMenuFormatBorderY
;
784 /// Emit an Invalidate just for this item's area
785 void MenuFloatingWindow::InvalidateItem(sal_uInt16 nPos
)
790 long nY
= GetInitialItemY();
791 size_t nCount
= pMenu
->pItemList
->size();
792 for (size_t n
= 0; n
< nCount
; n
++)
794 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
795 long nHeight
= pData
->aSz
.Height();
798 Size
aWidth( GetSizePixel() );
799 Rectangle
aRect(Point(0, nY
), Size(aWidth
.Width(), nHeight
));
806 void MenuFloatingWindow::RenderHighlightItem(vcl::RenderContext
& rRenderContext
, sal_uInt16 nPos
)
811 Size
aSz(GetOutputSizePixel());
815 long nY
= GetInitialItemY(&nStartY
);
818 nX
= pMenu
->pLogo
->aBitmap
.GetSizePixel().Width();
820 int nOuterSpaceX
= ImplGetSVData()->maNWFData
.mnMenuFormatBorderX
;
822 size_t nCount
= pMenu
->pItemList
->size();
823 for (size_t n
= 0; n
< nCount
; n
++)
825 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
828 SAL_WARN_IF(!pMenu
->ImplIsVisible(n
), "vcl", "Highlight: Item not visible!");
829 if (pData
->eType
!= MenuItemType::SEPARATOR
)
831 bool bRestoreLineColor
= false;
833 bool bDrawItemRect
= true;
835 Rectangle
aItemRect(Point(nX
+ nOuterSpaceX
, nY
), Size(aSz
.Width() - 2 * nOuterSpaceX
, pData
->aSz
.Height()));
836 if (pData
->nBits
& MenuItemBits::POPUPSELECT
)
838 long nFontHeight
= GetTextHeight();
839 aItemRect
.Right() -= nFontHeight
+ nFontHeight
/ 4;
842 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
844 Size
aPxSize(GetOutputSizePixel());
845 rRenderContext
.Push(PushFlags::CLIPREGION
);
846 rRenderContext
.IntersectClipRegion(Rectangle(Point(nX
, nY
), Size(aSz
.Width(), pData
->aSz
.Height())));
847 Rectangle
aCtrlRect(Point(nX
, 0), Size(aPxSize
.Width()-nX
, aPxSize
.Height()));
848 MenupopupValue
aVal(pMenu
->nTextPos
-GUTTERBORDER
, aItemRect
);
849 rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::Entire
,
850 aCtrlRect
, ControlState::ENABLED
, aVal
, OUString());
851 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::MenuItem
))
853 bDrawItemRect
= false;
854 if (!rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::MenuItem
, aItemRect
,
855 ControlState::SELECTED
| (pData
->bEnabled
856 ? ControlState::ENABLED
857 : ControlState::NONE
),
860 bDrawItemRect
= true;
864 bDrawItemRect
= true;
865 rRenderContext
.Pop();
870 rRenderContext
.SetFillColor(rRenderContext
.GetSettings().GetStyleSettings().GetMenuHighlightColor());
873 rRenderContext
.SetFillColor();
874 oldLineColor
= rRenderContext
.GetLineColor();
875 rRenderContext
.SetLineColor(rRenderContext
.GetSettings().GetStyleSettings().GetMenuHighlightColor());
876 bRestoreLineColor
= true;
879 rRenderContext
.DrawRect(aItemRect
);
881 pMenu
->ImplPaint(rRenderContext
, nScrollerHeight
, nStartY
, pData
, true/*bHighlight*/);
882 if (bRestoreLineColor
)
883 rRenderContext
.SetLineColor(oldLineColor
);
888 nY
+= pData
->aSz
.Height();
892 Rectangle
MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos
)
898 Size aSz
= GetOutputSizePixel();
899 long nStartY
= ImplGetStartY();
900 long nY
= nScrollerHeight
+nStartY
;
904 nX
= pMenu
->pLogo
->aBitmap
.GetSizePixel().Width();
906 size_t nCount
= pMenu
->pItemList
->size();
907 for ( size_t n
= 0; n
< nCount
; n
++ )
909 MenuItemData
* pData
= pMenu
->pItemList
->GetDataFromPos( n
);
912 SAL_WARN_IF( !pMenu
->ImplIsVisible( n
), "vcl", "ImplGetItemRect: Item not visible!" );
913 if ( pData
->eType
!= MenuItemType::SEPARATOR
)
915 aRect
= Rectangle( Point( nX
, nY
), Size( aSz
.Width(), pData
->aSz
.Height() ) );
916 if ( pData
->nBits
& MenuItemBits::POPUPSELECT
)
918 long nFontHeight
= GetTextHeight();
919 aRect
.Right() -= nFontHeight
+ nFontHeight
/4;
924 nY
+= pData
->aSz
.Height();
929 void MenuFloatingWindow::ImplCursorUpDown( bool bUp
, bool bHomeEnd
)
934 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
936 sal_uInt16 n
= nHighlightedItem
;
937 if ( n
== ITEMPOS_INVALID
)
942 n
= pMenu
->GetItemCount()-1;
945 sal_uInt16 nLoop
= n
;
949 // absolute positioning
952 n
= pMenu
->GetItemCount();
969 if ( !IsScrollMenu() || ( nHighlightedItem
== ITEMPOS_INVALID
) )
970 n
= pMenu
->GetItemCount()-1;
977 if ( n
>= pMenu
->GetItemCount() )
979 if ( !IsScrollMenu() || ( nHighlightedItem
== ITEMPOS_INVALID
) )
986 MenuItemData
* pData
= pMenu
->GetItemList()->GetDataFromPos( n
);
987 if ( ( pData
->bEnabled
|| !rSettings
.GetSkipDisabledInMenus() )
988 && ( pData
->eType
!= MenuItemType::SEPARATOR
) && pMenu
->ImplIsVisible( n
) && pMenu
->ImplIsSelectable( n
) )
990 // Is selection in visible area?
991 if ( IsScrollMenu() )
993 ChangeHighlightItem( ITEMPOS_INVALID
, false );
995 while ( n
< nFirstEntry
)
998 Size aOutSz
= GetOutputSizePixel();
999 sal_uInt16 nLastVisible
;
1000 static_cast<PopupMenu
*>(pMenu
.get())->ImplCalcVisEntries( aOutSz
.Height(), nFirstEntry
, &nLastVisible
);
1001 while ( n
> nLastVisible
)
1003 ImplScroll( false );
1004 static_cast<PopupMenu
*>(pMenu
.get())->ImplCalcVisEntries( aOutSz
.Height(), nFirstEntry
, &nLastVisible
);
1007 ChangeHighlightItem( n
, false );
1010 } while ( n
!= nLoop
);
1013 void MenuFloatingWindow::KeyInput( const KeyEvent
& rKEvent
)
1015 VclPtr
<vcl::Window
> xWindow
= this;
1017 bool accel
= ImplGetSVData()->maNWFData
.mbEnableAccel
;
1018 bool autoacc
= ImplGetSVData()->maNWFData
.mbAutoAccel
;
1019 sal_uInt16 nCode
= rKEvent
.GetKeyCode().GetCode();
1026 ImplCursorUpDown( nCode
== KEY_UP
);
1032 ImplCursorUpDown( nCode
== KEY_END
, true );
1038 // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document
1039 if( nCode
== KEY_F6
&& !rKEvent
.GetKeyCode().IsMod1() )
1043 if ( !pMenu
->pStartedFrom
)
1048 else if (pMenu
->pStartedFrom
->IsMenuBar())
1050 pMenu
->pStartedFrom
->MenuBarKeyInput(rKEvent
);
1055 PopupMenu
* pPopupMenu
= static_cast<PopupMenu
*>(pMenu
->pStartedFrom
.get());
1056 MenuFloatingWindow
* pFloat
= pPopupMenu
->ImplGetFloatingWindow();
1057 pFloat
->GrabFocus();
1058 pFloat
->KillActivePopup();
1059 pPopupMenu
->ImplCallHighlight(pFloat
->nHighlightedItem
);
1066 if ( pMenu
&& pMenu
->pStartedFrom
)
1069 if (pMenu
->pStartedFrom
->IsMenuBar())
1071 pMenu
->pStartedFrom
->MenuBarKeyInput(rKEvent
);
1075 MenuFloatingWindow
* pFloat
= static_cast<PopupMenu
*>(pMenu
->pStartedFrom
.get())->ImplGetFloatingWindow();
1076 pFloat
->GrabFocus();
1077 pFloat
->KillActivePopup();
1078 sal_uInt16 highlightItem
= pFloat
->GetHighlightedItem();
1079 pFloat
->ChangeHighlightItem(highlightItem
, false);
1089 if ( nHighlightedItem
!= ITEMPOS_INVALID
)
1091 MenuItemData
* pData
= pMenu
->GetItemList()->GetDataFromPos( nHighlightedItem
);
1092 if ( pData
&& pData
->pSubMenu
)
1094 HighlightChanged( nullptr );
1100 Menu
* pStart
= pMenu
->ImplGetStartMenu();
1101 if (pStart
&& pStart
->IsMenuBar())
1104 pStart
->ImplGetWindow()->KeyInput( rKEvent
);
1114 MenuItemData
* pData
= pMenu
->GetItemList()->GetDataFromPos( nHighlightedItem
);
1115 if ( pData
&& pData
->bEnabled
)
1117 if ( pData
->pSubMenu
)
1118 HighlightChanged( nullptr );
1131 Menu
* pStart
= pMenu
->ImplGetStartMenu();
1132 if (pStart
&& pStart
->IsMenuBar())
1135 pStart
->ImplGetWindow()->KeyInput( rKEvent
);
1142 sal_Unicode nCharCode
= rKEvent
.GetCharCode();
1143 sal_uInt16 nPos
= 0;
1144 sal_uInt16 nDuplicates
= 0;
1145 MenuItemData
* pData
= (nCharCode
&& pMenu
&& accel
) ?
1146 pMenu
->GetItemList()->SearchItem(nCharCode
, rKEvent
.GetKeyCode(), nPos
, nDuplicates
, nHighlightedItem
) : nullptr;
1149 if ( pData
->pSubMenu
|| nDuplicates
> 1 )
1151 ChangeHighlightItem( nPos
, false );
1152 HighlightChanged( nullptr );
1156 nHighlightedItem
= nPos
;
1161 FloatingWindow::KeyInput( rKEvent
);
1165 if (pMenu
&& pMenu
->pStartedFrom
&& pMenu
->pStartedFrom
->IsMenuBar())
1167 MenuBar
*pMenuBar
= static_cast<MenuBar
*>(pMenu
->pStartedFrom
.get());
1168 const bool bShowAccels
= nCode
!= KEY_ESCAPE
;
1169 if (pMenuBar
->getMenuBarWindow()->GetMBWMenuKey() != bShowAccels
)
1171 pMenuBar
->getMenuBarWindow()->SetMBWMenuKey(bShowAccels
);
1172 pMenuBar
->getMenuBarWindow()->SetMBWHideAccel(!bShowAccels
);
1173 if (accel
&& autoacc
)
1174 Invalidate(InvalidateFlags::Update
);
1178 // #105474# check if menu window was not destroyed
1179 if ( !xWindow
->IsDisposed() )
1185 void MenuFloatingWindow::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
&rPaintRect
)
1190 rRenderContext
.Push( PushFlags::CLIPREGION
);
1191 rRenderContext
.SetClipRegion(vcl::Region(rPaintRect
));
1193 if (rRenderContext
.IsNativeControlSupported(ControlType::MenuPopup
, ControlPart::Entire
))
1195 rRenderContext
.SetClipRegion();
1196 long nX
= pMenu
->pLogo
? pMenu
->pLogo
->aBitmap
.GetSizePixel().Width() : 0;
1197 Size
aPxSize(GetOutputSizePixel());
1198 aPxSize
.Width() -= nX
;
1199 ImplControlValue
aVal(pMenu
->nTextPos
- GUTTERBORDER
);
1200 rRenderContext
.DrawNativeControl(ControlType::MenuPopup
, ControlPart::Entire
,
1201 Rectangle(Point(nX
, 0), aPxSize
),
1202 ControlState::ENABLED
, aVal
, OUString());
1203 InitMenuClipRegion(rRenderContext
);
1207 ImplDrawScroller(rRenderContext
, true);
1208 ImplDrawScroller(rRenderContext
, false);
1210 rRenderContext
.SetFillColor(rRenderContext
.GetSettings().GetStyleSettings().GetMenuColor());
1211 pMenu
->ImplPaint(rRenderContext
, nScrollerHeight
, ImplGetStartY());
1212 if (nHighlightedItem
!= ITEMPOS_INVALID
)
1213 RenderHighlightItem(rRenderContext
, nHighlightedItem
);
1215 rRenderContext
.Pop();
1218 void MenuFloatingWindow::ImplDrawScroller(vcl::RenderContext
& rRenderContext
, bool bUp
)
1223 rRenderContext
.SetClipRegion();
1225 Size
aOutSz(GetOutputSizePixel());
1226 long nY
= bUp
? 0 : (aOutSz
.Height() - nScrollerHeight
);
1227 long nX
= pMenu
->pLogo
? pMenu
->pLogo
->aBitmap
.GetSizePixel().Width() : 0;
1228 Rectangle
aRect(Point(nX
, nY
), Size(aOutSz
.Width() - nX
, nScrollerHeight
));
1230 DecorationView
aDecoView(&rRenderContext
);
1231 SymbolType eSymbol
= bUp
? SymbolType::SPIN_UP
: SymbolType::SPIN_DOWN
;
1233 DrawSymbolFlags nStyle
= DrawSymbolFlags::NONE
;
1234 if ((bUp
&& !bScrollUp
) || (!bUp
&& !bScrollDown
))
1235 nStyle
|= DrawSymbolFlags::Disable
;
1237 aDecoView
.DrawSymbol(aRect
, eSymbol
, rRenderContext
.GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle
);
1239 InitMenuClipRegion(rRenderContext
);
1242 void MenuFloatingWindow::RequestHelp( const HelpEvent
& rHEvt
)
1244 sal_uInt16 nId
= nHighlightedItem
;
1246 vcl::Window
* pW
= this;
1248 // #102618# Get item rect before destroying the window in EndExecute() call
1249 Rectangle
aHighlightRect( ImplGetItemRect( nHighlightedItem
) );
1251 if ( rHEvt
.GetMode() & (HelpEventMode::CONTEXT
| HelpEventMode::EXTENDED
) )
1253 nHighlightedItem
= ITEMPOS_INVALID
;
1258 if( !ImplHandleHelpEvent( pW
, pM
, nId
, rHEvt
, aHighlightRect
) )
1259 Window::RequestHelp( rHEvt
);
1262 void MenuFloatingWindow::StateChanged( StateChangedType nType
)
1264 FloatingWindow::StateChanged( nType
);
1266 if ( ( nType
== StateChangedType::ControlForeground
) || ( nType
== StateChangedType::ControlBackground
) )
1268 ApplySettings(*this);
1273 void MenuFloatingWindow::DataChanged( const DataChangedEvent
& rDCEvt
)
1275 FloatingWindow::DataChanged( rDCEvt
);
1277 if ( (rDCEvt
.GetType() == DataChangedEventType::FONTS
) ||
1278 (rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
) ||
1279 ((rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
1280 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
)) )
1282 ApplySettings(*this);
1287 void MenuFloatingWindow::Command( const CommandEvent
& rCEvt
)
1289 if ( rCEvt
.GetCommand() == CommandEventId::Wheel
)
1291 const CommandWheelData
* pData
= rCEvt
.GetWheelData();
1292 if( !pData
->GetModifier() && ( pData
->GetMode() == CommandWheelMode::SCROLL
) )
1294 ImplScroll( pData
->GetDelta() > 0 );
1295 MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
1300 css::uno::Reference
<css::accessibility::XAccessible
> MenuFloatingWindow::CreateAccessible()
1302 css::uno::Reference
<css::accessibility::XAccessible
> xAcc
;
1304 if (pMenu
&& !pMenu
->pStartedFrom
)
1305 xAcc
= pMenu
->GetAccessible();
1310 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */