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.
This commit is contained in:
ShikiSuen 2022-01-16 11:55:04 +08:00
parent b62097e0eb
commit 95e9ab0d90
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 */,