2010-06-17 22:31:34 +02:00
|
|
|
/* This file is part of Clementine.
|
|
|
|
|
|
|
|
Clementine is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Clementine is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "macglobalshortcutbackend.h"
|
|
|
|
|
2010-06-22 22:57:05 +02:00
|
|
|
#include "config.h"
|
2010-06-17 22:31:34 +02:00
|
|
|
#include "globalshortcuts.h"
|
|
|
|
#include "mac_startup.h"
|
|
|
|
|
|
|
|
#include <boost/noncopyable.hpp>
|
|
|
|
|
|
|
|
#include <QAction>
|
|
|
|
#include <QList>
|
2010-06-17 23:33:16 +02:00
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QPushButton>
|
2010-06-17 22:31:34 +02:00
|
|
|
#include <QtDebug>
|
|
|
|
|
|
|
|
#include <AppKit/NSEvent.h>
|
2010-06-17 23:33:16 +02:00
|
|
|
#include <AppKit/NSWorkspace.h>
|
2010-06-17 22:31:34 +02:00
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
#include <IOKit/hidsystem/ev_keymap.h>
|
|
|
|
|
|
|
|
class MacGlobalShortcutBackendPrivate : boost::noncopyable {
|
|
|
|
public:
|
|
|
|
explicit MacGlobalShortcutBackendPrivate(MacGlobalShortcutBackend* backend)
|
2010-06-17 23:33:16 +02:00
|
|
|
: global_monitor_(nil),
|
|
|
|
local_monitor_(nil),
|
2010-06-17 22:31:34 +02:00
|
|
|
backend_(backend) {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Register() {
|
2010-06-18 13:41:16 +02:00
|
|
|
#ifdef SNOW_LEOPARD
|
2010-06-17 23:33:16 +02:00
|
|
|
global_monitor_ = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
|
2010-06-17 22:31:34 +02:00
|
|
|
handler:^(NSEvent* event) {
|
|
|
|
HandleKeyEvent(event);
|
|
|
|
}];
|
2010-06-17 23:33:16 +02:00
|
|
|
local_monitor_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask
|
|
|
|
handler:^(NSEvent* event) {
|
2010-09-13 12:14:46 +02:00
|
|
|
// Filter event if we handle it as a global shortcut.
|
|
|
|
return HandleKeyEvent(event) ? nil : event;
|
2010-06-17 23:33:16 +02:00
|
|
|
}];
|
2010-06-17 22:31:34 +02:00
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Unregister() {
|
2010-06-18 13:41:16 +02:00
|
|
|
#ifdef SNOW_LEOPARD
|
2010-06-17 23:33:16 +02:00
|
|
|
[NSEvent removeMonitor:global_monitor_];
|
|
|
|
[NSEvent removeMonitor:local_monitor_];
|
2010-06-18 13:41:16 +02:00
|
|
|
#endif
|
2010-06-17 23:33:16 +02:00
|
|
|
}
|
|
|
|
|
2010-06-17 22:31:34 +02:00
|
|
|
private:
|
|
|
|
static QKeySequence GetSequence(NSEvent* event) {
|
|
|
|
NSString* str = [event charactersIgnoringModifiers];
|
2010-06-18 00:55:34 +02:00
|
|
|
NSString* upper = [str uppercaseString];
|
|
|
|
const char* chars = [upper UTF8String];
|
2010-06-17 22:31:34 +02:00
|
|
|
NSUInteger modifiers = [event modifierFlags];
|
2010-06-18 00:55:34 +02:00
|
|
|
int key = 0;
|
|
|
|
unsigned char c = chars[0];
|
|
|
|
switch (c) {
|
|
|
|
case 0x1b: key = Qt::Key_Escape; break;
|
|
|
|
case 0x09: key = Qt::Key_Tab; break;
|
|
|
|
case 0x0d: key = Qt::Key_Return; break;
|
|
|
|
case 0x08: key = Qt::Key_Backspace; break;
|
2010-06-29 20:44:36 +02:00
|
|
|
case 0x03: key = Qt::Key_Enter; break;
|
2010-06-18 00:55:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (key == 0) {
|
|
|
|
if (c >= 0x20 && c <= 0x7e) { // ASCII from space to ~
|
|
|
|
key = c;
|
|
|
|
} else {
|
|
|
|
key = MapFunctionKey([event keyCode]);
|
|
|
|
if (key == 0) {
|
|
|
|
return QKeySequence();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-17 22:31:34 +02:00
|
|
|
if (modifiers & NSShiftKeyMask) {
|
|
|
|
key += Qt::SHIFT;
|
|
|
|
}
|
|
|
|
if (modifiers & NSControlKeyMask) {
|
|
|
|
key += Qt::META;
|
|
|
|
}
|
|
|
|
if (modifiers & NSAlternateKeyMask) {
|
|
|
|
key += Qt::ALT;
|
|
|
|
}
|
|
|
|
if (modifiers & NSCommandKeyMask) {
|
|
|
|
key += Qt::CTRL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QKeySequence(key);
|
|
|
|
}
|
|
|
|
|
2010-09-13 12:14:46 +02:00
|
|
|
bool HandleKeyEvent(NSEvent* event) {
|
2010-06-17 22:31:34 +02:00
|
|
|
QKeySequence sequence = GetSequence(event);
|
2010-09-13 12:14:46 +02:00
|
|
|
return backend_->KeyPressed(sequence);
|
2010-06-17 22:31:34 +02:00
|
|
|
}
|
|
|
|
|
2010-06-18 00:55:34 +02:00
|
|
|
static int MapFunctionKey(int keycode) {
|
|
|
|
switch (keycode) {
|
|
|
|
// Function keys
|
|
|
|
case NSInsertFunctionKey: return Qt::Key_Insert;
|
|
|
|
case NSDeleteFunctionKey: return Qt::Key_Delete;
|
|
|
|
case NSPauseFunctionKey: return Qt::Key_Pause;
|
|
|
|
case NSPrintFunctionKey: return Qt::Key_Print;
|
|
|
|
case NSSysReqFunctionKey: return Qt::Key_SysReq;
|
|
|
|
case NSHomeFunctionKey: return Qt::Key_Home;
|
|
|
|
case NSEndFunctionKey: return Qt::Key_End;
|
|
|
|
case NSLeftArrowFunctionKey: return Qt::Key_Left;
|
|
|
|
case NSUpArrowFunctionKey: return Qt::Key_Up;
|
|
|
|
case NSRightArrowFunctionKey: return Qt::Key_Right;
|
|
|
|
case NSDownArrowFunctionKey: return Qt::Key_Down;
|
|
|
|
case NSPageUpFunctionKey: return Qt::Key_PageUp;
|
|
|
|
case NSPageDownFunctionKey: return Qt::Key_PageDown;
|
|
|
|
case NSScrollLockFunctionKey: return Qt::Key_ScrollLock;
|
|
|
|
case NSF1FunctionKey: return Qt::Key_F1;
|
|
|
|
case NSF2FunctionKey: return Qt::Key_F2;
|
|
|
|
case NSF3FunctionKey: return Qt::Key_F3;
|
|
|
|
case NSF4FunctionKey: return Qt::Key_F4;
|
|
|
|
case NSF5FunctionKey: return Qt::Key_F5;
|
|
|
|
case NSF6FunctionKey: return Qt::Key_F6;
|
|
|
|
case NSF7FunctionKey: return Qt::Key_F7;
|
|
|
|
case NSF8FunctionKey: return Qt::Key_F8;
|
|
|
|
case NSF9FunctionKey: return Qt::Key_F9;
|
|
|
|
case NSF10FunctionKey: return Qt::Key_F10;
|
|
|
|
case NSF11FunctionKey: return Qt::Key_F11;
|
|
|
|
case NSF12FunctionKey: return Qt::Key_F12;
|
|
|
|
case NSF13FunctionKey: return Qt::Key_F13;
|
|
|
|
case NSF14FunctionKey: return Qt::Key_F14;
|
|
|
|
case NSF15FunctionKey: return Qt::Key_F15;
|
|
|
|
case NSF16FunctionKey: return Qt::Key_F16;
|
|
|
|
case NSF17FunctionKey: return Qt::Key_F17;
|
|
|
|
case NSF18FunctionKey: return Qt::Key_F18;
|
|
|
|
case NSF19FunctionKey: return Qt::Key_F19;
|
|
|
|
case NSF20FunctionKey: return Qt::Key_F20;
|
|
|
|
case NSF21FunctionKey: return Qt::Key_F21;
|
|
|
|
case NSF22FunctionKey: return Qt::Key_F22;
|
|
|
|
case NSF23FunctionKey: return Qt::Key_F23;
|
|
|
|
case NSF24FunctionKey: return Qt::Key_F24;
|
|
|
|
case NSF25FunctionKey: return Qt::Key_F25;
|
|
|
|
case NSF26FunctionKey: return Qt::Key_F26;
|
|
|
|
case NSF27FunctionKey: return Qt::Key_F27;
|
|
|
|
case NSF28FunctionKey: return Qt::Key_F28;
|
|
|
|
case NSF29FunctionKey: return Qt::Key_F29;
|
|
|
|
case NSF30FunctionKey: return Qt::Key_F30;
|
|
|
|
case NSF31FunctionKey: return Qt::Key_F31;
|
|
|
|
case NSF32FunctionKey: return Qt::Key_F32;
|
|
|
|
case NSF33FunctionKey: return Qt::Key_F33;
|
|
|
|
case NSF34FunctionKey: return Qt::Key_F34;
|
|
|
|
case NSF35FunctionKey: return Qt::Key_F35;
|
|
|
|
case NSMenuFunctionKey: return Qt::Key_Menu;
|
|
|
|
case NSHelpFunctionKey: return Qt::Key_Help;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-06-17 23:33:16 +02:00
|
|
|
id global_monitor_;
|
|
|
|
id local_monitor_;
|
2010-06-17 22:31:34 +02:00
|
|
|
MacGlobalShortcutBackend* backend_;
|
|
|
|
};
|
|
|
|
|
|
|
|
MacGlobalShortcutBackend::MacGlobalShortcutBackend(GlobalShortcuts* parent)
|
|
|
|
: GlobalShortcutBackend(parent),
|
|
|
|
p_(new MacGlobalShortcutBackendPrivate(this)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
MacGlobalShortcutBackend::~MacGlobalShortcutBackend() {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MacGlobalShortcutBackend::DoRegister() {
|
2010-06-17 23:33:16 +02:00
|
|
|
// Always enable media keys.
|
2010-06-17 22:31:34 +02:00
|
|
|
mac::SetShortcutHandler(this);
|
2010-06-17 23:33:16 +02:00
|
|
|
|
2010-06-18 01:11:51 +02:00
|
|
|
if (AXAPIEnabled()) {
|
2010-06-17 23:33:16 +02:00
|
|
|
foreach (const GlobalShortcuts::Shortcut& shortcut, manager_->shortcuts().values()) {
|
|
|
|
shortcuts_[shortcut.action->shortcut()] = shortcut.action;
|
|
|
|
}
|
|
|
|
return p_->Register();
|
2010-06-17 22:31:34 +02:00
|
|
|
}
|
2010-06-17 23:33:16 +02:00
|
|
|
|
|
|
|
return false;
|
2010-06-17 22:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MacGlobalShortcutBackend::DoUnregister() {
|
|
|
|
p_->Unregister();
|
2010-06-17 23:33:16 +02:00
|
|
|
shortcuts_.clear();
|
2010-06-17 22:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MacGlobalShortcutBackend::MacMediaKeyPressed(int key) {
|
|
|
|
switch (key) {
|
|
|
|
case NX_KEYTYPE_PLAY:
|
2010-06-17 23:54:42 +02:00
|
|
|
KeyPressed(Qt::Key_MediaPlay);
|
2010-06-17 22:31:34 +02:00
|
|
|
break;
|
|
|
|
case NX_KEYTYPE_FAST:
|
2010-06-17 23:54:42 +02:00
|
|
|
KeyPressed(Qt::Key_MediaNext);
|
2010-06-17 22:31:34 +02:00
|
|
|
break;
|
|
|
|
case NX_KEYTYPE_REWIND:
|
2010-06-17 23:54:42 +02:00
|
|
|
KeyPressed(Qt::Key_MediaPrevious);
|
2010-06-17 22:31:34 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-13 12:14:46 +02:00
|
|
|
bool MacGlobalShortcutBackend::KeyPressed(const QKeySequence& sequence) {
|
2010-06-29 20:44:36 +02:00
|
|
|
if (sequence.isEmpty()) {
|
2010-09-13 12:14:46 +02:00
|
|
|
return false;
|
2010-06-29 20:44:36 +02:00
|
|
|
}
|
2010-06-17 22:31:34 +02:00
|
|
|
QAction* action = shortcuts_[sequence];
|
|
|
|
if (action) {
|
|
|
|
action->trigger();
|
2010-09-13 12:14:46 +02:00
|
|
|
return true;
|
2010-06-17 22:31:34 +02:00
|
|
|
}
|
2010-09-13 12:14:46 +02:00
|
|
|
return false;
|
2010-06-17 22:31:34 +02:00
|
|
|
}
|
2010-06-17 23:33:16 +02:00
|
|
|
|
2010-06-18 01:11:51 +02:00
|
|
|
bool MacGlobalShortcutBackend::IsAccessibilityEnabled() const {
|
2010-06-18 13:41:16 +02:00
|
|
|
#ifdef SNOW_LEOPARD
|
2010-06-18 01:11:51 +02:00
|
|
|
return AXAPIEnabled();
|
2010-06-18 13:41:16 +02:00
|
|
|
#else
|
|
|
|
return true; // It's not really enabled but it doesn't matter.
|
|
|
|
#endif
|
2010-06-18 01:11:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MacGlobalShortcutBackend::ShowAccessibilityDialog() {
|
2010-06-18 13:41:16 +02:00
|
|
|
#ifdef SNOW_LEOPARD
|
2010-06-18 01:11:51 +02:00
|
|
|
NSArray* paths = NSSearchPathForDirectoriesInDomains(
|
|
|
|
NSPreferencePanesDirectory, NSSystemDomainMask, YES);
|
|
|
|
if ([paths count] == 1) {
|
|
|
|
NSURL* prefpane_url = [NSURL fileURLWithPath:
|
|
|
|
[[paths objectAtIndex:0] stringByAppendingPathComponent:@"UniversalAccessPref.prefPane"]];
|
|
|
|
[[NSWorkspace sharedWorkspace] openURL:prefpane_url];
|
|
|
|
}
|
2010-06-18 13:41:16 +02:00
|
|
|
#endif
|
2010-06-17 23:33:16 +02:00
|
|
|
}
|