1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "menufloatingwindow.hxx"
21 #include "menuitemlist.hxx"
22 #include "menubarwindow.hxx"
23 #include "bufferdevice.hxx"
24 
25 #include <sal/log.hxx>
26 #include <salframe.hxx>
27 #include <svdata.hxx>
28 #include <vcl/decoview.hxx>
29 #include <vcl/settings.hxx>
30 #include <window.h>
31 
MenuFloatingWindow(Menu * pMen,vcl::Window * pParent,WinBits nStyle)32 MenuFloatingWindow::MenuFloatingWindow( Menu* pMen, vcl::Window* pParent, WinBits nStyle ) :
33     FloatingWindow( pParent, nStyle ),
34     pMenu(pMen),
35     nHighlightedItem(ITEMPOS_INVALID),
36     nMBDownPos(ITEMPOS_INVALID),
37     nScrollerHeight(0),
38     nFirstEntry(0),
39     nPosInParent(ITEMPOS_INVALID),
40     bInExecute(false),
41     bScrollMenu(false),
42     bScrollUp(false),
43     bScrollDown(false),
44     bIgnoreFirstMove(true),
45     bKeyInput(false)
46 {
47     mpWindowImpl->mbMenuFloatingWindow= true;
48 
49     ApplySettings(*GetOutDev());
50 
51     SetPopupModeEndHdl( LINK( this, MenuFloatingWindow, PopupEnd ) );
52 
53     aHighlightChangedTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, HighlightChanged ) );
54     aHighlightChangedTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
55     aHighlightChangedTimer.SetDebugName( "vcl::MenuFloatingWindow aHighlightChangedTimer" );
56 
57     aSubmenuCloseTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
58     aSubmenuCloseTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, SubmenuClose ) );
59     aSubmenuCloseTimer.SetDebugName( "vcl::MenuFloatingWindow aSubmenuCloseTimer" );
60 
61     aScrollTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, AutoScroll ) );
62     aScrollTimer.SetDebugName( "vcl::MenuFloatingWindow aScrollTimer" );
63 
64     AddEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
65 }
66 
doShutdown()67 void MenuFloatingWindow::doShutdown()
68 {
69     if( !pMenu )
70         return;
71 
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())
77     {
78         // #102461# remove highlight in parent
79         size_t i, nCount = pMenu->pStartedFrom->pItemList->size();
80         for(i = 0; i < nCount; i++)
81         {
82             MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
83             if( pData && ( pData->pSubMenu == pMenu ) )
84                 break;
85         }
86         if( i < nCount )
87         {
88             MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
89             if (pPWin)
90                 pPWin->InvalidateItem(i);
91         }
92     }
93 
94     // free the reference to the accessible component
95     SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
96 
97     aHighlightChangedTimer.Stop();
98 
99     // #95056# invalidate screen area covered by system window
100     // so this can be taken into account if the commandhandler performs a scroll operation
101     if( GetParent() )
102     {
103         tools::Rectangle aInvRect( GetWindowExtentsRelative( GetParent() ) );
104         GetParent()->Invalidate( aInvRect );
105     }
106     pMenu = nullptr;
107     RemoveEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
108 
109     aScrollTimer.Stop();
110     aSubmenuCloseTimer.Stop();
111     aSubmenuCloseTimer.Stop();
112     aHighlightChangedTimer.Stop();
113     aHighlightChangedTimer.Stop();
114 
115 }
116 
~MenuFloatingWindow()117 MenuFloatingWindow::~MenuFloatingWindow()
118 {
119     disposeOnce();
120 }
121 
dispose()122 void MenuFloatingWindow::dispose()
123 {
124     doShutdown();
125     pMenu.clear();
126     pActivePopup.clear();
127     xSaveFocusId.clear();
128     FloatingWindow::dispose();
129 }
130 
Resize()131 void MenuFloatingWindow::Resize()
132 {
133     InitMenuClipRegion(*GetOutDev()); // FIXME
134 }
135 
ApplySettings(vcl::RenderContext & rRenderContext)136 void MenuFloatingWindow::ApplySettings(vcl::RenderContext& rRenderContext)
137 {
138     FloatingWindow::ApplySettings(rRenderContext);
139 
140     if (IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem) &&
141         IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
142     {
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)
148         {
149             aStyle.SetMenuHighlightTextColor(aHighlightTextColor);
150         }
151         aSettings.SetStyleSettings(aStyle);
152         GetOutDev()->SetSettings(aSettings);
153     }
154 
155     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
156     SetPointFont(rRenderContext, rStyleSettings.GetMenuFont());
157 
158     if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
159     {
160         rRenderContext.SetBackground(); // background will be drawn by NWF
161     }
162     else
163         rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetMenuColor()));
164 
165     rRenderContext.SetTextColor(rStyleSettings.GetMenuTextColor());
166     rRenderContext.SetTextFillColor();
167     rRenderContext.SetLineColor();
168 }
169 
170 /// Get a negative pixel offset for an offset menu
ImplGetStartY() const171 tools::Long MenuFloatingWindow::ImplGetStartY() const
172 {
173     tools::Long nY = 0;
174     if( pMenu )
175     {
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) )
178         {
179             return 0;
180         }
181 
182         for ( sal_uInt16 n = 0; n < nFirstEntry; n++ )
183             nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height();
184         nY -= pMenu->GetTitleHeight();
185     }
186     return -nY;
187 }
188 
ImplCalcClipRegion() const189 vcl::Region MenuFloatingWindow::ImplCalcClipRegion() const
190 {
191     Size aOutSz = GetOutputSizePixel();
192     tools::Rectangle aRect( Point(), aOutSz );
193     aRect.AdjustTop(nScrollerHeight );
194     aRect.AdjustBottom( -nScrollerHeight );
195 
196     vcl::Region aRegion(aRect);
197 
198     return aRegion;
199 }
200 
InitMenuClipRegion(vcl::RenderContext & rRenderContext)201 void MenuFloatingWindow::InitMenuClipRegion(vcl::RenderContext& rRenderContext)
202 {
203     if (IsScrollMenu())
204     {
205         rRenderContext.SetClipRegion(ImplCalcClipRegion());
206     }
207     else
208     {
209         rRenderContext.SetClipRegion();
210     }
211 }
212 
ImplHighlightItem(const MouseEvent & rMEvt,bool bMBDown)213 void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, bool bMBDown )
214 {
215     if( ! pMenu )
216         return;
217 
218     tools::Long nY = GetInitialItemY();
219     tools::Long nMouseY = rMEvt.GetPosPixel().Y();
220     Size aOutSz = GetOutputSizePixel();
221     if ( ( nMouseY >= nY ) && ( nMouseY < aOutSz.Height() ) )
222     {
223         bool bHighlighted = false;
224         size_t nCount = pMenu->pItemList->size();
225         for ( size_t n = 0; !bHighlighted && ( n < nCount ); n++ )
226         {
227             if ( pMenu->ImplIsVisible( n ) )
228             {
229                 MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( n );
230                 tools::Long nOldY = nY;
231                 nY += pItemData->aSz.Height();
232                 if ( ( nOldY <= nMouseY ) && ( nY > nMouseY ) && pMenu->ImplIsSelectable( n ) )
233                 {
234                     bool bPopupArea = true;
235                     if ( pItemData->nBits & MenuItemBits::POPUPSELECT )
236                     {
237                         // only when clicked over the arrow...
238                         Size aSz = GetOutputSizePixel();
239                         tools::Long nFontHeight = GetTextHeight();
240                         bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) );
241                     }
242 
243                     if ( bMBDown )
244                     {
245                         if ( n != nHighlightedItem )
246                         {
247                             ChangeHighlightItem( static_cast<sal_uInt16>(n), false );
248                         }
249 
250                         bool bAllowNewPopup = true;
251                         if ( pActivePopup )
252                         {
253                             MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
254                             bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup );
255                             if ( bAllowNewPopup )
256                                 KillActivePopup();
257                         }
258 
259                         if ( bPopupArea && bAllowNewPopup )
260                         {
261                             HighlightChanged( nullptr );
262                         }
263                     }
264                     else
265                     {
266                         if ( n != nHighlightedItem )
267                         {
268                             ChangeHighlightItem( static_cast<sal_uInt16>(n), true );
269                         }
270                         else if ( pItemData->nBits & MenuItemBits::POPUPSELECT )
271                         {
272                             if ( bPopupArea && ( pActivePopup != pItemData->pSubMenu ) )
273                                 HighlightChanged( nullptr );
274                         }
275                     }
276                     bHighlighted = true;
277                 }
278             }
279         }
280         if ( !bHighlighted )
281             ChangeHighlightItem( ITEMPOS_INVALID, true );
282     }
283     else
284     {
285         ImplScroll( rMEvt.GetPosPixel() );
286         ChangeHighlightItem( ITEMPOS_INVALID, true );
287     }
288 }
289 
IMPL_LINK_NOARG(MenuFloatingWindow,PopupEnd,FloatingWindow *,void)290 IMPL_LINK_NOARG(MenuFloatingWindow, PopupEnd, FloatingWindow*, void)
291 {
292     // "this" will be deleted before the end of this method!
293     Menu* pM = pMenu;
294     if ( bInExecute )
295     {
296         End();
297         if ( pActivePopup )
298         {
299             KillActivePopup(); // should be ok to just remove it
300             //pActivePopup->bCanceled = true;
301         }
302         pMenu->bInCallback = true;
303         pMenu->Deactivate();
304         pMenu->bInCallback = false;
305     }
306     else
307     {
308         if (pMenu && pMenu->pStartedFrom)
309             pMenu->pStartedFrom->ClosePopup(pMenu);
310     }
311 
312     if ( pM )
313         pM->pStartedFrom = nullptr;
314 }
315 
IMPL_LINK_NOARG(MenuFloatingWindow,AutoScroll,Timer *,void)316 IMPL_LINK_NOARG(MenuFloatingWindow, AutoScroll, Timer *, void)
317 {
318     ImplScroll( GetPointerPosPixel() );
319 }
320 
IMPL_LINK(MenuFloatingWindow,HighlightChanged,Timer *,pTimer,void)321 IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer, void )
322 {
323     if( ! pMenu )
324         return;
325 
326     MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
327     if ( !pItemData )
328         return;
329 
330     if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) )
331     {
332         FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
333         SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose );
334         KillActivePopup();
335         SetPopupModeFlags( nOldFlags );
336     }
337     if ( !(pItemData->bEnabled && pItemData->pSubMenu && pItemData->pSubMenu->GetItemCount() && ( pItemData->pSubMenu != pActivePopup )) )
338         return;
339 
340     pActivePopup = static_cast<PopupMenu*>(pItemData->pSubMenu.get());
341     tools::Long nY = nScrollerHeight+ImplGetStartY();
342     MenuItemData* pData = nullptr;
343     for ( sal_uLong n = 0; n < nHighlightedItem; n++ )
344     {
345         pData = pMenu->pItemList->GetDataFromPos( n );
346         nY += pData->aSz.Height();
347     }
348     pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
349     Size MySize = GetOutputSizePixel();
350     Point aItemTopLeft( 0, nY );
351     Point aItemBottomRight( aItemTopLeft );
352     aItemBottomRight.AdjustX(MySize.Width() );
353     aItemBottomRight.AdjustY(pData->aSz.Height() );
354 
355     // shift the popups a little
356     aItemTopLeft.AdjustX(2 );
357     aItemBottomRight.AdjustX( -2 );
358     if ( nHighlightedItem )
359         aItemTopLeft.AdjustY( -2 );
360     else
361     {
362         sal_Int32 nL, nT, nR, nB;
363         GetBorder( nL, nT, nR, nB );
364         aItemTopLeft.AdjustY( -nT );
365     }
366 
367     // pTest: crash due to Reschedule() in call of Activate()
368     // Also it is prevented that submenus are displayed which
369     // were for long in Activate Rescheduled and which should not be
370     // displayed now.
371     Menu* pTest = pActivePopup;
372     FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
373     SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose );
374     sal_uInt16 nRet = pActivePopup->ImplExecute( this, tools::Rectangle( aItemTopLeft, aItemBottomRight ), FloatWinPopupFlags::Right, pMenu, pTimer == nullptr );
375     SetPopupModeFlags( nOldFlags );
376 
377     // nRet != 0, if it was stopped during Activate()...
378     if ( !nRet && ( pActivePopup == pTest ) && pActivePopup->ImplGetWindow() )
379         pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
380 }
381 
IMPL_LINK_NOARG(MenuFloatingWindow,SubmenuClose,Timer *,void)382 IMPL_LINK_NOARG(MenuFloatingWindow, SubmenuClose, Timer *, void)
383 {
384     if( pMenu && pMenu->pStartedFrom )
385     {
386         MenuFloatingWindow* pWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->GetWindow());
387         if( pWin )
388             pWin->KillActivePopup();
389     }
390 }
391 
IMPL_LINK(MenuFloatingWindow,ShowHideListener,VclWindowEvent &,rEvent,void)392 IMPL_LINK( MenuFloatingWindow, ShowHideListener, VclWindowEvent&, rEvent, void )
393 {
394     if( ! pMenu )
395         return;
396 
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 );
401 }
402 
EnableScrollMenu(bool b)403 void MenuFloatingWindow::EnableScrollMenu( bool b )
404 {
405     bScrollMenu = b;
406     nScrollerHeight = b ? static_cast<sal_uInt16>(GetSettings().GetStyleSettings().GetScrollBarSize()) /2 : 0;
407     bScrollDown = true;
408     InitMenuClipRegion(*GetOutDev());
409 }
410 
Start()411 void MenuFloatingWindow::Start()
412 {
413     if (bInExecute)
414         return;
415     bInExecute = true;
416     if (GetParent())
417         GetParent()->IncModalCount();
418 }
419 
MenuInHierarchyHasFocus() const420 bool MenuFloatingWindow::MenuInHierarchyHasFocus() const
421 {
422     if (HasChildPathFocus())
423         return true;
424     PopupMenu* pSub = GetActivePopup();
425     if (!pSub)
426         return false;
427     return pSub->ImplGetFloatingWindow()->HasChildPathFocus();
428 }
429 
End()430 void MenuFloatingWindow::End()
431 {
432     if (!bInExecute)
433         return;
434 
435     if (GetParent() && !GetParent()->isDisposed())
436         GetParent()->DecModalCount();
437 
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())
442     {
443         ImplGetSVData()->mpWinData->mbNoDeactivate = false;
444         Window::EndSaveFocus(xFocusId);
445     }
446 
447     bInExecute = false;
448 }
449 
Execute()450 void MenuFloatingWindow::Execute()
451 {
452     ImplSVData* pSVData = ImplGetSVData();
453 
454     pSVData->maAppData.mpActivePopupMenu = static_cast<PopupMenu*>(pMenu.get());
455 
456     Start();
457 
458     while (bInExecute && !Application::IsQuit())
459         Application::Yield();
460 
461     pSVData->maAppData.mpActivePopupMenu = nullptr;
462 }
463 
StopExecute()464 void MenuFloatingWindow::StopExecute()
465 {
466     End();
467 
468     ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xSaveFocusId);
469 
470     aHighlightChangedTimer.Stop();
471     if (pActivePopup)
472     {
473         KillActivePopup();
474     }
475     // notify parent, needed for accessibility
476     if( pMenu && pMenu->pStartedFrom )
477         pMenu->pStartedFrom->ImplCallEventListeners( VclEventId::MenuSubmenuDeactivate, nPosInParent );
478 }
479 
KillActivePopup(PopupMenu * pThisOnly)480 void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly )
481 {
482     if ( !pActivePopup || ( pThisOnly && ( pThisOnly != pActivePopup ) ) )
483         return;
484 
485     if( pActivePopup->pWindow )
486         if( static_cast<FloatingWindow *>(pActivePopup->pWindow.get())->IsInCleanUp() )
487             return; // kill it later
488     if ( pActivePopup->bInCallback )
489         pActivePopup->bCanceled = true;
490 
491     // For all actions pActivePopup = 0, if e.g.
492     // PopupModeEndHdl the popups to destroy were called synchronous
493     PopupMenu* pPopup = pActivePopup;
494     pActivePopup = nullptr;
495     pPopup->bInCallback = true;
496     pPopup->Deactivate();
497     pPopup->bInCallback = false;
498     if ( pPopup->ImplGetWindow() )
499     {
500         pPopup->ImplGetFloatingWindow()->StopExecute();
501         pPopup->ImplGetFloatingWindow()->doShutdown();
502         pPopup->pWindow->SetParentToDefaultWindow();
503         pPopup->pWindow.disposeAndClear();
504 
505         PaintImmediately();
506     }
507 }
508 
EndExecute()509 void MenuFloatingWindow::EndExecute()
510 {
511     Menu* pStart = pMenu ? pMenu->ImplGetStartMenu() : nullptr;
512 
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())
518     {
519         pWin = static_cast<PopupMenu*>(pWin->pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
520     }
521     if ( pWin )
522         pCleanUpFrom = pWin;
523 
524     // this window will be destroyed => store date locally...
525     Menu* pM = pMenu;
526     sal_uInt16 nItem = nHighlightedItem;
527 
528     pCleanUpFrom->StopExecute();
529 
530     if ( !(nItem != ITEMPOS_INVALID && pM) )
531         return;
532 
533     MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem );
534     if ( pItemData && !pItemData->bIsTemporary )
535     {
536         pM->nSelectedId = pItemData->nId;
537         pM->sSelectedIdent = pItemData->sIdent;
538         if (pStart)
539         {
540             pStart->nSelectedId = pItemData->nId;
541             pStart->sSelectedIdent = pItemData->sIdent;
542         }
543 
544         pM->ImplSelect();
545     }
546 }
547 
EndExecute(sal_uInt16 nId)548 void MenuFloatingWindow::EndExecute( sal_uInt16 nId )
549 {
550     size_t nPos;
551     if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) )
552         nHighlightedItem = nPos;
553     else
554         nHighlightedItem = ITEMPOS_INVALID;
555 
556     EndExecute();
557 }
558 
MouseButtonDown(const MouseEvent & rMEvt)559 void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt )
560 {
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 );
566 
567     ImplHighlightItem( rMEvt, true );
568 
569     nMBDownPos = nHighlightedItem;
570 }
571 
MouseButtonUp(const MouseEvent & rMEvt)572 void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt )
573 {
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 )) )
580         return;
581 
582     if ( !pData->pSubMenu )
583     {
584         EndExecute();
585     }
586     else if ( ( pData->nBits & MenuItemBits::POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) )
587     {
588         // not when clicked over the arrow...
589         Size aSz = GetOutputSizePixel();
590         tools::Long nFontHeight = GetTextHeight();
591         if ( rMEvt.GetPosPixel().X() < ( aSz.Width() - nFontHeight - nFontHeight/4 ) )
592             EndExecute();
593     }
594 
595 }
596 
MouseMove(const MouseEvent & rMEvt)597 void MenuFloatingWindow::MouseMove( const MouseEvent& rMEvt )
598 {
599     if ( !IsVisible() || rMEvt.IsSynthetic() || rMEvt.IsEnterWindow() )
600         return;
601 
602     if ( rMEvt.IsLeaveWindow() )
603     {
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();
609 
610         if( !pActivePopup || (pData && pData->pSubMenu != pActivePopup ) )
611             ChangeHighlightItem( ITEMPOS_INVALID, false );
612 
613         if ( IsScrollMenu() )
614             ImplScroll( rMEvt.GetPosPixel() );
615     }
616     else
617     {
618         aSubmenuCloseTimer.Stop();
619         if( bIgnoreFirstMove )
620             bIgnoreFirstMove = false;
621         else
622             ImplHighlightItem( rMEvt, false );
623     }
624 }
625 
ImplScroll(bool bUp)626 void MenuFloatingWindow::ImplScroll( bool bUp )
627 {
628     KillActivePopup();
629     PaintImmediately();
630 
631     if (!pMenu)
632         return;
633 
634     Invalidate();
635 
636     pMenu->ImplKillLayoutData();
637 
638     if ( bScrollUp && bUp )
639     {
640         nFirstEntry = pMenu->ImplGetPrevVisible( nFirstEntry );
641         SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
642 
643         // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
644         const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
645         if ( pItemData )
646         {
647             tools::Long nScrollEntryHeight = pItemData->aSz.Height();
648 
649             if ( !bScrollDown )
650             {
651                 bScrollDown = true;
652                 Invalidate();
653             }
654 
655             if ( pMenu->ImplGetPrevVisible( nFirstEntry ) == ITEMPOS_INVALID )
656             {
657                 bScrollUp = false;
658                 Invalidate();
659             }
660 
661             Scroll( 0, nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
662         }
663     }
664     else if ( bScrollDown && !bUp )
665     {
666         // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
667         const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
668         if ( pItemData )
669         {
670             tools::Long nScrollEntryHeight = pItemData->aSz.Height();
671 
672             nFirstEntry = pMenu->ImplGetNextVisible( nFirstEntry );
673             SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
674 
675             if ( !bScrollUp )
676             {
677                 bScrollUp = true;
678                 Invalidate();
679             }
680 
681             tools::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 )
685             {
686                 bScrollDown = false;
687                 Invalidate();
688             }
689 
690             Scroll( 0, -nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
691         }
692     }
693 
694     Invalidate();
695 }
696 
ImplScroll(const Point & rMousePos)697 void MenuFloatingWindow::ImplScroll( const Point& rMousePos )
698 {
699     Size aOutSz = GetOutputSizePixel();
700 
701     tools::Long nY = nScrollerHeight;
702     tools::Long nMouseY = rMousePos.Y();
703     tools::Long nDelta = 0;
704 
705     if ( bScrollUp && ( nMouseY < nY ) )
706     {
707         ImplScroll( true );
708         nDelta = nY - nMouseY;
709     }
710     else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) )
711     {
712         ImplScroll( false );
713         nDelta = nMouseY - ( aOutSz.Height() - nY );
714     }
715 
716     if ( !nDelta )
717         return;
718 
719     aScrollTimer.Stop();    // if scrolled through MouseMove.
720     tools::Long nTimeout;
721     if ( nDelta < 3 )
722         nTimeout = 200;
723     else if ( nDelta < 5 )
724         nTimeout = 100;
725     else if ( nDelta < 8 )
726         nTimeout = 70;
727     else if ( nDelta < 12 )
728         nTimeout = 40;
729     else
730         nTimeout = 20;
731     aScrollTimer.SetTimeout( nTimeout );
732     aScrollTimer.Start();
733 }
ChangeHighlightItem(sal_uInt16 n,bool bStartPopupTimer)734 void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer )
735 {
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();
742 
743     aSubmenuCloseTimer.Stop();
744     if( ! pMenu )
745         return;
746 
747     if ( nHighlightedItem != ITEMPOS_INVALID )
748     {
749         InvalidateItem(nHighlightedItem);
750         pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, nHighlightedItem );
751     }
752 
753     nHighlightedItem = n;
754     SAL_WARN_IF( !pMenu->ImplIsVisible( nHighlightedItem ) && nHighlightedItem != ITEMPOS_INVALID, "vcl", "ChangeHighlightItem: Not visible!" );
755     if( nHighlightedItem != ITEMPOS_INVALID )
756     {
757         if (pMenu->pStartedFrom && !pMenu->pStartedFrom->IsMenuBar())
758         {
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++)
762             {
763                 MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
764                 if( pData && ( pData->pSubMenu == pMenu ) )
765                     break;
766             }
767             if( i < nCount )
768             {
769                 MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
770                 if( pPWin && pPWin->nHighlightedItem != i )
771                 {
772                     pPWin->InvalidateItem(i);
773                     pPWin->nHighlightedItem = i;
774                 }
775             }
776         }
777         InvalidateItem(nHighlightedItem);
778         pMenu->ImplCallHighlight( nHighlightedItem );
779     }
780     else
781     {
782         pMenu->nSelectedId = 0;
783         pMenu->sSelectedIdent.clear();
784     }
785 
786     if ( bStartPopupTimer )
787     {
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();
794         else
795             HighlightChanged( &aHighlightChangedTimer );
796     }
797 }
798 
799 /// Calculate the initial vertical pixel offset of the first item.
800 /// May be negative for scrolled windows.
GetInitialItemY(tools::Long * pStartY) const801 tools::Long MenuFloatingWindow::GetInitialItemY(tools::Long *pStartY) const
802 {
803     tools::Long nStartY = ImplGetStartY();
804     if (pStartY)
805         *pStartY = nStartY;
806     return nScrollerHeight + nStartY +
807         ImplGetSVData()->maNWFData.mnMenuFormatBorderY;
808 }
809 
810 /// Emit an Invalidate just for this item's area
InvalidateItem(sal_uInt16 nPos)811 void MenuFloatingWindow::InvalidateItem(sal_uInt16 nPos)
812 {
813     if (!pMenu)
814         return;
815 
816     tools::Long nY = GetInitialItemY();
817     size_t nCount = pMenu->pItemList->size();
818     for (size_t n = 0; n < nCount; n++)
819     {
820         MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
821         tools::Long nHeight = pData->aSz.Height();
822         if (n == nPos)
823         {
824             Size aWidth( GetSizePixel() );
825             tools::Rectangle aRect(Point(0, nY), Size(aWidth.Width(), nHeight));
826             Invalidate( aRect );
827         }
828         nY += nHeight;
829     }
830 }
831 
RenderHighlightItem(vcl::RenderContext & rRenderContext,sal_uInt16 nPos)832 void MenuFloatingWindow::RenderHighlightItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos)
833 {
834     if (!pMenu)
835         return;
836 
837     Size aSz(GetOutputSizePixel());
838 
839     tools::Long nX = 0;
840     tools::Long nStartY;
841     tools::Long nY = GetInitialItemY(&nStartY);
842 
843     int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
844 
845     size_t nCount = pMenu->pItemList->size();
846     for (size_t n = 0; n < nCount; n++)
847     {
848         MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
849         if (n == nPos)
850         {
851             SAL_WARN_IF(!pMenu->ImplIsVisible(n), "vcl", "Highlight: Item not visible!");
852             if (pData->eType != MenuItemType::SEPARATOR)
853             {
854                 bool bRestoreLineColor = false;
855                 Color oldLineColor;
856                 bool bDrawItemRect = true;
857 
858                 tools::Rectangle aItemRect(Point(nX + nOuterSpaceX, nY), Size(aSz.Width() - 2 * nOuterSpaceX, pData->aSz.Height()));
859                 if (pData->nBits & MenuItemBits::POPUPSELECT)
860                 {
861                     tools::Long nFontHeight = GetTextHeight();
862                     aItemRect.AdjustRight( -(nFontHeight + nFontHeight / 4) );
863                 }
864 
865                 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
866                 {
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))
875                     {
876                         bDrawItemRect = false;
877                         if (!rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::MenuItem, aItemRect,
878                                                               ControlState::SELECTED | (pData->bEnabled
879                                                                                             ? ControlState::ENABLED
880                                                                                             : ControlState::NONE),
881                                                               aVal, OUString()))
882                         {
883                             bDrawItemRect = true;
884                         }
885                     }
886                     else
887                         bDrawItemRect = true;
888                     rRenderContext.Pop();
889                 }
890                 if (bDrawItemRect)
891                 {
892                     if (pData->bEnabled)
893                         rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
894                     else
895                     {
896                         rRenderContext.SetFillColor();
897                         oldLineColor = rRenderContext.GetLineColor();
898                         rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
899                         bRestoreLineColor = true;
900                     }
901 
902                     rRenderContext.DrawRect(aItemRect);
903                 }
904                 pMenu->ImplPaint(rRenderContext, GetOutputSizePixel(), nScrollerHeight, nStartY, pData, true/*bHighlight*/);
905                 if (bRestoreLineColor)
906                     rRenderContext.SetLineColor(oldLineColor);
907             }
908             return;
909         }
910 
911         nY += pData->aSz.Height();
912     }
913 }
914 
ImplGetItemRect(sal_uInt16 nPos)915 tools::Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos )
916 {
917     if( ! pMenu )
918         return tools::Rectangle();
919 
920     tools::Rectangle aRect;
921     Size    aSz = GetOutputSizePixel();
922     tools::Long    nStartY = ImplGetStartY();
923     tools::Long    nY = nScrollerHeight+nStartY;
924 
925     size_t nCount = pMenu->pItemList->size();
926     for ( size_t n = 0; n < nCount; n++ )
927     {
928         MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
929         if ( n == nPos )
930         {
931             SAL_WARN_IF( !pMenu->ImplIsVisible( n ), "vcl", "ImplGetItemRect: Item not visible!" );
932             if ( pData->eType != MenuItemType::SEPARATOR )
933             {
934                 aRect = tools::Rectangle( Point( 0, nY ), Size( aSz.Width(), pData->aSz.Height() ) );
935                 if ( pData->nBits & MenuItemBits::POPUPSELECT )
936                 {
937                     tools::Long nFontHeight = GetTextHeight();
938                     aRect.AdjustRight( -(nFontHeight + nFontHeight/4) );
939                 }
940             }
941             break;
942         }
943         nY += pData->aSz.Height();
944     }
945     return aRect;
946 }
947 
ImplCursorUpDown(bool bUp,bool bHomeEnd)948 void MenuFloatingWindow::ImplCursorUpDown( bool bUp, bool bHomeEnd )
949 {
950     if( ! pMenu )
951         return;
952 
953     const StyleSettings& rSettings = GetSettings().GetStyleSettings();
954 
955     sal_uInt16 n = nHighlightedItem;
956     if ( n == ITEMPOS_INVALID )
957     {
958         if ( bUp )
959             n = 0;
960         else
961             n = pMenu->GetItemCount()-1;
962     }
963 
964     sal_uInt16 nLoop = n;
965 
966     if( bHomeEnd )
967     {
968         // absolute positioning
969         if( bUp )
970         {
971             n = pMenu->GetItemCount();
972             nLoop = n-1;
973         }
974         else
975         {
976             n = sal_uInt16(-1);
977             nLoop = n+1;
978         }
979     }
980 
981     do
982     {
983         if ( bUp )
984         {
985             if ( n )
986                 n--;
987             else
988                 if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
989                     n = pMenu->GetItemCount()-1;
990                 else
991                     break;
992         }
993         else
994         {
995             n++;
996             if ( n >= pMenu->GetItemCount() )
997             {
998                 if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
999                     n = 0;
1000                 else
1001                     break;
1002             }
1003         }
1004 
1005         MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( n );
1006         if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() )
1007               && ( pData->eType != MenuItemType::SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) )
1008         {
1009             // Is selection in visible area?
1010             if ( IsScrollMenu() )
1011             {
1012                 ChangeHighlightItem( ITEMPOS_INVALID, false );
1013 
1014                 while ( n < nFirstEntry )
1015                     ImplScroll( true );
1016 
1017                 Size aOutSz = GetOutputSizePixel();
1018                 sal_uInt16 nLastVisible;
1019                 static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
1020                 while ( n > nLastVisible )
1021                 {
1022                     ImplScroll( false );
1023                     static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
1024                 }
1025             }
1026             ChangeHighlightItem( n, false );
1027             break;
1028         }
1029     } while ( n != nLoop );
1030 }
1031 
KeyInput(const KeyEvent & rKEvent)1032 void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent )
1033 {
1034     VclPtr<vcl::Window> xWindow = this;
1035 
1036     bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
1037     sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
1038     bKeyInput = true;
1039     switch ( nCode )
1040     {
1041         case KEY_UP:
1042         case KEY_DOWN:
1043         {
1044             ImplCursorUpDown( nCode == KEY_UP );
1045         }
1046         break;
1047         case KEY_END:
1048         case KEY_HOME:
1049         {
1050             ImplCursorUpDown( nCode == KEY_END, true );
1051         }
1052         break;
1053         case KEY_F6:
1054         case KEY_ESCAPE:
1055         {
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() )
1058                 break;
1059             if( pMenu )
1060             {
1061                 if ( !pMenu->pStartedFrom )
1062                 {
1063                     StopExecute();
1064                     KillActivePopup();
1065                 }
1066                 else if (pMenu->pStartedFrom->IsMenuBar())
1067                 {
1068                     pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
1069                 }
1070                 else
1071                 {
1072                     StopExecute();
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);
1078                 }
1079             }
1080         }
1081         break;
1082         case KEY_LEFT:
1083         {
1084             if ( pMenu && pMenu->pStartedFrom )
1085             {
1086                 StopExecute();
1087                 if (pMenu->pStartedFrom->IsMenuBar())
1088                 {
1089                     pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
1090                 }
1091                 else
1092                 {
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);
1098                 }
1099             }
1100         }
1101         break;
1102         case KEY_RIGHT:
1103         {
1104             if( pMenu )
1105             {
1106                 bool bDone = false;
1107                 if ( nHighlightedItem != ITEMPOS_INVALID )
1108                 {
1109                     MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
1110                     if ( pData && pData->pSubMenu )
1111                     {
1112                         HighlightChanged( nullptr );
1113                         bDone = true;
1114                     }
1115                 }
1116                 if ( !bDone )
1117                 {
1118                     Menu* pStart = pMenu->ImplGetStartMenu();
1119                     if (pStart && pStart->IsMenuBar())
1120                     {
1121                         // Forward...
1122                         pStart->ImplGetWindow()->KeyInput( rKEvent );
1123                     }
1124                 }
1125             }
1126         }
1127         break;
1128         case KEY_RETURN:
1129         {
1130             if( pMenu )
1131             {
1132                 MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
1133                 if ( pData && pData->bEnabled )
1134                 {
1135                     if ( pData->pSubMenu )
1136                         HighlightChanged( nullptr );
1137                     else
1138                         EndExecute();
1139                 }
1140                 else
1141                     StopExecute();
1142             }
1143         }
1144         break;
1145         case KEY_MENU:
1146         {
1147             if( pMenu )
1148             {
1149                 Menu* pStart = pMenu->ImplGetStartMenu();
1150                 if (pStart && pStart->IsMenuBar())
1151                 {
1152                     // Forward...
1153                     pStart->ImplGetWindow()->KeyInput( rKEvent );
1154                 }
1155             }
1156         }
1157         break;
1158         default:
1159         {
1160             sal_Unicode nCharCode = rKEvent.GetCharCode();
1161             size_t nPos = 0;
1162             size_t nDuplicates = 0;
1163             MenuItemData* pData = (nCharCode && pMenu) ?
1164                 pMenu->GetItemList()->SearchItem(nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem) : nullptr;
1165             if (pData)
1166             {
1167                 if ( pData->pSubMenu || nDuplicates > 1 )
1168                 {
1169                     ChangeHighlightItem( nPos, false );
1170                     HighlightChanged( nullptr );
1171                 }
1172                 else
1173                 {
1174                     nHighlightedItem = nPos;
1175                     EndExecute();
1176                 }
1177             }
1178             else
1179                 FloatingWindow::KeyInput( rKEvent );
1180         }
1181     }
1182 
1183     if (pMenu && pMenu->pStartedFrom && pMenu->pStartedFrom->IsMenuBar())
1184     {
1185         MenuBar *pMenuBar = static_cast<MenuBar*>(pMenu->pStartedFrom.get());
1186         const bool bShowAccels = nCode != KEY_ESCAPE;
1187         if (pMenuBar->getMenuBarWindow()->GetMBWMenuKey() != bShowAccels)
1188         {
1189             pMenuBar->getMenuBarWindow()->SetMBWMenuKey(bShowAccels);
1190             pMenuBar->getMenuBarWindow()->SetMBWHideAccel(!bShowAccels);
1191             if (autoacc)
1192                 Invalidate(InvalidateFlags::Update);
1193         }
1194     }
1195 
1196     // #105474# check if menu window was not destroyed
1197     if ( !xWindow->isDisposed() )
1198     {
1199         bKeyInput = false;
1200     }
1201 }
1202 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rPaintRect)1203 void MenuFloatingWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect)
1204 {
1205     if (!pMenu)
1206         return;
1207 
1208     // Set the clip before the buffering starts: rPaintRect may be larger than the current clip,
1209     // this way the buffer -> render context copy happens with this clip.
1210     rRenderContext.Push(PushFlags::CLIPREGION);
1211     rRenderContext.SetClipRegion(vcl::Region(rPaintRect));
1212 
1213     // Make sure that all actual rendering happens in one go to avoid flicker.
1214     vcl::BufferDevice pBuffer(this, rRenderContext);
1215 
1216     if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
1217     {
1218         pBuffer->SetClipRegion();
1219         tools::Long nX = 0;
1220         Size aPxSize(GetOutputSizePixel());
1221         aPxSize.AdjustWidth( -nX );
1222         ImplControlValue aVal(pMenu->nTextPos - GUTTERBORDER);
1223         pBuffer->DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
1224                                    tools::Rectangle(Point(nX, 0), aPxSize), ControlState::ENABLED,
1225                                    aVal, OUString());
1226         InitMenuClipRegion(*pBuffer);
1227     }
1228     if (IsScrollMenu())
1229     {
1230         ImplDrawScroller(*pBuffer, true);
1231         ImplDrawScroller(*pBuffer, false);
1232     }
1233     pBuffer->SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuColor());
1234     pMenu->ImplPaint(*pBuffer, GetOutputSizePixel(), nScrollerHeight, ImplGetStartY());
1235     if (nHighlightedItem != ITEMPOS_INVALID)
1236         RenderHighlightItem(*pBuffer, nHighlightedItem);
1237 
1238     pBuffer.Dispose();
1239     rRenderContext.Pop();
1240 }
1241 
ImplDrawScroller(vcl::RenderContext & rRenderContext,bool bUp)1242 void MenuFloatingWindow::ImplDrawScroller(vcl::RenderContext& rRenderContext, bool bUp)
1243 {
1244     if (!pMenu)
1245         return;
1246 
1247     rRenderContext.SetClipRegion();
1248 
1249     Size aOutSz(GetOutputSizePixel());
1250     tools::Long nY = bUp ? 0 : (aOutSz.Height() - nScrollerHeight);
1251     tools::Long nX = 0;
1252     tools::Rectangle aRect(Point(nX, nY), Size(aOutSz.Width() - nX, nScrollerHeight));
1253 
1254     DecorationView aDecoView(&rRenderContext);
1255     SymbolType eSymbol = bUp ? SymbolType::SPIN_UP : SymbolType::SPIN_DOWN;
1256 
1257     DrawSymbolFlags nStyle = DrawSymbolFlags::NONE;
1258     if ((bUp && !bScrollUp) || (!bUp && !bScrollDown))
1259         nStyle |= DrawSymbolFlags::Disable;
1260 
1261     aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle);
1262 
1263     InitMenuClipRegion(rRenderContext);
1264 }
1265 
RequestHelp(const HelpEvent & rHEvt)1266 void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt )
1267 {
1268     sal_uInt16 nId = nHighlightedItem;
1269     Menu* pM = pMenu;
1270     vcl::Window* pW = this;
1271 
1272     // #102618# Get item rect before destroying the window in EndExecute() call
1273     tools::Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) );
1274 
1275     if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
1276     {
1277         nHighlightedItem = ITEMPOS_INVALID;
1278         EndExecute();
1279         pW = nullptr;
1280     }
1281 
1282     if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) )
1283         Window::RequestHelp( rHEvt );
1284 }
1285 
StateChanged(StateChangedType nType)1286 void MenuFloatingWindow::StateChanged( StateChangedType nType )
1287 {
1288     FloatingWindow::StateChanged( nType );
1289 
1290     if ( ( nType == StateChangedType::ControlForeground ) || ( nType == StateChangedType::ControlBackground ) )
1291     {
1292         ApplySettings(*GetOutDev());
1293         Invalidate();
1294     }
1295 }
1296 
DataChanged(const DataChangedEvent & rDCEvt)1297 void MenuFloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
1298 {
1299     FloatingWindow::DataChanged( rDCEvt );
1300 
1301     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1302          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1303          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1304           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1305     {
1306         ApplySettings(*GetOutDev());
1307         Invalidate();
1308     }
1309 }
1310 
Command(const CommandEvent & rCEvt)1311 void MenuFloatingWindow::Command( const CommandEvent& rCEvt )
1312 {
1313     if ( rCEvt.GetCommand() == CommandEventId::Wheel )
1314     {
1315         const CommandWheelData* pData = rCEvt.GetWheelData();
1316         if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
1317         {
1318             ImplScroll( pData->GetDelta() > 0 );
1319             MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
1320         }
1321     }
1322 }
1323 
CreateAccessible()1324 css::uno::Reference<css::accessibility::XAccessible> MenuFloatingWindow::CreateAccessible()
1325 {
1326     css::uno::Reference<css::accessibility::XAccessible> xAcc;
1327 
1328     if (pMenu && !pMenu->pStartedFrom)
1329         xAcc = pMenu->GetAccessible();
1330 
1331     return xAcc;
1332 }
1333 
1334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1335