23 #include "kkeysequencewidget_p.h"
29 #include <QtCore/QHash>
30 #include <QHBoxLayout>
31 #include <QToolButton>
32 #include <QApplication>
44 class KKeySequenceWidgetPrivate
51 static QKeySequence appendToSequence(
const QKeySequence& seq,
int keyQt);
52 static bool isOkWhenModifierless(
int keyQt);
54 void updateShortcutDisplay();
55 void startRecording();
61 bool conflictWithStandardShortcuts(
const QKeySequence &seq);
67 bool conflictWithLocalShortcuts(
const QKeySequence &seq);
73 bool conflictWithGlobalShortcuts(
const QKeySequence &seq);
80 bool checkAgainstStandardShortcuts()
const
85 bool checkAgainstGlobalShortcuts()
const
90 bool checkAgainstLocalShortcuts()
const
95 void controlModifierlessTimout()
97 if (nKey != 0 && !modifierKeys) {
99 modifierlessTimeout.start(600);
102 modifierlessTimeout.stop();
108 void cancelRecording()
110 keySequence = oldKeySequence;
115 bool promptStealShortcutSystemwide(
117 const QHash<QKeySequence, QList<KGlobalShortcutInfo> > &shortcuts,
118 const QKeySequence &sequence)
120 if (shortcuts.isEmpty()) {
126 Q_FOREACH (
const QKeySequence &seq, shortcuts.keys()) {
128 clashingKeys +=
i18n(
"Shortcut '%1' in Application %2 for action %3\n",
135 const int hashSize = shortcuts.size();
137 QString message =
i18ncp(
"%1 is the number of conflicts (hidden), %2 is the key sequence of the shortcut that is problematic",
138 "The shortcut '%2' conflicts with the following key combination:\n",
139 "The shortcut '%2' conflicts with the following key combinations:\n",
140 hashSize, sequence.toString());
141 message+=clashingKeys;
143 QString title =
i18ncp(
"%1 is the number of shortcuts with which there is a conflict",
144 "Conflict with Registered Global Shortcut",
"Conflict with Registered Global Shortcuts", hashSize);
152 void doneRecording(
bool validate =
true);
157 KKeySequenceButton *keyButton;
160 QKeySequence keySequence;
161 QKeySequence oldKeySequence;
162 QTimer modifierlessTimeout;
163 bool allowModifierless;
167 bool multiKeyShortcutsAllowed;
171 KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes;
176 QList<QAction*> checkList;
181 QList<KActionCollection*> checkActionCollections;
186 QList<KAction*> stealActions;
188 bool stealShortcuts(
const QList<KAction *> &actions,
const QKeySequence &seq);
189 void wontStealShortcut(
QAction *item,
const QKeySequence &seq);
198 ,allowModifierless(false)
202 ,multiKeyShortcutsAllowed(true)
209 bool KKeySequenceWidgetPrivate::stealShortcuts(
210 const QList<KAction *> &actions,
211 const QKeySequence &seq)
214 const int listSize = actions.size();
216 QString title =
i18ncp(
"%1 is the number of conflicts",
"Shortcut Conflict",
"Shortcut Conflicts", listSize);
219 Q_FOREACH(
const KAction *action, actions) {
220 conflictingShortcuts +=
i18n(
"Shortcut '%1' for action '%2'\n",
224 QString message =
i18ncp(
"%1 is the number of ambigious shortcut clashes (hidden)",
225 "The \"%2\" shortcut is ambiguous with the following shortcut.\n"
226 "Do you want to assign an empty shortcut to this action?\n"
228 "The \"%2\" shortcut is ambiguous with the following shortcuts.\n"
229 "Do you want to assign an empty shortcut to these actions?\n"
232 seq.toString(QKeySequence::NativeText),
233 conflictingShortcuts);
241 void KKeySequenceWidgetPrivate::wontStealShortcut(
QAction *item,
const QKeySequence &seq)
244 QString msg(
i18n(
"<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
245 "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText) ,
253 d(new KKeySequenceWidgetPrivate(this))
256 setFocusProxy( d->keyButton );
259 connect(&d->modifierlessTimeout, SIGNAL(
timeout()),
this, SLOT(doneRecording()));
264 d->updateShortcutDisplay();
268 void KKeySequenceWidgetPrivate::init()
270 layout =
new QHBoxLayout(q);
271 layout->setMargin(0);
273 keyButton =
new KKeySequenceButton(
this, q);
274 keyButton->setFocusPolicy(Qt::StrongFocus);
275 keyButton->setIcon(
KIcon(
"configure"));
276 keyButton->setToolTip(
i18n(
"Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+a: hold the Ctrl key and press a."));
277 layout->addWidget(keyButton);
280 layout->addWidget(clearButton);
282 if (qApp->isLeftToRight())
283 clearButton->setIcon(
KIcon(
"edit-clear-locationbar-rtl"));
285 clearButton->setIcon(
KIcon(
"edit-clear-locationbar-ltr"));
297 return d->checkAgainstShortcutTypes;
303 d->componentName = componentName;
308 return d->multiKeyShortcutsAllowed;
314 d->multiKeyShortcutsAllowed = allowed;
320 d->checkAgainstShortcutTypes = types;
325 d->allowModifierless = allow;
331 if (keySequence.isEmpty())
333 return ! ( d->conflictWithLocalShortcuts(keySequence)
334 || d->conflictWithGlobalShortcuts(keySequence)
335 || d->conflictWithStandardShortcuts(keySequence));
341 return d->allowModifierless;
347 d->clearButton->setVisible(show);
350 #ifndef KDE_NO_DEPRECATED
353 d->checkList = checkList;
354 Q_ASSERT(d->checkActionCollections.isEmpty());
360 d->checkActionCollections = actionCollections;
372 return d->keySequence;
384 d->oldKeySequence = d->keySequence;
386 d->keySequence = seq;
387 d->doneRecording(validate ==
Validate);
400 QSet<KActionCollection *> changedCollections;
402 Q_FOREACH (
KAction *stealAction, d->stealActions) {
411 if (collection->
actions().contains(stealAction)) {
412 parentCollection = collection;
418 if (parentCollection) {
419 changedCollections.insert(parentCollection);
427 d->stealActions.clear();
430 void KKeySequenceButton::setText(
const QString &text)
432 QPushButton::setText(text);
437 void KKeySequenceWidgetPrivate::startRecording()
441 oldKeySequence = keySequence;
442 keySequence = QKeySequence();
444 keyButton->grabKeyboard();
446 if (!QWidget::keyboardGrabber()) {
447 kWarning() <<
"Failed to grab the keyboard! Most likely qt's nograb option is active";
450 keyButton->setDown(
true);
451 updateShortcutDisplay();
455 void KKeySequenceWidgetPrivate::doneRecording(
bool validate)
457 modifierlessTimeout.stop();
459 keyButton->releaseKeyboard();
460 keyButton->setDown(
false);
461 stealActions.clear();
463 if (keySequence==oldKeySequence) {
465 updateShortcutDisplay();
469 if (validate && !q->isKeySequenceAvailable(keySequence)) {
471 keySequence = oldKeySequence;
473 emit q->keySequenceChanged(keySequence);
476 updateShortcutDisplay();
480 bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(
const QKeySequence &keySequence)
486 QString message =
i18n(
"The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n"
487 "Please choose another one.");
500 QHash<QKeySequence, QList<KGlobalShortcutInfo> > others;
501 for (uint i=0; i<keySequence.count(); ++i) {
502 QKeySequence tmp(keySequence[i]);
509 if (!others.isEmpty()
510 && !promptStealShortcutSystemwide(q, others, keySequence)) {
520 for (uint i=0; i<keySequence.count(); ++i) {
527 bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(
const QKeySequence &keySequence)
540 QList<QAction*> allActions;
541 allActions += checkList;
543 allActions += collection->
actions();
563 QList<KAction*> conflictingActions;
566 foreach(
QAction * qaction , allActions ) {
573 conflictingActions.append(kaction);
575 wontStealShortcut(kaction, keySequence);
580 if(qaction->shortcut() == keySequence) {
583 wontStealShortcut(qaction, keySequence);
589 if (conflictingActions.isEmpty()) {
594 if(stealShortcuts(conflictingActions, keySequence)) {
595 stealActions = conflictingActions;
598 Q_FOREACH (
KAction *stealAction, stealActions) {
599 emit q->stealShortcut(
610 bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(
const QKeySequence &keySequence)
626 QString title =
i18n(
"Conflict with Standard Application Shortcut");
627 QString message =
i18n(
"The '%1' key combination is also used for the standard action "
628 "\"%2\" that some applications use.\n"
629 "Do you really want to use it as a global shortcut as well?",
639 void KKeySequenceWidgetPrivate::updateShortcutDisplay()
642 QString s = keySequence.toString(QKeySequence::NativeText);
643 s.replace(
'&', QLatin1String(
"&&"));
647 if (!s.isEmpty()) s.append(
",");
649 #if defined(Q_WS_MAC)
652 #elif defined(Q_WS_X11)
658 }
else if (nKey == 0) {
659 s =
i18nc(
"What the user inputs now will be taken as the new shortcut",
"Input");
666 s =
i18nc(
"No shortcut defined",
"None");
671 keyButton->setText(s);
675 KKeySequenceButton::~KKeySequenceButton()
681 bool KKeySequenceButton::event (QEvent* e)
683 if (d->isRecording && e->type() == QEvent::KeyPress) {
684 keyPressEvent(static_cast<QKeyEvent *>(e));
692 if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
697 return QPushButton::event(e);
701 void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
703 int keyQt = e->key();
709 i18n(
"The key you just pressed is not supported by Qt."),
710 i18n(
"Unsupported Key"));
711 return d->cancelRecording();
714 uint newModifiers = e->modifiers() & (
Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
718 if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
720 d->modifierKeys = newModifiers;
721 d->updateShortcutDisplay();
727 return QPushButton::keyPressEvent(e);
730 d->modifierKeys = newModifiers;
737 case Qt::Key_Control:
741 d->controlModifierlessTimout();
742 d->updateShortcutDisplay();
746 if (d->nKey == 0 && !(d->modifierKeys & ~
Qt::SHIFT)) {
749 if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt)
750 || d->allowModifierless)) {
758 if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys &
Qt::SHIFT)) {
759 keyQt = Qt::Key_Tab | d->modifierKeys;
762 keyQt |= d->modifierKeys;
765 keyQt |= (d->modifierKeys & ~Qt
::SHIFT);
768 d->keySequence = QKeySequence(keyQt);
771 KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
775 if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
779 d->controlModifierlessTimout();
780 d->updateShortcutDisplay();
786 void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
788 if (e->key() == -1) {
794 return QPushButton::keyReleaseEvent(e);
798 uint newModifiers = e->modifiers() & (
Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
801 if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
802 d->modifierKeys = newModifiers;
803 d->controlModifierlessTimout();
804 d->updateShortcutDisplay();
810 QKeySequence KKeySequenceWidgetPrivate::appendToSequence(
const QKeySequence& seq,
int keyQt)
812 switch (seq.count()) {
814 return QKeySequence(keyQt);
816 return QKeySequence(seq[0], keyQt);
818 return QKeySequence(seq[0], seq[1], keyQt);
820 return QKeySequence(seq[0], seq[1], seq[2], keyQt);
828 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(
int keyQt)
831 if (QKeySequence(keyQt).toString().length() == 1)
838 case Qt::Key_Backtab:
839 case Qt::Key_Backspace:
847 #include "kkeysequencewidget.moc"
848 #include "kkeysequencewidget_p.moc"