/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: */ |