Zonble: Pref Module + Interface to choose Conv Engine

- We removed the VXHanConvert from the codes inherited from upstream.
- OpenCC in vChewing is for converting things to KangXi. It doesn't convert Simplified Chinese to Traditional (nor vise versa).
- Fixed a bug to ensure that the candidate keys won't be niled in the preferencesWindowController.
- Fixed a bug to ensure that the customized candidate keys will always consist of unique characters. Entries like "1145141919" will be recognized as "1459" prior to the validity check process.

Co-Authored-By: Weizhong Yang a.k.a zonble <zonble@gmail.com>
This commit is contained in:
ShikiSuen 2022-01-16 11:55:04 +08:00
parent a541117ae3
commit 0bc695d081
11 changed files with 668 additions and 448 deletions

View File

@ -203,7 +203,7 @@
<constraints> <constraints>
<constraint firstAttribute="height" constant="16" id="o6K-VN-uxe"/> <constraint firstAttribute="height" constant="16" id="o6K-VN-uxe"/>
</constraints> </constraints>
<buttonCell key="cell" type="check" title="Check for updates automatically" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" state="on" inset="2" id="Z9t-P0-BLF"> <buttonCell key="cell" type="check" title="Check for updates automatically" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="Z9t-P0-BLF">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/> <font key="font" metaFont="cellTitle"/>
</buttonCell> </buttonCell>

View File

@ -0,0 +1,53 @@
//
// EmacsKeyHelper.swift
//
// Copyright (c) 2011-2022 The OpenVanilla Project.
//
// Contributors:
// Weizhong Yang (@zonble) @ OpenVanilla
//
// 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 vChewingEmacsKey: 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) -> vChewingEmacsKey {
if flags.contains(.control) {
return vChewingEmacsKey(rawValue: charCode) ?? .none
}
return .none;
}
}

View File

@ -80,11 +80,5 @@
// current input mode // current input mode
NSString *_inputMode; NSString *_inputMode;
// if Chinese conversion is enabled
BOOL _chineseConversionEnabled;
// if half-width punctuation is enabled
BOOL _halfWidthPunctuationEnabled;
} }
@end @end

View File

@ -45,8 +45,6 @@
#import "LanguageModelManager.h" #import "LanguageModelManager.h"
#import "vChewing-Swift.h" #import "vChewing-Swift.h"
@import OpenCC;
// C++ namespace usages // C++ namespace usages
using namespace std; using namespace std;
using namespace Formosa::Mandarin; using namespace Formosa::Mandarin;
@ -54,40 +52,7 @@ using namespace Formosa::Gramambular;
using namespace vChewing; using namespace vChewing;
using namespace OpenVanilla; using namespace OpenVanilla;
// default, min and max candidate list text size
static const NSInteger kDefaultCandidateListTextSize = 16;
static const NSInteger kMinKeyLabelSize = 10; static const NSInteger kMinKeyLabelSize = 10;
static const NSInteger kMinCandidateListTextSize = 12;
static const NSInteger kMaxCandidateListTextSize = 196;
// default, min and max composing buffer size (in codepoints)
// modern Macs can usually work up to 16 codepoints when the builder still
// walks the grid with good performance; slower Macs (like old PowerBooks)
// will start to sputter beyond 12; such is the algorithmatic complexity
// of the Viterbi algorithm used in the builder library (at O(N^2))
static const NSInteger kDefaultComposingBufferSize = 10;
static const NSInteger kMinComposingBufferSize = 4;
static const NSInteger kMaxComposingBufferSize = 20;
// user defaults (app perferences) key names; in this project we use
// NSUserDefaults throughout and do not wrap them in another config object
static NSString *const kKeyboardLayoutPreferenceKey = @"KeyboardLayout";
static NSString *const kBasisKeyboardLayoutPreferenceKey = @"BasisKeyboardLayout"; // alphanumeric ("ASCII") input basis
static NSString *const kFunctionKeyKeyboardLayoutPreferenceKey = @"FunctionKeyKeyboardLayout"; // alphanumeric ("ASCII") input basis
static NSString *const kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey = @"FunctionKeyKeyboardLayoutOverrideIncludeShift"; // whether include shift
static NSString *const kCandidateListTextSizeKey = @"CandidateListTextSize";
static NSString *const kSelectPhraseAfterCursorAsCandidatePreferenceKey = @"SelectPhraseAfterCursorAsCandidate";
static NSString *const kUseHorizontalCandidateListPreferenceKey = @"UseHorizontalCandidateList";
static NSString *const kComposingBufferSizePreferenceKey = @"ComposingBufferSize";
static NSString *const kChooseCandidateUsingSpaceKey = @"ChooseCandidateUsingSpaceKey";
static NSString *const kChineseConversionEnabledKey = @"ChineseConversionEnabledKey";
static NSString *const kHalfWidthPunctuationEnabledKey = @"HalfWidthPunctuationEnabledKey";
static NSString *const kEscToCleanInputBufferKey = @"EscToCleanInputBufferKey";
// advanced (usually optional) settings
static NSString *const kCandidateTextFontName = @"CandidateTextFontName";
static NSString *const kCandidateKeyLabelFontName = @"CandidateKeyLabelFontName";
static NSString *const kCandidateKeys = @"CandidateKeys";
// input modes // input modes
static NSString *const kBopomofoModeIdentifier = @"org.openvanilla.inputmethod.vChewing.Bopomofo"; static NSString *const kBopomofoModeIdentifier = @"org.openvanilla.inputmethod.vChewing.Bopomofo";
@ -107,16 +72,6 @@ enum {
kDeleteKeyCode = 117 kDeleteKeyCode = 117
}; };
typedef NS_ENUM(NSUInteger, vChewingEmacsKey) {
vChewingEmacsKeyNone,
vChewingEmacsKeyForward,
vChewingEmacsKeyBackward,
vChewingEmacsKeyHome,
vChewingEmacsKeyEnd,
vChewingEmacsKeyDelete,
vChewingEmacsKeyNextPage,
};
VTCandidateController *gCurrentCandidateController = nil; VTCandidateController *gCurrentCandidateController = nil;
// if DEBUG is defined, a DOT file (GraphViz format) will be written to the // if DEBUG is defined, a DOT file (GraphViz format) will be written to the
@ -131,12 +86,6 @@ static inline NSString *LocalizationNotNeeded(NSString *s) {
return s; return s;
} }
// private methods
@interface vChewingInputMethodController ()
+ (VTHorizontalCandidateController *)horizontalCandidateController;
+ (VTVerticalCandidateController *)verticalCandidateController;
@end
@interface vChewingInputMethodController (VTCandidateController) <VTCandidateControllerDelegate> @interface vChewingInputMethodController (VTCandidateController) <VTCandidateControllerDelegate>
@end @end
@ -202,8 +151,6 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
_composingBuffer = [[NSMutableString alloc] init]; _composingBuffer = [[NSMutableString alloc] init];
_inputMode = kBopomofoModeIdentifier; _inputMode = kBopomofoModeIdentifier;
_chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey];
_halfWidthPunctuationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kHalfWidthPunctuationEnabledKey];
} }
return self; return self;
@ -213,16 +160,16 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
{ {
// a menu instance (autoreleased) is requested every time the user click on the input menu // a menu instance (autoreleased) is requested every time the user click on the input menu
NSMenu *menu = [[NSMenu alloc] initWithTitle:LocalizationNotNeeded(@"Input Method Menu")]; NSMenu *menu = [[NSMenu alloc] initWithTitle:LocalizationNotNeeded(@"Input Method Menu")];
NSMenuItem *preferenceMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"vChewing Preferences", @"") action:@selector(showPreferences:) keyEquivalent:@""];
[menu addItem:preferenceMenuItem]; [menu addItemWithTitle:NSLocalizedString(@"vChewing Preferences", @"") action:@selector(showPreferences:) keyEquivalent:@""];
NSMenuItem *chineseConversionMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Chinese Conversion", @"") action:@selector(toggleChineseConverter:) keyEquivalent:@"K"]; NSMenuItem *chineseConversionMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Chinese Conversion", @"") action:@selector(toggleChineseConverter:) keyEquivalent:@"K"];
chineseConversionMenuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl; chineseConversionMenuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl;
chineseConversionMenuItem.state = _chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff; chineseConversionMenuItem.state = Preferences.chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff;
[menu addItem:chineseConversionMenuItem]; [menu addItem:chineseConversionMenuItem];
NSMenuItem *halfWidthPunctuationMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use Half-Width Punctuations", @"") action:@selector(toggleHalfWidthPunctuation:) keyEquivalent:@""]; NSMenuItem *halfWidthPunctuationMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use Half-Width Punctuations", @"") action:@selector(toggleHalfWidthPunctuation:) keyEquivalent:@""];
halfWidthPunctuationMenuItem.state = _halfWidthPunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff; halfWidthPunctuationMenuItem.state = Preferences.halfWidthPunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff;
[menu addItem:halfWidthPunctuationMenuItem]; [menu addItem:halfWidthPunctuationMenuItem];
[menu addItem:[NSMenuItem separatorItem]]; // ------------------------------ [menu addItem:[NSMenuItem separatorItem]]; // ------------------------------
@ -232,23 +179,15 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[menu addItem:editExcludedPhrasesItem]; [menu addItem:editExcludedPhrasesItem];
} }
else { else {
NSMenuItem *editUserPhrasesItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Edit User Phrases", @"") action:@selector(openUserPhrases:) keyEquivalent:@""]; [menu addItemWithTitle:NSLocalizedString(@"Edit User Phrases", @"") action:@selector(openUserPhrases:) keyEquivalent:@""];
[menu addItem:editUserPhrasesItem]; [menu addItemWithTitle:NSLocalizedString(@"Edit Excluded Phrases", @"") action:@selector(openExcludedPhrasesvChewing:) keyEquivalent:@""];
NSMenuItem *editExcludedPhrasesItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Edit Excluded Phrases", @"") action:@selector(openExcludedPhrasesvChewing:) keyEquivalent:@""];
[menu addItem:editExcludedPhrasesItem];
} }
[menu addItemWithTitle:NSLocalizedString(@"Reload User Phrases", @"") action:@selector(reloadUserPhrases:) keyEquivalent:@""];
NSMenuItem *reloadUserPhrasesItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Reload User Phrases", @"") action:@selector(reloadUserPhrases:) keyEquivalent:@""];
[menu addItem:reloadUserPhrasesItem];
[menu addItem:[NSMenuItem separatorItem]]; // ------------------------------ [menu addItem:[NSMenuItem separatorItem]]; // ------------------------------
NSMenuItem *updateCheckItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Check for Updates…", @"") action:@selector(checkForUpdate:) keyEquivalent:@""]; [menu addItemWithTitle:NSLocalizedString(@"Check for Updates…", @"") action:@selector(checkForUpdate:) keyEquivalent:@""];
[menu addItem:updateCheckItem]; [menu addItemWithTitle:NSLocalizedString(@"About vChewing…", @"") action:@selector(showAbout:) keyEquivalent:@""];
NSMenuItem *aboutMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"About vChewing…", @"") action:@selector(showAbout:) keyEquivalent:@""];
[menu addItem:aboutMenuItem];
return menu; return menu;
} }
@ -259,10 +198,7 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] synchronize];
// Override the keyboard layout. Use US if not set. // Override the keyboard layout. Use US if not set.
NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey]; NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout;
if (!basisKeyboardLayoutID) {
basisKeyboardLayoutID = @"com.apple.keylayout.US";
}
[client overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID]; [client overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID];
// reset the state // reset the state
@ -273,49 +209,28 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[_composingBuffer setString:@""]; [_composingBuffer setString:@""];
// checks and populates the default settings // checks and populates the default settings
NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey]; switch (Preferences.keyboardLayout) {
switch (keyboardLayout) { case KeyboardLayoutStandard:
case 0:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout());
break; break;
case 1: case KeyboardLayoutEten:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::ETenLayout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::ETenLayout());
break; break;
case 2: case KeyboardLayoutHsu:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::HsuLayout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::HsuLayout());
break; break;
case 3: case KeyboardLayoutEten26:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::ETen26Layout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::ETen26Layout());
break; break;
case 4: case KeyboardLayoutHanyuPinyin:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::HanyuPinyinLayout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::HanyuPinyinLayout());
break; break;
case 5: case KeyboardLayoutIBM:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::IBMLayout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::IBMLayout());
break; break;
default: default:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout()); _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout());
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:kKeyboardLayoutPreferenceKey]; Preferences.keyboardLayout = KeyboardLayoutStandard;
}
// set the size
NSInteger textSize = [[NSUserDefaults standardUserDefaults] integerForKey:kCandidateListTextSizeKey];
NSInteger previousTextSize = textSize;
if (textSize == 0) {
textSize = kDefaultCandidateListTextSize;
}
else if (textSize < kMinCandidateListTextSize) {
textSize = kMinCandidateListTextSize;
}
else if (textSize > kMaxCandidateListTextSize) {
textSize = kMaxCandidateListTextSize;
}
if (textSize != previousTextSize) {
[[NSUserDefaults standardUserDefaults] setInteger:textSize forKey:kCandidateListTextSizeKey];
}
if (![[NSUserDefaults standardUserDefaults] objectForKey:kChooseCandidateUsingSpaceKey]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kChooseCandidateUsingSpaceKey];
} }
[(AppDelegate *)[NSApp delegate] checkForUpdate]; [(AppDelegate *)[NSApp delegate] checkForUpdate];
@ -361,10 +276,7 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] synchronize];
// Remember to override the keyboard layout again -- treat this as an activate eventy // Remember to override the keyboard layout again -- treat this as an activate eventy
NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey]; NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout;
if (!basisKeyboardLayoutID) {
basisKeyboardLayoutID = @"com.apple.keylayout.US";
}
[sender overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID]; [sender overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID];
_inputMode = newInputMode; _inputMode = newInputMode;
@ -389,6 +301,13 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
#pragma mark - IMKServerInput protocol methods #pragma mark - IMKServerInput protocol methods
- (NSString *)_convertToKangXi:(NSString *)text
{
// return [VXHanConvert convertToSimplifiedFrom:text]; // VXHanConvert 這個引擎有點落後了,不支援詞組轉換、且修改轉換表的過程很麻煩。
// OpenCC 引擎別的都還好,就是有點肥。改日換成純 ObjC 的 OpenCC 實現方案。
return [OpenCCBridge convertToKangXi:text];
}
- (void)commitComposition:(id)client - (void)commitComposition:(id)client
{ {
// if it's Terminal, we don't commit at the first call (the client of which will not be IPMDServerClientWrapper) // if it's Terminal, we don't commit at the first call (the client of which will not be IPMDServerClientWrapper)
@ -403,9 +322,9 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
// Chinese conversion. // Chinese conversion.
NSString *buffer = _composingBuffer; NSString *buffer = _composingBuffer;
BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey];
if (chineseConversionEnabled) { if (Preferences.chineseConversionEnabled) {
buffer = [OpenCCBridge convert:_composingBuffer]; buffer = [self _convertToKangXi:_composingBuffer];
} }
// commit the text, clear the state // commit the text, clear the state
@ -415,7 +334,6 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[_composingBuffer setString:@""]; [_composingBuffer setString:@""];
gCurrentCandidateController.visible = NO; gCurrentCandidateController.visible = NO;
[_candidates removeAllObjects]; [_candidates removeAllObjects];
[self _hideTooltip]; [self _hideTooltip];
} }
@ -546,31 +464,16 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
// the user type along, the already composed text at front will // the user type along, the already composed text at front will
// be popped out // be popped out
NSInteger _composingBufferSize = [[NSUserDefaults standardUserDefaults] integerForKey:kComposingBufferSizePreferenceKey]; NSInteger composingBufferSize = Preferences.composingBufferSize;
NSInteger previousComposingBufferSize = _composingBufferSize;
if (_composingBufferSize == 0) { if (_builder->grid().width() > (size_t)composingBufferSize) {
_composingBufferSize = kDefaultComposingBufferSize;
}
else if (_composingBufferSize < kMinComposingBufferSize) {
_composingBufferSize = kMinComposingBufferSize;
}
else if (_composingBufferSize > kMaxComposingBufferSize) {
_composingBufferSize = kMaxComposingBufferSize;
}
if (_composingBufferSize != previousComposingBufferSize) {
[[NSUserDefaults standardUserDefaults] setInteger:_composingBufferSize forKey:kComposingBufferSizePreferenceKey];
}
if (_builder->grid().width() > (size_t)_composingBufferSize) {
if (_walkedNodes.size() > 0) { if (_walkedNodes.size() > 0) {
NodeAnchor &anchor = _walkedNodes[0]; NodeAnchor &anchor = _walkedNodes[0];
NSString *popedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()]; NSString *popedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()];
// Chinese conversion. // Chinese conversion.
BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; BOOL chineseConversionEnabled = Preferences.chineseConversionEnabled;
if (chineseConversionEnabled) { if (chineseConversionEnabled) {
popedText = [OpenCCBridge convert:popedText]; popedText = [self _convertToKangXi:popedText];
} }
[client insertText:popedText replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; [client insertText:popedText replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
_builder->removeHeadReadings(anchor.spanningLength); _builder->removeHeadReadings(anchor.spanningLength);
@ -588,59 +491,11 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
- (string)_currentLayout - (string)_currentLayout
{ {
string layout = string("Standard_");; NSString *keyboardLayoutName = Preferences.keyboardLayoutName;
NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey]; string layout = string(keyboardLayoutName.UTF8String) + string("_");
switch (keyboardLayout) {
case 0:
layout = string("Standard_");
break;
case 1:
layout = string("ETen_");
break;
case 2:
layout = string("ETen26_");
break;
case 3:
layout = string("Hsu_");
break;
case 4:
layout = string("HanyuPinyin_");
break;
case 5:
layout = string("IBM_");
break;
default:
break;
}
return layout; return layout;
} }
- (vChewingEmacsKey)_detectEmacsKeyFromCharCode:(UniChar)charCode modifiers:(NSUInteger)flags
{
if (flags & NSControlKeyMask) {
char c = charCode + 'a' - 1;
if (c == 'a') {
return vChewingEmacsKeyHome;
}
else if (c == 'e') {
return vChewingEmacsKeyEnd;
}
else if (c == 'f') {
return vChewingEmacsKeyForward;
}
else if (c == 'b') {
return vChewingEmacsKeyBackward;
}
else if (c == 'd') {
return vChewingEmacsKeyDelete;
}
else if (c == 'v') {
return vChewingEmacsKeyNextPage;
}
}
return vChewingEmacsKeyNone;
}
- (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client - (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client
{ {
NSRect textFrame = NSZeroRect; NSRect textFrame = NSZeroRect;
@ -666,7 +521,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
// get the unicode character code // get the unicode character code
UniChar charCode = [inputText length] ? [inputText characterAtIndex:0] : 0; UniChar charCode = [inputText length] ? [inputText characterAtIndex:0] : 0;
vChewingEmacsKey emacsKey = [self _detectEmacsKeyFromCharCode:charCode modifiers:flags]; vChewingEmacsKey emacsKey = [EmacsKeyHelper detectWithCharCode:charCode flags:flags];
if ([[client bundleIdentifier] isEqualToString:@"com.apple.Terminal"] && [NSStringFromClass([client class]) isEqualToString:@"IPMDServerClientWrapper"]) { if ([[client bundleIdentifier] isEqualToString:@"com.apple.Terminal"] && [NSStringFromClass([client class]) isEqualToString:@"IPMDServerClientWrapper"]) {
// special handling for com.apple.Terminal // special handling for com.apple.Terminal
@ -747,7 +602,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
[self updateClientComposingBuffer:client]; [self updateClientComposingBuffer:client];
return YES; return YES;
} }
// Shift + Left // Shift + Up in vertical tyinging mode // Shift + left
if ((keyCode == cursorBackwardKey || emacsKey == vChewingEmacsKeyBackward) if ((keyCode == cursorBackwardKey || emacsKey == vChewingEmacsKeyBackward)
&& (flags & NSShiftKeyMask)) { && (flags & NSShiftKeyMask)) {
if (_builder->markerCursorIndex() > 0) { if (_builder->markerCursorIndex() > 0) {
@ -759,7 +614,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
[self updateClientComposingBuffer:client]; [self updateClientComposingBuffer:client];
return YES; return YES;
} }
// Shift + Right // Shift + Down in vertical tyinging mode // Shift + Right
if ((keyCode == cursorForwardKey || emacsKey == vChewingEmacsKeyForward) if ((keyCode == cursorForwardKey || emacsKey == vChewingEmacsKeyForward)
&& (flags & NSShiftKeyMask)) { && (flags & NSShiftKeyMask)) {
if (_builder->markerCursorIndex() < _builder->length()) { if (_builder->markerCursorIndex() < _builder->length()) {
@ -836,7 +691,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
if (_bpmfReadingBuffer->isEmpty() && [_composingBuffer length] > 0 && (keyCode == extraChooseCandidateKey || charCode == 32 || (useVerticalMode && (keyCode == verticalModeOnlyChooseCandidateKey)))) { if (_bpmfReadingBuffer->isEmpty() && [_composingBuffer length] > 0 && (keyCode == extraChooseCandidateKey || charCode == 32 || (useVerticalMode && (keyCode == verticalModeOnlyChooseCandidateKey)))) {
if (charCode == 32) { if (charCode == 32) {
// if the spacebar is NOT set to be a selection key // if the spacebar is NOT set to be a selection key
if (![[NSUserDefaults standardUserDefaults] boolForKey:kChooseCandidateUsingSpaceKey]) { if (!Preferences.chooseCandidateUsingSpace) {
if (_builder->cursorIndex() >= _builder->length()) { if (_builder->cursorIndex() >= _builder->length()) {
[_composingBuffer appendString:@" "]; [_composingBuffer appendString:@" "];
[self commitComposition:client]; [self commitComposition:client];
@ -857,7 +712,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
// Esc // Esc
if (charCode == 27) { if (charCode == 27) {
BOOL escToClearInputBufferEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kEscToCleanInputBufferKey]; BOOL escToClearInputBufferEnabled = Preferences.escToCleanInputBuffer;
if (escToClearInputBufferEnabled) { if (escToClearInputBufferEnabled) {
// if the optioon is enabled, we clear everythiong including the composing // if the optioon is enabled, we clear everythiong including the composing
@ -1080,7 +935,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
// if nothing is matched, see if it's a punctuation key for current layout. // if nothing is matched, see if it's a punctuation key for current layout.
string layout = [self _currentLayout]; string layout = [self _currentLayout];
string punctuationNamePrefix = (_halfWidthPunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); string punctuationNamePrefix = Preferences.halfWidthPunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_");
string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode); string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode);
if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) {
return YES; return YES;
@ -1329,15 +1184,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
- (BOOL)handleEvent:(NSEvent *)event client:(id)client - (BOOL)handleEvent:(NSEvent *)event client:(id)client
{ {
if ([event type] == NSFlagsChanged) { if ([event type] == NSFlagsChanged) {
NSString *functionKeyKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kFunctionKeyKeyboardLayoutPreferenceKey]; NSString *functionKeyKeyboardLayoutID = Preferences.functionKeyboardLayout;
if (!functionKeyKeyboardLayoutID) { NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout;
functionKeyKeyboardLayoutID = @"com.apple.keylayout.US";
}
NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey];
if (!basisKeyboardLayoutID) {
basisKeyboardLayoutID = @"com.apple.keylayout.US";
}
// If no override is needed, just return NO. // If no override is needed, just return NO.
if ([functionKeyKeyboardLayoutID isEqualToString:basisKeyboardLayoutID]) { if ([functionKeyKeyboardLayoutID isEqualToString:basisKeyboardLayoutID]) {
@ -1345,7 +1193,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
} }
// Function key pressed. // Function key pressed.
BOOL includeShift = [[NSUserDefaults standardUserDefaults] boolForKey:kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey]; BOOL includeShift = Preferences.functionKeyKeyboardLayoutOverrideIncludeShiftKey;
if (([event modifierFlags] & ~NSShiftKeyMask) || (([event modifierFlags] & NSShiftKeyMask) && includeShift)) { if (([event modifierFlags] & ~NSShiftKeyMask) || (([event modifierFlags] & NSShiftKeyMask) && includeShift)) {
// Override the keyboard layout and let the OS do its thing // Override the keyboard layout and let the OS do its thing
[client overrideKeyboardWithKeyboardNamed:functionKeyKeyboardLayoutID]; [client overrideKeyboardWithKeyboardNamed:functionKeyKeyboardLayoutID];
@ -1418,10 +1266,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
- (size_t)actualCandidateCursorIndex - (size_t)actualCandidateCursorIndex
{ {
size_t cursorIndex = _builder->cursorIndex(); size_t cursorIndex = _builder->cursorIndex();
if (Preferences.selectPhraseAfterCursorAsCandidate) {
BOOL candidatePhraseLocatedAfterCursor = [[NSUserDefaults standardUserDefaults] boolForKey:kSelectPhraseAfterCursorAsCandidatePreferenceKey];
if (candidatePhraseLocatedAfterCursor) {
// MS Phonetics IME style, phrase is *after* the cursor, i.e. cursor is always *before* the phrase // MS Phonetics IME style, phrase is *after* the cursor, i.e. cursor is always *before* the phrase
if (cursorIndex < _builder->length()) { if (cursorIndex < _builder->length()) {
++cursorIndex; ++cursorIndex;
@ -1439,12 +1284,11 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
- (void)_showCandidateWindowUsingVerticalMode:(BOOL)useVerticalMode client:(id)client - (void)_showCandidateWindowUsingVerticalMode:(BOOL)useVerticalMode client:(id)client
{ {
// set the candidate panel style // set the candidate panel style
BOOL useHorizontalCandidateList = [[NSUserDefaults standardUserDefaults] boolForKey:kUseHorizontalCandidateListPreferenceKey];
if (useVerticalMode) { if (useVerticalMode) {
gCurrentCandidateController = [vChewingInputMethodController verticalCandidateController]; gCurrentCandidateController = [vChewingInputMethodController verticalCandidateController];
} }
else if (useHorizontalCandidateList) { else if (Preferences.useHorizontalCandidateList) {
gCurrentCandidateController = [vChewingInputMethodController horizontalCandidateController]; gCurrentCandidateController = [vChewingInputMethodController horizontalCandidateController];
} }
else { else {
@ -1452,16 +1296,16 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
} }
// set the attributes for the candidate panel (which uses NSAttributedString) // set the attributes for the candidate panel (which uses NSAttributedString)
NSInteger textSize = [[NSUserDefaults standardUserDefaults] integerForKey:kCandidateListTextSizeKey]; NSInteger textSize = Preferences.candidateListTextSize;
NSInteger keyLabelSize = textSize / 2; NSInteger keyLabelSize = textSize / 2;
if (keyLabelSize < kMinKeyLabelSize) { if (keyLabelSize < kMinKeyLabelSize) {
keyLabelSize = kMinKeyLabelSize; keyLabelSize = kMinKeyLabelSize;
} }
NSString *ctFontName = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateTextFontName]; NSString *ctFontName = Preferences.candidateTextFontName;
NSString *klFontName = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeyLabelFontName]; NSString *klFontName = Preferences.candidateKeyLabelFontName;
NSString *ckeys = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeys]; NSString *ckeys = Preferences.candidateKeys;
gCurrentCandidateController.keyLabelFont = klFontName ? [NSFont fontWithName:klFontName size:keyLabelSize] : [NSFont systemFontOfSize:keyLabelSize]; gCurrentCandidateController.keyLabelFont = klFontName ? [NSFont fontWithName:klFontName size:keyLabelSize] : [NSFont systemFontOfSize:keyLabelSize];
gCurrentCandidateController.candidateFont = ctFontName ? [NSFont fontWithName:ctFontName size:textSize] : [NSFont systemFontOfSize:textSize]; gCurrentCandidateController.candidateFont = ctFontName ? [NSFont fontWithName:ctFontName size:textSize] : [NSFont systemFontOfSize:textSize];
@ -1662,25 +1506,21 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
- (void)openUserPhrases:(id)sender - (void)openUserPhrases:(id)sender
{ {
NSLog(@"openUserPhrases called");
[self _openUserFile:[LanguageModelManager userPhrasesDataPathBopomofo]]; [self _openUserFile:[LanguageModelManager userPhrasesDataPathBopomofo]];
} }
- (void)openExcludedPhrasesSimpBopomofo:(id)sender - (void)openExcludedPhrasesSimpBopomofo:(id)sender
{ {
NSLog(@"openExcludedPhrasesSimpBopomofo called");
[self _openUserFile:[LanguageModelManager excludedPhrasesDataPathSimpBopomofo]]; [self _openUserFile:[LanguageModelManager excludedPhrasesDataPathSimpBopomofo]];
} }
- (void)openExcludedPhrasesvChewing:(id)sender - (void)openExcludedPhrasesvChewing:(id)sender
{ {
NSLog(@"openExcludedPhrasesvChewing called");
[self _openUserFile:[LanguageModelManager excludedPhrasesDataPathBopomofo]]; [self _openUserFile:[LanguageModelManager excludedPhrasesDataPathBopomofo]];
} }
- (void)reloadUserPhrases:(id)sender - (void)reloadUserPhrases:(id)sender
{ {
NSLog(@"reloadUserPhrases called");
[LanguageModelManager loadUserPhrasesModel]; [LanguageModelManager loadUserPhrasesModel];
} }
@ -1693,16 +1533,16 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
- (void)toggleChineseConverter:(id)sender - (void)toggleChineseConverter:(id)sender
{ {
_chineseConversionEnabled = !_chineseConversionEnabled; BOOL chineseConversionEnabled = [Preferences toggleChineseConversionEnabled];
[[NSUserDefaults standardUserDefaults] setBool:_chineseConversionEnabled forKey:kChineseConversionEnabledKey]; [NotifierController notifyWithMessage:[NSString stringWithFormat:@"%@%@%@", NSLocalizedString(@"Chinese Conversion", @""), @"\n", chineseConversionEnabled ? NSLocalizedString(@"NotificationSwitchON", @"") : NSLocalizedString(@"NotificationSwitchOFF", @"")] stay:NO];
[NotifierController notifyWithMessage:[NSString stringWithFormat:@"%@%@%@", NSLocalizedString(@"Chinese Conversion", @""), @"\n", _chineseConversionEnabled ? NSLocalizedString(@"NotificationSwitchON", @"") : NSLocalizedString(@"NotificationSwitchOFF", @"")] stay:NO];
} }
- (void)toggleHalfWidthPunctuation:(id)sender - (void)toggleHalfWidthPunctuation:(id)sender
{ {
_halfWidthPunctuationEnabled = !_halfWidthPunctuationEnabled; #pragma GCC diagnostic push
[[NSUserDefaults standardUserDefaults] setBool:_halfWidthPunctuationEnabled forKey:kHalfWidthPunctuationEnabledKey]; #pragma GCC diagnostic ignored "-Wunused-result"
[Preferences toogleHalfWidthPunctuationEnabled];
#pragma GCC diagnostic pop
} }
@end @end

View File

@ -1,5 +1,5 @@
// //
// PreferencesWindowController.swift // OpenCCBridge.swift
// //
// Copyright (c) 2011-2022 The OpenVanilla Project. // Copyright (c) 2011-2022 The OpenVanilla Project.
// //
@ -34,22 +34,23 @@
import Foundation import Foundation
import OpenCC import OpenCC
// Since SwiftyLibreCC only provide Swift classes, we create an NSObject subclass /// A bridge to let Objctive-C code to access SwiftyOpenCC.
// in Swift in order to bridge the Swift classes into our Objective-C++ project. ///
class OpenCCBridge : NSObject { /// Since SwiftyOpenCC only provide Swift classes, we create an NSObject subclass
/// in Swift in order to bridge the Swift classes into our Objective-C++ project.
public class OpenCCBridge: NSObject {
private static let shared = OpenCCBridge() private static let shared = OpenCCBridge()
private var converter: ChineseConverter? private var converter: ChineseConverter?
private override init() { private override init() {
try? converter = ChineseConverter(options: .twStandardRev) try? converter = ChineseConverter(options: .twStandardRev)
super.init() super.init()
} }
@objc static func convert(_ string: String) -> String? { /// Converts to Simplified Chinese.
///
/// - Parameter string: Text in Traditional Chinese.
/// - Returns: Text in Simplified Chinese.
@objc public static func convertToKangXi(_ string: String) -> String? {
shared.converter?.convert(string) shared.converter?.convert(string)
} }
private func convert(_ string: String) -> String? {
converter?.convert(string)
}
} }

View File

@ -0,0 +1,289 @@
//
// Preferences.swift
//
// Copyright (c) 2011-2022 The OpenVanilla Project.
//
// Contributors:
// Weizhong Yang (@zonble) @ OpenVanilla
//
// 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
private let kKeyboardLayoutPreferenceKey = "KeyboardLayout"
private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout"; // alphanumeric ("ASCII") input basi
private let kFunctionKeyKeyboardLayoutPreferenceKey = "FunctionKeyKeyboardLayout"; // alphanumeric ("ASCII") input basi
private let kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey = "FunctionKeyKeyboardLayoutOverrideIncludeShift"; // whether include shif
private let kCandidateListTextSizeKey = "CandidateListTextSize"
private let kSelectPhraseAfterCursorAsCandidatePreferenceKey = "SelectPhraseAfterCursorAsCandidate"
private let kUseHorizontalCandidateListPreferenceKey = "UseHorizontalCandidateList"
private let kComposingBufferSizePreferenceKey = "ComposingBufferSize"
private let kChooseCandidateUsingSpaceKey = "ChooseCandidateUsingSpaceKey"
private let kChineseConversionEnabledKey = "ChineseConversionEnabled"
private let kHalfWidthPunctuationEnabledKey = "HalfWidthPunctuationEnable"
private let kEscToCleanInputBufferKey = "EscToCleanInputBuffer"
private let kCandidateTextFontName = "CandidateTextFontName"
private let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
private let kCandidateKeys = "CandidateKeys"
private let kChineseConversionEngineKey = "ChineseConversionEngine"
private let kDefaultCandidateListTextSize: CGFloat = 18
private let kMinKeyLabelSize: CGFloat = 10
private let kMinCandidateListTextSize: CGFloat = 12
private let kMaxCandidateListTextSize: CGFloat = 196
// default, min and max composing buffer size (in codepoints)
// modern Macs can usually work up to 16 codepoints when the builder still
// walks the grid with good performance; slower Macs (like old PowerBooks)
// will start to sputter beyond 12; such is the algorithmatic complexity
// of the Viterbi algorithm used in the builder library (at O(N^2))
private let kDefaultComposingBufferSize = 10
private let kMinComposingBufferSize = 4
private let kMaxComposingBufferSize = 20
private let kDefaultKeys = "123456789"
// MARK: Property wrappers
@propertyWrapper
struct UserDefault<Value> {
let key: String
let defaultValue: Value
var container: UserDefaults = .standard
var wrappedValue: Value {
get {
return container.object(forKey: key) as? Value ?? defaultValue
}
set {
container.set(newValue, forKey: key)
}
}
}
@propertyWrapper
struct CandidateListTextSize {
let key: String
let defaultValue: CGFloat = kDefaultCandidateListTextSize
lazy var container: UserDefault = {
UserDefault(key: key, defaultValue: defaultValue) }()
var wrappedValue: CGFloat {
mutating get {
var value = container.wrappedValue
if value < kMinCandidateListTextSize {
value = kMinCandidateListTextSize
} else if value > kMaxCandidateListTextSize {
value = kMaxCandidateListTextSize
}
return value
}
set {
var value = newValue
if value < kMinCandidateListTextSize {
value = kMinCandidateListTextSize
} else if value > kMaxCandidateListTextSize {
value = kMaxCandidateListTextSize
}
container.wrappedValue = value
}
}
}
@propertyWrapper
struct ComposingBufferSize {
let key: String
let defaultValue: Int = kDefaultComposingBufferSize
lazy var container: UserDefault = {
UserDefault(key: key, defaultValue: defaultValue) }()
var wrappedValue: Int {
mutating get {
let currentValue = container.wrappedValue
if currentValue < kMinComposingBufferSize {
return kMinComposingBufferSize
} else if currentValue > kMaxComposingBufferSize {
return kMaxComposingBufferSize
}
return currentValue
}
set {
var value = newValue
if value < kMinComposingBufferSize {
value = kMinComposingBufferSize
} else if value > kMaxComposingBufferSize {
value = kMaxComposingBufferSize
}
container.wrappedValue = value
}
}
}
@propertyWrapper
struct ComposingKeys {
let key: String
let defaultValue: String? = kCandidateKeys
lazy var container: UserDefault = {
UserDefault(key: key, defaultValue: defaultValue) }()
var wrappedValue: String? {
mutating get {
let value = container.wrappedValue
if let value = value {
if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return nil
}
}
return value
}
set {
let value = newValue
if let value = value {
if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
container.wrappedValue = nil
return
}
}
container.wrappedValue = value
}
}
}
// MARK: -
@objc enum KeyboardLayout: Int {
case standard
case eten
case eten26
case hsu
case hanyuPinyin
case IBM
var name: String {
switch (self) {
case .standard:
return "Standard"
case .eten:
return "ETen"
case .eten26:
return "ETen26"
case .hsu:
return "Hsu"
case .hanyuPinyin:
return "HanyuPinyin"
case .IBM:
return "IBM"
}
}
}
@objc enum ChineseConversionEngine: Int {
case openCC
case vxHanConvert
var name: String {
switch (self) {
case .openCC:
return "OpenCC"
case .vxHanConvert:
return "VXHanConvert"
}
}
}
// MARK: -
class Preferences: NSObject {
@UserDefault(key: kKeyboardLayoutPreferenceKey, defaultValue: 0)
@objc static var keyboardLayout: Int
@objc static var keyboardLayoutName: String {
(KeyboardLayout(rawValue: self.keyboardLayout) ?? KeyboardLayout.standard).name
}
@UserDefault(key: kBasisKeyboardLayoutPreferenceKey, defaultValue: "com.apple.keylayout.US")
@objc static var basisKeyboardLayout: String
@UserDefault(key: kFunctionKeyKeyboardLayoutPreferenceKey, defaultValue: "com.apple.keylayout.US")
@objc static var functionKeyboardLayout: String
@UserDefault(key: kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey, defaultValue: false)
@objc static var functionKeyKeyboardLayoutOverrideIncludeShiftKey: Bool
@CandidateListTextSize(key: kCandidateListTextSizeKey)
@objc static var candidateListTextSize: CGFloat
@UserDefault(key: kSelectPhraseAfterCursorAsCandidatePreferenceKey, defaultValue: false)
@objc static var selectPhraseAfterCursorAsCandidate: Bool
@UserDefault(key: kUseHorizontalCandidateListPreferenceKey, defaultValue: false)
@objc static var useHorizontalCandidateList: Bool
@ComposingBufferSize(key: kComposingBufferSizePreferenceKey)
@objc static var composingBufferSize: Int
@UserDefault(key: kChooseCandidateUsingSpaceKey, defaultValue: true)
@objc static var chooseCandidateUsingSpace: Bool
@UserDefault(key: kChineseConversionEnabledKey, defaultValue: false)
@objc static var chineseConversionEnabled: Bool
@objc static func toggleChineseConversionEnabled() -> Bool {
chineseConversionEnabled = !chineseConversionEnabled
return chineseConversionEnabled
}
@UserDefault(key: kHalfWidthPunctuationEnabledKey, defaultValue: false)
@objc static var halfWidthPunctuationEnabled: Bool
@objc static func toogleHalfWidthPunctuationEnabled() -> Bool {
halfWidthPunctuationEnabled = !halfWidthPunctuationEnabled
return halfWidthPunctuationEnabled;
}
@UserDefault(key: kEscToCleanInputBufferKey, defaultValue: false)
@objc static var escToCleanInputBuffer: Bool
// MARK: Optional settings
@UserDefault(key: kCandidateTextFontName, defaultValue: nil)
@objc static var candidateTextFontName: String?
@UserDefault(key: kCandidateKeyLabelFontName, defaultValue: nil)
@objc static var candidateKeyLabelFontName: String?
@ComposingKeys(key: kCandidateKeys)
@objc static var candidateKeys: String?
@objc static var defaultKeys: String {
kDefaultKeys
}
@UserDefault(key: kChineseConversionEngineKey, defaultValue: 0)
@objc static var chineseConversionEngine: Int
@objc static var chineseConversionEngineName: String? {
return ChineseConversionEngine(rawValue: chineseConversionEngine)?.name
}
}

View File

@ -36,9 +36,13 @@
import Cocoa import Cocoa
import Carbon import Carbon
private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout" // Extend the RangeReplaceableCollection to allow it clean duplicated characters.
private let kCandidateKeys = "CandidateKeys" extension RangeReplaceableCollection where Element: Hashable {
private let kDefaultKeys = "123456789" var charDeDuplicate: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
// Please note that the class should be exposed as "PreferencesWindowController" // Please note that the class should be exposed as "PreferencesWindowController"
// in Objective-C in order to let IMK to see the same class name as // in Objective-C in order to let IMK to see the same class name as
@ -55,47 +59,70 @@ private let kDefaultKeys = "123456789"
basisKeyboardLayoutButton.menu?.removeAllItems() basisKeyboardLayoutButton.menu?.removeAllItems()
let basisKeyboardLayoutID = UserDefaults.standard.string(forKey: kBasisKeyboardLayoutPreferenceKey) let basisKeyboardLayoutID = Preferences.basisKeyboardLayout
for source in list { for source in list {
if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) {
let category = Unmanaged<CFString>.fromOpaque(categoryPtr).takeUnretainedValue() func getString(_ key: CFString) -> String? {
if category != kTISCategoryKeyboardInputSource { if let ptr = TISGetInputSourceProperty(source, key) {
return String(Unmanaged<CFString>.fromOpaque(ptr).takeUnretainedValue())
}
return nil
}
func getBool(_ key: CFString) -> Bool? {
if let ptr = TISGetInputSourceProperty(source, key) {
return Unmanaged<CFBoolean>.fromOpaque(ptr).takeUnretainedValue() == kCFBooleanTrue
}
return nil
}
if let category = getString(kTISPropertyInputSourceCategory) {
if category != String(kTISCategoryKeyboardInputSource) {
continue continue
} }
} else { } else {
continue continue
} }
if let asciiCapablePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsASCIICapable) { if let asciiCapable = getBool(kTISPropertyInputSourceIsASCIICapable) {
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr).takeUnretainedValue() if !asciiCapable {
if asciiCapable != kCFBooleanTrue {
continue continue
} }
} else { } else {
continue continue
} }
if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) { if let sourceType = getString(kTISPropertyInputSourceType) {
let sourceType = Unmanaged<CFString>.fromOpaque(sourceTypePtr).takeUnretainedValue() if sourceType != String(kTISTypeKeyboardLayout) {
if sourceType != kTISTypeKeyboardLayout {
continue continue
} }
} else { } else {
continue continue
} }
guard let sourceIDPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceID), guard let sourceID = getString(kTISPropertyInputSourceID),
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName) else { let localizedName = getString(kTISPropertyLocalizedName) else {
continue continue
} }
let sourceID = String(Unmanaged<CFString>.fromOpaque(sourceIDPtr).takeUnretainedValue())
let localizedName = String(Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
let menuItem = NSMenuItem() let menuItem = NSMenuItem()
menuItem.title = localizedName menuItem.title = localizedName
menuItem.representedObject = sourceID menuItem.representedObject = sourceID
if let iconPtr = TISGetInputSourceProperty(source, kTISPropertyIconRef) {
let icon = IconRef(iconPtr)
let image = NSImage(iconRef: icon)
func resize( _ image: NSImage) -> NSImage {
let newImage = NSImage(size: NSSize(width: 16, height: 16))
newImage.lockFocus()
image.draw(in: NSRect(x: 0, y: 0, width: 16, height: 16))
newImage.unlockFocus()
return newImage
}
menuItem.image = resize(image)
}
if sourceID == "com.apple.keylayout.US" { if sourceID == "com.apple.keylayout.US" {
usKeyboardLayoutItem = menuItem usKeyboardLayoutItem = menuItem
} }
@ -107,37 +134,37 @@ private let kDefaultKeys = "123456789"
basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem) basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem)
selectionKeyComboBox.usesDataSource = false selectionKeyComboBox.usesDataSource = false
selectionKeyComboBox.addItems(withObjectValues: [kDefaultKeys, "asdfghjkl", "asdfzxcvb"]) selectionKeyComboBox.removeAllItems()
selectionKeyComboBox.addItems(withObjectValues: [Preferences.defaultKeys, "ASDFGHJKL", "ASDFZXCVB"])
var candidateSelectionKeys = (UserDefaults.standard.string(forKey: kCandidateKeys) ?? kDefaultKeys) var candidateSelectionKeys = Preferences.candidateKeys ?? Preferences.defaultKeys
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if candidateSelectionKeys.isEmpty { if candidateSelectionKeys.isEmpty {
candidateSelectionKeys = kDefaultKeys candidateSelectionKeys = Preferences.defaultKeys
} }
selectionKeyComboBox.stringValue = candidateSelectionKeys selectionKeyComboBox.stringValue = candidateSelectionKeys
} }
@IBAction func updateBasisKeyboardLayoutAction(_ sender: Any) { @IBAction func updateBasisKeyboardLayoutAction(_ sender: Any) {
if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject { if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject as? String {
UserDefaults.standard.set(sourceID, forKey: kBasisKeyboardLayoutPreferenceKey) Preferences.basisKeyboardLayout = sourceID
} }
} }
@IBAction func changeSelectionKeyAction(_ sender: Any) { @IBAction func changeSelectionKeyAction(_ sender: Any) {
let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).charDeDuplicate
if keys.count != 9 || !keys.canBeConverted(to: .ascii) { if keys.count != 9 || !keys.canBeConverted(to: .ascii) {
selectionKeyComboBox.stringValue = kDefaultKeys selectionKeyComboBox.stringValue = Preferences.defaultKeys
UserDefaults.standard.removeObject(forKey: kCandidateKeys) Preferences.candidateKeys = nil
NSSound.beep() NSSound.beep()
return return
} }
selectionKeyComboBox.stringValue = keys selectionKeyComboBox.stringValue = keys
if keys == kDefaultKeys { if keys == Preferences.defaultKeys {
UserDefaults.standard.removeObject(forKey: kCandidateKeys) Preferences.candidateKeys = nil
} else { } else {
UserDefaults.standard.set(keys, forKey: kCandidateKeys) Preferences.candidateKeys = keys
} }
} }

View File

@ -17,8 +17,8 @@
"Visit Website" = "Visit Website"; "Visit Website" = "Visit Website";
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@"; "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@";
"Chinese Conversion" = "Convert zh-TW Kanji to KangXi Variants"; "Chinese Conversion" = "Convert zh-TW Kanji to KangXi Variants";
"NotificationSwitchON" = " ON"; "NotificationSwitchON" = " ON";
"NotificationSwitchOFF" = " OFF"; "NotificationSwitchOFF" = " OFF";
"Edit User Phrases" = "Edit User Phrases"; "Edit User Phrases" = "Edit User Phrases";
"Reload User Phrases" = "Reload User Phrases"; "Reload User Phrases" = "Reload User Phrases";
"Unable to create the user phrase file." = "Unable to create the user phrase file."; "Unable to create the user phrase file." = "Unable to create the user phrase file.";

View File

@ -17,8 +17,8 @@
"Visit Website" = "前往网站"; "Visit Website" = "前往网站";
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "目前使用的威注音官方版本是 %1$@ (%2$@),网路上有更新版本 %3$@ (%4$@) 可供下载。是否要前往威注音网站下载新版来安装?%5$@"; "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "目前使用的威注音官方版本是 %1$@ (%2$@),网路上有更新版本 %3$@ (%4$@) 可供下载。是否要前往威注音网站下载新版来安装?%5$@";
"Chinese Conversion" = "优先使用康熙繁体字"; "Chinese Conversion" = "优先使用康熙繁体字";
"NotificationSwitchON" = " 已启用"; "NotificationSwitchON" = " 已启用";
"NotificationSwitchOFF" = " 已停用"; "NotificationSwitchOFF" = " 已停用";
"Edit User Phrases" = "编辑自订语汇"; "Edit User Phrases" = "编辑自订语汇";
"Reload User Phrases" = "重载自订语汇"; "Reload User Phrases" = "重载自订语汇";
"Unable to create the user phrase file." = "无法创建自订语汇档案。"; "Unable to create the user phrase file." = "无法创建自订语汇档案。";

View File

@ -17,8 +17,8 @@
"Visit Website" = "前往網站"; "Visit Website" = "前往網站";
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "目前使用的威注音官方版本是 %1$@ (%2$@),網路上有更新版本 %3$@ (%4$@) 可供下載。是否要前往威注音網站下載新版來安裝?%5$@"; "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "目前使用的威注音官方版本是 %1$@ (%2$@),網路上有更新版本 %3$@ (%4$@) 可供下載。是否要前往威注音網站下載新版來安裝?%5$@";
"Chinese Conversion" = "優先使用康熙繁體字"; "Chinese Conversion" = "優先使用康熙繁體字";
"NotificationSwitchON" = " 已啟用"; "NotificationSwitchON" = " 已啟用";
"NotificationSwitchOFF" = " 已停用"; "NotificationSwitchOFF" = " 已停用";
"Edit User Phrases" = "編輯自訂語彙"; "Edit User Phrases" = "編輯自訂語彙";
"Reload User Phrases" = "重載自訂語彙"; "Reload User Phrases" = "重載自訂語彙";
"Unable to create the user phrase file." = "無法創建自訂語彙檔案。"; "Unable to create the user phrase file." = "無法創建自訂語彙檔案。";

View File

@ -17,6 +17,8 @@
5B5F4F972792A4EA00922DC2 /* UserPhrasesLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B5F4F962792A4EA00922DC2 /* UserPhrasesLM.cpp */; }; 5B5F4F972792A4EA00922DC2 /* UserPhrasesLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B5F4F962792A4EA00922DC2 /* UserPhrasesLM.cpp */; };
5BC2D2872793B434002C0BEC /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BC2D2852793B434002C0BEC /* CMakeLists.txt */; }; 5BC2D2872793B434002C0BEC /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BC2D2852793B434002C0BEC /* CMakeLists.txt */; };
5BC2D2882793B434002C0BEC /* KeyValueBlobReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D2862793B434002C0BEC /* KeyValueBlobReader.cpp */; }; 5BC2D2882793B434002C0BEC /* KeyValueBlobReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D2862793B434002C0BEC /* KeyValueBlobReader.cpp */; };
5BC2D28B2793B8FB002C0BEC /* EmacsKeyHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D28A2793B8FB002C0BEC /* EmacsKeyHelper.swift */; };
5BC2D28D2793B98F002C0BEC /* PreferencesModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D28C2793B98F002C0BEC /* PreferencesModule.swift */; };
5BC3EE1B278FC48C00F5E44C /* VerticalCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE18278FC48C00F5E44C /* VerticalCandidateController.swift */; }; 5BC3EE1B278FC48C00F5E44C /* VerticalCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE18278FC48C00F5E44C /* VerticalCandidateController.swift */; };
5BC3EE1C278FC48C00F5E44C /* VTCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE19278FC48C00F5E44C /* VTCandidateController.swift */; }; 5BC3EE1C278FC48C00F5E44C /* VTCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE19278FC48C00F5E44C /* VTCandidateController.swift */; };
5BC3EE1D278FC48C00F5E44C /* HorizontalCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE1A278FC48C00F5E44C /* HorizontalCandidateController.swift */; }; 5BC3EE1D278FC48C00F5E44C /* HorizontalCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE1A278FC48C00F5E44C /* HorizontalCandidateController.swift */; };
@ -105,6 +107,8 @@
5BC2D2842793B434002C0BEC /* KeyValueBlobReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyValueBlobReader.h; sourceTree = "<group>"; }; 5BC2D2842793B434002C0BEC /* KeyValueBlobReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyValueBlobReader.h; sourceTree = "<group>"; };
5BC2D2852793B434002C0BEC /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; }; 5BC2D2852793B434002C0BEC /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
5BC2D2862793B434002C0BEC /* KeyValueBlobReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyValueBlobReader.cpp; sourceTree = "<group>"; }; 5BC2D2862793B434002C0BEC /* KeyValueBlobReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyValueBlobReader.cpp; sourceTree = "<group>"; };
5BC2D28A2793B8FB002C0BEC /* EmacsKeyHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmacsKeyHelper.swift; sourceTree = "<group>"; };
5BC2D28C2793B98F002C0BEC /* PreferencesModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesModule.swift; sourceTree = "<group>"; };
5BC3EE18278FC48C00F5E44C /* VerticalCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticalCandidateController.swift; sourceTree = "<group>"; }; 5BC3EE18278FC48C00F5E44C /* VerticalCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticalCandidateController.swift; sourceTree = "<group>"; };
5BC3EE19278FC48C00F5E44C /* VTCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VTCandidateController.swift; sourceTree = "<group>"; }; 5BC3EE19278FC48C00F5E44C /* VTCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VTCandidateController.swift; sourceTree = "<group>"; };
5BC3EE1A278FC48C00F5E44C /* HorizontalCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalCandidateController.swift; sourceTree = "<group>"; }; 5BC3EE1A278FC48C00F5E44C /* HorizontalCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalCandidateController.swift; sourceTree = "<group>"; };
@ -272,6 +276,14 @@
path = vChewing; path = vChewing;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5BC2D2892793B8DB002C0BEC /* Keyboard */ = {
isa = PBXGroup;
children = (
5BC2D28A2793B8FB002C0BEC /* EmacsKeyHelper.swift */,
);
path = Keyboard;
sourceTree = "<group>";
};
5BE798A12792E50F00337FF9 /* UI */ = { 5BE798A12792E50F00337FF9 /* UI */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -336,7 +348,6 @@
6A0D4F1215FC0EB100ABF4B3 /* Engine */, 6A0D4F1215FC0EB100ABF4B3 /* Engine */,
6ACA41E715FC1D9000935EF6 /* Installer */, 6ACA41E715FC1D9000935EF6 /* Installer */,
6A0D4F4715FC0EB900ABF4B3 /* Resources */, 6A0D4F4715FC0EB900ABF4B3 /* Resources */,
5BDF2D052791DA6700838ADB /* AppDelegate.swift */,
5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */, 5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */,
5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */, 5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */,
6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */, 6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */,
@ -345,10 +356,12 @@
5B5F4F92279294A300922DC2 /* LanguageModelManager.mm */, 5B5F4F92279294A300922DC2 /* LanguageModelManager.mm */,
6A0D4EC815FC0D6400ABF4B3 /* main.m */, 6A0D4EC815FC0D6400ABF4B3 /* main.m */,
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */, 6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */,
5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */, 5BDF2D052791DA6700838ADB /* AppDelegate.swift */,
5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */,
D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */,
5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */, 5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */,
5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */,
D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */,
5BC2D28C2793B98F002C0BEC /* PreferencesModule.swift */,
5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */,
D427A9BF25ED28CC005D43E0 /* vChewing-Bridging-Header.h */, D427A9BF25ED28CC005D43E0 /* vChewing-Bridging-Header.h */,
5BA923AC2791B7C20001323A /* vChewingInstaller-Bridging-Header.h */, 5BA923AC2791B7C20001323A /* vChewingInstaller-Bridging-Header.h */,
); );
@ -380,6 +393,7 @@
6A0D4F1215FC0EB100ABF4B3 /* Engine */ = { 6A0D4F1215FC0EB100ABF4B3 /* Engine */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5BC2D2892793B8DB002C0BEC /* Keyboard */,
5BC2D2832793B434002C0BEC /* vChewing */, 5BC2D2832793B434002C0BEC /* vChewing */,
5BA8DAFE27928120009C9FFF /* LanguageModel */, 5BA8DAFE27928120009C9FFF /* LanguageModel */,
6A0D4F1315FC0EB100ABF4B3 /* Gramambular */, 6A0D4F1315FC0EB100ABF4B3 /* Gramambular */,
@ -688,6 +702,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5BDF2CFE2791BE4400838ADB /* InputSourceHelper.swift in Sources */, 5BDF2CFE2791BE4400838ADB /* InputSourceHelper.swift in Sources */,
5BC2D28B2793B8FB002C0BEC /* EmacsKeyHelper.swift in Sources */,
5BE798A72793280C00337FF9 /* NotifierController.swift in Sources */, 5BE798A72793280C00337FF9 /* NotifierController.swift in Sources */,
5B5F4F93279294A300922DC2 /* LanguageModelManager.mm in Sources */, 5B5F4F93279294A300922DC2 /* LanguageModelManager.mm in Sources */,
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */, 6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
@ -704,6 +719,7 @@
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */, 6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */,
5BDF2D012791C03B00838ADB /* PreferencesWindowController.swift in Sources */, 5BDF2D012791C03B00838ADB /* PreferencesWindowController.swift in Sources */,
5BC2D28D2793B98F002C0BEC /* PreferencesModule.swift in Sources */,
5BC3EE1C278FC48C00F5E44C /* VTCandidateController.swift in Sources */, 5BC3EE1C278FC48C00F5E44C /* VTCandidateController.swift in Sources */,
5BDF2D032791C71200838ADB /* NonModalAlertWindowController.swift in Sources */, 5BDF2D032791C71200838ADB /* NonModalAlertWindowController.swift in Sources */,
5BC3EE1D278FC48C00F5E44C /* HorizontalCandidateController.swift in Sources */, 5BC3EE1D278FC48C00F5E44C /* HorizontalCandidateController.swift in Sources */,