From 25b3a1b766ea395c2291df4daf7cfdb8f2fa5bc7 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 2 May 2022 13:49:41 +0800 Subject: [PATCH] KeyHandler // Say goodbye to ObjCpp. --- .../OVMandarin/Composer.hh} | 39 ++--- Source/3rdParty/OVMandarin/Composer.mm | 116 +++++++++++++ Source/Headers/vChewing-Bridging-Header.h | 3 +- .../{KeyHandlerSputnik.swift => CTools.h} | 21 +-- Source/Modules/ControllerModules/CTools.m | 32 ++++ .../Modules/ControllerModules/KeyHandler.mm | 155 ------------------ ...ler_Kernel.swift => KeyHandler_Core.swift} | 123 ++++++++------ .../KeyHandler_HandleCandidate.swift | 4 +- .../KeyHandler_HandleInput.swift | 24 +-- .../ControllerModules/KeyHandler_Misc.swift | 2 +- .../ControllerModules/KeyHandler_States.swift | 32 ++-- .../Modules/IMEModules/ctlInputMethod.swift | 2 +- vChewing.xcodeproj/project.pbxproj | 30 ++-- 13 files changed, 283 insertions(+), 300 deletions(-) rename Source/{Modules/ControllerModules/KeyHandler.h => 3rdParty/OVMandarin/Composer.hh} (59%) create mode 100644 Source/3rdParty/OVMandarin/Composer.mm rename Source/Modules/ControllerModules/{KeyHandlerSputnik.swift => CTools.h} (61%) create mode 100644 Source/Modules/ControllerModules/CTools.m delete mode 100644 Source/Modules/ControllerModules/KeyHandler.mm rename Source/Modules/ControllerModules/{KeyHandler_Kernel.swift => KeyHandler_Core.swift} (71%) diff --git a/Source/Modules/ControllerModules/KeyHandler.h b/Source/3rdParty/OVMandarin/Composer.hh similarity index 59% rename from Source/Modules/ControllerModules/KeyHandler.h rename to Source/3rdParty/OVMandarin/Composer.hh index 9ab4eb47..d620b766 100644 --- a/Source/Modules/ControllerModules/KeyHandler.h +++ b/Source/3rdParty/OVMandarin/Composer.hh @@ -26,37 +26,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import -@class InputHandler; -@class InputState; -@class KeyHandlerSputnik; - NS_ASSUME_NONNULL_BEGIN -@class KeyHandler; - -@protocol KeyHandlerDelegate -- (id)ctlCandidateForKeyHandler:(KeyHandler *)keyHandler; -- (void)keyHandler:(KeyHandler *)keyHandler didSelectCandidateAtIndex:(NSInteger)index ctlCandidate:(id)controller; -- (BOOL)keyHandler:(KeyHandler *)keyHandler didRequestWriteUserPhraseWithState:(InputState *)state; -@end - -@interface KeyHandler : NSObject - -@property(weak, nonatomic) id delegate; - -// The following items need to be exposed to Swift: - -- (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer; -- (BOOL)chkKeyValidity:(UniChar)value; -- (BOOL)isPhoneticReadingBufferEmpty; -- (BOOL)isPrintable:(UniChar)charCode; -- (NSString *)getCompositionFromPhoneticReadingBuffer; -- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer; -- (void)clearPhoneticReadingBuffer; -- (void)combinePhoneticReadingBufferKey:(UniChar)charCode; -- (void)doBackSpaceToPhoneticReadingBuffer; -- (void)ensurePhoneticParser; - +@interface Composer : NSObject ++ (BOOL)chkKeyValidity:(UniChar)charCode; ++ (BOOL)isBufferEmpty; ++ (void)clearBuffer; ++ (void)combineReadingKey:(UniChar)charCode; ++ (BOOL)checkWhetherToneMarkerConfirms; ++ (NSString *)getSyllableComposition; ++ (void)doBackSpaceToBuffer; ++ (NSString *)getComposition; ++ (void)ensureParser; @end NS_ASSUME_NONNULL_END diff --git a/Source/3rdParty/OVMandarin/Composer.mm b/Source/3rdParty/OVMandarin/Composer.mm new file mode 100644 index 00000000..05bf391b --- /dev/null +++ b/Source/3rdParty/OVMandarin/Composer.mm @@ -0,0 +1,116 @@ +// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). +// All possible vChewing-specific modifications are of: +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +/* +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: + +1. The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +2. No trademark license is granted to use the trade names, trademarks, service +marks, or product names of Contributor, except as required to fulfill notice +requirements above. + +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 "Composer.hh" +#import "Mandarin.h" +#import "vChewing-Swift.h" + +static Mandarin::BopomofoReadingBuffer *PhoneticBuffer; + +@implementation Composer + ++ (BOOL)chkKeyValidity:(UniChar)charCode +{ + return PhoneticBuffer->isValidKey((char)charCode); +} + ++ (BOOL)isBufferEmpty +{ + return PhoneticBuffer->isEmpty(); +} + ++ (void)clearBuffer +{ + PhoneticBuffer->clear(); +} + ++ (void)combineReadingKey:(UniChar)charCode +{ + PhoneticBuffer->combineKey((char)charCode); +} + ++ (BOOL)checkWhetherToneMarkerConfirms +{ + return PhoneticBuffer->hasToneMarker(); +} + ++ (NSString *)getSyllableComposition +{ + return [NSString stringWithUTF8String:PhoneticBuffer->syllable().composedString().c_str()]; +} + ++ (void)doBackSpaceToBuffer +{ + PhoneticBuffer->backspace(); +} + ++ (NSString *)getComposition +{ + return [NSString stringWithUTF8String:PhoneticBuffer->composedString().c_str()]; +} + ++ (void)ensureParser +{ + if (PhoneticBuffer) + { + switch (mgrPrefs.mandarinParser) + { + case MandarinParserOfStandard: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout()); + break; + case MandarinParserOfEten: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETenLayout()); + break; + case MandarinParserOfHsu: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HsuLayout()); + break; + case MandarinParserOfEen26: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETen26Layout()); + break; + case MandarinParserOfIBM: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::IBMLayout()); + break; + case MandarinParserOfMiTAC: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::MiTACLayout()); + break; + case MandarinParserOfFakeSeigyou: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::FakeSeigyouLayout()); + break; + case MandarinParserOfHanyuPinyin: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HanyuPinyinLayout()); + break; + default: + PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout()); + mgrPrefs.mandarinParser = MandarinParserOfStandard; + } + } + else + { + PhoneticBuffer = new Mandarin::BopomofoReadingBuffer(Mandarin::BopomofoKeyboardLayout::StandardLayout()); + } +} + +@end diff --git a/Source/Headers/vChewing-Bridging-Header.h b/Source/Headers/vChewing-Bridging-Header.h index fd510fc9..fd5078af 100644 --- a/Source/Headers/vChewing-Bridging-Header.h +++ b/Source/Headers/vChewing-Bridging-Header.h @@ -30,4 +30,5 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @import Foundation; -#import "KeyHandler.h" +#import "CTools.h" +#import "Composer.hh" diff --git a/Source/Modules/ControllerModules/KeyHandlerSputnik.swift b/Source/Modules/ControllerModules/CTools.h similarity index 61% rename from Source/Modules/ControllerModules/KeyHandlerSputnik.swift rename to Source/Modules/ControllerModules/CTools.h index fc100936..3a246b3c 100644 --- a/Source/Modules/ControllerModules/KeyHandlerSputnik.swift +++ b/Source/Modules/ControllerModules/CTools.h @@ -1,6 +1,4 @@ // Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). -// Refactored from the ObjCpp-version of this class by: -// (c) 2011 and onwards The OpenVanilla Project (MIT License). /* 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 @@ -24,19 +22,12 @@ 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 +#import -// MARK: - KeyHandler Sputnik. +NS_ASSUME_NONNULL_BEGIN -// Swift Extension 不允許直接存放這些變數,所以就寫了這個衛星型別。 -// 一旦 Mandarin 模組被 Swift 化,整個 KeyHandler 就可以都用 Swift。 -// 屆時會考慮將該衛星型別內的變數與常數都挪回 KeyHandler_Kernel 內。 +@interface CTools : NSObject ++ (BOOL)isPrintable:(UniChar)charCode; +@end -class KeyHandlerSputnik: NSObject { - static let kEpsilon: Double = 0.000001 - static var inputMode: String = "" - static var languageModel: vChewing.LMInstantiator = .init() - static var userOverrideModel: vChewing.LMUserOverride = .init() - static var builder: Megrez.BlockReadingBuilder = .init(lm: languageModel) - static var walkedNodes: [Megrez.NodeAnchor] = [] -} +NS_ASSUME_NONNULL_END diff --git a/Source/Modules/ControllerModules/CTools.m b/Source/Modules/ControllerModules/CTools.m new file mode 100644 index 00000000..97662c9b --- /dev/null +++ b/Source/Modules/ControllerModules/CTools.m @@ -0,0 +1,32 @@ +// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). +/* +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: + +1. The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +2. No trademark license is granted to use the trade names, trademarks, service +marks, or product names of Contributor, except as required to fulfill notice +requirements above. + +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 "CTools.h" + +@implementation CTools ++ (BOOL)isPrintable:(UniChar)charCode +{ + return isprint(charCode); +} +@end diff --git a/Source/Modules/ControllerModules/KeyHandler.mm b/Source/Modules/ControllerModules/KeyHandler.mm deleted file mode 100644 index c50efb22..00000000 --- a/Source/Modules/ControllerModules/KeyHandler.mm +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: -// (c) 2021 and onwards The vChewing Project (MIT-NTL License). -/* -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: - -1. The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -2. No trademark license is granted to use the trade names, trademarks, service -marks, or product names of Contributor, except as required to fulfill notice -requirements above. - -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 "KeyHandler.h" -#import "Mandarin.h" -#import "vChewing-Swift.h" -#import - -typedef Mandarin::BopomofoReadingBuffer PhoneticBuffer; - -// NON-SWIFTIFIABLE -@implementation KeyHandler -{ - // the reading buffer that takes user input - PhoneticBuffer *_bpmfReadingBuffer; -} - -@synthesize delegate = _delegate; - -// Not migrable as long as there's still ObjC++ components needed. -// Will deprecate this once Mandarin gets Swiftified. -- (instancetype)init -{ - self = [super init]; - if (self) - { - [self ensurePhoneticParser]; - [self setInputMode:ctlInputMethod.currentInputMode]; - } - return self; -} - -// NON-SWIFTIFIABLE: Mandarin -- (void)dealloc -{ // clean up everything - if (_bpmfReadingBuffer) - delete _bpmfReadingBuffer; -} - -// MARK: - 目前到這裡了 - -#pragma mark - 必須用 ObjCpp 處理的部分: Mandarin - -- (BOOL)chkKeyValidity:(UniChar)charCode -{ - return _bpmfReadingBuffer->isValidKey((char)charCode); -} - -- (BOOL)isPhoneticReadingBufferEmpty -{ - return _bpmfReadingBuffer->isEmpty(); -} - -- (void)clearPhoneticReadingBuffer -{ - _bpmfReadingBuffer->clear(); -} - -- (void)combinePhoneticReadingBufferKey:(UniChar)charCode -{ - _bpmfReadingBuffer->combineKey((char)charCode); -} - -- (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer -{ - return _bpmfReadingBuffer->hasToneMarker(); -} - -- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer -{ - return [NSString stringWithUTF8String:_bpmfReadingBuffer->syllable().composedString().c_str()]; -} - -- (void)doBackSpaceToPhoneticReadingBuffer -{ - _bpmfReadingBuffer->backspace(); -} - -- (NSString *)getCompositionFromPhoneticReadingBuffer -{ - return [NSString stringWithUTF8String:_bpmfReadingBuffer->composedString().c_str()]; -} - -- (void)ensurePhoneticParser -{ - if (_bpmfReadingBuffer) - { - switch (mgrPrefs.mandarinParser) - { - case MandarinParserOfStandard: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout()); - break; - case MandarinParserOfEten: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETenLayout()); - break; - case MandarinParserOfHsu: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HsuLayout()); - break; - case MandarinParserOfEen26: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETen26Layout()); - break; - case MandarinParserOfIBM: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::IBMLayout()); - break; - case MandarinParserOfMiTAC: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::MiTACLayout()); - break; - case MandarinParserOfFakeSeigyou: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::FakeSeigyouLayout()); - break; - case MandarinParserOfHanyuPinyin: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HanyuPinyinLayout()); - break; - default: - _bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout()); - mgrPrefs.mandarinParser = MandarinParserOfStandard; - } - } - else - { - _bpmfReadingBuffer = new Mandarin::BopomofoReadingBuffer(Mandarin::BopomofoKeyboardLayout::StandardLayout()); - } -} - -#pragma mark - 威注音認為有必要單獨拿出來處理的部分,交給 Swift 則有些困難。 - -- (BOOL)isPrintable:(UniChar)charCode -{ - return isprint(charCode); -} - -@end diff --git a/Source/Modules/ControllerModules/KeyHandler_Kernel.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift similarity index 71% rename from Source/Modules/ControllerModules/KeyHandler_Kernel.swift rename to Source/Modules/ControllerModules/KeyHandler_Core.swift index a04cf18d..3f5bac2c 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Kernel.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -34,16 +34,31 @@ public enum InputMode: String { // MARK: - Delegate. +protocol KeyHandlerDelegate: NSObjectProtocol { + func ctlCandidate(for _: KeyHandler) -> Any + func keyHandler( + _: KeyHandler, didSelectCandidateAt index: Int, + ctlCandidate controller: Any + ) + func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputState) + -> Bool +} + // MARK: - Kernel. -extension KeyHandler { - var kEpsilon: Double { - KeyHandlerSputnik.kEpsilon - } +class KeyHandler: NSObject { + let kEpsilon: Double = 0.000001 + var _inputMode: String = "" + var _languageModel: vChewing.LMInstantiator = .init() + var _userOverrideModel: vChewing.LMUserOverride = .init() + var _builder: Megrez.BlockReadingBuilder + var _walkedNodes: [Megrez.NodeAnchor] = [] + + weak var delegate: KeyHandlerDelegate? var inputMode: InputMode { get { - switch KeyHandlerSputnik.inputMode { + switch _inputMode { case "org.atelierInmu.inputmethod.vChewing.IMECHS": return InputMode.imeModeCHS case "org.atelierInmu.inputmethod.vChewing.IMECHT": @@ -55,17 +70,17 @@ extension KeyHandler { set { setInputMode(newValue.rawValue) } } - // TODO: Will reenable this once Mandarin gets Swiftified. - // override public init() { - // self.ensurePhoneticParser() - // self.setInputMode(ctlInputMethod.currentInputMode) - // super.init() - // } + override init() { + _builder = Megrez.BlockReadingBuilder(lm: _languageModel) + super.init() + Composer.ensureParser() + setInputMode(ctlInputMethod.currentInputMode) + } func clear() { - clearPhoneticReadingBuffer() - KeyHandlerSputnik.builder.clear() - KeyHandlerSputnik.walkedNodes.removeAll() + Composer.clearBuffer() + _builder.clear() + _walkedNodes.removeAll() } // 這個函數得獨立出來給 ObjC 使用。 @@ -79,7 +94,7 @@ extension KeyHandler { mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode // 拿當前的 _inputMode 與 ctlInputMethod 的提報結果對比,不同的話則套用新設定: - if KeyHandlerSputnik.inputMode != ctlInputMethod.currentInputMode { + if _inputMode != ctlInputMethod.currentInputMode { // Reinitiate language models if necessary setInputModesToLM(isCHS: isCHS) @@ -89,12 +104,12 @@ extension KeyHandler { // Create new grid builder. createNewBuilder() - if !isPhoneticReadingBufferEmpty() { - clearPhoneticReadingBuffer() + if !Composer.isBufferEmpty() { + Composer.clearBuffer() } } // 直接寫到衛星模組內,省得類型轉換 - KeyHandlerSputnik.inputMode = ctlInputMethod.currentInputMode + _inputMode = ctlInputMethod.currentInputMode } // MARK: - Functions dealing with Megrez. @@ -103,14 +118,14 @@ extension KeyHandler { // Retrieve the most likely trellis, i.e. a Maximum Likelihood Estimation // of the best possible Mandarin characters given the input syllables, // using the Viterbi algorithm implemented in the Gramambular library - let walker = Megrez.Walker(grid: KeyHandlerSputnik.builder.grid()) + let walker = Megrez.Walker(grid: _builder.grid()) // the reverse walk traces the trellis from the end - let walked: [Megrez.NodeAnchor] = walker.reverseWalk(at: KeyHandlerSputnik.builder.grid().width()) + let walked: [Megrez.NodeAnchor] = walker.reverseWalk(at: _builder.grid().width()) // then we use ".reversed()" to reverse the nodes so that we get the forward-walked nodes - KeyHandlerSputnik.walkedNodes.removeAll() - KeyHandlerSputnik.walkedNodes.append(contentsOf: walked.reversed()) + _walkedNodes.removeAll() + _walkedNodes.append(contentsOf: walked.reversed()) } func popOverflowComposingTextAndWalk() -> String { @@ -123,13 +138,13 @@ extension KeyHandler { // (i.e. popped out.) var poppedText = "" - if KeyHandlerSputnik.builder.grid().width() > mgrPrefs.composingBufferSize { - if KeyHandlerSputnik.walkedNodes.count > 0 { - let anchor: Megrez.NodeAnchor = KeyHandlerSputnik.walkedNodes[0] + if _builder.grid().width() > mgrPrefs.composingBufferSize { + if _walkedNodes.count > 0 { + let anchor: Megrez.NodeAnchor = _walkedNodes[0] if let theNode = anchor.node { poppedText = theNode.currentKeyValue().value } - KeyHandlerSputnik.builder.removeHeadReadings(count: anchor.spanningLength) + _builder.removeHeadReadings(count: anchor.spanningLength) } } walk() @@ -138,15 +153,15 @@ extension KeyHandler { func buildAssociatePhraseArray(withKey key: String) -> [String] { var arrResult: [String] = [] - if KeyHandlerSputnik.languageModel.hasAssociatedPhrasesForKey(key) { - arrResult.append(contentsOf: KeyHandlerSputnik.languageModel.associatedPhrasesForKey(key)) + if _languageModel.hasAssociatedPhrasesForKey(key) { + arrResult.append(contentsOf: _languageModel.associatedPhrasesForKey(key)) } return arrResult } func fixNode(value: String) { let cursorIndex: Int = getActualCandidateCursorIndex() - let selectedNode: Megrez.NodeAnchor = KeyHandlerSputnik.builder.grid().fixNodeSelectedCandidate( + let selectedNode: Megrez.NodeAnchor = _builder.grid().fixNodeSelectedCandidate( location: cursorIndex, value: value ) // 不要針對逐字選字模式啟用臨時半衰記憶模型。 @@ -167,8 +182,8 @@ extension KeyHandler { } } if addToUserOverrideModel { - KeyHandlerSputnik.userOverrideModel.observe( - walkedNodes: KeyHandlerSputnik.walkedNodes, cursorIndex: cursorIndex, candidate: value, + _userOverrideModel.observe( + walkedNodes: _walkedNodes, cursorIndex: cursorIndex, candidate: value, timestamp: NSDate().timeIntervalSince1970 ) } @@ -177,7 +192,7 @@ extension KeyHandler { if mgrPrefs.moveCursorAfterSelectingCandidate { var nextPosition = 0 - for node in KeyHandlerSputnik.walkedNodes { + for node in _walkedNodes { if nextPosition >= cursorIndex { break } nextPosition += node.spanningLength } @@ -216,13 +231,13 @@ extension KeyHandler { var overrideValue = mgrPrefs.useSCPCTypingMode ? "" - : KeyHandlerSputnik.userOverrideModel.suggest( - walkedNodes: KeyHandlerSputnik.walkedNodes, cursorIndex: getBuilderCursorIndex(), + : _userOverrideModel.suggest( + walkedNodes: _walkedNodes, cursorIndex: getBuilderCursorIndex(), timestamp: NSDate().timeIntervalSince1970 ) if !overrideValue.isEmpty { - KeyHandlerSputnik.builder.grid().overrideNodeScoreForSelectedCandidate( + _builder.grid().overrideNodeScoreForSelectedCandidate( location: getActualCandidateCursorIndex(), value: &overrideValue, overridingScore: findHighestScore(nodes: getRawNodes(), epsilon: kEpsilon) @@ -245,65 +260,65 @@ extension KeyHandler { // MARK: - Extracted methods and functions. - func isBuilderEmpty() -> Bool { KeyHandlerSputnik.builder.grid().width() == 0 } + func isBuilderEmpty() -> Bool { _builder.grid().width() == 0 } func getRawNodes() -> [Megrez.NodeAnchor] { /// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。 /// 微軟新注音輸入法的游標後置風格也是不允許 nodeCrossing 的,但目前 Megrez 暫時缺乏對該特性的支援。 /// 所以暫時只能將威注音的游標後置風格描述成「跟 Windows 版雅虎奇摩注音一致」。 mgrPrefs.setRearCursorMode - ? KeyHandlerSputnik.builder.grid().nodesCrossingOrEndingAt(location: getActualCandidateCursorIndex()) - : KeyHandlerSputnik.builder.grid().nodesEndingAt(location: getActualCandidateCursorIndex()) + ? _builder.grid().nodesCrossingOrEndingAt(location: getActualCandidateCursorIndex()) + : _builder.grid().nodesEndingAt(location: getActualCandidateCursorIndex()) } func setInputModesToLM(isCHS: Bool) { - KeyHandlerSputnik.languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT - KeyHandlerSputnik.userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT + _languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT + _userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT } func syncBaseLMPrefs() { - KeyHandlerSputnik.languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled - KeyHandlerSputnik.languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled - KeyHandlerSputnik.languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled + _languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled + _languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled + _languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled } func createNewBuilder() { - KeyHandlerSputnik.builder = Megrez.BlockReadingBuilder(lm: KeyHandlerSputnik.languageModel) + _builder = Megrez.BlockReadingBuilder(lm: _languageModel) // Each Mandarin syllable is separated by a hyphen. - KeyHandlerSputnik.builder.setJoinSeparator(separator: "-") + _builder.setJoinSeparator(separator: "-") } - func currentReadings() -> [String] { KeyHandlerSputnik.builder.readings() } + func currentReadings() -> [String] { _builder.readings() } func ifLangModelHasUnigrams(forKey reading: String) -> Bool { - KeyHandlerSputnik.languageModel.hasUnigramsFor(key: reading) + _languageModel.hasUnigramsFor(key: reading) } func insertReadingToBuilderAtCursor(reading: String) { - KeyHandlerSputnik.builder.insertReadingAtCursor(reading: reading) + _builder.insertReadingAtCursor(reading: reading) } func setBuilderCursorIndex(value: Int) { - KeyHandlerSputnik.builder.setCursorIndex(newIndex: value) + _builder.setCursorIndex(newIndex: value) } func getBuilderCursorIndex() -> Int { - KeyHandlerSputnik.builder.cursorIndex() + _builder.cursorIndex() } func getBuilderLength() -> Int { - KeyHandlerSputnik.builder.length() + _builder.length() } func deleteBuilderReadingInFrontOfCursor() { - KeyHandlerSputnik.builder.deleteReadingBeforeCursor() + _builder.deleteReadingBeforeCursor() } func deleteBuilderReadingAfterCursor() { - KeyHandlerSputnik.builder.deleteReadingAfterCursor() + _builder.deleteReadingAfterCursor() } func getKeyLengthAtIndexZero() -> Int { - KeyHandlerSputnik.walkedNodes[0].node?.currentKeyValue().value.count ?? 0 + _walkedNodes[0].node?.currentKeyValue().value.count ?? 0 } } diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift index 42746492..5ff6fcfb 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift @@ -28,7 +28,7 @@ import Cocoa // MARK: - § Handle Candidate State. -@objc extension KeyHandler { +extension KeyHandler { func handleCandidate( state: InputState, input: InputHandler, @@ -331,7 +331,7 @@ import Cocoa let punctuation: String = arrPunctuations.joined(separator: "") var shouldAutoSelectCandidate: Bool = - chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation) + Composer.chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation) || ifLangModelHasUnigrams(forKey: punctuation) if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey { diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index 4f23b995..792fa8b5 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -28,7 +28,7 @@ import Cocoa // MARK: - § Handle Input with States. -@objc extension KeyHandler { +extension KeyHandler { func handle( input: InputHandler, state: InputState, @@ -75,7 +75,7 @@ import Cocoa // If ASCII but not printable, don't use insertText:replacementRange: // Certain apps don't handle non-ASCII char insertions. - if charCode < 0x80, !isPrintable(charCode) { + if charCode < 0x80, !CTools.isPrintable(charCode) { return false } @@ -90,7 +90,7 @@ import Cocoa if input.isNumericPad { if !input.isLeft, !input.isRight, !input.isDown, - !input.isUp, !input.isSpace, isPrintable(charCode) + !input.isUp, !input.isSpace, CTools.isPrintable(charCode) { clear() stateCallback(InputState.Empty()) @@ -139,13 +139,13 @@ import Cocoa let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold // See if Phonetic reading is valid. - if !skipPhoneticHandling && chkKeyValidity(charCode) { - combinePhoneticReadingBufferKey(charCode) + if !skipPhoneticHandling && Composer.chkKeyValidity(charCode) { + Composer.combineReadingKey(charCode) // If we have a tone marker, we have to insert the reading to the // builder in other words, if we don't have a tone marker, we just // update the composing buffer. - composeReading = checkWhetherToneMarkerConfirmsPhoneticReadingBuffer() + composeReading = Composer.checkWhetherToneMarkerConfirms() if !composeReading { stateCallback(buildInputtingState()) return true @@ -155,9 +155,9 @@ import Cocoa // See if we have composition if Enter/Space is hit and buffer is not empty. // We use "|=" conditioning so that the tone marker key is also taken into account. // However, Swift does not support "|=". - composeReading = composeReading || (!isPhoneticReadingBufferEmpty() && (input.isSpace || input.isEnter)) + composeReading = composeReading || (!Composer.isBufferEmpty() && (input.isSpace || input.isEnter)) if composeReading { - let reading = getSyllableCompositionFromPhoneticReadingBuffer() + let reading = Composer.getSyllableComposition() if !ifLangModelHasUnigrams(forKey: reading) { IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。") @@ -176,7 +176,7 @@ import Cocoa dealWithOverrideModelSuggestions() // ... then update the text. - clearPhoneticReadingBuffer() + Composer.clearBuffer() let inputting = buildInputtingState() inputting.poppedText = poppedText @@ -216,7 +216,7 @@ import Cocoa // MARK: Calling candidate window using Space or Down or PageUp / PageDn. if let currentState = state as? InputState.NotEmpty { - if isPhoneticReadingBufferEmpty(), + if Composer.isBufferEmpty(), input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace || input.isPageDown || input.isPageUp || input.isTab || (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey)) @@ -329,7 +329,7 @@ import Cocoa if input.isSymbolMenuPhysicalKey && !input.isShiftHold { if !input.isOptionHold { if ifLangModelHasUnigrams(forKey: "_punctuation_list") { - if isPhoneticReadingBufferEmpty() { + if Composer.isBufferEmpty() { insertReadingToBuilderAtCursor(reading: "_punctuation_list") let poppedText: String! = popOverflowComposingTextAndWalk() let inputting = buildInputtingState() @@ -418,7 +418,7 @@ import Cocoa // "thinking" that the key is not actually consumed. // 砍掉這一段會導致「F1-F12 按鍵干擾組字區」的問題。 // 暫時只能先恢復這段,且補上偵錯彙報機制,方便今後排查故障。 - if (state is InputState.NotEmpty) || !isPhoneticReadingBufferEmpty() { + if (state is InputState.NotEmpty) || !Composer.isBufferEmpty() { IME.prtDebugIntel( "Blocked data: charCode: \(charCode), keyCode: \(input.keyCode)") IME.prtDebugIntel("A9BFF20E") diff --git a/Source/Modules/ControllerModules/KeyHandler_Misc.swift b/Source/Modules/ControllerModules/KeyHandler_Misc.swift index ffa6ec7c..d32c58b8 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Misc.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Misc.swift @@ -28,7 +28,7 @@ import Cocoa // MARK: - § Misc functions. -@objc extension KeyHandler { +extension KeyHandler { func getCurrentMandarinParser() -> String { mgrPrefs.mandarinParserName + "_" } diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index e2d0a645..6dea9292 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -28,7 +28,7 @@ import Cocoa // MARK: - § State managements. -@objc extension KeyHandler { +extension KeyHandler { // MARK: - 構築狀態(State Building) func buildInputtingState() -> InputState.Inputting { @@ -44,7 +44,7 @@ import Cocoa // i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars // locations. These processes are inherited from the ObjC++ version of this class and might be // unnecessary in Swift, but this deduction requires further experiments. - for walkedNode in KeyHandlerSputnik.walkedNodes { + for walkedNode in _walkedNodes { if let theNode = walkedNode.node { let strNodeValue = theNode.currentKeyValue().value composingBuffer += strNodeValue @@ -87,7 +87,7 @@ import Cocoa // The reading text is what the user is typing. let head = String((composingBuffer as NSString).substring(to: composedStringCursorIndex)) - let reading = getCompositionFromPhoneticReadingBuffer() + let reading = Composer.getComposition() let tail = String((composingBuffer as NSString).substring(from: composedStringCursorIndex)) let composedText = head + reading + tail let cursorIndex = composedStringCursorIndex + reading.count @@ -213,14 +213,14 @@ import Cocoa return false } - if isPhoneticReadingBufferEmpty() { + if Composer.isBufferEmpty() { insertReadingToBuilderAtCursor(reading: customPunctuation) let poppedText = popOverflowComposingTextAndWalk() let inputting = buildInputtingState() inputting.poppedText = poppedText stateCallback(inputting) - if mgrPrefs.useSCPCTypingMode, isPhoneticReadingBufferEmpty() { + if mgrPrefs.useSCPCTypingMode, Composer.isBufferEmpty() { let candidateState = buildCandidate( state: inputting, useVerticalMode: useVerticalMode @@ -303,7 +303,7 @@ import Cocoa return false } - if isPhoneticReadingBufferEmpty() { + if Composer.isBufferEmpty() { if getBuilderCursorIndex() >= 0 { deleteBuilderReadingInFrontOfCursor() walk() @@ -314,10 +314,10 @@ import Cocoa return true } } else { - doBackSpaceToPhoneticReadingBuffer() + Composer.doBackSpaceToBuffer() } - if isPhoneticReadingBufferEmpty(), getBuilderLength() == 0 { + if Composer.isBufferEmpty(), getBuilderLength() == 0 { stateCallback(InputState.EmptyIgnoringPreviousState()) } else { stateCallback(buildInputtingState()) @@ -336,7 +336,7 @@ import Cocoa return false } - if isPhoneticReadingBufferEmpty() { + if Composer.isBufferEmpty() { if getBuilderCursorIndex() != getBuilderLength() { deleteBuilderReadingAfterCursor() walk() @@ -371,7 +371,7 @@ import Cocoa if !(state is InputState.Inputting) { return false } - if !isPhoneticReadingBufferEmpty() { + if !Composer.isBufferEmpty() { IME.prtDebugIntel("9B6F908D") errorCallback() } @@ -390,7 +390,7 @@ import Cocoa return false } - if !isPhoneticReadingBufferEmpty() { + if !Composer.isBufferEmpty() { IME.prtDebugIntel("ABC44080") errorCallback() stateCallback(state) @@ -420,7 +420,7 @@ import Cocoa return false } - if !isPhoneticReadingBufferEmpty() { + if !Composer.isBufferEmpty() { IME.prtDebugIntel("9B69908D") errorCallback() stateCallback(state) @@ -459,8 +459,8 @@ import Cocoa stateCallback(InputState.EmptyIgnoringPreviousState()) } else { // If reading is not empty, we cancel the reading. - if !isPhoneticReadingBufferEmpty() { - clearPhoneticReadingBuffer() + if !Composer.isBufferEmpty() { + Composer.clearBuffer() if getBuilderLength() == 0 { stateCallback(InputState.Empty()) } else { @@ -481,7 +481,7 @@ import Cocoa ) -> Bool { if !(state is InputState.Inputting) { return false } - if !isPhoneticReadingBufferEmpty() { + if !Composer.isBufferEmpty() { IME.prtDebugIntel("B3BA5257") errorCallback() stateCallback(state) @@ -532,7 +532,7 @@ import Cocoa ) -> Bool { if !(state is InputState.Inputting) { return false } - if !isPhoneticReadingBufferEmpty() { + if !Composer.isBufferEmpty() { IME.prtDebugIntel("6ED95318") errorCallback() stateCallback(state) diff --git a/Source/Modules/IMEModules/ctlInputMethod.swift b/Source/Modules/IMEModules/ctlInputMethod.swift index ad7fc055..869eac6b 100644 --- a/Source/Modules/IMEModules/ctlInputMethod.swift +++ b/Source/Modules/IMEModules/ctlInputMethod.swift @@ -93,7 +93,7 @@ class ctlInputMethod: IMKInputController { currentClient = client keyHandler.clear() - keyHandler.ensurePhoneticParser() + Composer.ensureParser() if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() { if bundleCheckID != Bundle.main.bundleIdentifier { // Override the keyboard layout to the basic one. diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 6875b9c2..5e46a720 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; }; 5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; }; 5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; }; + 5B407153281F94E6009C24CB /* Composer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B407152281F94E6009C24CB /* Composer.mm */; }; 5B40730C281672610023DFFF /* lmAssociates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B407309281672610023DFFF /* lmAssociates.swift */; }; 5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; }; 5B5D28AC281EA1E900523D4D /* lmLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D28AB281EA1E800523D4D /* lmLite.swift */; }; @@ -83,9 +84,8 @@ 5BBBB77627AED70B0023B93A /* MenuIcon-TCVIM.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB77227AED70B0023B93A /* MenuIcon-TCVIM.png */; }; 5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBBB77927AEDC690023B93A /* clsSFX.swift */; }; 5BC2652227E04B7E00700291 /* uninstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5BC2652127E04B7B00700291 /* uninstall.sh */; }; - 5BC4F6382819FF4500A2514A /* KeyHandlerSputnik.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC4F6372819FF4500A2514A /* KeyHandlerSputnik.swift */; }; 5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113A28180D6100609769 /* LMInstantiator.swift */; }; - 5BD0113D2818543900609769 /* KeyHandler_Kernel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113C2818543900609769 /* KeyHandler_Kernel.swift */; }; + 5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113C2818543900609769 /* KeyHandler_Core.swift */; }; 5BD05B8127B22F3C004C4F1D /* char-kanji-cns.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05B8027B22F3C004C4F1D /* char-kanji-cns.txt */; }; 5BD05BCA27B2A43D004C4F1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; }; 5BD05C5D27B2BBA9004C4F1D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05C5B27B2BBA9004C4F1D /* Main.storyboard */; }; @@ -118,7 +118,7 @@ D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */; }; D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; }; D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; }; - D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; }; + D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* CTools.m */; }; D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */; }; D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE0279AF8B30071253C /* AppDelegate.swift */; }; D4F0BBE4279B08900071253C /* Chronosphere.m in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE3279B08900071253C /* Chronosphere.m */; }; @@ -201,6 +201,8 @@ 5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = ""; }; 5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = ""; }; 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = ""; tabWidth = 2; usesTabs = 1; }; + 5B407151281F94E6009C24CB /* Composer.hh */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Composer.hh; sourceTree = ""; }; + 5B407152281F94E6009C24CB /* Composer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Composer.mm; sourceTree = ""; }; 5B407309281672610023DFFF /* lmAssociates.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmAssociates.swift; sourceTree = ""; usesTabs = 1; }; 5B40730A281672610023DFFF /* lmReplacements.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmReplacements.swift; sourceTree = ""; usesTabs = 1; }; 5B5D28AB281EA1E800523D4D /* lmLite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lmLite.swift; sourceTree = ""; }; @@ -263,9 +265,8 @@ 5BC0AAC927F58472002D33E9 /* pkgPreInstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pkgPreInstall.sh; sourceTree = ""; }; 5BC0AACA27F58472002D33E9 /* pkgPostInstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pkgPostInstall.sh; sourceTree = ""; }; 5BC2652127E04B7B00700291 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = uninstall.sh; sourceTree = ""; }; - 5BC4F6372819FF4500A2514A /* KeyHandlerSputnik.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = KeyHandlerSputnik.swift; sourceTree = ""; usesTabs = 1; }; 5BD0113A28180D6100609769 /* LMInstantiator.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = LMInstantiator.swift; sourceTree = ""; usesTabs = 1; }; - 5BD0113C2818543900609769 /* KeyHandler_Kernel.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = KeyHandler_Kernel.swift; sourceTree = ""; usesTabs = 1; }; + 5BD0113C2818543900609769 /* KeyHandler_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = KeyHandler_Core.swift; sourceTree = ""; usesTabs = 1; }; 5BD05B8027B22F3C004C4F1D /* char-kanji-cns.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "char-kanji-cns.txt"; path = "Data/components/common/char-kanji-cns.txt"; sourceTree = ""; }; 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewingPhraseEditor.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5BD05BC627B2A42A004C4F1D /* vChewingPhraseEditor.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewingPhraseEditor.entitlements; sourceTree = ""; }; @@ -334,8 +335,8 @@ D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod.swift; sourceTree = ""; tabWidth = 2; usesTabs = 1; }; D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; - D4E569DA27A34CC100AC2CEF /* KeyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KeyHandler.h; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; - D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = KeyHandler.mm; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; + D4E569DA27A34CC100AC2CEF /* CTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CTools.h; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; + D4E569DB27A34CC100AC2CEF /* CTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CTools.m; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ArchiveUtil.swift; sourceTree = ""; tabWidth = 2; usesTabs = 1; }; D4F0BBE0279AF8B30071253C /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; tabWidth = 2; usesTabs = 1; }; D4F0BBE2279B08900071253C /* Chronosphere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Chronosphere.h; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; @@ -419,6 +420,8 @@ children = ( 6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */, 6A0D4F2115FC0EB100ABF4B3 /* Mandarin.h */, + 5B407151281F94E6009C24CB /* Composer.hh */, + 5B407152281F94E6009C24CB /* Composer.mm */, ); path = OVMandarin; sourceTree = ""; @@ -435,16 +438,15 @@ isa = PBXGroup; children = ( 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */, + D4E569DA27A34CC100AC2CEF /* CTools.h */, + D4E569DB27A34CC100AC2CEF /* CTools.m */, D456576D279E4F7B00DF6BC9 /* InputHandler.swift */, D461B791279DAC010070E734 /* InputState.swift */, + 5BD0113C2818543900609769 /* KeyHandler_Core.swift */, 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */, 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */, - 5BD0113C2818543900609769 /* KeyHandler_Kernel.swift */, 5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */, 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */, - 5BC4F6372819FF4500A2514A /* KeyHandlerSputnik.swift */, - D4E569DA27A34CC100AC2CEF /* KeyHandler.h */, - D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */, 5B62A33727AE79CD00A19448 /* NSStringUtils.swift */, 5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */, ); @@ -1088,9 +1090,8 @@ D47B92C027972AD100458394 /* main.swift in Sources */, D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */, 5BA9FD4827FEF3C9002DE248 /* PreferencesWindowController.swift in Sources */, - 5BC4F6382819FF4500A2514A /* KeyHandlerSputnik.swift in Sources */, 5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */, - D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */, + D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */, 5BA9FD4627FEF3C9002DE248 /* Container.swift in Sources */, D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */, 5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */, @@ -1101,7 +1102,7 @@ 5BA9FD4327FEF3C8002DE248 /* Preferences.swift in Sources */, 5BA9FD4427FEF3C8002DE248 /* SegmentedControlStyleViewController.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */, - 5BD0113D2818543900609769 /* KeyHandler_Kernel.swift in Sources */, + 5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */, 5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */, 5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */, 5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */, @@ -1117,6 +1118,7 @@ 5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */, 5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */, 5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */, + 5B407153281F94E6009C24CB /* Composer.mm in Sources */, 5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */, 5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */, 5B38F59C281E2E49007D5F5D /* 2_Grid.swift in Sources */,