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