Coverage Report

Created: 2026-02-14 09:37

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