VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIHotKeyEditor.cpp@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use