Unity 8
globalshortcutregistry.cpp
1 /*
2  * Copyright (C) 2015 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <QDebug>
18 #include <QGuiApplication>
19 #include <QKeyEvent>
20 #include <QKeySequence>
21 
22 #include "globalshortcutregistry.h"
23 
24 namespace {
25 QWindow* windowForShortcut(GlobalShortcut *sc) {
26  QObject* parent= sc;
27  while(parent) {
28  if (auto item = qobject_cast<QQuickItem*>(parent)) {
29  auto window = item->window();
30  if (window) return window;
31  }
32  parent = parent->parent();
33  }
34  return nullptr;
35 }
36 } // namespace
37 
38 GlobalShortcutRegistry::GlobalShortcutRegistry(QObject *parent)
39  : QObject(parent)
40 {
41  connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &GlobalShortcutRegistry::setupFilterOnWindow);
42  setupFilterOnWindow(qGuiApp->focusWindow());
43 }
44 
45 GlobalShortcutList GlobalShortcutRegistry::shortcuts() const
46 {
47  return m_shortcuts;
48 }
49 
50 bool GlobalShortcutRegistry::hasShortcut(const QVariant &seq) const
51 {
52  return m_shortcuts.contains(seq);
53 }
54 
56 {
57  if (sc) {
58  if (!m_shortcuts.contains(seq)) { // create a new entry
59  m_shortcuts.insert(seq, {sc});
60  } else { // append to an existing one
61  auto shortcuts = m_shortcuts[seq];
62  shortcuts.append(sc);
63  m_shortcuts.insert(seq, shortcuts);
64  }
65 
66  connect(sc, &GlobalShortcut::destroyed, this, &GlobalShortcutRegistry::removeShortcut);
67  }
68 }
69 
70 void GlobalShortcutRegistry::removeShortcut(QObject *obj)
71 {
72  QMutableMapIterator<QVariant, QVector<QPointer<GlobalShortcut>>> it(m_shortcuts);
73  while (it.hasNext()) {
74  it.next();
75  GlobalShortcut * scObj = static_cast<GlobalShortcut *>(obj);
76  if (scObj && it.value().contains(scObj)) {
77  it.value().removeAll(scObj);
78  if (it.value().isEmpty()) {
79  it.remove();
80  }
81  }
82  }
83 }
84 
85 bool GlobalShortcutRegistry::eventFilter(QObject *obj, QEvent *event)
86 {
87  Q_ASSERT(m_filteredWindow);
88  Q_ASSERT(obj == static_cast<QObject*>(m_filteredWindow.data()));
89 
90  if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
91 
92  QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
93 
94  // Make a copy of the event so we don't alter it for passing on.
95  QKeyEvent eCopy(keyEvent->type(),
96  keyEvent->key(),
97  keyEvent->modifiers(),
98  keyEvent->text(),
99  keyEvent->isAutoRepeat(),
100  keyEvent->count());
101  eCopy.ignore();
102 
103  int seq = keyEvent->key() + keyEvent->modifiers();
104  if (m_shortcuts.contains(seq)) {
105  const auto shortcuts = m_shortcuts.value(seq);
106  Q_FOREACH(const auto &shortcut, shortcuts) {
107  if (shortcut) {
108  auto window = windowForShortcut(shortcut);
109  if (!window || window == obj) { // accept shortcut if it's not attached to a window or it's window is active.
110  qApp->sendEvent(shortcut, &eCopy);
111  }
112  }
113  }
114  }
115 
116  return eCopy.isAccepted();
117  }
118 
119  return QObject::eventFilter(obj, event);
120 }
121 
123 {
124  if (m_filteredWindow) {
125  m_filteredWindow->removeEventFilter(this);
126  m_filteredWindow.clear();
127  }
128 
129  if (window) {
130  m_filteredWindow = window;
131  window->installEventFilter(this);
132  }
133 }
void setupFilterOnWindow(QWindow *window)
void addShortcut(const QVariant &seq, GlobalShortcut *sc)
The GlobalShortcut class.
GlobalShortcutList shortcuts() const
bool hasShortcut(const QVariant &seq) const