VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/UIShortcutPool.cpp@ 104158

Last change on this file since 104158 was 103771, checked in by vboxsync, 9 months ago

FE/Qt: UICommon: Switching dependency from UICommon to UIGlobalSession whenever is possible.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: UIShortcutPool.cpp 103771 2024-03-11 15:16:04Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIShortcutPool class implementation.
4 */
5
6/*
7 * Copyright (C) 2011-2023 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/* GUI includes: */
29#include "UIActionPool.h"
30#include "UIExtraDataManager.h"
31#include "UIGlobalSession.h"
32#include "UIShortcutPool.h"
33
34
35/* Namespaces: */
36using namespace UIExtraDataDefs;
37
38
39/*********************************************************************************************************************************
40* Class UIShortcut implementation. *
41*********************************************************************************************************************************/
42
43void UIShortcut::setScope(const QString &strScope)
44{
45 m_strScope = strScope;
46}
47
48const QString &UIShortcut::scope() const
49{
50 return m_strScope;
51}
52
53void UIShortcut::setDescription(const QString &strDescription)
54{
55 m_strDescription = strDescription;
56}
57
58const QString &UIShortcut::description() const
59{
60 return m_strDescription;
61}
62
63void UIShortcut::setSequences(const QList<QKeySequence> &sequences)
64{
65 m_sequences = sequences;
66}
67
68const QList<QKeySequence> &UIShortcut::sequences() const
69{
70 return m_sequences;
71}
72
73void UIShortcut::setDefaultSequence(const QKeySequence &defaultSequence)
74{
75 m_defaultSequence = defaultSequence;
76}
77
78const QKeySequence &UIShortcut::defaultSequence() const
79{
80 return m_defaultSequence;
81}
82
83void UIShortcut::setStandardSequence(const QKeySequence &standardSequence)
84{
85 m_standardSequence = standardSequence;
86}
87
88const QKeySequence &UIShortcut::standardSequence() const
89{
90 return m_standardSequence;
91}
92
93QString UIShortcut::primaryToNativeText() const
94{
95 return m_sequences.isEmpty() ? QString() : m_sequences.first().toString(QKeySequence::NativeText);
96}
97
98QString UIShortcut::primaryToPortableText() const
99{
100 return m_sequences.isEmpty() ? QString() : m_sequences.first().toString(QKeySequence::PortableText);
101}
102
103
104/*********************************************************************************************************************************
105* Class UIShortcutPool implementation. *
106*********************************************************************************************************************************/
107
108/* static */
109UIShortcutPool *UIShortcutPool::s_pInstance = 0;
110const QString UIShortcutPool::s_strShortcutKeyTemplate = QString("%1/%2");
111const QString UIShortcutPool::s_strShortcutKeyTemplateRuntime = s_strShortcutKeyTemplate.arg(GUI_Input_MachineShortcuts);
112
113void UIShortcutPool::create(UIType enmType)
114{
115 /* Check that instance do NOT exists: */
116 if (s_pInstance)
117 return;
118
119 /* Create instance: */
120 new UIShortcutPool(enmType);
121
122 /* Prepare instance: */
123 s_pInstance->prepare();
124}
125
126void UIShortcutPool::destroy()
127{
128 /* Check that instance exists: */
129 if (!s_pInstance)
130 return;
131
132 /* Cleanup instance: */
133 s_pInstance->cleanup();
134
135 /* Delete instance: */
136 delete s_pInstance;
137}
138
139UIShortcut &UIShortcutPool::shortcut(UIActionPool *pActionPool, UIAction *pAction)
140{
141 /* Compose shortcut key: */
142 const QString strShortcutKey(s_strShortcutKeyTemplate.arg(pActionPool->shortcutsExtraDataID(),
143 pAction->shortcutExtraDataID()));
144 /* Return existing if any: */
145 if (m_shortcuts.contains(strShortcutKey))
146 return shortcut(strShortcutKey);
147 /* Create and return new one: */
148 UIShortcut &newShortcut = m_shortcuts[strShortcutKey];
149 newShortcut.setScope(pAction->shortcutScope());
150 newShortcut.setDescription(pAction->name());
151 const QKeySequence &defaultSequence = pAction->defaultShortcut(pActionPool->type());
152 const QKeySequence &standardSequence = pAction->standardShortcut(pActionPool->type());
153 newShortcut.setSequences(QList<QKeySequence>() << defaultSequence << standardSequence);
154 newShortcut.setDefaultSequence(defaultSequence);
155 newShortcut.setStandardSequence(standardSequence);
156 return newShortcut;
157}
158
159UIShortcut &UIShortcutPool::shortcut(const QString &strPoolID, const QString &strActionID)
160{
161 /* Return if present, autocreate if necessary: */
162 return shortcut(s_strShortcutKeyTemplate.arg(strPoolID, strActionID));
163}
164
165void UIShortcutPool::setOverrides(const QMap<QString, QString> &overrides)
166{
167 /* Iterate over all the overrides: */
168 const QList<QString> shortcutKeys = overrides.keys();
169 foreach (const QString &strShortcutKey, shortcutKeys)
170 {
171 /* Make no changes if there is no such shortcut: */
172 if (!m_shortcuts.contains(strShortcutKey))
173 continue;
174 /* Assign overridden sequences to the shortcut: */
175 m_shortcuts[strShortcutKey].setSequences(QList<QKeySequence>() << overrides[strShortcutKey]);
176 }
177 /* Save overrides: */
178 saveOverrides();
179}
180
181void UIShortcutPool::applyShortcuts(UIActionPool *pActionPool)
182{
183 /* For each the action of the passed action-pool: */
184 foreach (UIAction *pAction, pActionPool->actions())
185 {
186 /* Skip menu actions: */
187 if (pAction->type() == UIActionType_Menu)
188 continue;
189
190 /* Compose shortcut key: */
191 const QString strShortcutKey = s_strShortcutKeyTemplate.arg(pActionPool->shortcutsExtraDataID(),
192 pAction->shortcutExtraDataID());
193 /* If shortcut key is already known: */
194 if (m_shortcuts.contains(strShortcutKey))
195 {
196 /* Get corresponding shortcut: */
197 UIShortcut &existingShortcut = m_shortcuts[strShortcutKey];
198 /* Copy the scope from the action to the shortcut: */
199 existingShortcut.setScope(pAction->shortcutScope());
200 /* Copy the description from the action to the shortcut: */
201 existingShortcut.setDescription(pAction->name());
202 /* Copy the sequences from the shortcut to the action: */
203 pAction->setShortcuts(existingShortcut.sequences());
204 pAction->retranslateUi();
205 /* Copy default and standard sequences from the action to the shortcut: */
206 if (pActionPool->type() == m_enmType)
207 {
208 existingShortcut.setDefaultSequence(pAction->defaultShortcut(pActionPool->type()));
209 existingShortcut.setStandardSequence(pAction->standardShortcut(pActionPool->type()));
210 }
211 }
212 /* If shortcut key is NOT known yet: */
213 else
214 {
215 /* Create corresponding shortcut: */
216 UIShortcut &newShortcut = m_shortcuts[strShortcutKey];
217 /* Copy the action's default sequence to both the shortcut & the action: */
218 const QKeySequence &defaultSequence = pAction->defaultShortcut(pActionPool->type());
219 const QKeySequence &standardSequence = pAction->standardShortcut(pActionPool->type());
220 newShortcut.setSequences(QList<QKeySequence>() << defaultSequence << standardSequence);
221 newShortcut.setDefaultSequence(defaultSequence);
222 newShortcut.setStandardSequence(standardSequence);
223 pAction->setShortcuts(newShortcut.sequences());
224 pAction->retranslateUi();
225 /* Copy the description from the action to the shortcut: */
226 newShortcut.setScope(pAction->shortcutScope());
227 newShortcut.setDescription(pAction->name());
228 }
229 }
230}
231
232/* static */
233QKeySequence UIShortcutPool::standardSequence(QKeySequence::StandardKey enmKey)
234{
235 /* We have some overrides: */
236 switch (enmKey)
237 {
238#ifdef VBOX_WS_MAC
239 /* So called Apple default sequence for HelpContents action (CMD+?)
240 * is no more the default one on macOS for many years.
241 * Instead they have redesigned this shortcut to open
242 * system-wide Help menu with native search field.
243 * But the Contents action has no shortcut anymore.
244 * We could leave it empty or make it CMD+/ instead. */
245 case QKeySequence::HelpContents:
246 return QKeySequence("Ctrl+/");
247#endif
248 default:
249 break;
250 }
251
252 /* Use QKeySequence constructor by default: */
253 return QKeySequence(enmKey);
254}
255
256void UIShortcutPool::retranslateUi()
257{
258 /* Translate own defaults: */
259 m_shortcuts[s_strShortcutKeyTemplateRuntime.arg("PopupMenu")]
260 .setDescription(QApplication::translate("UIActionPool", "Popup Menu"));
261}
262
263void UIShortcutPool::sltReloadSelectorShortcuts()
264{
265 /* Clear selector shortcuts first: */
266 const QList<QString> shortcutKeyList = m_shortcuts.keys();
267 foreach (const QString &strShortcutKey, shortcutKeyList)
268 if (strShortcutKey.startsWith(GUI_Input_SelectorShortcuts))
269 m_shortcuts.remove(strShortcutKey);
270
271 /* Load selector defaults: */
272 loadDefaultsFor(GUI_Input_SelectorShortcuts);
273 /* Load selector overrides: */
274 loadOverridesFor(GUI_Input_SelectorShortcuts);
275
276 /* Notify manager shortcuts reloaded: */
277 emit sigManagerShortcutsReloaded();
278}
279
280void UIShortcutPool::sltReloadMachineShortcuts()
281{
282 /* Clear machine shortcuts first: */
283 const QList<QString> shortcutKeyList = m_shortcuts.keys();
284 foreach (const QString &strShortcutKey, shortcutKeyList)
285 if (strShortcutKey.startsWith(GUI_Input_MachineShortcuts))
286 m_shortcuts.remove(strShortcutKey);
287
288 /* Load machine defaults: */
289 loadDefaultsFor(GUI_Input_MachineShortcuts);
290 /* Load machine overrides: */
291 loadOverridesFor(GUI_Input_MachineShortcuts);
292
293 /* Notify runtime shortcuts reloaded: */
294 emit sigRuntimeShortcutsReloaded();
295}
296
297UIShortcutPool::UIShortcutPool(UIType enmType)
298 : m_enmType(enmType)
299{
300 /* Prepare instance: */
301 if (!s_pInstance)
302 s_pInstance = this;
303}
304
305UIShortcutPool::~UIShortcutPool()
306{
307 /* Cleanup instance: */
308 if (s_pInstance == this)
309 s_pInstance = 0;
310}
311
312void UIShortcutPool::prepare()
313{
314 /* Load defaults: */
315 loadDefaults();
316 /* Load overrides: */
317 loadOverrides();
318 /* Prepare connections: */
319 prepareConnections();
320}
321
322void UIShortcutPool::prepareConnections()
323{
324 /* Connect to extra-data signals: */
325 connect(gEDataManager, &UIExtraDataManager::sigSelectorUIShortcutChange,
326 this, &UIShortcutPool::sltReloadSelectorShortcuts);
327 connect(gEDataManager, &UIExtraDataManager::sigRuntimeUIShortcutChange,
328 this, &UIShortcutPool::sltReloadMachineShortcuts);
329}
330
331void UIShortcutPool::loadDefaults()
332{
333 /* Load selector defaults: */
334 loadDefaultsFor(GUI_Input_SelectorShortcuts);
335 /* Load machine defaults: */
336 loadDefaultsFor(GUI_Input_MachineShortcuts);
337}
338
339void UIShortcutPool::loadDefaultsFor(const QString &strPoolExtraDataID)
340{
341 /* Default shortcuts for Selector UI: */
342 if (strPoolExtraDataID == GUI_Input_SelectorShortcuts)
343 {
344 /* Nothing for now.. */
345 }
346 /* Default shortcuts for Runtime UI: */
347 else if (strPoolExtraDataID == GUI_Input_MachineShortcuts)
348 {
349 /* Default shortcut for the Runtime Popup Menu: */
350 m_shortcuts.insert(s_strShortcutKeyTemplateRuntime.arg("PopupMenu"),
351 UIShortcut(QString(),
352 QApplication::translate("UIActionPool", "Popup Menu"),
353 QList<QKeySequence>() << QString("Home"),
354 QString("Home"),
355 QString()));
356 }
357}
358
359void UIShortcutPool::loadOverrides()
360{
361 /* Load selector overrides: */
362 loadOverridesFor(GUI_Input_SelectorShortcuts);
363 /* Load machine overrides: */
364 loadOverridesFor(GUI_Input_MachineShortcuts);
365}
366
367void UIShortcutPool::loadOverridesFor(const QString &strPoolExtraDataID)
368{
369 /* Compose shortcut key template: */
370 const QString strShortcutKeyTemplate(s_strShortcutKeyTemplate.arg(strPoolExtraDataID));
371 /* Iterate over all the overrides: */
372 const QStringList overrides = gEDataManager->shortcutOverrides(strPoolExtraDataID);
373 foreach (const QString &strKeyValuePair, overrides)
374 {
375 /* Make sure override structure is valid: */
376 int iDelimiterPosition = strKeyValuePair.indexOf('=');
377 if (iDelimiterPosition < 0)
378 continue;
379
380 /* Get shortcut ID/sequence: */
381 QString strShortcutExtraDataID = strKeyValuePair.left(iDelimiterPosition);
382 const QString strShortcutSequence = strKeyValuePair.right(strKeyValuePair.length() - iDelimiterPosition - 1);
383
384 // Hack for handling "Save" as "SaveState":
385 if (strShortcutExtraDataID == "Save")
386 strShortcutExtraDataID = "SaveState";
387
388 /* Compose corresponding shortcut key: */
389 const QString strShortcutKey(strShortcutKeyTemplate.arg(strShortcutExtraDataID));
390 /* Modify map with composed key/value: */
391 if (!m_shortcuts.contains(strShortcutKey))
392 m_shortcuts.insert(strShortcutKey,
393 UIShortcut(QString(),
394 QString(),
395 QList<QKeySequence>() << strShortcutSequence,
396 QString(),
397 QString()));
398 else
399 {
400 /* Get corresponding value: */
401 UIShortcut &shortcut = m_shortcuts[strShortcutKey];
402 /* Check if corresponding shortcut overridden by value: */
403 if (shortcut.primaryToPortableText().compare(strShortcutSequence, Qt::CaseInsensitive) != 0)
404 {
405 /* Shortcut unassigned? */
406 if (strShortcutSequence.compare("None", Qt::CaseInsensitive) == 0)
407 shortcut.setSequences(QList<QKeySequence>());
408 /* Or reassigned? */
409 else
410 shortcut.setSequences(QList<QKeySequence>() << strShortcutSequence);
411 }
412 }
413 }
414}
415
416void UIShortcutPool::saveOverrides()
417{
418 /* Load selector overrides: */
419 saveOverridesFor(GUI_Input_SelectorShortcuts);
420 /* Load machine overrides: */
421 saveOverridesFor(GUI_Input_MachineShortcuts);
422}
423
424void UIShortcutPool::saveOverridesFor(const QString &strPoolExtraDataID)
425{
426 /* Compose shortcut prefix: */
427 const QString strShortcutPrefix(s_strShortcutKeyTemplate.arg(strPoolExtraDataID, QString()));
428 /* Populate the list of all the known overrides: */
429 QStringList overrides;
430 const QList<QString> shortcutKeys = m_shortcuts.keys();
431 foreach (const QString &strShortcutKey, shortcutKeys)
432 {
433 /* Check if the key starts from the proper prefix: */
434 if (!strShortcutKey.startsWith(strShortcutPrefix))
435 continue;
436 /* Get corresponding shortcut: */
437 const UIShortcut &shortcut = m_shortcuts[strShortcutKey];
438 /* Check if the sequence for that shortcut differs from default or standard: */
439 if ( shortcut.sequences().contains(shortcut.defaultSequence())
440 || ( !shortcut.standardSequence().isEmpty()
441 && shortcut.sequences().contains(shortcut.standardSequence())))
442 continue;
443 /* Add the shortcut sequence into overrides list: */
444 overrides << QString("%1=%2").arg(QString(strShortcutKey).remove(strShortcutPrefix),
445 shortcut.primaryToPortableText());
446 }
447 /* Save overrides into the extra-data: */
448 gpGlobalSession->virtualBox().SetExtraDataStringList(strPoolExtraDataID, overrides);
449}
450
451UIShortcut &UIShortcutPool::shortcut(const QString &strShortcutKey)
452{
453 return m_shortcuts[strShortcutKey];
454}
455
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