all repos — mgba @ ac686e3942bd3ba02b233b31286db2cae4661ba3

mGBA Game Boy Advance Emulator

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}