diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 50b2ef0e..7eb7a0c6 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -48,6 +48,7 @@ D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B5279086F6004A2160 /* InputSourceHelper */; }; D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7C027908EFC004A2160 /* OpenCCBridge */; }; D44FB74527915565003C80A6 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74427915555003C80A6 /* Preferences.swift */; }; + D44FB74727919D35003C80A6 /* EmacsKeyHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */; }; D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; }; D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; }; D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; }; @@ -177,6 +178,7 @@ D427F7B2279086B5004A2160 /* InputSourceHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InputSourceHelper; path = Packages/InputSourceHelper; sourceTree = ""; }; D427F7BF27908EAC004A2160 /* OpenCCBridge */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenCCBridge; path = Packages/OpenCCBridge; sourceTree = ""; }; D44FB74427915555003C80A6 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; + D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmacsKeyHelper.swift; sourceTree = ""; }; D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = ""; }; D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserOverrideModel.h; sourceTree = ""; }; @@ -254,6 +256,7 @@ 6A0D4EC815FC0D6400ABF4B3 /* main.m */, D427F76B278CA1BA004A2160 /* AppDelegate.swift */, D44FB74427915555003C80A6 /* Preferences.swift */, + D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */, D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */, D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */, 6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */, @@ -579,6 +582,7 @@ buildActionMask = 2147483647; files = ( D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */, + D44FB74727919D35003C80A6 /* EmacsKeyHelper.swift in Sources */, 6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */, D44FB74527915565003C80A6 /* Preferences.swift in Sources */, D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */, diff --git a/Source/EmacsKeyHelper.swift b/Source/EmacsKeyHelper.swift new file mode 100644 index 00000000..8a28df47 --- /dev/null +++ b/Source/EmacsKeyHelper.swift @@ -0,0 +1,54 @@ +// +// EmacsKeyHelper.swift +// +// Copyright (c) 2011 The McBopomofo Project. +// +// Contributors: +// Mengjuei Hsieh (@mjhsieh) +// Weizhong Yang (@zonble) +// +// Based on the Syrup Project and the Formosana Library +// by Lukhnos Liu (@lukhnos). +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import Cocoa + +@objc enum McBopomofoEmacsKey: UInt16 { + case none = 0 + case forward = 6 // F + case backward = 2 // B + case home = 1 // A + case end = 5 // E + case delete = 4 // D + case nextPage = 22 // V +} + +class EmacsKeyHelper: NSObject { + @objc static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> McBopomofoEmacsKey { + if flags.contains(.control) { + return McBopomofoEmacsKey(rawValue: charCode) ?? .none + } + return .none; + } +} diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 4c4b4319..51dc8f97 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -73,16 +73,6 @@ enum { kDeleteKeyCode = 117 }; -typedef NS_ENUM(NSUInteger, McBpomofoEmacsKey) { - McBpomofoEmacsKeyNone, - McBpomofoEmacsKeyForward, - McBpomofoEmacsKeyBackward, - McBpomofoEmacsKeyHome, - McBpomofoEmacsKeyEnd, - McBpomofoEmacsKeyDelete, - McBpomofoEmacsKeyNextPage, -}; - VTCandidateController *gCurrentCandidateController = nil; // if DEBUG is defined, a DOT file (GraphViz format) will be written to the @@ -499,32 +489,6 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return layout; } -- (McBpomofoEmacsKey)_detectEmacsKeyFromCharCode:(UniChar)charCode modifiers:(NSUInteger)flags -{ - if (flags & NSControlKeyMask) { - char c = charCode + 'a' - 1; - if (c == 'a') { - return McBpomofoEmacsKeyHome; - } - else if (c == 'e') { - return McBpomofoEmacsKeyEnd; - } - else if (c == 'f') { - return McBpomofoEmacsKeyForward; - } - else if (c == 'b') { - return McBpomofoEmacsKeyBackward; - } - else if (c == 'd') { - return McBpomofoEmacsKeyDelete; - } - else if (c == 'v') { - return McBpomofoEmacsKeyNextPage; - } - } - return McBpomofoEmacsKeyNone; -} - - (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client { NSRect textFrame = NSZeroRect; @@ -550,7 +514,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // get the unicode character code UniChar charCode = [inputText length] ? [inputText characterAtIndex:0] : 0; - McBpomofoEmacsKey emacsKey = [self _detectEmacsKeyFromCharCode:charCode modifiers:flags]; + McBopomofoEmacsKey emacsKey = [EmacsKeyHelper detectWithCharCode:charCode flags:flags]; if ([[client bundleIdentifier] isEqualToString:@"com.apple.Terminal"] && [NSStringFromClass([client class]) isEqualToString:@"IPMDServerClientWrapper"]) { // special handling for com.apple.Terminal @@ -609,7 +573,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // if we have candidate, it means we need to pass the event to the candidate handler if ([_candidates count]) { - return [self _handleCandidateEventWithInputText:inputText charCode:charCode keyCode:keyCode emacsKey:(McBpomofoEmacsKey)emacsKey]; + return [self _handleCandidateEventWithInputText:inputText charCode:charCode keyCode:keyCode emacsKey:(McBopomofoEmacsKey)emacsKey]; } // If we have marker index. @@ -632,7 +596,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } // Shift + left - if ((keyCode == cursorBackwardKey || emacsKey == McBpomofoEmacsKeyBackward) + if ((keyCode == cursorBackwardKey || emacsKey == McBopomofoEmacsKeyBackward) && (flags & NSShiftKeyMask)) { if (_builder->markerCursorIndex() > 0) { _builder->setMarkerCursorIndex(_builder->markerCursorIndex() - 1); @@ -644,7 +608,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } // Shift + Right - if ((keyCode == cursorForwardKey || emacsKey == McBpomofoEmacsKeyForward) + if ((keyCode == cursorForwardKey || emacsKey == McBopomofoEmacsKeyForward) && (flags & NSShiftKeyMask)) { if (_builder->markerCursorIndex() < _builder->length()) { _builder->setMarkerCursorIndex(_builder->markerCursorIndex() + 1); @@ -778,7 +742,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // handle cursor backward - if (keyCode == cursorBackwardKey || emacsKey == McBpomofoEmacsKeyBackward) { + if (keyCode == cursorBackwardKey || emacsKey == McBopomofoEmacsKeyBackward) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -810,7 +774,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // handle cursor forward - if (keyCode == cursorForwardKey || emacsKey == McBpomofoEmacsKeyForward) { + if (keyCode == cursorForwardKey || emacsKey == McBopomofoEmacsKeyForward) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -840,7 +804,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - if (keyCode == kHomeKeyCode || emacsKey == McBpomofoEmacsKeyHome) { + if (keyCode == kHomeKeyCode || emacsKey == McBopomofoEmacsKeyHome) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -861,7 +825,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - if (keyCode == kEndKeyCode || emacsKey == McBpomofoEmacsKeyEnd) { + if (keyCode == kEndKeyCode || emacsKey == McBopomofoEmacsKeyEnd) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -914,7 +878,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // Delete - if (keyCode == kDeleteKeyCode || emacsKey == McBpomofoEmacsKeyDelete) { + if (keyCode == kDeleteKeyCode || emacsKey == McBopomofoEmacsKeyDelete) { if (_bpmfReadingBuffer->isEmpty()) { if (![_composingBuffer length]) { return NO; @@ -1014,7 +978,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return NO; } -- (BOOL)_handleCandidateEventWithInputText:(NSString *)inputText charCode:(UniChar)charCode keyCode:(NSUInteger)keyCode emacsKey:(McBpomofoEmacsKey)emacsKey +- (BOOL)_handleCandidateEventWithInputText:(NSString *)inputText charCode:(UniChar)charCode keyCode:(NSUInteger)keyCode emacsKey:(McBopomofoEmacsKey)emacsKey { BOOL cancelCandidateKey = (charCode == 27) || @@ -1037,7 +1001,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [self candidateController:gCurrentCandidateController didSelectCandidateAtIndex:gCurrentCandidateController.selectedCandidateIndex]; return YES; } - else if (charCode == 32 || keyCode == kPageDownKeyCode || emacsKey == McBpomofoEmacsKeyNextPage) { + else if (charCode == 32 || keyCode == kPageDownKeyCode || emacsKey == McBopomofoEmacsKeyNextPage) { BOOL updated = [gCurrentCandidateController showNextPage]; if (!updated) { [self beep]; @@ -1071,7 +1035,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } - else if (emacsKey == McBpomofoEmacsKeyBackward) { + else if (emacsKey == McBopomofoEmacsKeyBackward) { BOOL updated = [gCurrentCandidateController highlightPreviousCandidate]; if (!updated) { [self beep]; @@ -1097,7 +1061,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } - else if (emacsKey == McBpomofoEmacsKeyForward) { + else if (emacsKey == McBopomofoEmacsKeyForward) { BOOL updated = [gCurrentCandidateController highlightNextCandidate]; if (!updated) { [self beep]; @@ -1141,7 +1105,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } - else if (keyCode == kHomeKeyCode || emacsKey == McBpomofoEmacsKeyHome) { + else if (keyCode == kHomeKeyCode || emacsKey == McBopomofoEmacsKeyHome) { if (gCurrentCandidateController.selectedCandidateIndex == 0) { [self beep]; @@ -1153,7 +1117,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [self updateClientComposingBuffer:_currentCandidateClient]; return YES; } - else if ((keyCode == kEndKeyCode || emacsKey == McBpomofoEmacsKeyEnd) && [_candidates count] > 0) { + else if ((keyCode == kEndKeyCode || emacsKey == McBopomofoEmacsKeyEnd) && [_candidates count] > 0) { if (gCurrentCandidateController.selectedCandidateIndex == [_candidates count] - 1) { [self beep]; }