| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "content/browser/cocoa/system_hotkey_map.h" |
| |
| #import <Carbon/Carbon.h> |
| |
| #include "base/mac/scoped_nsobject.h" |
| |
| #pragma mark - NSDictionary Helper Functions |
| |
| namespace { |
| |
| // All 4 following functions return nil if the object doesn't exist, or isn't of |
| // the right class. |
| id ObjectForKey(NSDictionary* dict, NSString* key, Class aClass) { |
| id object = [dict objectForKey:key]; |
| if (![object isKindOfClass:aClass]) |
| return nil; |
| return object; |
| } |
| |
| NSDictionary* DictionaryForKey(NSDictionary* dict, NSString* key) { |
| return ObjectForKey(dict, key, [NSDictionary class]); |
| } |
| |
| NSArray* ArrayForKey(NSDictionary* dict, NSString* key) { |
| return ObjectForKey(dict, key, [NSArray class]); |
| } |
| |
| NSNumber* NumberForKey(NSDictionary* dict, NSString* key) { |
| return ObjectForKey(dict, key, [NSNumber class]); |
| } |
| |
| NSString* StringForKey(NSDictionary* dict, NSString* key) { |
| return ObjectForKey(dict, key, [NSString class]); |
| } |
| |
| } // namespace |
| |
| #pragma mark - SystemHotkey |
| |
| namespace content { |
| |
| struct SystemHotkey { |
| unsigned short key_code; |
| NSUInteger modifiers; |
| }; |
| |
| #pragma mark - SystemHotkeyMap |
| |
| SystemHotkeyMap::SystemHotkeyMap() { |
| } |
| SystemHotkeyMap::~SystemHotkeyMap() { |
| } |
| |
| NSDictionary* SystemHotkeyMap::DictionaryFromData(NSData* data) { |
| if (!data) |
| return nil; |
| |
| NSError* error = nil; |
| NSPropertyListFormat format; |
| NSDictionary* dictionary = |
| [NSPropertyListSerialization propertyListWithData:data |
| options:0 |
| format:&format |
| error:&error]; |
| |
| if (![dictionary isKindOfClass:[NSDictionary class]]) |
| return nil; |
| |
| return dictionary; |
| } |
| |
| bool SystemHotkeyMap::ParseDictionary(NSDictionary* dictionary) { |
| system_hotkeys_.clear(); |
| |
| if (!dictionary) |
| return false; |
| |
| NSDictionary* user_hotkey_dictionaries = |
| DictionaryForKey(dictionary, @"AppleSymbolicHotKeys"); |
| if (!user_hotkey_dictionaries) |
| return false; |
| |
| // Start with a dictionary of default OS X hotkeys that are not necessarily |
| // listed in com.apple.symbolichotkeys.plist, but should still be handled as |
| // reserved. |
| // If the user has overridden or disabled any of these hotkeys, |
| // -NSMutableDictionary addEntriesFromDictionary:] will ensure that the new |
| // values are used. |
| // See https://crbug.com/145062#c8 |
| base::scoped_nsobject<NSMutableDictionary> hotkey_dictionaries([@{ |
| // Default Window switch key binding: Command + ` |
| // Note: The first parameter @96 is not used by |SystemHotkeyMap|. |
| @"27" : @{ |
| @"enabled" : @YES, |
| @"value" : @{ |
| @"type" : @"standard", |
| @"parameters" : |
| @[ @96 /* unused */, @(kVK_ANSI_Grave), @(NSCommandKeyMask) ], |
| } |
| } |
| } mutableCopy]); |
| [hotkey_dictionaries addEntriesFromDictionary:user_hotkey_dictionaries]; |
| |
| for (NSString* hotkey_system_effect in [hotkey_dictionaries allKeys]) { |
| if (![hotkey_system_effect isKindOfClass:[NSString class]]) |
| continue; |
| |
| NSDictionary* hotkey_dictionary = |
| [hotkey_dictionaries objectForKey:hotkey_system_effect]; |
| if (![hotkey_dictionary isKindOfClass:[NSDictionary class]]) |
| continue; |
| |
| NSNumber* enabled = NumberForKey(hotkey_dictionary, @"enabled"); |
| if (!enabled || enabled.boolValue == NO) |
| continue; |
| |
| NSDictionary* value = DictionaryForKey(hotkey_dictionary, @"value"); |
| if (!value) |
| continue; |
| |
| NSString* type = StringForKey(value, @"type"); |
| if (!type || ![type isEqualToString:@"standard"]) |
| continue; |
| |
| NSArray* parameters = ArrayForKey(value, @"parameters"); |
| if (!parameters || [parameters count] != 3) |
| continue; |
| |
| NSNumber* key_code = [parameters objectAtIndex:1]; |
| if (![key_code isKindOfClass:[NSNumber class]]) |
| continue; |
| |
| NSNumber* modifiers = [parameters objectAtIndex:2]; |
| if (![modifiers isKindOfClass:[NSNumber class]]) |
| continue; |
| |
| ReserveHotkey(key_code.unsignedShortValue, |
| modifiers.unsignedIntegerValue, |
| hotkey_system_effect); |
| } |
| |
| return true; |
| } |
| |
| bool SystemHotkeyMap::IsEventReserved(NSEvent* event) const { |
| return IsHotkeyReserved(event.keyCode, event.modifierFlags); |
| } |
| |
| bool SystemHotkeyMap::IsHotkeyReserved(unsigned short key_code, |
| NSUInteger modifiers) const { |
| modifiers &= NSDeviceIndependentModifierFlagsMask; |
| std::vector<SystemHotkey>::const_iterator it; |
| for (it = system_hotkeys_.begin(); it != system_hotkeys_.end(); ++it) { |
| if (it->key_code == key_code && it->modifiers == modifiers) |
| return true; |
| } |
| return false; |
| } |
| |
| void SystemHotkeyMap::ReserveHotkey(unsigned short key_code, |
| NSUInteger modifiers, |
| NSString* system_effect) { |
| ReserveHotkey(key_code, modifiers); |
| |
| // If a hotkey exists for toggling through the windows of an application, then |
| // adding shift to that hotkey toggles through the windows backwards. |
| if ([system_effect isEqualToString:@"27"]) |
| ReserveHotkey(key_code, modifiers | NSShiftKeyMask); |
| } |
| |
| void SystemHotkeyMap::ReserveHotkey(unsigned short key_code, |
| NSUInteger modifiers) { |
| // Hotkeys require at least one of control, command, or alternate keys to be |
| // down. |
| NSUInteger required_modifiers = |
| NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask; |
| if ((modifiers & required_modifiers) == 0) |
| return; |
| |
| SystemHotkey hotkey; |
| hotkey.key_code = key_code; |
| hotkey.modifiers = modifiers; |
| system_hotkeys_.push_back(hotkey); |
| } |
| |
| } // namespace content |