Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/control/MenuButton.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <vcl/dockwin.hxx>
21
#include <vcl/event.hxx>
22
#include <vcl/toolkit/floatwin.hxx>
23
#include <vcl/menu.hxx>
24
#include <vcl/timer.hxx>
25
#include <vcl/toolkit/MenuButton.hxx>
26
#include <vcl/settings.hxx>
27
#include <vcl/uitest/uiobject.hxx>
28
#include <vcl/uitest/logger.hxx>
29
#include <vcl/uitest/eventdescription.hxx>
30
#include <menutogglebutton.hxx>
31
#include <tools/json_writer.hxx>
32
33
namespace
34
{
35
void collectUIInformation( const OUString& aID, const OUString& aevent , const OUString& akey , const OUString& avalue)
36
0
{
37
0
    EventDescription aDescription;
38
0
    aDescription.aID = aID;
39
0
    aDescription.aParameters = {{ akey ,  avalue}};
40
0
    aDescription.aAction = aevent;
41
0
    aDescription.aParent = "MainWindow";
42
0
    aDescription.aKeyWord = "MenuButton";
43
0
    UITestLogger::getInstance().logEvent(aDescription);
44
0
}
45
}
46
47
void MenuButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
48
0
{
49
0
    if ( !(nStyle & WB_NOTABSTOP) )
50
0
        nStyle |= WB_TABSTOP;
51
52
0
    PushButton::ImplInit( pParent, nStyle );
53
0
    EnableRTL( AllSettings::GetLayoutRTL() );
54
0
}
55
56
void MenuButton::ExecuteMenu()
57
0
{
58
0
    mbStartingMenu = true;
59
60
0
    PrepareExecute();
61
62
0
    if (!mpMenu && !mpFloatingWindow)
63
0
    {
64
0
        mbStartingMenu = false;
65
0
        return;
66
0
    }
67
68
0
    Size aSize = GetSizePixel();
69
0
    SetPressed( true );
70
0
    EndSelection();
71
0
    if (mpMenu)
72
0
    {
73
0
        Point aPos(0, 1);
74
0
        tools::Rectangle aRect(aPos, aSize );
75
0
        mpMenu->Execute(this, aRect, PopupMenuFlags::ExecuteDown);
76
77
0
        if (isDisposed())
78
0
            return;
79
80
0
        mnCurItemId = mpMenu->GetCurItemId();
81
0
        msCurItemIdent = mpMenu->GetCurItemIdent();
82
0
    }
83
0
    else
84
0
    {
85
0
        Point aPos(GetParent()->OutputToScreenPixel(GetPosPixel()));
86
0
        tools::Rectangle aRect(aPos, aSize );
87
0
        FloatWinPopupFlags nFlags = FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus;
88
0
        if (mpFloatingWindow->GetType() == WindowType::FLOATINGWINDOW)
89
0
            static_cast<FloatingWindow*>(mpFloatingWindow.get())->StartPopupMode(aRect, nFlags);
90
0
        else
91
0
        {
92
0
            mpFloatingWindow->EnableDocking();
93
0
            vcl::Window::GetDockingManager()->StartPopupMode(mpFloatingWindow, aRect, nFlags);
94
0
        }
95
0
    }
96
97
0
    Activate();
98
99
0
    mbStartingMenu = false;
100
101
0
    SetPressed(false);
102
0
    OUString aID = get_id(); // tdf#136678 take a copy if we are destroyed by Select callback
103
0
    if (mnCurItemId)
104
0
    {
105
0
        Select();
106
0
        mnCurItemId = 0;
107
0
        msCurItemIdent.clear();
108
0
    }
109
0
    collectUIInformation(aID,u"OPENLIST"_ustr,u""_ustr,u""_ustr);
110
0
}
111
112
void MenuButton::CancelMenu()
113
0
{
114
0
    if (!mpMenu && !mpFloatingWindow)
115
0
        return;
116
117
0
    if (mpMenu)
118
0
    {
119
0
        mpMenu->EndExecute();
120
0
    }
121
0
    else
122
0
    {
123
0
        if (mpFloatingWindow->GetType() == WindowType::FLOATINGWINDOW)
124
0
            static_cast<FloatingWindow*>(mpFloatingWindow.get())->EndPopupMode();
125
0
        else
126
0
            vcl::Window::GetDockingManager()->EndPopupMode(mpFloatingWindow);
127
0
    }
128
0
    collectUIInformation(get_id(),u"CLOSELIST"_ustr,u""_ustr,u""_ustr);
129
0
}
130
131
bool MenuButton::InPopupMode() const
132
0
{
133
0
    if (mbStartingMenu)
134
0
        return true;
135
136
0
    if (!mpMenu && !mpFloatingWindow)
137
0
        return false;
138
139
0
    if (mpMenu)
140
0
       return PopupMenu::GetActivePopupMenu() == mpMenu;
141
0
    else
142
0
    {
143
0
        if (mpFloatingWindow->GetType() == WindowType::FLOATINGWINDOW)
144
0
            return static_cast<const FloatingWindow*>(mpFloatingWindow.get())->IsInPopupMode();
145
0
        else
146
0
            return vcl::Window::GetDockingManager()->IsInPopupMode(mpFloatingWindow);
147
0
    }
148
0
}
149
150
MenuButton::MenuButton( vcl::Window* pParent, WinBits nWinBits )
151
0
    : PushButton(WindowType::MENUBUTTON)
152
0
    , mnCurItemId(0)
153
0
    , mbDelayMenu(false)
154
0
    , mbStartingMenu(false)
155
0
{
156
0
    mnDDStyle = PushButtonDropdownStyle::MenuButton;
157
0
    ImplInit(pParent, nWinBits);
158
0
}
Unexecuted instantiation: MenuButton::MenuButton(vcl::Window*, long)
Unexecuted instantiation: MenuButton::MenuButton(vcl::Window*, long)
159
160
MenuButton::~MenuButton()
161
0
{
162
0
    disposeOnce();
163
0
}
164
165
void MenuButton::dispose()
166
0
{
167
0
    mpMenuTimer.reset();
168
0
    mpFloatingWindow.reset();
169
0
    if (mpMenu && mbOwnPopupMenu)
170
0
        mpMenu->dispose();
171
0
    mpMenu.reset();
172
0
    PushButton::dispose();
173
0
}
174
175
IMPL_LINK_NOARG(MenuButton, ImplMenuTimeoutHdl, Timer *, void)
176
0
{
177
    // See if Button Tracking is still active, as it could've been cancelled earlier
178
0
    if ( IsTracking() )
179
0
    {
180
0
        if ( !(GetStyle() & WB_NOPOINTERFOCUS) )
181
0
            GrabFocus();
182
0
        ExecuteMenu();
183
0
    }
184
0
}
185
186
void MenuButton::MouseButtonDown( const MouseEvent& rMEvt )
187
0
{
188
0
    bool bExecute = true;
189
0
    if (mbDelayMenu)
190
0
    {
191
        // If the separated dropdown symbol is not hit, delay the popup execution
192
0
        if( rMEvt.GetPosPixel().X() <= ImplGetSeparatorX() )
193
0
        {
194
0
            if ( !mpMenuTimer )
195
0
            {
196
0
                mpMenuTimer.reset(new Timer("MenuTimer"));
197
0
                mpMenuTimer->SetInvokeHandler( LINK( this, MenuButton, ImplMenuTimeoutHdl ) );
198
0
            }
199
200
0
            mpMenuTimer->SetTimeout( MouseSettings::GetActionDelay() );
201
0
            mpMenuTimer->Start();
202
203
0
            PushButton::MouseButtonDown( rMEvt );
204
0
            bExecute = false;
205
0
        }
206
0
    }
207
0
    if( bExecute )
208
0
    {
209
0
        if ( PushButton::ImplHitTestPushButton( this, rMEvt.GetPosPixel() ) )
210
0
        {
211
0
            if ( !(GetStyle() & WB_NOPOINTERFOCUS) )
212
0
                GrabFocus();
213
0
            ExecuteMenu();
214
0
        }
215
0
    }
216
0
}
217
218
void MenuButton::KeyInput( const KeyEvent& rKEvt )
219
0
{
220
0
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
221
0
    sal_uInt16 nCode = aKeyCode.GetCode();
222
0
    if ( (nCode == KEY_DOWN) && aKeyCode.IsMod2() )
223
0
        ExecuteMenu();
224
0
    else if ( !mbDelayMenu &&
225
0
              !aKeyCode.GetModifier() &&
226
0
              ((nCode == KEY_RETURN) || (nCode == KEY_SPACE)) )
227
0
        ExecuteMenu();
228
0
    else
229
0
        PushButton::KeyInput( rKEvt );
230
0
}
231
232
void MenuButton::Activate()
233
0
{
234
0
    maActivateHdl.Call( this );
235
0
}
236
237
void MenuButton::Select()
238
0
{
239
0
    if (mnCurItemId)
240
0
        collectUIInformation(get_id(),u"OPENFROMLIST"_ustr,u"POS"_ustr,OUString::number(mnCurItemId));
241
242
0
    maSelectHdl.Call( this );
243
0
}
244
245
void MenuButton::SetPopupMenu(PopupMenu* pNewMenu, bool bTakeOwnership)
246
0
{
247
0
    if (pNewMenu == mpMenu)
248
0
        return;
249
250
0
    if (mpMenu && mbOwnPopupMenu)
251
0
        mpMenu->dispose();
252
253
0
    mpMenu = pNewMenu;
254
0
    mbOwnPopupMenu = bTakeOwnership;
255
0
}
256
257
void MenuButton::SetPopover(Window* pWindow)
258
0
{
259
0
    if (pWindow == mpFloatingWindow)
260
0
        return;
261
262
0
    mpFloatingWindow = pWindow;
263
0
}
264
265
266
FactoryFunction MenuButton::GetUITestFactory() const
267
0
{
268
0
    return MenuButtonUIObject::create;
269
0
}
270
271
0
void MenuButton::SetCurItemId(){
272
0
    mnCurItemId = mpMenu->GetCurItemId();
273
0
    msCurItemIdent = mpMenu->GetCurItemIdent();
274
0
}
275
276
void MenuButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
277
0
{
278
0
    PushButton::DumpAsPropertyTree(rJsonWriter);
279
280
0
    if (mpMenu)
281
0
    {
282
0
        auto aMenuNode = rJsonWriter.startArray("menu");
283
0
        for (int i = 0; i < mpMenu->GetItemCount(); i++)
284
0
        {
285
0
            auto aEntryNode = rJsonWriter.startStruct();
286
0
            auto sId = mpMenu->GetItemId(i);
287
0
            rJsonWriter.put("id", mpMenu->GetItemIdent(sId));
288
0
            rJsonWriter.put("text", mpMenu->GetItemText(sId));
289
0
        }
290
0
    }
291
0
}
292
293
//class MenuToggleButton ----------------------------------------------------
294
295
MenuToggleButton::MenuToggleButton( vcl::Window* pParent, WinBits nWinBits )
296
0
    : MenuButton( pParent, nWinBits )
297
0
{
298
0
}
Unexecuted instantiation: MenuToggleButton::MenuToggleButton(vcl::Window*, long)
Unexecuted instantiation: MenuToggleButton::MenuToggleButton(vcl::Window*, long)
299
300
MenuToggleButton::~MenuToggleButton()
301
0
{
302
0
    disposeOnce();
303
0
}
304
305
void MenuToggleButton::SetActive( bool bSel )
306
0
{
307
0
    mbIsActive = bSel;
308
0
}
309
310
bool MenuToggleButton::GetActive() const
311
0
{
312
0
    return mbIsActive;
313
0
}
314
315
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */