VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserHandlerKeyboard.cpp

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.6 KB
Line 
1/* $Id: UIChooserHandlerKeyboard.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIChooserHandlerKeyboard class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QKeyEvent>
30
31/* GUI incluedes: */
32#include "UIChooserHandlerKeyboard.h"
33#include "UIChooserModel.h"
34#include "UIChooserItemGroup.h"
35#include "UIChooserItemMachine.h"
36#include "UIChooserNodeGroup.h"
37#include "UIChooserNodeMachine.h"
38
39
40UIChooserHandlerKeyboard::UIChooserHandlerKeyboard(UIChooserModel *pParent)
41 : QObject(pParent)
42 , m_pModel(pParent)
43{
44 /* Setup shift map: */
45 m_shiftMap[Qt::Key_Up] = UIItemShiftSize_Item;
46 m_shiftMap[Qt::Key_Down] = UIItemShiftSize_Item;
47 m_shiftMap[Qt::Key_Home] = UIItemShiftSize_Full;
48 m_shiftMap[Qt::Key_End] = UIItemShiftSize_Full;
49}
50
51bool UIChooserHandlerKeyboard::handle(QKeyEvent *pEvent, UIKeyboardEventType type) const
52{
53 /* Process passed event: */
54 switch (type)
55 {
56 case UIKeyboardEventType_Press: return handleKeyPress(pEvent);
57 case UIKeyboardEventType_Release: return handleKeyRelease(pEvent);
58 }
59 /* Pass event if unknown: */
60 return false;
61}
62
63UIChooserModel* UIChooserHandlerKeyboard::model() const
64{
65 return m_pModel;
66}
67
68bool UIChooserHandlerKeyboard::handleKeyPress(QKeyEvent *pEvent) const
69{
70 /* Which key it was? */
71 switch (pEvent->key())
72 {
73 /* Key UP? */
74 case Qt::Key_Up:
75 /* Key HOME? */
76 case Qt::Key_Home:
77 {
78 /* Was control modifier pressed? */
79#ifdef VBOX_WS_MAC
80 if (pEvent->modifiers() & Qt::ControlModifier &&
81 pEvent->modifiers() & Qt::KeypadModifier)
82#else /* VBOX_WS_MAC */
83 if (pEvent->modifiers() == Qt::ControlModifier)
84#endif /* !VBOX_WS_MAC */
85 {
86 /* Shift item up: */
87 shift(UIItemShiftDirection_Up, m_shiftMap[pEvent->key()]);
88 return true;
89 }
90
91 /* Was shift modifier pressed? */
92#ifdef VBOX_WS_MAC
93 else if (pEvent->modifiers() & Qt::ShiftModifier &&
94 pEvent->modifiers() & Qt::KeypadModifier)
95#else /* VBOX_WS_MAC */
96 else if (pEvent->modifiers() == Qt::ShiftModifier)
97#endif /* !VBOX_WS_MAC */
98 {
99 /* Determine current-item position: */
100 UIChooserItem *pCurrentItem = model()->currentItem();
101 int iPosition = model()->navigationItems().indexOf(pCurrentItem);
102 /* Determine 'previous' item: */
103 UIChooserItem *pPreviousItem = 0;
104 if (iPosition > 0)
105 {
106 if ( pEvent->key() == Qt::Key_Up
107 || pEvent->key() == Qt::Key_Home)
108 {
109 UIChooserItem *pPossiblePreviousItem = 0;
110 const int iLimit = pEvent->key() == Qt::Key_Up ? iPosition - 1 : 0;
111 for (int i = iPosition - 1; i >= iLimit; --i)
112 {
113 pPossiblePreviousItem = model()->navigationItems().at(i);
114 if (( pCurrentItem->type() == UIChooserNodeType_Global
115 && pPossiblePreviousItem->type() == UIChooserNodeType_Global)
116 || ( pCurrentItem->type() != UIChooserNodeType_Global
117 && pPossiblePreviousItem->type() != UIChooserNodeType_Global))
118 pPreviousItem = pPossiblePreviousItem;
119 }
120 }
121 }
122 if (pPreviousItem)
123 {
124 /* Make sure 'previous' item is visible: */
125 pPreviousItem->makeSureItsVisible();
126 /* Calculate positions: */
127 UIChooserItem *pFirstItem = model()->firstSelectedItem();
128 int iFirstPosition = model()->navigationItems().indexOf(pFirstItem);
129 int iPreviousPosition = model()->navigationItems().indexOf(pPreviousItem);
130 /* Populate list of items from 'first' to 'previous': */
131 QList<UIChooserItem*> items;
132 if (iFirstPosition <= iPreviousPosition)
133 for (int i = iFirstPosition; i <= iPreviousPosition; ++i)
134 items << model()->navigationItems().at(i);
135 else
136 for (int i = iFirstPosition; i >= iPreviousPosition; --i)
137 items << model()->navigationItems().at(i);
138 /* Set that list as selected: */
139 model()->setSelectedItems(items);
140 /* Make 'previous' item current one: */
141 model()->setCurrentItem(pPreviousItem);
142 /* Filter-out this event: */
143 return true;
144 }
145 }
146
147 /* There is no modifiers pressed? */
148#ifdef VBOX_WS_MAC
149 else if (pEvent->modifiers() == Qt::KeypadModifier)
150#else /* VBOX_WS_MAC */
151 else if (pEvent->modifiers() == Qt::NoModifier)
152#endif /* !VBOX_WS_MAC */
153 {
154 /* Determine current-item position: */
155 int iPosition = model()->navigationItems().indexOf(model()->currentItem());
156 /* Determine 'previous' item: */
157 UIChooserItem *pPreviousItem = 0;
158 if (iPosition > 0)
159 {
160 if (pEvent->key() == Qt::Key_Up)
161 pPreviousItem = model()->navigationItems().at(iPosition - 1);
162 else if (pEvent->key() == Qt::Key_Home)
163 pPreviousItem = model()->navigationItems().first();
164 }
165 if (pPreviousItem)
166 {
167 /* Make sure 'previous' item is visible: */
168 pPreviousItem->makeSureItsVisible();
169 /* Make 'previous' item the only selected: */
170 model()->setSelectedItem(pPreviousItem);
171 /* Filter-out this event: */
172 return true;
173 }
174 }
175 /* Pass this event: */
176 return false;
177 }
178 /* Key DOWN? */
179 case Qt::Key_Down:
180 /* Key END? */
181 case Qt::Key_End:
182 {
183 /* Was control modifier pressed? */
184#ifdef VBOX_WS_MAC
185 if (pEvent->modifiers() & Qt::ControlModifier &&
186 pEvent->modifiers() & Qt::KeypadModifier)
187#else /* VBOX_WS_MAC */
188 if (pEvent->modifiers() == Qt::ControlModifier)
189#endif /* !VBOX_WS_MAC */
190 {
191 /* Shift item down: */
192 shift(UIItemShiftDirection_Down, m_shiftMap[pEvent->key()]);
193 return true;
194 }
195
196 /* Was shift modifier pressed? */
197#ifdef VBOX_WS_MAC
198 else if (pEvent->modifiers() & Qt::ShiftModifier &&
199 pEvent->modifiers() & Qt::KeypadModifier)
200#else /* VBOX_WS_MAC */
201 else if (pEvent->modifiers() == Qt::ShiftModifier)
202#endif /* !VBOX_WS_MAC */
203 {
204 /* Determine current-item position: */
205 UIChooserItem *pCurrentItem = model()->currentItem();
206 int iPosition = model()->navigationItems().indexOf(pCurrentItem);
207 /* Determine 'next' item: */
208 UIChooserItem *pNextItem = 0;
209 if (iPosition < model()->navigationItems().size() - 1)
210 {
211 if ( pEvent->key() == Qt::Key_Down
212 || pEvent->key() == Qt::Key_End)
213 {
214 UIChooserItem *pPossibleNextItem = 0;
215 const int iLimit = pEvent->key() == Qt::Key_Down ? iPosition + 1 : 0;
216 for (int i = iPosition + 1; i <= iLimit; ++i)
217 {
218 pPossibleNextItem = model()->navigationItems().at(i);
219 if (( pCurrentItem->type() == UIChooserNodeType_Global
220 && pPossibleNextItem->type() == UIChooserNodeType_Global)
221 || ( pCurrentItem->type() != UIChooserNodeType_Global
222 && pPossibleNextItem->type() != UIChooserNodeType_Global))
223 pNextItem = pPossibleNextItem;
224 }
225 }
226 }
227 if (pNextItem)
228 {
229 /* Make sure 'next' item is visible: */
230 pNextItem->makeSureItsVisible();
231 /* Calculate positions: */
232 UIChooserItem *pFirstItem = model()->firstSelectedItem();
233 int iFirstPosition = model()->navigationItems().indexOf(pFirstItem);
234 int iNextPosition = model()->navigationItems().indexOf(pNextItem);
235 /* Populate list of items from 'first' to 'next': */
236 QList<UIChooserItem*> items;
237 if (iFirstPosition <= iNextPosition)
238 for (int i = iFirstPosition; i <= iNextPosition; ++i)
239 items << model()->navigationItems().at(i);
240 else
241 for (int i = iFirstPosition; i >= iNextPosition; --i)
242 items << model()->navigationItems().at(i);
243 /* Set that list as selected: */
244 model()->setSelectedItems(items);
245 /* Make 'next' item current one: */
246 model()->setCurrentItem(pNextItem);
247 /* Filter-out this event: */
248 return true;
249 }
250 }
251
252 /* There is no modifiers pressed? */
253#ifdef VBOX_WS_MAC
254 else if (pEvent->modifiers() == Qt::KeypadModifier)
255#else /* VBOX_WS_MAC */
256 else if (pEvent->modifiers() == Qt::NoModifier)
257#endif /* !VBOX_WS_MAC */
258 {
259 /* Determine current-item position: */
260 int iPosition = model()->navigationItems().indexOf(model()->currentItem());
261 /* Determine 'next' item: */
262 UIChooserItem *pNextItem = 0;
263 if (iPosition < model()->navigationItems().size() - 1)
264 {
265 if (pEvent->key() == Qt::Key_Down)
266 pNextItem = model()->navigationItems().at(iPosition + 1);
267 else if (pEvent->key() == Qt::Key_End)
268 pNextItem = model()->navigationItems().last();
269 }
270 if (pNextItem)
271 {
272 /* Make sure 'next' item is visible: */
273 pNextItem->makeSureItsVisible();
274 /* Make 'next' item the only selected one: */
275 model()->setSelectedItem(pNextItem);
276 /* Filter-out this event: */
277 return true;
278 }
279 }
280 /* Pass this event: */
281 return false;
282 }
283 /* Key F2? */
284 case Qt::Key_F2:
285 {
286 /* If this item is of group type: */
287 if (model()->currentItem()->type() == UIChooserNodeType_Group)
288 {
289 /* Start editing selected group item name: */
290 model()->startEditingSelectedGroupItemName();
291 /* Filter that event out: */
292 return true;
293 }
294 /* Pass event to other items: */
295 return false;
296 }
297 case Qt::Key_Return:
298 case Qt::Key_Enter:
299 {
300 /* If this item is of group or machine type: */
301 if ( model()->currentItem()->type() == UIChooserNodeType_Group
302 || model()->currentItem()->type() == UIChooserNodeType_Machine)
303 {
304 /* Start or show selected items: */
305 model()->startOrShowSelectedItems();
306 /* And filter out that event: */
307 return true;
308 }
309 /* Pass event to other items: */
310 return false;
311 }
312 case Qt::Key_Space:
313 {
314 /* If there is a current-item: */
315 if (UIChooserItem *pCurrentItem = model()->currentItem())
316 {
317 /* Of the group type: */
318 if (pCurrentItem->type() == UIChooserNodeType_Group)
319 {
320 /* Toggle that group: */
321 UIChooserItemGroup *pGroupItem = pCurrentItem->toGroupItem();
322 if (pGroupItem->isClosed())
323 pGroupItem->open();
324 else if (pGroupItem->isOpened())
325 pGroupItem->close();
326 /* Filter that event out: */
327 return true;
328 }
329 }
330 /* Pass event to other items: */
331 return false;
332 }
333 case Qt::Key_Escape:
334 {
335 /* Make sure that vm search widget is hidden: */
336 model()->setSearchWidgetVisible(false);
337 break;
338 }
339 default:
340 {
341 /* Start lookup only for non-empty and printable strings: */
342 const QString strText = pEvent->text();
343 if (!strText.isEmpty() && pEvent->modifiers() == Qt::NoModifier && pEvent->text().at(0).isPrint())
344 model()->lookFor(strText);
345 break;
346 }
347 }
348 /* Pass all other events: */
349 return false;
350}
351
352bool UIChooserHandlerKeyboard::handleKeyRelease(QKeyEvent*) const
353{
354 /* Pass all events: */
355 return false;
356}
357
358void UIChooserHandlerKeyboard::shift(UIItemShiftDirection enmDirection, UIItemShiftType enmShiftType) const
359{
360 /* Get current-node and its parent: */
361 UIChooserNode *pCurrentNode = model()->currentItem()->node();
362 UIChooserNode *pParentNode = pCurrentNode->parentNode();
363 /* Get current-node position: */
364 const int iCurrentNodePosition = pCurrentNode->position();
365
366 /* Calculate new position: */
367 int iNewCurrentNodePosition = -1;
368 switch (enmDirection)
369 {
370 case UIItemShiftDirection_Up:
371 {
372 if (iCurrentNodePosition > 0)
373 switch (enmShiftType)
374 {
375 case UIItemShiftSize_Item: iNewCurrentNodePosition = iCurrentNodePosition - 1; break;
376 case UIItemShiftSize_Full: iNewCurrentNodePosition = 0; break;
377 default: break;
378 }
379 break;
380 }
381 case UIItemShiftDirection_Down:
382 {
383 if (iCurrentNodePosition < pParentNode->nodes(pCurrentNode->type()).size() - 1)
384 switch (enmShiftType)
385 {
386 case UIItemShiftSize_Item: iNewCurrentNodePosition = iCurrentNodePosition + 2; break;
387 case UIItemShiftSize_Full: iNewCurrentNodePosition = pParentNode->nodes(pCurrentNode->type()).size(); break;
388 default: break;
389 }
390 break;
391 }
392 default:
393 break;
394 }
395 /* Filter out invalid requests: */
396 if (iNewCurrentNodePosition == -1)
397 return;
398
399 /* Create shifted node/item: */
400 UIChooserItem *pShiftedItem = 0;
401 switch (pCurrentNode->type())
402 {
403 case UIChooserNodeType_Group:
404 {
405 UIChooserNodeGroup *pNewNode = new UIChooserNodeGroup(pParentNode, iNewCurrentNodePosition, pCurrentNode->toGroupNode());
406 pShiftedItem = new UIChooserItemGroup(pParentNode->item(), pNewNode);
407 break;
408 }
409 case UIChooserNodeType_Machine:
410 {
411 UIChooserNodeMachine *pNewNode = new UIChooserNodeMachine(pParentNode, iNewCurrentNodePosition, pCurrentNode->toMachineNode());
412 pShiftedItem = new UIChooserItemMachine(pParentNode->item(), pNewNode);
413 break;
414 }
415 default:
416 break;
417 }
418
419 /* Delete old node/item: */
420 delete pCurrentNode;
421
422 /* Update model: */
423 model()->wipeOutEmptyGroups();
424 model()->updateNavigationItemList();
425 model()->updateLayout();
426 model()->setSelectedItem(pShiftedItem);
427 model()->saveGroups();
428}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette