VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/settings/editors/UIHotKeyEditor.cpp

Last change on this file was 104313, checked in by vboxsync, 7 weeks ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the settings related GUI classes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VBoxHeadless/VirtualBox/src/widgets/UIHostComboEditor.cpp82454
    /branches/VBox-4.1/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp74233,​78414,​78691,​81841,​82127
    /branches/VBox-4.2/src/VBox/Frontends/VirtualBox/src/widgets/UIHotKeyEditor.cpp91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Frontends/VirtualBox/src/widgets/UIHotKeyEditor.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIHotKeyEditor.cpp91223
    /branches/andy/guestctrl20/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp78916,​78930
    /branches/dsen/gui/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp79645-79692
File size: 15.5 KB
Line 
1/* $Id: UIHotKeyEditor.cpp 104313 2024-04-12 13:10:30Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIHotKeyEditor class implementation.
4 */
5
6/*
7 * Copyright (C) 2013-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/* Qt includes: */
29#include <QApplication>
30#include <QHBoxLayout>
31#include <QLineEdit>
32#include <QKeyEvent>
33#include <QStyle>
34
35/* GUI includes; */
36#include "QIToolButton.h"
37#include "UIHostComboEditor.h"
38#include "UIHotKeyEditor.h"
39#include "UIIconPool.h"
40#include "UITranslationEventListener.h"
41
42
43/** QLineEdit extension representing hot-key editor. */
44class UIHotKeyLineEdit : public QLineEdit
45{
46 Q_OBJECT;
47
48public:
49
50 /** Constructs hot-key editor passing @a pParent to the base-class. */
51 UIHotKeyLineEdit(QWidget *pParent);
52
53protected slots:
54
55 /** Deselects the hot-key editor text. */
56 void sltDeselect() { deselect(); }
57
58protected:
59
60 /** Handles key-press @a pEvent. */
61 virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
62 /** Handles key-release @a pEvent. */
63 virtual void keyReleaseEvent(QKeyEvent *pEvent) RT_OVERRIDE;
64
65private:
66
67 /** Returns whether the passed @a pevent should be ignored. */
68 bool isKeyEventIgnored(QKeyEvent *pEvent);
69};
70
71
72/*********************************************************************************************************************************
73* Class UIHotKeyLineEdit implementation. *
74*********************************************************************************************************************************/
75
76UIHotKeyLineEdit::UIHotKeyLineEdit(QWidget *pParent)
77 : QLineEdit(pParent)
78{
79 /* Configure self: */
80 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
81 setContextMenuPolicy(Qt::NoContextMenu);
82
83 /* Connect selection preserver: */
84 connect(this, &UIHotKeyLineEdit::selectionChanged, this, &UIHotKeyLineEdit::sltDeselect);
85}
86
87void UIHotKeyLineEdit::keyPressEvent(QKeyEvent *pEvent)
88{
89 /* Is this event ignored? */
90 if (isKeyEventIgnored(pEvent))
91 return;
92 /* Call to base-class: */
93 QLineEdit::keyPressEvent(pEvent);
94}
95
96void UIHotKeyLineEdit::keyReleaseEvent(QKeyEvent *pEvent)
97{
98 /* Is this event ignored? */
99 if (isKeyEventIgnored(pEvent))
100 return;
101 /* Call to base-class: */
102 QLineEdit::keyReleaseEvent(pEvent);
103}
104
105bool UIHotKeyLineEdit::isKeyEventIgnored(QKeyEvent *pEvent)
106{
107 /* Ignore some keys: */
108 switch (pEvent->key())
109 {
110 /* Ignore cursor keys: */
111 case Qt::Key_Left:
112 case Qt::Key_Right:
113 case Qt::Key_Up:
114 case Qt::Key_Down:
115 pEvent->ignore();
116 return true;
117 /* Default handling for others: */
118 default: break;
119 }
120 /* Do not ignore key by default: */
121 return false;
122}
123
124
125/*********************************************************************************************************************************
126* Class UIHotKeyEditor implementation. *
127*********************************************************************************************************************************/
128
129UIHotKeyEditor::UIHotKeyEditor(QWidget *pParent)
130 : QWidget(pParent)
131 , m_fIsModifiersAllowed(false)
132 , m_pMainLayout(new QHBoxLayout(this))
133 , m_pButtonLayout(new QHBoxLayout)
134 , m_pLineEdit(new UIHotKeyLineEdit(this))
135 , m_pResetButton(new QIToolButton(this))
136 , m_pClearButton(new QIToolButton(this))
137 , m_iTakenKey(-1)
138 , m_fSequenceTaken(false)
139{
140 /* Make sure QIStyledDelegate aware of us: */
141 setProperty("has_sigCommitData", true);
142 /* Configure self: */
143 setAutoFillBackground(true);
144 setFocusProxy(m_pLineEdit);
145
146 /* Configure layout: */
147#ifdef VBOX_WS_MAC
148 m_pMainLayout->setSpacing(5);
149#else
150 m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
151#endif
152 m_pMainLayout->setContentsMargins(0, 0, 0, 0);
153 m_pMainLayout->addWidget(m_pLineEdit);
154 m_pMainLayout->addLayout(m_pButtonLayout);
155
156 /* Configure button layout: */
157 m_pButtonLayout->setSpacing(0);
158 m_pButtonLayout->setContentsMargins(0, 0, 0, 0);
159 m_pButtonLayout->addWidget(m_pResetButton);
160 m_pButtonLayout->addWidget(m_pClearButton);
161
162 /* Configure line-edit: */
163 m_pLineEdit->installEventFilter(this);
164
165 /* Configure tool-buttons: */
166 m_pResetButton->removeBorder();
167 m_pResetButton->setIcon(UIIconPool::iconSet(":/import_16px.png"));
168 connect(m_pResetButton, &QToolButton::clicked, this, &UIHotKeyEditor::sltReset);
169 m_pClearButton->removeBorder();
170 m_pClearButton->setIcon(UIIconPool::iconSet(":/eraser_16px.png"));
171 connect(m_pClearButton, &QToolButton::clicked, this, &UIHotKeyEditor::sltClear);
172
173 /* Translate finally: */
174 sltRetranslateUI();
175 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
176 this, &UIHotKeyEditor::sltRetranslateUI);
177}
178
179void UIHotKeyEditor::sltReset()
180{
181 /* Reset the seuence of the hot-key: */
182 m_hotKey.setSequence(m_hotKey.defaultSequence());
183 /* Redraw sequence: */
184 drawSequence();
185 /* Move the focut to text-field: */
186 m_pLineEdit->setFocus();
187 /* Commit data to the listener: */
188 emit sigCommitData(this);
189}
190
191void UIHotKeyEditor::sltClear()
192{
193 /* Clear the seuence of the hot-key: */
194 m_hotKey.setSequence(QString());
195 /* Redraw sequence: */
196 drawSequence();
197 /* Move the focut to text-field: */
198 m_pLineEdit->setFocus();
199 /* Commit data to the listener: */
200 emit sigCommitData(this);
201}
202
203bool UIHotKeyEditor::eventFilter(QObject *pWatched, QEvent *pEvent)
204{
205 /* Special handling for our line-edit only: */
206 if (pWatched != m_pLineEdit)
207 return QWidget::eventFilter(pWatched, pEvent);
208
209 /* Special handling for key events only: */
210 if (pEvent->type() != QEvent::KeyPress &&
211 pEvent->type() != QEvent::KeyRelease)
212 return QWidget::eventFilter(pWatched, pEvent);
213
214 /* Cast passed event to required type: */
215 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
216
217 /* Should we skip that event to our line-edit? */
218 if (shouldWeSkipKeyEventToLineEdit(pKeyEvent))
219 return false;
220
221 /* Fetch modifiers state: */
222 fetchModifiersState();
223
224 /* Handle key event: */
225 switch (pEvent->type())
226 {
227 case QEvent::KeyPress: handleKeyPress(pKeyEvent); break;
228 case QEvent::KeyRelease: handleKeyRelease(pKeyEvent); break;
229 default: break;
230 }
231
232 /* Fetch host-combo modifier state: */
233 checkIfHostModifierNeeded();
234
235 /* Reflect sequence: */
236 reflectSequence();
237
238 /* Prevent further key event handling: */
239 return true;
240}
241
242void UIHotKeyEditor::sltRetranslateUI()
243{
244 m_pResetButton->setToolTip(tr("Reset shortcut to default"));
245 m_pClearButton->setToolTip(tr("Unset shortcut"));
246}
247
248void UIHotKeyEditor::keyPressEvent(QKeyEvent *pEvent)
249{
250 /* Is this event ignored? */
251 if (isKeyEventIgnored(pEvent))
252 return;
253 /* Call to base-class: */
254 return QWidget::keyPressEvent(pEvent);
255}
256
257void UIHotKeyEditor::keyReleaseEvent(QKeyEvent *pEvent)
258{
259 /* Is this event ignored? */
260 if (isKeyEventIgnored(pEvent))
261 return;
262 /* Call to base-class: */
263 return QWidget::keyReleaseEvent(pEvent);
264}
265
266bool UIHotKeyEditor::shouldWeSkipKeyEventToLineEdit(QKeyEvent *pEvent)
267{
268 /* Special handling for some keys: */
269 switch (pEvent->key())
270 {
271 /* Skip Escape to our line-edit: */
272 case Qt::Key_Escape: return true;
273 /* Skip Return/Enter to our line-edit: */
274 case Qt::Key_Return:
275 case Qt::Key_Enter: return true;
276 /* Skip cursor keys to our line-edit: */
277 case Qt::Key_Left:
278 case Qt::Key_Right:
279 case Qt::Key_Up:
280 case Qt::Key_Down: return true;
281 /* Default handling for others: */
282 default: break;
283 }
284 /* Do not skip by default: */
285 return false;
286}
287
288bool UIHotKeyEditor::isKeyEventIgnored(QKeyEvent *pEvent)
289{
290 /* Ignore some keys: */
291 switch (pEvent->key())
292 {
293 /* Ignore cursor keys: */
294 case Qt::Key_Left:
295 case Qt::Key_Right:
296 case Qt::Key_Up:
297 case Qt::Key_Down:
298 pEvent->ignore();
299 return true;
300 /* Default handling for others: */
301 default: break;
302 }
303 /* Do not ignore key by default: */
304 return false;
305}
306
307void UIHotKeyEditor::fetchModifiersState()
308{
309 /* Make sure modifiers are allowed: */
310 if (!m_fIsModifiersAllowed)
311 return;
312
313 /* If full sequence was not yet taken: */
314 if (!m_fSequenceTaken)
315 {
316 /* Recreate the set of taken modifiers: */
317 m_takenModifiers.clear();
318 Qt::KeyboardModifiers currentModifiers = QApplication::keyboardModifiers();
319 if (currentModifiers != Qt::NoModifier)
320 {
321 if ((m_takenModifiers.size() < 3) && (currentModifiers & Qt::ControlModifier))
322 m_takenModifiers << Qt::CTRL;
323 if ((m_takenModifiers.size() < 3) && (currentModifiers & Qt::AltModifier))
324 m_takenModifiers << Qt::ALT;
325 if ((m_takenModifiers.size() < 3) && (currentModifiers & Qt::MetaModifier))
326 m_takenModifiers << Qt::META;
327 }
328 }
329}
330
331void UIHotKeyEditor::checkIfHostModifierNeeded()
332{
333 /* Make sure other modifiers are NOT allowed: */
334 if (m_fIsModifiersAllowed)
335 return;
336
337 /* Clear the set of taken modifiers: */
338 m_takenModifiers.clear();
339
340 /* If taken key was set: */
341 if (m_iTakenKey != -1)
342 /* We have to add Host+ modifier: */
343 m_takenModifiers << UIHostCombo::hostComboModifierIndex();
344}
345
346bool UIHotKeyEditor::approvedKeyPressed(QKeyEvent *pKeyEvent)
347{
348 /* Qt by some reason generates text for complex cases like
349 * Backspace or Del but skip other similar things like
350 * F1 - F35, Home, End, Page UP, Page DOWN and so on.
351 * We should declare all the approved keys. */
352
353 /* Compose the set of the approved keys: */
354 QSet<int> approvedKeys;
355
356 /* Add Fn keys: */
357 for (int i = Qt::Key_F1; i <= Qt::Key_F35; ++i)
358 approvedKeys << i;
359
360 /* Add digit keys: */
361 for (int i = Qt::Key_0; i <= Qt::Key_9; ++i)
362 approvedKeys << i;
363
364 /* We allow to use only English letters in shortcuts.
365 * The reason is by some reason Qt distinguish native language
366 * letters only with no modifiers pressed.
367 * With modifiers pressed Qt thinks the letter is always English. */
368 for (int i = Qt::Key_A; i <= Qt::Key_Z; ++i)
369 approvedKeys << i;
370
371 /* Add few more special cases: */
372 approvedKeys << Qt::Key_Space << Qt::Key_Backspace
373 << Qt::Key_Insert << Qt::Key_Delete
374 << Qt::Key_Pause << Qt::Key_Print
375 << Qt::Key_Home << Qt::Key_End
376 << Qt::Key_PageUp << Qt::Key_PageDown
377 << Qt::Key_QuoteLeft << Qt::Key_AsciiTilde
378 << Qt::Key_Minus << Qt::Key_Underscore
379 << Qt::Key_Equal << Qt::Key_Plus
380 << Qt::Key_ParenLeft << Qt::Key_ParenRight
381 << Qt::Key_BraceLeft << Qt::Key_BraceRight
382 << Qt::Key_BracketLeft << Qt::Key_BracketRight
383 << Qt::Key_Backslash << Qt::Key_Bar
384 << Qt::Key_Semicolon << Qt::Key_Colon
385 << Qt::Key_Apostrophe << Qt::Key_QuoteDbl
386 << Qt::Key_Comma << Qt::Key_Period << Qt::Key_Slash
387 << Qt::Key_Less << Qt::Key_Greater << Qt::Key_Question;
388
389 /* Is this one of the approved keys? */
390 if (approvedKeys.contains(pKeyEvent->key()))
391 return true;
392
393 /* False by default: */
394 return false;
395}
396
397void UIHotKeyEditor::handleKeyPress(QKeyEvent *pKeyEvent)
398{
399 /* If full sequence was not yet taken: */
400 if (!m_fSequenceTaken)
401 {
402 /* If finalizing key is pressed: */
403 if (approvedKeyPressed(pKeyEvent))
404 {
405 /* Remember taken key: */
406 m_iTakenKey = pKeyEvent->key();
407 /* Mark full sequence taken: */
408 m_fSequenceTaken = true;
409 }
410 /* If something other is pressed: */
411 else
412 {
413 /* Clear taken key: */
414 m_iTakenKey = -1;
415 }
416 }
417}
418
419void UIHotKeyEditor::handleKeyRelease(QKeyEvent *pKeyEvent)
420{
421 /* If full sequence was taken already and no modifiers are currently held: */
422 if (m_fSequenceTaken && (pKeyEvent->modifiers() == Qt::NoModifier))
423 {
424 /* Reset taken sequence: */
425 m_fSequenceTaken = false;
426 }
427}
428
429void UIHotKeyEditor::reflectSequence()
430{
431 /* Acquire modifier names: */
432 QString strModifierNames;
433 QStringList modifierNames;
434 foreach (int iTakenModifier, m_takenModifiers)
435 {
436 if (iTakenModifier == UIHostCombo::hostComboModifierIndex())
437 modifierNames << UIHostCombo::hostComboModifierName();
438 else
439 modifierNames << QKeySequence(iTakenModifier).toString(QKeySequence::NativeText);
440 }
441 if (!modifierNames.isEmpty())
442 strModifierNames = modifierNames.join("");
443 /* Acquire main key name: */
444 QString strMainKeyName;
445 if (m_iTakenKey != -1)
446 strMainKeyName = QKeySequence(m_iTakenKey).toString(QKeySequence::NativeText);
447
448 /* Compose the text to reflect: */
449 QString strText;
450 /* If modifiers were set: */
451 if (!strModifierNames.isEmpty())
452 /* Append the text with modifier names: */
453 strText.append(strModifierNames);
454 /* If main key was set: */
455 if (!strMainKeyName.isEmpty())
456 /* Append the sequence with the main key name: */
457 strText.append(strMainKeyName);
458 /* Reflect what we've got: */
459 m_pLineEdit->setText(strText);
460
461 /* Compose the sequence to save: */
462 QString strSequence;
463 /* If main key was set: */
464 if (!strMainKeyName.isEmpty())
465 {
466 /* Append the sequence with the main key name: */
467 strSequence.append(strMainKeyName);
468 /* If modifiers are allowed: */
469 if (m_fIsModifiersAllowed)
470 /* Prepend the sequence with modifier names: */
471 strSequence.prepend(strModifierNames);
472 }
473 /* Save what we've got: */
474 m_hotKey.setSequence(strSequence);
475 /* Commit data to the listener: */
476 emit sigCommitData(this);
477}
478
479void UIHotKeyEditor::drawSequence()
480{
481 /* Compose the text to reflect: */
482 QString strText = m_hotKey.sequence();
483 /* If modifiers are not allowed and the text is not empty: */
484 if (!m_fIsModifiersAllowed && !strText.isEmpty())
485 /* Prepend the text with Host+ modifier name: */
486 strText.prepend(UIHostCombo::hostComboModifierName());
487 /* Reflect what we've got: */
488 m_pLineEdit->setText(strText);
489}
490
491UIHotKey UIHotKeyEditor::hotKey() const
492{
493 /* Return hot-key: */
494 return m_hotKey;
495}
496
497void UIHotKeyEditor::setHotKey(const UIHotKey &hotKey)
498{
499 /* Remember passed hot-key: */
500 m_hotKey = hotKey;
501 /* Remember if modifiers are allowed: */
502 m_fIsModifiersAllowed = m_hotKey.type() == UIHotKeyType_WithModifiers;
503 /* Redraw sequence: */
504 drawSequence();
505}
506
507
508#include "UIHotKeyEditor.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use