src/platform/qt/ShortcutController.cpp (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "ShortcutController.h"
7
8#include "GamepadButtonEvent.h"
9
10#include <QAction>
11#include <QKeyEvent>
12#include <QMenu>
13
14using namespace QGBA;
15
16ShortcutController::ShortcutController(QObject* parent)
17 : QAbstractItemModel(parent)
18 , m_rootMenu(nullptr)
19{
20}
21
22QVariant ShortcutController::data(const QModelIndex& index, int role) const {
23 if (role != Qt::DisplayRole || !index.isValid()) {
24 return QVariant();
25 }
26 int row = index.row();
27 const ShortcutItem* item = static_cast<const ShortcutItem*>(index.internalPointer());
28 switch (index.column()) {
29 case 0:
30 return item->visibleName();
31 case 1:
32 return item->shortcut().toString(QKeySequence::NativeText);
33 case 2:
34 if (item->button() >= 0) {
35 return item->button();
36 }
37 break;
38 }
39 return QVariant();
40}
41
42QVariant ShortcutController::headerData(int section, Qt::Orientation orientation, int role) const {
43 if (role != Qt::DisplayRole) {
44 return QAbstractItemModel::headerData(section, orientation, role);
45 }
46 if (orientation == Qt::Horizontal) {
47 switch (section) {
48 case 0:
49 return tr("Action");
50 case 1:
51 return tr("Keyboard");
52 case 2:
53 return tr("Gamepad");
54 }
55 }
56 return section;
57}
58
59QModelIndex ShortcutController::index(int row, int column, const QModelIndex& parent) const {
60 const ShortcutItem* pmenu = &m_rootMenu;
61 if (parent.isValid()) {
62 pmenu = static_cast<ShortcutItem*>(parent.internalPointer());
63 }
64 return createIndex(row, column, const_cast<ShortcutItem*>(&pmenu->items()[row]));
65}
66
67QModelIndex ShortcutController::parent(const QModelIndex& index) const {
68 if (!index.isValid() || !index.internalPointer()) {
69 return QModelIndex();
70 }
71 ShortcutItem* item = static_cast<ShortcutItem*>(index.internalPointer());
72 if (!item->parent() || !item->parent()->parent()) {
73 return QModelIndex();
74 }
75 return createIndex(item->parent()->parent()->items().indexOf(*item->parent()), 0, item->parent());
76}
77
78int ShortcutController::columnCount(const QModelIndex& index) const {
79 return 3;
80}
81
82int ShortcutController::rowCount(const QModelIndex& index) const {
83 if (!index.isValid()) {
84 return m_rootMenu.items().count();
85 }
86 const ShortcutItem* item = static_cast<const ShortcutItem*>(index.internalPointer());
87 return item->items().count();
88}
89
90void ShortcutController::addAction(QMenu* menu, QAction* action, const QString& name) {
91 ShortcutItem* smenu = m_menuMap[menu];
92 if (!smenu) {
93 return;
94 }
95 ShortcutItem* pmenu = smenu->parent();
96 int row = pmenu->items().indexOf(*smenu);
97 QModelIndex parent = createIndex(row, 0, smenu);
98 beginInsertRows(parent, smenu->items().count(), smenu->items().count());
99 smenu->addAction(action, name);
100 endInsertRows();
101 ShortcutItem* item = &smenu->items().last();
102 emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item));
103}
104
105void ShortcutController::addFunctions(QMenu* menu, std::function<void ()> press, std::function<void ()> release, const QKeySequence& shortcut, const QString& visibleName, const QString& name) {
106 ShortcutItem* smenu = m_menuMap[menu];
107 if (!smenu) {
108 return;
109 }
110 ShortcutItem* pmenu = smenu->parent();
111 int row = pmenu->items().indexOf(*smenu);
112 QModelIndex parent = createIndex(row, 0, smenu);
113 beginInsertRows(parent, smenu->items().count(), smenu->items().count());
114 smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name);
115 endInsertRows();
116 ShortcutItem* item = &smenu->items().last();
117 m_heldKeys[shortcut] = item;
118 emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item));
119}
120
121void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) {
122 ShortcutItem* smenu = m_menuMap[parentMenu];
123 if (!smenu) {
124 smenu = &m_rootMenu;
125 }
126 QModelIndex parent;
127 ShortcutItem* pmenu = smenu->parent();
128 if (pmenu) {
129 int row = pmenu->items().indexOf(*smenu);
130 parent = createIndex(row, 0, smenu);
131 }
132 beginInsertRows(parent, smenu->items().count(), smenu->items().count());
133 smenu->addSubmenu(menu);
134 endInsertRows();
135 ShortcutItem* item = &smenu->items().last();
136 emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item));
137 m_menuMap[menu] = item;
138}
139
140ShortcutController::ShortcutItem* ShortcutController::itemAt(const QModelIndex& index) {
141 if (!index.isValid()) {
142 return nullptr;
143 }
144 return static_cast<ShortcutItem*>(index.internalPointer());
145}
146
147const ShortcutController::ShortcutItem* ShortcutController::itemAt(const QModelIndex& index) const {
148 if (!index.isValid()) {
149 return nullptr;
150 }
151 return static_cast<const ShortcutItem*>(index.internalPointer());
152}
153
154const QAction* ShortcutController::actionAt(const QModelIndex& index) const {
155 const ShortcutItem* item = itemAt(index);
156 if (!item) {
157 return nullptr;
158 }
159 return item->action();
160}
161
162void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& keySequence) {
163 if (!index.isValid()) {
164 return;
165 }
166 const QModelIndex& parent = index.parent();
167 if (!parent.isValid()) {
168 return;
169 }
170 ShortcutItem* item = itemAt(index);
171 if (item->functions().first) {
172 QKeySequence oldShortcut = item->shortcut();
173 if (!oldShortcut.isEmpty()) {
174 m_heldKeys.take(oldShortcut);
175 }
176 m_heldKeys[keySequence] = item;
177 }
178 item->setShortcut(keySequence);
179 emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer()));
180}
181
182void ShortcutController::updateButton(const QModelIndex& index, int button) {
183 if (!index.isValid()) {
184 return;
185 }
186 const QModelIndex& parent = index.parent();
187 if (!parent.isValid()) {
188 return;
189 }
190 ShortcutItem* item = itemAt(index);
191 int oldButton = item->button();
192 item->setButton(button);
193 if (oldButton >= 0) {
194 m_buttons.take(oldButton);
195 }
196 m_buttons[button] = item;
197 emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer()));
198}
199
200bool ShortcutController::eventFilter(QObject*, QEvent* event) {
201 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
202 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
203 if (keyEvent->isAutoRepeat()) {
204 return false;
205 }
206 auto item = m_heldKeys.find(keyEventToSequence(keyEvent));
207 if (item == m_heldKeys.end()) {
208 return false;
209 }
210 ShortcutItem::Functions pair = item.value()->functions();
211 if (event->type() == QEvent::KeyPress) {
212 if (pair.first) {
213 pair.first();
214 }
215 } else {
216 if (pair.second) {
217 pair.second();
218 }
219 }
220 event->accept();
221 return true;
222 }
223 if (event->type() == GamepadButtonEvent::Down()) {
224 auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
225 if (item == m_buttons.end()) {
226 return false;
227 }
228 QAction* action = item.value()->action();
229 if (action && action->isEnabled()) {
230 action->trigger();
231 }
232 ShortcutItem::Functions pair = item.value()->functions();
233 if (pair.first) {
234 pair.first();
235 }
236 event->accept();
237 return true;
238 }
239 if (event->type() == GamepadButtonEvent::Up()) {
240 auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
241 if (item == m_buttons.end()) {
242 return false;
243 }
244 ShortcutItem::Functions pair = item.value()->functions();
245 if (pair.second) {
246 pair.second();
247 }
248 event->accept();
249 return true;
250 }
251 return false;
252}
253
254QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) {
255 QString modifier = QString::null;
256
257 if (event->modifiers() & Qt::ShiftModifier) {
258 modifier += "Shift+";
259 }
260 if (event->modifiers() & Qt::ControlModifier) {
261 modifier += "Ctrl+";
262 }
263 if (event->modifiers() & Qt::AltModifier) {
264 modifier += "Alt+";
265 }
266 if (event->modifiers() & Qt::MetaModifier) {
267 modifier += "Meta+";
268 }
269
270 QString key = QKeySequence(event->key()).toString();
271 return QKeySequence(modifier + key);
272}
273
274ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent)
275 : m_action(action)
276 , m_shortcut(action->shortcut())
277 , m_menu(nullptr)
278 , m_name(name)
279 , m_button(-1)
280 , m_parent(parent)
281{
282 m_visibleName = action->text()
283 .remove(QRegExp("&(?!&)"))
284 .remove("...");
285}
286
287ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent)
288 : m_action(nullptr)
289 , m_shortcut(shortcut)
290 , m_functions(functions)
291 , m_menu(nullptr)
292 , m_name(name)
293 , m_visibleName(visibleName)
294 , m_button(-1)
295 , m_parent(parent)
296{
297}
298
299ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent)
300 : m_action(nullptr)
301 , m_menu(menu)
302 , m_button(-1)
303 , m_parent(parent)
304{
305 if (menu) {
306 m_visibleName = menu->title()
307 .remove(QRegExp("&(?!&)"))
308 .remove("...");
309 }
310}
311
312void ShortcutController::ShortcutItem::addAction(QAction* action, const QString& name) {
313 m_items.append(ShortcutItem(action, name, this));
314}
315
316void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name) {
317 m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this));
318}
319
320void ShortcutController::ShortcutItem::addSubmenu(QMenu* menu) {
321 m_items.append(ShortcutItem(menu, this));
322}
323
324void ShortcutController::ShortcutItem::setShortcut(const QKeySequence& shortcut) {
325 m_shortcut = shortcut;
326 if (m_action) {
327 m_action->setShortcut(shortcut);
328 }
329}