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     aHighlightChangedTimer("vcl::MenuFloatingWindow aHighlightChangedTimer"),
36     aSubmenuCloseTimer( "vcl::MenuFloatingWindow aSubmenuCloseTimer" ),
37     aScrollTimer( "vcl::MenuFloatingWindow aScrollTimer" ),
38     nHighlightedItem(ITEMPOS_INVALID),
39     nMBDownPos(ITEMPOS_INVALID),
40     nScrollerHeight(0),
41     nFirstEntry(0),
42     nPosInParent(ITEMPOS_INVALID),
43     bInExecute(false),
44     bScrollMenu(false),
45     bScrollUp(false),
46     bScrollDown(false),
47     bIgnoreFirstMove(true),
48     bKeyInput(false)
49 {
50     mpWindowImpl->mbMenuFloatingWindow= true;
51 
52     ApplySettings(*GetOutDev());
53 
54     SetPopupModeEndHdl( LINK( this, MenuFloatingWindow, PopupEnd ) );
55 
56     aHighlightChangedTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, HighlightChanged ) );
57     aHighlightChangedTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
58 
59     aSubmenuCloseTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
60     aSubmenuCloseTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, SubmenuClose ) );
61 
62     aScrollTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, AutoScroll ) );
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 = 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.disposeAndClear();
503 
504         PaintImmediately();
505     }
506 }
507 
EndExecute()508 void MenuFloatingWindow::EndExecute()
509 {
510     Menu* pStart = pMenu ? pMenu->ImplGetStartMenu() : nullptr;
511 
512     // if started elsewhere, cleanup there as well
513     MenuFloatingWindow* pCleanUpFrom = this;
514     MenuFloatingWindow* pWin = this;
515     while (pWin && !pWin->bInExecute &&
516         pWin->pMenu->pStartedFrom && !pWin->pMenu->pStartedFrom->IsMenuBar())
517     {
518         pWin = static_cast<PopupMenu*>(pWin->pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
519     }
520     if ( pWin )
521         pCleanUpFrom = pWin;
522 
523     // this window will be destroyed => store date locally...
524     Menu* pM = pMenu;
525     sal_uInt16 nItem = nHighlightedItem;
526 
527     pCleanUpFrom->StopExecute();
528 
529     if ( !(nItem != ITEMPOS_INVALID && pM) )
530         return;
531 
532     MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem );
533     if ( pItemData && !pItemData->bIsTemporary )
534     {
535         pM->nSelectedId = pItemData->nId;
536         pM->sSelectedIdent = pItemData->sIdent;
537         if (pStart)
538         {
539             pStart->nSelectedId = pItemData->nId;
540             pStart->sSelectedIdent = pItemData->sIdent;
541         }
542 
543         pM->ImplSelect();
544     }
545 }
546 
EndExecute(sal_uInt16 nId)547 void MenuFloatingWindow::EndExecute( sal_uInt16 nId )
548 {
549     size_t nPos;
550     if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) )
551         nHighlightedItem = nPos;
552     else
553         nHighlightedItem = ITEMPOS_INVALID;
554 
555     EndExecute();
556 }
557 
MouseButtonDown(const MouseEvent & rMEvt)558 void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt )
559 {
560     // TH creates a ToTop on this window, but the active popup
561     // should stay on top...
562     // due to focus change this would close all menus -> don't do it (#94123)
563     //if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup )
564     //    pActivePopup->ImplGetFloatingWindow()->ToTop( ToTopFlags::NoGrabFocus );
565 
566     ImplHighlightItem( rMEvt, true );
567 
568     nMBDownPos = nHighlightedItem;
569 }
570 
MouseButtonUp(const MouseEvent & rMEvt)571 void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt )
572 {
573     MenuItemData* pData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : nullptr;
574     // nMBDownPos store in local variable and reset immediately,
575     // as it will be too late after EndExecute
576     sal_uInt16 _nMBDownPos = nMBDownPos;
577     nMBDownPos = ITEMPOS_INVALID;
578     if ( !(pData && pData->bEnabled && ( pData->eType != MenuItemType::SEPARATOR )) )
579         return;
580 
581     if ( !pData->pSubMenu )
582     {
583         EndExecute();
584     }
585     else if ( ( pData->nBits & MenuItemBits::POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) )
586     {
587         // not when clicked over the arrow...
588         Size aSz = GetOutputSizePixel();
589         tools::Long nFontHeight = GetTextHeight();
590         if ( rMEvt.GetPosPixel().X() < ( aSz.Width() - nFontHeight - nFontHeight/4 ) )
591             EndExecute();
592     }
593 
594 }
595 
MouseMove(const MouseEvent & rMEvt)596 void MenuFloatingWindow::MouseMove( const MouseEvent& rMEvt )
597 {
598     if ( !IsVisible() || rMEvt.IsSynthetic() || rMEvt.IsEnterWindow() )
599         return;
600 
601     if ( rMEvt.IsLeaveWindow() )
602     {
603         // #102461# do not remove highlight if a popup menu is open at this position
604         MenuItemData* pData = pMenu ? pMenu->pItemList->GetDataFromPos( nHighlightedItem ) : nullptr;
605         // close popup with some delayed if we leave somewhere else
606         if( pActivePopup && pData && pData->pSubMenu != pActivePopup )
607             pActivePopup->ImplGetFloatingWindow()->aSubmenuCloseTimer.Start();
608 
609         if( !pActivePopup || (pData && pData->pSubMenu != pActivePopup ) )
610             ChangeHighlightItem( ITEMPOS_INVALID, false );
611 
612         if ( IsScrollMenu() )
613             ImplScroll( rMEvt.GetPosPixel() );
614     }
615     else
616     {
617         aSubmenuCloseTimer.Stop();
618         if( bIgnoreFirstMove )
619             bIgnoreFirstMove = false;
620         else
621             ImplHighlightItem( rMEvt, false );
622     }
623 }
624 
ImplScroll(bool bUp)625 void MenuFloatingWindow::ImplScroll( bool bUp )
626 {
627     KillActivePopup();
628     PaintImmediately();
629 
630     if (!pMenu)
631         return;
632 
633     Invalidate();
634 
635     pMenu->ImplKillLayoutData();
636 
637     if ( bScrollUp && bUp )
638     {
639         nFirstEntry = pMenu->ImplGetPrevVisible( nFirstEntry );
640         SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
641 
642         // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
643         const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
644         if ( pItemData )
645         {
646             tools::Long nScrollEntryHeight = pItemData->aSz.Height();
647 
648             if ( !bScrollDown )
649             {
650                 bScrollDown = true;
651                 Invalidate();
652             }
653 
654             if ( pMenu->ImplGetPrevVisible( nFirstEntry ) == ITEMPOS_INVALID )
655             {
656                 bScrollUp = false;
657                 Invalidate();
658             }
659 
660             Scroll( 0, nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
661         }
662     }
663     else if ( bScrollDown && !bUp )
664     {
665         // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
666         const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
667         if ( pItemData )
668         {
669             tools::Long nScrollEntryHeight = pItemData->aSz.Height();
670 
671             nFirstEntry = pMenu->ImplGetNextVisible( nFirstEntry );
672             SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
673 
674             if ( !bScrollUp )
675             {
676                 bScrollUp = true;
677                 Invalidate();
678             }
679 
680             tools::Long nHeight = GetOutputSizePixel().Height();
681             sal_uInt16 nLastVisible;
682             static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible );
683             if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID )
684             {
685                 bScrollDown = false;
686                 Invalidate();
687             }
688 
689             Scroll( 0, -nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
690         }
691     }
692 
693     Invalidate();
694 }
695 
ImplScroll(const Point & rMousePos)696 void MenuFloatingWindow::ImplScroll( const Point& rMousePos )
697 {
698     Size aOutSz = GetOutputSizePixel();
699 
700     tools::Long nY = nScrollerHeight;
701     tools::Long nMouseY = rMousePos.Y();
702     tools::Long nDelta = 0;
703 
704     if ( bScrollUp && ( nMouseY < nY ) )
705     {
706         ImplScroll( true );
707         nDelta = nY - nMouseY;
708     }
709     else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) )
710     {
711         ImplScroll( false );
712         nDelta = nMouseY - ( aOutSz.Height() - nY );
713     }
714 
715     if ( !nDelta )
716         return;
717 
718     aScrollTimer.Stop();    // if scrolled through MouseMove.
719     tools::Long nTimeout;
720     if ( nDelta < 3 )
721         nTimeout = 200;
722     else if ( nDelta < 5 )
723         nTimeout = 100;
724     else if ( nDelta < 8 )
725         nTimeout = 70;
726     else if ( nDelta < 12 )
727         nTimeout = 40;
728     else
729         nTimeout = 20;
730     aScrollTimer.SetTimeout( nTimeout );
731     aScrollTimer.Start();
732 }
ChangeHighlightItem(sal_uInt16 n,bool bStartPopupTimer)733 void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer )
734 {
735     // #57934# if necessary, immediately close the active, as TH's backgroundstorage works.
736     // #65750# we prefer to refrain from the background storage of small lines.
737     //         otherwise the menus are difficult to operate.
738     //  MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
739     //  if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) )
740     //      KillActivePopup();
741 
742     aSubmenuCloseTimer.Stop();
743     if( ! pMenu )
744         return;
745 
746     if ( nHighlightedItem != ITEMPOS_INVALID )
747     {
748         InvalidateItem(nHighlightedItem);
749         pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, nHighlightedItem );
750     }
751 
752     nHighlightedItem = n;
753     SAL_WARN_IF( !pMenu->ImplIsVisible( nHighlightedItem ) && nHighlightedItem != ITEMPOS_INVALID, "vcl", "ChangeHighlightItem: Not visible!" );
754     if( nHighlightedItem != ITEMPOS_INVALID )
755     {
756         if (pMenu->pStartedFrom && !pMenu->pStartedFrom->IsMenuBar())
757         {
758             // #102461# make sure parent entry is highlighted as well
759             size_t i, nCount = pMenu->pStartedFrom->pItemList->size();
760             for(i = 0; i < nCount; i++)
761             {
762                 MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
763                 if( pData && ( pData->pSubMenu == pMenu ) )
764                     break;
765             }
766             if( i < nCount )
767             {
768                 MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
769                 if( pPWin && pPWin->nHighlightedItem != i )
770                 {
771                     pPWin->InvalidateItem(i);
772                     pPWin->nHighlightedItem = i;
773                 }
774             }
775         }
776         InvalidateItem(nHighlightedItem);
777         pMenu->ImplCallHighlight( nHighlightedItem );
778     }
779     else
780     {
781         pMenu->nSelectedId = 0;
782         pMenu->sSelectedIdent.clear();
783     }
784 
785     if ( bStartPopupTimer )
786     {
787         // #102438# Menu items are not selectable
788         // If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue
789         // or XAccessibleSelection interface, and the parent popup menus are not executed yet,
790         // the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected.
791         if ( GetSettings().GetMouseSettings().GetMenuDelay() )
792             aHighlightChangedTimer.Start();
793         else
794             HighlightChanged( &aHighlightChangedTimer );
795     }
796 }
797 
798 /// Calculate the initial vertical pixel offset of the first item.
799 /// May be negative for scrolled windows.
GetInitialItemY(tools::Long * pStartY) const800 tools::Long MenuFloatingWindow::GetInitialItemY(tools::Long *pStartY) const
801 {
802     tools::Long nStartY = ImplGetStartY();
803     if (pStartY)
804         *pStartY = nStartY;
805     return nScrollerHeight + nStartY +
806         ImplGetSVData()->maNWFData.mnMenuFormatBorderY;
807 }
808 
809 /// Emit an Invalidate just for this item's area
InvalidateItem(sal_uInt16 nPos)810 void MenuFloatingWindow::InvalidateItem(sal_uInt16 nPos)
811 {
812     if (!pMenu)
813         return;
814 
815     tools::Long nY = GetInitialItemY();
816     size_t nCount = pMenu->pItemList->size();
817     for (size_t n = 0; n < nCount; n++)
818     {
819         MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
820         tools::Long nHeight = pData->aSz.Height();
821         if (n == nPos)
822         {
823             Size aWidth( GetSizePixel() );
824             tools::Rectangle aRect(Point(0, nY), Size(aWidth.Width(), nHeight));
825             Invalidate( aRect );
826         }
827         nY += nHeight;
828     }
829 }
830 
RenderHighlightItem(vcl::RenderContext & rRenderContext,sal_uInt16 nPos)831 void MenuFloatingWindow::RenderHighlightItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos)
832 {
833     if (!pMenu)
834         return;
835 
836     Size aSz(GetOutputSizePixel());
837 
838     tools::Long nX = 0;
839     tools::Long nStartY;
840     tools::Long nY = GetInitialItemY(&nStartY);
841 
842     int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
843 
844     size_t nCount = pMenu->pItemList->size();
845     for (size_t n = 0; n < nCount; n++)
846     {
847         MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
848         if (n == nPos)
849         {
850             SAL_WARN_IF(!pMenu->ImplIsVisible(n), "vcl", "Highlight: Item not visible!");
851             if (pData->eType != MenuItemType::SEPARATOR)
852             {
853                 bool bRestoreLineColor = false;
854                 Color oldLineColor;
855                 bool bDrawItemRect = true;
856 
857                 tools::Rectangle aItemRect(Point(nX + nOuterSpaceX, nY), Size(aSz.Width() - 2 * nOuterSpaceX, pData->aSz.Height()));
858                 if (pData->nBits & MenuItemBits::POPUPSELECT)
859                 {
860                     tools::Long nFontHeight = GetTextHeight();
861                     aItemRect.AdjustRight( -(nFontHeight + nFontHeight / 4) );
862                 }
863 
864                 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
865                 {
866                     Size aPxSize(GetOutputSizePixel());
867                     rRenderContext.Push(vcl::PushFlags::CLIPREGION);
868                     rRenderContext.IntersectClipRegion(tools::Rectangle(Point(nX, nY), Size(aSz.Width(), pData->aSz.Height())));
869                     tools::Rectangle aCtrlRect(Point(nX, 0), Size(aPxSize.Width()-nX, aPxSize.Height()));
870                     MenupopupValue aVal(pMenu->nTextPos-GUTTERBORDER, aItemRect);
871                     rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
872                                                      aCtrlRect, ControlState::ENABLED, aVal, OUString());
873                     if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem))
874                     {
875                         bDrawItemRect = false;
876                         if (!rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::MenuItem, aItemRect,
877                                                               ControlState::SELECTED | (pData->bEnabled
878                                                                                             ? ControlState::ENABLED
879                                                                                             : ControlState::NONE),
880                                                               aVal, OUString()))
881                         {
882                             bDrawItemRect = true;
883                         }
884                     }
885                     else
886                         bDrawItemRect = true;
887                     rRenderContext.Pop();
888                 }
889                 if (bDrawItemRect)
890                 {
891                     if (pData->bEnabled)
892                         rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
893                     else
894                     {
895                         rRenderContext.SetFillColor();
896                         oldLineColor = rRenderContext.GetLineColor();
897                         rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
898                         bRestoreLineColor = true;
899                     }
900 
901                     rRenderContext.DrawRect(aItemRect);
902                 }
903                 pMenu->ImplPaint(rRenderContext, GetOutputSizePixel(), nScrollerHeight, nStartY, pData, true/*bHighlight*/);
904                 if (bRestoreLineColor)
905                     rRenderContext.SetLineColor(oldLineColor);
906             }
907             return;
908         }
909 
910         nY += pData->aSz.Height();
911     }
912 }
913 
ImplGetItemRect(sal_uInt16 nPos) const914 tools::Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos ) const
915 {
916     if( ! pMenu )
917         return tools::Rectangle();
918 
919     tools::Rectangle aRect;
920     Size    aSz = GetOutputSizePixel();
921     tools::Long    nStartY = ImplGetStartY();
922     tools::Long    nY = nScrollerHeight+nStartY;
923 
924     size_t nCount = pMenu->pItemList->size();
925     for ( size_t n = 0; n < nCount; n++ )
926     {
927         MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
928         if ( n == nPos )
929         {
930             SAL_WARN_IF( !pMenu->ImplIsVisible( n ), "vcl", "ImplGetItemRect: Item not visible!" );
931             if ( pData->eType != MenuItemType::SEPARATOR )
932             {
933                 aRect = tools::Rectangle( Point( 0, nY ), Size( aSz.Width(), pData->aSz.Height() ) );
934                 if ( pData->nBits & MenuItemBits::POPUPSELECT )
935                 {
936                     tools::Long nFontHeight = GetTextHeight();
937                     aRect.AdjustRight( -(nFontHeight + nFontHeight/4) );
938                 }
939             }
940             break;
941         }
942         nY += pData->aSz.Height();
943     }
944     return aRect;
945 }
946 
ImplCursorUpDown(bool bUp,bool bHomeEnd)947 void MenuFloatingWindow::ImplCursorUpDown( bool bUp, bool bHomeEnd )
948 {
949     if( ! pMenu )
950         return;
951 
952     const StyleSettings& rSettings = GetSettings().GetStyleSettings();
953 
954     sal_uInt16 n = nHighlightedItem;
955     if ( n == ITEMPOS_INVALID )
956     {
957         if ( bUp )
958             n = 0;
959         else
960             n = pMenu->GetItemCount()-1;
961     }
962 
963     sal_uInt16 nLoop = n;
964 
965     if( bHomeEnd )
966     {
967         // absolute positioning
968         if( bUp )
969         {
970             n = pMenu->GetItemCount();
971             nLoop = n-1;
972         }
973         else
974         {
975             n = sal_uInt16(-1);
976             nLoop = n+1;
977         }
978     }
979 
980     do
981     {
982         if ( bUp )
983         {
984             if ( n )
985                 n--;
986             else
987                 if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
988                     n = pMenu->GetItemCount()-1;
989                 else
990                     break;
991         }
992         else
993         {
994             n++;
995             if ( n >= pMenu->GetItemCount() )
996             {
997                 if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
998                     n = 0;
999                 else
1000                     break;
1001             }
1002         }
1003 
1004         MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( n );
1005         if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() )
1006               && ( pData->eType != MenuItemType::SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) )
1007         {
1008             // Is selection in visible area?
1009             if ( IsScrollMenu() )
1010             {
1011                 ChangeHighlightItem( ITEMPOS_INVALID, false );
1012 
1013                 while ( n < nFirstEntry )
1014                     ImplScroll( true );
1015 
1016                 Size aOutSz = GetOutputSizePixel();
1017                 sal_uInt16 nLastVisible;
1018                 static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
1019                 while ( n > nLastVisible )
1020                 {
1021                     ImplScroll( false );
1022                     static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
1023                 }
1024             }
1025             ChangeHighlightItem( n, false );
1026             break;
1027         }
1028     } while ( n != nLoop );
1029 }
1030 
KeyInput(const KeyEvent & rKEvent)1031 void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent )
1032 {
1033     VclPtr<vcl::Window> xWindow = this;
1034 
1035     bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
1036     sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
1037     bKeyInput = true;
1038     switch ( nCode )
1039     {
1040         case KEY_UP:
1041         case KEY_DOWN:
1042         {
1043             ImplCursorUpDown( nCode == KEY_UP );
1044         }
1045         break;
1046         case KEY_END:
1047         case KEY_HOME:
1048         {
1049             ImplCursorUpDown( nCode == KEY_END, true );
1050         }
1051         break;
1052         case KEY_F6:
1053         case KEY_ESCAPE:
1054         {
1055             // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document
1056             if( nCode == KEY_F6 && !rKEvent.GetKeyCode().IsMod1() )
1057                 break;
1058             if( pMenu )
1059             {
1060                 if ( !pMenu->pStartedFrom )
1061                 {
1062                     StopExecute();
1063                     KillActivePopup();
1064                 }
1065                 else if (pMenu->pStartedFrom->IsMenuBar())
1066                 {
1067                     pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
1068                 }
1069                 else
1070                 {
1071                     StopExecute();
1072                     PopupMenu* pPopupMenu = static_cast<PopupMenu*>(pMenu->pStartedFrom.get());
1073                     MenuFloatingWindow* pFloat = pPopupMenu->ImplGetFloatingWindow();
1074                     pFloat->GrabFocus();
1075                     pFloat->KillActivePopup();
1076                     pPopupMenu->ImplCallHighlight(pFloat->nHighlightedItem);
1077                 }
1078             }
1079         }
1080         break;
1081         case KEY_LEFT:
1082         {
1083             if ( pMenu && pMenu->pStartedFrom )
1084             {
1085                 StopExecute();
1086                 if (pMenu->pStartedFrom->IsMenuBar())
1087                 {
1088                     pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
1089                 }
1090                 else
1091                 {
1092                     MenuFloatingWindow* pFloat = static_cast<PopupMenu*>(pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
1093                     pFloat->GrabFocus();
1094                     pFloat->KillActivePopup();
1095                     sal_uInt16 highlightItem = pFloat->GetHighlightedItem();
1096                     pFloat->ChangeHighlightItem(highlightItem, false);
1097                 }
1098             }
1099         }
1100         break;
1101         case KEY_RIGHT:
1102         {
1103             if( pMenu )
1104             {
1105                 bool bDone = false;
1106                 if ( nHighlightedItem != ITEMPOS_INVALID )
1107                 {
1108                     MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
1109                     if ( pData && pData->pSubMenu )
1110                     {
1111                         HighlightChanged( nullptr );
1112                         bDone = true;
1113                     }
1114                 }
1115                 if ( !bDone )
1116                 {
1117                     Menu* pStart = pMenu->ImplGetStartMenu();
1118                     if (pStart && pStart->IsMenuBar())
1119                     {
1120                         // Forward...
1121                         pStart->ImplGetWindow()->KeyInput( rKEvent );
1122                     }
1123                 }
1124             }
1125         }
1126         break;
1127         case KEY_RETURN:
1128         {
1129             if( pMenu )
1130             {
1131                 MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
1132                 if ( pData && pData->bEnabled )
1133                 {
1134                     if ( pData->pSubMenu )
1135                         HighlightChanged( nullptr );
1136                     else
1137                         EndExecute();
1138                 }
1139                 else
1140                     StopExecute();
1141             }
1142         }
1143         break;
1144         case KEY_MENU:
1145         {
1146             if( pMenu )
1147             {
1148                 Menu* pStart = pMenu->ImplGetStartMenu();
1149                 if (pStart && pStart->IsMenuBar())
1150                 {
1151                     // Forward...
1152                     pStart->ImplGetWindow()->KeyInput( rKEvent );
1153                 }
1154             }
1155         }
1156         break;
1157         default:
1158         {
1159             sal_Unicode nCharCode = rKEvent.GetCharCode();
1160             size_t nPos = 0;
1161             size_t nDuplicates = 0;
1162             MenuItemData* pData = (nCharCode && pMenu) ?
1163                 pMenu->GetItemList()->SearchItem(nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem) : nullptr;
1164             if (pData)
1165             {
1166                 if ( pData->pSubMenu || nDuplicates > 1 )
1167                 {
1168                     ChangeHighlightItem( nPos, false );
1169                     HighlightChanged( nullptr );
1170                 }
1171                 else
1172                 {
1173                     nHighlightedItem = nPos;
1174                     EndExecute();
1175                 }
1176             }
1177             else
1178                 FloatingWindow::KeyInput( rKEvent );
1179         }
1180     }
1181 
1182     if (pMenu && pMenu->pStartedFrom && pMenu->pStartedFrom->IsMenuBar())
1183     {
1184         MenuBar *pMenuBar = static_cast<MenuBar*>(pMenu->pStartedFrom.get());
1185         const bool bShowAccels = !autoacc || nCode != KEY_ESCAPE;
1186         if (pMenuBar->getMenuBarWindow()->GetMBWMenuKey() != bShowAccels)
1187         {
1188             pMenuBar->getMenuBarWindow()->SetMBWMenuKey(bShowAccels);
1189             pMenuBar->getMenuBarWindow()->SetMBWHideAccel(!bShowAccels);
1190             if (autoacc)
1191                 Invalidate(InvalidateFlags::Update);
1192         }
1193     }
1194 
1195     // #105474# check if menu window was not destroyed
1196     if ( !xWindow->isDisposed() )
1197     {
1198         bKeyInput = false;
1199     }
1200 }
1201 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rPaintRect)1202 void MenuFloatingWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect)
1203 {
1204     if (!pMenu)
1205         return;
1206 
1207     // Set the clip before the buffering starts: rPaintRect may be larger than the current clip,
1208     // this way the buffer -> render context copy happens with this clip.
1209     rRenderContext.Push(vcl::PushFlags::CLIPREGION);
1210     rRenderContext.SetClipRegion(vcl::Region(rPaintRect));
1211 
1212     // Make sure that all actual rendering happens in one go to avoid flicker.
1213     vcl::BufferDevice pBuffer(this, rRenderContext);
1214 
1215     if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
1216     {
1217         pBuffer->SetClipRegion();
1218         tools::Long nX = 0;
1219         Size aPxSize(GetOutputSizePixel());
1220         aPxSize.AdjustWidth( -nX );
1221         ImplControlValue aVal(pMenu->nTextPos - GUTTERBORDER);
1222         pBuffer->DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
1223                                    tools::Rectangle(Point(nX, 0), aPxSize), ControlState::ENABLED,
1224                                    aVal, OUString());
1225         InitMenuClipRegion(*pBuffer);
1226     }
1227     if (IsScrollMenu())
1228     {
1229         ImplDrawScroller(*pBuffer, true);
1230         ImplDrawScroller(*pBuffer, false);
1231     }
1232     pBuffer->SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuColor());
1233     pMenu->ImplPaint(*pBuffer, GetOutputSizePixel(), nScrollerHeight, ImplGetStartY());
1234     if (nHighlightedItem != ITEMPOS_INVALID)
1235         RenderHighlightItem(*pBuffer, nHighlightedItem);
1236 
1237     pBuffer.Dispose();
1238     rRenderContext.Pop();
1239 }
1240 
ImplDrawScroller(vcl::RenderContext & rRenderContext,bool bUp)1241 void MenuFloatingWindow::ImplDrawScroller(vcl::RenderContext& rRenderContext, bool bUp)
1242 {
1243     if (!pMenu)
1244         return;
1245 
1246     rRenderContext.SetClipRegion();
1247 
1248     Size aOutSz(GetOutputSizePixel());
1249     tools::Long nY = bUp ? 0 : (aOutSz.Height() - nScrollerHeight);
1250     tools::Long nX = 0;
1251     tools::Rectangle aRect(Point(nX, nY), Size(aOutSz.Width() - nX, nScrollerHeight));
1252 
1253     DecorationView aDecoView(&rRenderContext);
1254     SymbolType eSymbol = bUp ? SymbolType::SPIN_UP : SymbolType::SPIN_DOWN;
1255 
1256     DrawSymbolFlags nStyle = DrawSymbolFlags::NONE;
1257     if ((bUp && !bScrollUp) || (!bUp && !bScrollDown))
1258         nStyle |= DrawSymbolFlags::Disable;
1259 
1260     aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle);
1261 
1262     InitMenuClipRegion(rRenderContext);
1263 }
1264 
RequestHelp(const HelpEvent & rHEvt)1265 void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt )
1266 {
1267     sal_uInt16 nId = nHighlightedItem;
1268     Menu* pM = pMenu;
1269     vcl::Window* pW = this;
1270 
1271     // #102618# Get item rect before destroying the window in EndExecute() call
1272     tools::Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) );
1273 
1274     if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
1275     {
1276         nHighlightedItem = ITEMPOS_INVALID;
1277         EndExecute();
1278         pW = nullptr;
1279     }
1280 
1281     if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) )
1282         Window::RequestHelp( rHEvt );
1283 }
1284 
StateChanged(StateChangedType nType)1285 void MenuFloatingWindow::StateChanged( StateChangedType nType )
1286 {
1287     FloatingWindow::StateChanged( nType );
1288 
1289     if ( ( nType == StateChangedType::ControlForeground ) || ( nType == StateChangedType::ControlBackground ) )
1290     {
1291         ApplySettings(*GetOutDev());
1292         Invalidate();
1293     }
1294 }
1295 
DataChanged(const DataChangedEvent & rDCEvt)1296 void MenuFloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
1297 {
1298     FloatingWindow::DataChanged( rDCEvt );
1299 
1300     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1301          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1302          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1303           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1304     {
1305         ApplySettings(*GetOutDev());
1306         Invalidate();
1307     }
1308 }
1309 
Command(const CommandEvent & rCEvt)1310 void MenuFloatingWindow::Command( const CommandEvent& rCEvt )
1311 {
1312     if ( rCEvt.GetCommand() == CommandEventId::Wheel )
1313     {
1314         const CommandWheelData* pData = rCEvt.GetWheelData();
1315         if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
1316         {
1317             ImplScroll( pData->GetDelta() > 0 );
1318             MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
1319         }
1320     }
1321 }
1322 
CreateAccessible()1323 css::uno::Reference<css::accessibility::XAccessible> MenuFloatingWindow::CreateAccessible()
1324 {
1325     css::uno::Reference<css::accessibility::XAccessible> xAcc;
1326 
1327     if (pMenu && !pMenu->pStartedFrom)
1328         xAcc = pMenu->GetAccessible();
1329 
1330     return xAcc;
1331 }
1332 
1333 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1334