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 5f0a0bad6f
commit 56f76b71d5
11 changed files with 668 additions and 448 deletions

View File

@ -203,7 +203,7 @@
<constraints>
<constraint firstAttribute="height" constant="16" id="o6K-VN-uxe"/>
</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"/>
<font key="font" metaFont="cellTitle"/>
</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
NSString *_inputMode;
// if Chinese conversion is enabled
BOOL _chineseConversionEnabled;
// if half-width punctuation is enabled
BOOL _halfWidthPunctuationEnabled;
}
@end

View File

@ -45,8 +45,6 @@
#import "LanguageModelManager.h"
#import "vChewing-Swift.h"
@import OpenCC;
// C++ namespace usages
using namespace std;
using namespace Formosa::Mandarin;
@ -54,40 +52,7 @@ using namespace Formosa::Gramambular;
using namespace vChewing;
using namespace OpenVanilla;
// default, min and max candidate list text size
static const NSInteger kDefaultCandidateListTextSize = 16;
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
static NSString *const kBopomofoModeIdentifier = @"org.openvanilla.inputmethod.vChewing.Bopomofo";
@ -107,16 +72,6 @@ enum {
kDeleteKeyCode = 117
};
typedef NS_ENUM(NSUInteger, vChewingEmacsKey) {
vChewingEmacsKeyNone,
vChewingEmacsKeyForward,
vChewingEmacsKeyBackward,
vChewingEmacsKeyHome,
vChewingEmacsKeyEnd,
vChewingEmacsKeyDelete,
vChewingEmacsKeyNextPage,
};
VTCandidateController *gCurrentCandidateController = nil;
// 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;
}
// private methods
@interface vChewingInputMethodController ()
+ (VTHorizontalCandidateController *)horizontalCandidateController;
+ (VTVerticalCandidateController *)verticalCandidateController;
@end
@interface vChewingInputMethodController (VTCandidateController) <VTCandidateControllerDelegate>
@end
@ -202,8 +151,6 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
_composingBuffer = [[NSMutableString alloc] init];
_inputMode = kBopomofoModeIdentifier;
_chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey];
_halfWidthPunctuationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kHalfWidthPunctuationEnabledKey];
}
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
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"];
chineseConversionMenuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl;
chineseConversionMenuItem.state = _chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff;
chineseConversionMenuItem.state = Preferences.chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff;
[menu addItem:chineseConversionMenuItem];
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:[NSMenuItem separatorItem]]; // ------------------------------
@ -232,23 +179,15 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[menu addItem:editExcludedPhrasesItem];
}
else {
NSMenuItem *editUserPhrasesItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Edit User Phrases", @"") action:@selector(openUserPhrases:) keyEquivalent:@""];
[menu addItem:editUserPhrasesItem];
NSMenuItem *editExcludedPhrasesItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Edit Excluded Phrases", @"") action:@selector(openExcludedPhrasesvChewing:) keyEquivalent:@""];
[menu addItem:editExcludedPhrasesItem];
[menu addItemWithTitle:NSLocalizedString(@"Edit User Phrases", @"") action:@selector(openUserPhrases:) keyEquivalent:@""];
[menu addItemWithTitle:NSLocalizedString(@"Edit Excluded Phrases", @"") action:@selector(openExcludedPhrasesvChewing:) keyEquivalent:@""];
}
NSMenuItem *reloadUserPhrasesItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Reload User Phrases", @"") action:@selector(reloadUserPhrases:) keyEquivalent:@""];
[menu addItem:reloadUserPhrasesItem];
[menu addItemWithTitle:NSLocalizedString(@"Reload User Phrases", @"") action:@selector(reloadUserPhrases:) keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]]; // ------------------------------
NSMenuItem *updateCheckItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Check for Updates…", @"") action:@selector(checkForUpdate:) keyEquivalent:@""];
[menu addItem:updateCheckItem];
NSMenuItem *aboutMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"About vChewing…", @"") action:@selector(showAbout:) keyEquivalent:@""];
[menu addItem:aboutMenuItem];
[menu addItemWithTitle:NSLocalizedString(@"Check for Updates…", @"") action:@selector(checkForUpdate:) keyEquivalent:@""];
[menu addItemWithTitle:NSLocalizedString(@"About vChewing…", @"") action:@selector(showAbout:) keyEquivalent:@""];
return menu;
}
@ -259,10 +198,7 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[[NSUserDefaults standardUserDefaults] synchronize];
// Override the keyboard layout. Use US if not set.
NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey];
if (!basisKeyboardLayoutID) {
basisKeyboardLayoutID = @"com.apple.keylayout.US";
}
NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout;
[client overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID];
// reset the state
@ -273,49 +209,28 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[_composingBuffer setString:@""];
// checks and populates the default settings
NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey];
switch (keyboardLayout) {
case 0:
switch (Preferences.keyboardLayout) {
case KeyboardLayoutStandard:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout());
break;
case 1:
case KeyboardLayoutEten:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::ETenLayout());
break;
case 2:
case KeyboardLayoutHsu:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::HsuLayout());
break;
case 3:
case KeyboardLayoutEten26:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::ETen26Layout());
break;
case 4:
case KeyboardLayoutHanyuPinyin:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::HanyuPinyinLayout());
break;
case 5:
case KeyboardLayoutIBM:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::IBMLayout());
break;
default:
_bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout());
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:kKeyboardLayoutPreferenceKey];
}
// 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];
Preferences.keyboardLayout = KeyboardLayoutStandard;
}
[(AppDelegate *)[NSApp delegate] checkForUpdate];
@ -361,10 +276,7 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[[NSUserDefaults standardUserDefaults] synchronize];
// Remember to override the keyboard layout again -- treat this as an activate eventy
NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey];
if (!basisKeyboardLayoutID) {
basisKeyboardLayoutID = @"com.apple.keylayout.US";
}
NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout;
[sender overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID];
_inputMode = newInputMode;
@ -389,6 +301,13 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
#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
{
// 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.
NSString *buffer = _composingBuffer;
BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey];
if (chineseConversionEnabled) {
buffer = [OpenCCBridge convert:_composingBuffer];
if (Preferences.chineseConversionEnabled) {
buffer = [self _convertToKangXi:_composingBuffer];
}
// commit the text, clear the state
@ -415,7 +334,6 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
[_composingBuffer setString:@""];
gCurrentCandidateController.visible = NO;
[_candidates removeAllObjects];
[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
// be popped out
NSInteger _composingBufferSize = [[NSUserDefaults standardUserDefaults] integerForKey:kComposingBufferSizePreferenceKey];
NSInteger previousComposingBufferSize = _composingBufferSize;
NSInteger composingBufferSize = Preferences.composingBufferSize;
if (_composingBufferSize == 0) {
_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 (_builder->grid().width() > (size_t)composingBufferSize) {
if (_walkedNodes.size() > 0) {
NodeAnchor &anchor = _walkedNodes[0];
NSString *popedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()];
// Chinese conversion.
BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey];
BOOL chineseConversionEnabled = Preferences.chineseConversionEnabled;
if (chineseConversionEnabled) {
popedText = [OpenCCBridge convert:popedText];
popedText = [self _convertToKangXi:popedText];
}
[client insertText:popedText replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
_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 layout = string("Standard_");;
NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey];
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;
}
NSString *keyboardLayoutName = Preferences.keyboardLayoutName;
string layout = string(keyboardLayoutName.UTF8String) + string("_");
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
{
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
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"]) {
// 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];
return YES;
}
// Shift + Left // Shift + Up in vertical tyinging mode
// Shift + left
if ((keyCode == cursorBackwardKey || emacsKey == vChewingEmacsKeyBackward)
&& (flags & NSShiftKeyMask)) {
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];
return YES;
}
// Shift + Right // Shift + Down in vertical tyinging mode
// Shift + Right
if ((keyCode == cursorForwardKey || emacsKey == vChewingEmacsKeyForward)
&& (flags & NSShiftKeyMask)) {
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 (charCode == 32) {
// if the spacebar is NOT set to be a selection key
if (![[NSUserDefaults standardUserDefaults] boolForKey:kChooseCandidateUsingSpaceKey]) {
if (!Preferences.chooseCandidateUsingSpace) {
if (_builder->cursorIndex() >= _builder->length()) {
[_composingBuffer appendString:@" "];
[self commitComposition:client];
@ -857,7 +712,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
// Esc
if (charCode == 27) {
BOOL escToClearInputBufferEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kEscToCleanInputBufferKey];
BOOL escToClearInputBufferEnabled = Preferences.escToCleanInputBuffer;
if (escToClearInputBufferEnabled) {
// 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.
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);
if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) {
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
{
if ([event type] == NSFlagsChanged) {
NSString *functionKeyKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kFunctionKeyKeyboardLayoutPreferenceKey];
if (!functionKeyKeyboardLayoutID) {
functionKeyKeyboardLayoutID = @"com.apple.keylayout.US";
}
NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey];
if (!basisKeyboardLayoutID) {
basisKeyboardLayoutID = @"com.apple.keylayout.US";
}
NSString *functionKeyKeyboardLayoutID = Preferences.functionKeyboardLayout;
NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout;
// If no override is needed, just return NO.
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.
BOOL includeShift = [[NSUserDefaults standardUserDefaults] boolForKey:kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey];
BOOL includeShift = Preferences.functionKeyKeyboardLayoutOverrideIncludeShiftKey;
if (([event modifierFlags] & ~NSShiftKeyMask) || (([event modifierFlags] & NSShiftKeyMask) && includeShift)) {
// Override the keyboard layout and let the OS do its thing
[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 cursorIndex = _builder->cursorIndex();
BOOL candidatePhraseLocatedAfterCursor = [[NSUserDefaults standardUserDefaults] boolForKey:kSelectPhraseAfterCursorAsCandidatePreferenceKey];
if (candidatePhraseLocatedAfterCursor) {
if (Preferences.selectPhraseAfterCursorAsCandidate) {
// MS Phonetics IME style, phrase is *after* the cursor, i.e. cursor is always *before* the phrase
if (cursorIndex < _builder->length()) {
++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
{
// set the candidate panel style
BOOL useHorizontalCandidateList = [[NSUserDefaults standardUserDefaults] boolForKey:kUseHorizontalCandidateListPreferenceKey];
if (useVerticalMode) {
gCurrentCandidateController = [vChewingInputMethodController verticalCandidateController];
}
else if (useHorizontalCandidateList) {
else if (Preferences.useHorizontalCandidateList) {
gCurrentCandidateController = [vChewingInputMethodController horizontalCandidateController];
}
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)
NSInteger textSize = [[NSUserDefaults standardUserDefaults] integerForKey:kCandidateListTextSizeKey];
NSInteger textSize = Preferences.candidateListTextSize;
NSInteger keyLabelSize = textSize / 2;
if (keyLabelSize < kMinKeyLabelSize) {
keyLabelSize = kMinKeyLabelSize;
}
NSString *ctFontName = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateTextFontName];
NSString *klFontName = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeyLabelFontName];
NSString *ckeys = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeys];
NSString *ctFontName = Preferences.candidateTextFontName;
NSString *klFontName = Preferences.candidateKeyLabelFontName;
NSString *ckeys = Preferences.candidateKeys;
gCurrentCandidateController.keyLabelFont = klFontName ? [NSFont fontWithName:klFontName size:keyLabelSize] : [NSFont systemFontOfSize:keyLabelSize];
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
{
NSLog(@"openUserPhrases called");
[self _openUserFile:[LanguageModelManager userPhrasesDataPathBopomofo]];
}
- (void)openExcludedPhrasesSimpBopomofo:(id)sender
{
NSLog(@"openExcludedPhrasesSimpBopomofo called");
[self _openUserFile:[LanguageModelManager excludedPhrasesDataPathSimpBopomofo]];
}
- (void)openExcludedPhrasesvChewing:(id)sender
{
NSLog(@"openExcludedPhrasesvChewing called");
[self _openUserFile:[LanguageModelManager excludedPhrasesDataPathBopomofo]];
}
- (void)reloadUserPhrases:(id)sender
{
NSLog(@"reloadUserPhrases called");
[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
{
_chineseConversionEnabled = !_chineseConversionEnabled;
[[NSUserDefaults standardUserDefaults] setBool:_chineseConversionEnabled forKey:kChineseConversionEnabledKey];
[NotifierController notifyWithMessage:[NSString stringWithFormat:@"%@%@%@", NSLocalizedString(@"Chinese Conversion", @""), @"\n", _chineseConversionEnabled ? NSLocalizedString(@"NotificationSwitchON", @"") : NSLocalizedString(@"NotificationSwitchOFF", @"")] stay:NO];
BOOL chineseConversionEnabled = [Preferences toggleChineseConversionEnabled];
[NotifierController notifyWithMessage:[NSString stringWithFormat:@"%@%@%@", NSLocalizedString(@"Chinese Conversion", @""), @"\n", chineseConversionEnabled ? NSLocalizedString(@"NotificationSwitchON", @"") : NSLocalizedString(@"NotificationSwitchOFF", @"")] stay:NO];
}
- (void)toggleHalfWidthPunctuation:(id)sender
{
_halfWidthPunctuationEnabled = !_halfWidthPunctuationEnabled;
[[NSUserDefaults standardUserDefaults] setBool:_halfWidthPunctuationEnabled forKey:kHalfWidthPunctuationEnabledKey];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
[Preferences toogleHalfWidthPunctuationEnabled];
#pragma GCC diagnostic pop
}
@end

View File

@ -1,5 +1,5 @@
//
// PreferencesWindowController.swift
// OpenCCBridge.swift
//
// Copyright (c) 2011-2022 The OpenVanilla Project.
//
@ -34,22 +34,23 @@
import Foundation
import OpenCC
// Since SwiftyLibreCC only provide Swift classes, we create an NSObject subclass
// in Swift in order to bridge the Swift classes into our Objective-C++ project.
class OpenCCBridge : NSObject {
/// A bridge to let Objctive-C code to access SwiftyOpenCC.
///
/// 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 var converter: ChineseConverter?
private override init() {
try? converter = ChineseConverter(options: .twStandardRev)
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)
}
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 Carbon
private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout"
private let kCandidateKeys = "CandidateKeys"
private let kDefaultKeys = "123456789"
// Extend the RangeReplaceableCollection to allow it clean duplicated characters.
extension RangeReplaceableCollection where Element: Hashable {
var charDeDuplicate: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
// 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
@ -55,47 +59,70 @@ private let kDefaultKeys = "123456789"
basisKeyboardLayoutButton.menu?.removeAllItems()
let basisKeyboardLayoutID = UserDefaults.standard.string(forKey: kBasisKeyboardLayoutPreferenceKey)
let basisKeyboardLayoutID = Preferences.basisKeyboardLayout
for source in list {
if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) {
let category = Unmanaged<CFString>.fromOpaque(categoryPtr).takeUnretainedValue()
if category != kTISCategoryKeyboardInputSource {
func getString(_ key: CFString) -> String? {
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
}
} else {
continue
}
if let asciiCapablePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsASCIICapable) {
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr).takeUnretainedValue()
if asciiCapable != kCFBooleanTrue {
if let asciiCapable = getBool(kTISPropertyInputSourceIsASCIICapable) {
if !asciiCapable {
continue
}
} else {
continue
}
if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) {
let sourceType = Unmanaged<CFString>.fromOpaque(sourceTypePtr).takeUnretainedValue()
if sourceType != kTISTypeKeyboardLayout {
if let sourceType = getString(kTISPropertyInputSourceType) {
if sourceType != String(kTISTypeKeyboardLayout) {
continue
}
} else {
continue
}
guard let sourceIDPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceID),
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName) else {
guard let sourceID = getString(kTISPropertyInputSourceID),
let localizedName = getString(kTISPropertyLocalizedName) else {
continue
}
let sourceID = String(Unmanaged<CFString>.fromOpaque(sourceIDPtr).takeUnretainedValue())
let localizedName = String(Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
let menuItem = NSMenuItem()
menuItem.title = localizedName
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" {
usKeyboardLayoutItem = menuItem
}
@ -107,37 +134,37 @@ private let kDefaultKeys = "123456789"
basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem)
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)
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
var candidateSelectionKeys = Preferences.candidateKeys ?? Preferences.defaultKeys
if candidateSelectionKeys.isEmpty {
candidateSelectionKeys = kDefaultKeys
candidateSelectionKeys = Preferences.defaultKeys
}
selectionKeyComboBox.stringValue = candidateSelectionKeys
}
@IBAction func updateBasisKeyboardLayoutAction(_ sender: Any) {
if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject {
UserDefaults.standard.set(sourceID, forKey: kBasisKeyboardLayoutPreferenceKey)
if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject as? String {
Preferences.basisKeyboardLayout = sourceID
}
}
@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) {
selectionKeyComboBox.stringValue = kDefaultKeys
UserDefaults.standard.removeObject(forKey: kCandidateKeys)
selectionKeyComboBox.stringValue = Preferences.defaultKeys
Preferences.candidateKeys = nil
NSSound.beep()
return
}
selectionKeyComboBox.stringValue = keys
if keys == kDefaultKeys {
UserDefaults.standard.removeObject(forKey: kCandidateKeys)
if keys == Preferences.defaultKeys {
Preferences.candidateKeys = nil
} else {
UserDefaults.standard.set(keys, forKey: kCandidateKeys)
Preferences.candidateKeys = keys
}
}

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?%@" = "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";
"NotificationSwitchON" = " ON";
"NotificationSwitchOFF" = " OFF";
"NotificationSwitchON" = " ON";
"NotificationSwitchOFF" = " OFF";
"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" = "前往网站";
"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" = "优先使用康熙繁体字";
"NotificationSwitchON" = " 已启用";
"NotificationSwitchOFF" = " 已停用";
"NotificationSwitchON" = " 已启用";
"NotificationSwitchOFF" = " 已停用";
"Edit User Phrases" = "编辑自订语汇";
"Reload User Phrases" = "重载自订语汇";
"Unable to create the user phrase file." = "无法创建自订语汇档案。";

View File

@ -17,8 +17,8 @@
"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$@";
"Chinese Conversion" = "優先使用康熙繁體字";
"NotificationSwitchON" = " 已啟用";
"NotificationSwitchOFF" = " 已停用";
"NotificationSwitchON" = " 已啟用";
"NotificationSwitchOFF" = " 已停用";
"Edit User Phrases" = "編輯自訂語彙";
"Reload User Phrases" = "重載自訂語彙";
"Unable to create the user phrase file." = "無法創建自訂語彙檔案。";

View File

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