2.4.0 // Dezonblization - phase 1. Merge PR #120 from upd/2.4.0

This commit is contained in:
ShikiSuen 2022-09-06 00:51:31 +08:00 committed by GitHub
commit bd30564bdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1365 additions and 2077 deletions

View File

@ -15,12 +15,11 @@ $ 3rd-Party Modules Used:
$ Contributors and volunteers of the upstream repo, having no responsibility in discussing anything in the current repo: $ Contributors and volunteers of the upstream repo, having no responsibility in discussing anything in the current repo:
- Zonble Yang: - Zonble Yang:
- McBopomofo for macOS 2.x architect, especially state-based IME behavior management. - McBopomofo for macOS 2.x architect.
- Voltaire candidate window MK2 (massively modified as MK3 in vChewing by Shiki Suen). - Voltaire candidate window MK2 (massively modified as MK3 in vChewing by Shiki Suen).
- Notifier window and Tooltip UI. - Notifier window and Tooltip UI.
- NSStringUtils and FSEventStreamHelper. - FSEventStreamHelper.
- App-style installer (only preserved for developer purposes). - App-style installer (only preserved for developer purposes).
- InputSource Helper.
- mgrPrefs (userdefaults manager). - mgrPrefs (userdefaults manager).
- apiUpdate. - apiUpdate.
- Mengjuei Hsieh: - Mengjuei Hsieh:

View File

@ -9,6 +9,7 @@
// requirements defined in MIT License. // requirements defined in MIT License.
import Cocoa import Cocoa
import InputMethodKit
private let kTargetBin = "vChewing" private let kTargetBin = "vChewing"
private let kTargetType = "app" private let kTargetType = "app"
@ -47,6 +48,17 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
private var translocationRemovalStartTime: Date? private var translocationRemovalStartTime: Date?
private var currentVersionNumber: Int = 0 private var currentVersionNumber: Int = 0
let imeURLInstalled = realHomeDir.appendingPathComponent("Library/Input Methods/vChewing.app")
var allRegisteredInstancesOfThisInputMethod: [TISInputSource] {
guard let components = Bundle(url: imeURLInstalled)?.infoDictionary?["ComponentInputModeDict"] as? [String: Any],
let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any]
else {
return []
}
return tsInputModeListKey.keys.compactMap { TISInputSource.generate(from: $0) }
}
func runAlertPanel(title: String, message: String, buttonTitle: String) { func runAlertPanel(title: String, message: String, buttonTitle: String) {
let alert = NSAlert() let alert = NSAlert()
alert.alertStyle = .informational alert.alertStyle = .informational
@ -230,23 +242,20 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
endAppWithDelay() endAppWithDelay()
} }
let imeURLInstalled = realHomeDir.appendingPathComponent("Library/Input Methods/vChewing.app")
_ = try? shell("/usr/bin/xattr -drs com.apple.quarantine \(kTargetPartialPath)") _ = try? shell("/usr/bin/xattr -drs com.apple.quarantine \(kTargetPartialPath)")
guard let imeBundle = Bundle(url: imeURLInstalled), guard let theBundle = Bundle(url: imeURLInstalled),
let imeIdentifier = imeBundle.bundleIdentifier let imeIdentifier = theBundle.bundleIdentifier
else { else {
endAppWithDelay() endAppWithDelay()
return return
} }
let imeBundleURL = imeBundle.bundleURL let imeBundleURL = theBundle.bundleURL
var inputSource = InputSourceHelper.inputSource(for: imeIdentifier)
if inputSource == nil { if allRegisteredInstancesOfThisInputMethod.isEmpty {
NSLog("Registering input source \(imeIdentifier) at \(imeBundleURL.absoluteString).") NSLog("Registering input source \(imeIdentifier) at \(imeBundleURL.absoluteString).")
let status = InputSourceHelper.registerTnputSource(at: imeBundleURL) let status = (TISRegisterInputSource(imeBundleURL as CFURL) == noErr)
if !status { if !status {
let message = String( let message = String(
format: NSLocalizedString( format: NSLocalizedString(
@ -262,8 +271,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
return return
} }
inputSource = InputSourceHelper.inputSource(for: imeIdentifier) if allRegisteredInstancesOfThisInputMethod.isEmpty {
if inputSource == nil {
let message = String( let message = String(
format: NSLocalizedString( format: NSLocalizedString(
"Cannot find input source %@ after registration.", comment: "" "Cannot find input source %@ after registration.", comment: ""
@ -285,19 +293,20 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
NSLog("Installer runs with the pre-macOS 12 flow.") NSLog("Installer runs with the pre-macOS 12 flow.")
} }
// If the IME is not enabled, enable it. Also, unconditionally enable it on macOS 12.0+, // Unconditionally enable the IME on macOS 12.0+,
// as the kTISPropertyInputSourceIsEnabled can still be true even if the IME is *not* // as the kTISPropertyInputSourceIsEnabled can still be true even if the IME is *not*
// enabled in the user's current set of IMEs (which means the IME does not show up in // enabled in the user's current set of IMEs (which means the IME does not show up in
// the user's input menu). // the user's input menu).
var mainInputSourceEnabled = InputSourceHelper.inputSourceEnabled(for: inputSource!) var mainInputSourceEnabled = false
if !mainInputSourceEnabled || isMacOS12OrAbove {
mainInputSourceEnabled = InputSourceHelper.enable(inputSource: inputSource!) allRegisteredInstancesOfThisInputMethod.forEach {
if mainInputSourceEnabled { if $0.activate() {
NSLog("Input method enabled: \(imeIdentifier)") NSLog("Input method enabled: \(imeIdentifier)")
} else { } else {
NSLog("Failed to enable input method: \(imeIdentifier)") NSLog("Failed to enable input method: \(imeIdentifier)")
} }
mainInputSourceEnabled = $0.isActivated
} }
// Alert Panel // Alert Panel

@ -1 +1 @@
Subproject commit 4d6ed238c037c4d4f5464f6914198a503fd1f21a Subproject commit 0293f06c92ba9b95dd42debf662b8f8bb5834bdd

View File

@ -12,7 +12,7 @@ import Cocoa
import InputMethodKit import InputMethodKit
@objc(AppDelegate) @objc(AppDelegate)
class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelegate, class AppDelegate: NSObject, NSApplicationDelegate,
FSEventStreamHelperDelegate, NSUserNotificationCenterDelegate FSEventStreamHelperDelegate, NSUserNotificationCenterDelegate
{ {
func helper(_: FSEventStreamHelper, didReceive _: [FSEventStreamHelper.Event]) { func helper(_: FSEventStreamHelper, didReceive _: [FSEventStreamHelper.Event]) {
@ -30,7 +30,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
private var ctlPrefWindowInstance: ctlPrefWindow? private var ctlPrefWindowInstance: ctlPrefWindow?
private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window
private var checkTask: URLSessionTask? private var checkTask: URLSessionTask?
private var updateNextStepURL: URL?
public var fsStreamHelper = FSEventStreamHelper( public var fsStreamHelper = FSEventStreamHelper(
path: mgrLangModel.dataFolderPath(isDefaultFolder: false), path: mgrLangModel.dataFolderPath(isDefaultFolder: false),
queue: DispatchQueue(label: "vChewing User Phrases") queue: DispatchQueue(label: "vChewing User Phrases")
@ -42,7 +41,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
ctlPrefWindowInstance = nil ctlPrefWindowInstance = nil
ctlAboutWindowInstance = nil ctlAboutWindowInstance = nil
checkTask = nil checkTask = nil
updateNextStepURL = nil
fsStreamHelper.stop() fsStreamHelper.stop()
fsStreamHelper.delegate = nil fsStreamHelper.delegate = nil
} }
@ -137,7 +135,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
case .success(let apiResult): case .success(let apiResult):
switch apiResult { switch apiResult {
case .shouldUpdate(let report): case .shouldUpdate(let report):
updateNextStepURL = report.siteUrl
let content = String( let content = String(
format: NSLocalizedString( format: NSLocalizedString(
"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?%@",
@ -150,21 +147,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
report.versionDescription report.versionDescription
) )
IME.prtDebugIntel("vChewingDebug: \(content)") IME.prtDebugIntel("vChewingDebug: \(content)")
currentAlertType = "Update" let alert = NSAlert()
ctlNonModalAlertWindow.shared.show( alert.messageText = NSLocalizedString("New Version Available", comment: "")
title: NSLocalizedString( alert.informativeText = content
"New Version Available", comment: "" alert.addButton(withTitle: NSLocalizedString("Visit Website", comment: ""))
), alert.addButton(withTitle: NSLocalizedString("Not Now", comment: ""))
content: content, let result = alert.runModal()
confirmButtonTitle: NSLocalizedString( if result == NSApplication.ModalResponse.alertFirstButtonReturn {
"Visit Website", comment: "" if let siteURL = report.siteUrl {
), NSWorkspace.shared.open(siteURL)
cancelButtonTitle: NSLocalizedString( }
"Not Now", comment: "" }
),
cancelAsDefault: false,
delegate: self
)
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
case .noNeedToUpdate, .ignored: case .noNeedToUpdate, .ignored:
break break
@ -172,9 +165,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
case .failure(let error): case .failure(let error):
switch error { switch error {
case VersionUpdateApiError.connectionError(let message): case VersionUpdateApiError.connectionError(let message):
let title = NSLocalizedString( let title = NSLocalizedString("Update Check Failed", comment: "")
"Update Check Failed", comment: ""
)
let content = String( let content = String(
format: NSLocalizedString( format: NSLocalizedString(
"There may be no internet connection or the server failed to respond.\n\nError message: %@", "There may be no internet connection or the server failed to respond.\n\nError message: %@",
@ -183,13 +174,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
) )
let buttonTitle = NSLocalizedString("Dismiss", comment: "") let buttonTitle = NSLocalizedString("Dismiss", comment: "")
IME.prtDebugIntel("vChewingDebug: \(content)") IME.prtDebugIntel("vChewingDebug: \(content)")
currentAlertType = "Update"
ctlNonModalAlertWindow.shared.show( let alert = NSAlert()
title: title, content: content, alert.messageText = title
confirmButtonTitle: buttonTitle, alert.informativeText = content
cancelButtonTitle: nil, alert.addButton(withTitle: buttonTitle)
cancelAsDefault: false, delegate: nil alert.runModal()
)
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
default: default:
break break
@ -205,39 +195,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
"This will remove vChewing Input Method from this user account, requiring your confirmation.", "This will remove vChewing Input Method from this user account, requiring your confirmation.",
comment: "" comment: ""
)) ))
ctlNonModalAlertWindow.shared.show( let alert = NSAlert()
title: NSLocalizedString("Uninstallation", comment: ""), content: content, alert.messageText = NSLocalizedString("Uninstallation", comment: "")
confirmButtonTitle: NSLocalizedString("OK", comment: ""), alert.informativeText = content
cancelButtonTitle: NSLocalizedString("Not Now", comment: ""), cancelAsDefault: false, alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
delegate: self alert.addButton(withTitle: NSLocalizedString("Not Now", comment: ""))
) let result = alert.runModal()
NSApp.setActivationPolicy(.accessory) if result == NSApplication.ModalResponse.alertFirstButtonReturn {
}
func ctlNonModalAlertWindowDidConfirm(_: ctlNonModalAlertWindow) {
switch currentAlertType {
case "Uninstall":
NSWorkspace.shared.openFile( NSWorkspace.shared.openFile(
mgrLangModel.dataFolderPath(isDefaultFolder: true), withApplication: "Finder" mgrLangModel.dataFolderPath(isDefaultFolder: true), withApplication: "Finder"
) )
IME.uninstall(isSudo: false, selfKill: true) IME.uninstall(isSudo: false, selfKill: true)
case "Update":
if let updateNextStepURL = updateNextStepURL {
NSWorkspace.shared.open(updateNextStepURL)
}
updateNextStepURL = nil
default:
break
}
}
func ctlNonModalAlertWindowDidCancel(_: ctlNonModalAlertWindow) {
switch currentAlertType {
case "Update":
updateNextStepURL = nil
default:
break
} }
NSApp.setActivationPolicy(.accessory)
} }
// New About Window // New About Window

View File

@ -7,23 +7,8 @@
// requirements defined in MIT License. // requirements defined in MIT License.
enum AppleKeyboardConverter { enum AppleKeyboardConverter {
static let arrDynamicBasicKeyLayout: [String] = [
"com.apple.keylayout.ZhuyinBopomofo",
"com.apple.keylayout.ZhuyinEten",
"org.atelierInmu.vChewing.keyLayouts.vchewingdachen",
"org.atelierInmu.vChewing.keyLayouts.vchewingmitac",
"org.atelierInmu.vChewing.keyLayouts.vchewingibm",
"org.atelierInmu.vChewing.keyLayouts.vchewingseigyou",
"org.atelierInmu.vChewing.keyLayouts.vchewingeten",
"org.unknown.keylayout.vChewingDachen",
"org.unknown.keylayout.vChewingFakeSeigyou",
"org.unknown.keylayout.vChewingETen",
"org.unknown.keylayout.vChewingIBM",
"org.unknown.keylayout.vChewingMiTAC",
]
static var isDynamicBasicKeyboardLayoutEnabled: Bool { static var isDynamicBasicKeyboardLayoutEnabled: Bool {
AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout)
} }
static func cnvStringApple2ABC(_ strProcessed: String) -> String { static func cnvStringApple2ABC(_ strProcessed: String) -> String {

View File

@ -0,0 +1,206 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Foundation
// enum
public enum StateType: String {
case ofDeactivated = "Deactivated"
case ofEmpty = "Empty"
case ofAbortion = "Abortion" // Empty
case ofCommitting = "Committing"
case ofAssociates = "Associates"
case ofNotEmpty = "NotEmpty"
case ofInputting = "Inputting"
case ofMarking = "Marking"
case ofCandidates = "Candidates"
case ofSymbolTable = "SymbolTable"
}
// IMEState
public protocol IMEStateProtocol {
var type: StateType { get }
var data: StateData { get }
var candidates: [(String, String)] { get }
var hasComposition: Bool { get }
var isCandidateContainer: Bool { get }
var displayedText: String { get }
var textToCommit: String { get set }
var tooltip: String { get set }
var attributedString: NSAttributedString { get }
var convertedToInputting: IMEState { get }
var isFilterable: Bool { get }
var node: SymbolNode { get set }
}
/// ctlInputMethod
///
/// Finite State Machine/
/// 使
/// 使
///
///
/// IMEState
/// 使
///
///
///
/// IMEState
/// IMEState.ofMarking IMEState.ofInputting
///
/// (Constructor)
///
///
///
/// - .Deactivated: 使使
/// - .AssociatedPhrases:
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .Abortion: Empty
/// .Empty()
/// - .Committing:
/// - .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
public struct IMEState: IMEStateProtocol {
public var type: StateType = .ofEmpty
public var data: StateData = .init()
public var node: SymbolNode = .init("")
init(_ data: StateData = .init(), type: StateType = .ofEmpty) {
self.data = data
self.type = type
}
init(_ data: StateData = .init(), type: StateType = .ofEmpty, node: SymbolNode) {
self.data = data
self.type = type
self.node = node
self.data.candidates = { node.children?.map(\.title) ?? [String]() }().map { ("", $0) }
}
}
// MARK: -
extension IMEState {
public static func ofDeactivated() -> IMEState { .init(type: .ofDeactivated) }
public static func ofEmpty() -> IMEState { .init(type: .ofEmpty) }
public static func ofAbortion() -> IMEState { .init(type: .ofAbortion) }
public static func ofCommitting(textToCommit: String) -> IMEState {
var result = IMEState(type: .ofCommitting)
result.data.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &result.data.textToCommit)
return result
}
public static func ofAssociates(candidates: [(String, String)]) -> IMEState {
var result = IMEState(type: .ofAssociates)
result.data.candidates = candidates
return result
}
public static func ofNotEmpty(displayTextSegments: [String], cursor: Int) -> IMEState {
var result = IMEState(type: .ofNotEmpty)
// displayTextSegments
result.data.displayTextSegments = displayTextSegments
result.data.cursor = cursor
return result
}
public static func ofInputting(displayTextSegments: [String], cursor: Int) -> IMEState {
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofInputting
return result
}
public static func ofMarking(
displayTextSegments: [String], markedReadings: [String], cursor: Int, marker: Int
)
-> IMEState
{
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofMarking
result.data.marker = marker
result.data.markedReadings = markedReadings
StateData.Marking.updateParameters(&result.data)
return result
}
public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int)
-> IMEState
{
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofCandidates
result.data.candidates = candidates
return result
}
public static func ofSymbolTable(node: SymbolNode) -> IMEState {
var result = IMEState(type: .ofNotEmpty, node: node)
result.type = .ofSymbolTable
return result
}
}
// MARK: -
extension IMEState {
public var isFilterable: Bool { data.isFilterable }
public var candidates: [(String, String)] { data.candidates }
public var convertedToInputting: IMEState {
if type == .ofInputting { return self }
var result = IMEState.ofInputting(displayTextSegments: data.displayTextSegments, cursor: data.cursor)
result.tooltip = data.tooltipBackupForInputting
return result
}
public var textToCommit: String {
get {
data.textToCommit
}
set {
data.textToCommit = newValue
}
}
public var tooltip: String {
get {
data.tooltip
}
set {
data.tooltip = newValue
}
}
public var attributedString: NSAttributedString {
switch type {
case .ofMarking: return data.attributedStringMarking
case .ofAssociates, .ofSymbolTable: return data.attributedStringPlaceholder
default: return data.attributedStringNormal
}
}
public var hasComposition: Bool {
switch type {
case .ofNotEmpty, .ofInputting, .ofMarking, .ofCandidates: return true
default: return false
}
}
public var isCandidateContainer: Bool {
switch type {
case .ofCandidates, .ofAssociates, .ofSymbolTable: return true
default: return false
}
}
public var displayedText: String { data.displayedText }
}

View File

@ -0,0 +1,240 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Foundation
public struct StateData {
var displayedText: String = "" {
didSet {
let result = IME.kanjiConversionIfRequired(displayedText)
if result.utf16.count == displayedText.utf16.count, result.count == displayedText.count {
displayedText = result
}
}
}
// MARK: Cursor & Marker & Range for UTF8
var cursor: Int = 0 {
didSet {
cursor = min(max(cursor, 0), displayedText.count)
}
}
var marker: Int = 0 {
didSet {
marker = min(max(marker, 0), displayedText.count)
}
}
var markedRange: Range<Int> {
min(cursor, marker)..<max(cursor, marker)
}
// MARK: Cursor & Marker & Range for UTF16 (Read-Only)
/// IMK UTF8 emoji
/// Swift.utf16NSString.length()
///
var u16Cursor: Int {
displayedText.charComponents[0..<cursor].joined().utf16.count
}
var u16Marker: Int {
displayedText.charComponents[0..<marker].joined().utf16.count
}
var u16MarkedRange: Range<Int> {
min(u16Cursor, u16Marker)..<max(u16Cursor, u16Marker)
}
// MARK: Other data for non-empty states.
var markedTargetExists: Bool = false
var displayTextSegments = [String]() {
didSet {
displayedText = displayTextSegments.joined()
}
}
var reading: String = ""
var markedReadings = [String]()
var candidates = [(String, String)]()
var textToCommit: String = ""
var tooltip: String = ""
var tooltipBackupForInputting: String = ""
var attributedStringPlaceholder: NSAttributedString = .init(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
var isFilterable: Bool {
markedTargetExists ? mgrPrefs.allowedMarkRange.contains(markedRange.count) : false
}
var attributedStringNormal: NSAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedText)
var newBegin = 0
for (i, neta) in displayTextSegments.enumerated() {
attributedString.setAttributes(
[
/// .thick
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: i,
], range: NSRange(location: newBegin, length: neta.utf16.count)
)
newBegin += neta.utf16.count
}
return attributedString
}
var attributedStringMarking: NSAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedText)
let end = u16MarkedRange.upperBound
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
], range: NSRange(location: 0, length: u16MarkedRange.lowerBound)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1,
],
range: NSRange(
location: u16MarkedRange.lowerBound,
length: u16MarkedRange.upperBound - u16MarkedRange.lowerBound
)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 2,
],
range: NSRange(
location: end,
length: displayedText.utf16.count - end
)
)
return attributedString
}
}
// MARK: - IMEState
extension StateData {
var chkIfUserPhraseExists: Bool {
let text = displayedText.charComponents[markedRange].joined()
let joined = markedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
}
var userPhrase: String {
let text = displayedText.charComponents[markedRange].joined()
let joined = markedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
return "\(text) \(joined)\(nerfedScore)"
}
var userPhraseConverted: String {
let text =
ChineseConverter.crossConvert(displayedText.charComponents[markedRange].joined()) ?? ""
let joined = markedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
return "\(text) \(joined)\(nerfedScore)\t\(convertedMark)"
}
enum Marking {
private static func generateReadingThread(_ data: StateData) -> String {
var arrOutput = [String]()
for neta in data.markedReadings {
var neta = neta
if neta.isEmpty { continue }
if neta.contains("_") {
arrOutput.append("??")
continue
}
if mgrPrefs.showHanyuPinyinInCompositionBuffer { // ->->調
neta = Tekkon.restoreToneOneInZhuyinKey(target: neta)
neta = Tekkon.cnvPhonaToHanyuPinyin(target: neta)
neta = Tekkon.cnvHanyuPinyinToTextbookStyle(target: neta)
} else {
neta = Tekkon.cnvZhuyinChainToTextbookReading(target: neta)
}
arrOutput.append(neta)
}
return arrOutput.joined(separator: " ")
}
///
/// - Parameter data:
public static func updateParameters(_ data: inout StateData) {
var tooltipGenerated: String {
if mgrPrefs.phraseReplacementEnabled {
ctlInputMethod.tooltipController.setColor(state: .warning)
return NSLocalizedString(
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry.", comment: ""
)
}
if data.markedRange.isEmpty {
return ""
}
let text = data.displayedText.charComponents[data.markedRange].joined()
if data.markedRange.count < mgrPrefs.allowedMarkRange.lowerBound {
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
return String(
format: NSLocalizedString(
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""
) + "\n// " + generateReadingThread(data), text
)
} else if data.markedRange.count > mgrPrefs.allowedMarkRange.upperBound {
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
return String(
format: NSLocalizedString(
"\"%@\" length should ≤ %d for a user phrase.", comment: ""
) + "\n// " + generateReadingThread(data), text, mgrPrefs.allowedMarkRange.upperBound
)
}
let joined = data.markedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
if exist {
data.markedTargetExists = exist
ctlInputMethod.tooltipController.setColor(state: .prompt)
return String(
format: NSLocalizedString(
"\"%@\" already exists: ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude.",
comment: ""
) + "\n// " + generateReadingThread(data), text
)
}
ctlInputMethod.tooltipController.resetColor()
return String(
format: NSLocalizedString("\"%@\" selected. ENTER to add user phrase.", comment: "") + "\n// "
+ generateReadingThread(data),
text
)
}
data.tooltip = tooltipGenerated
}
}
}

View File

@ -1,582 +0,0 @@
// (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).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Foundation
// InputState 使 Struct Struct
// enum
public enum StateType {
case ofDeactivated
case ofAssociatedPhrases
case ofEmpty
case ofEmptyIgnoringPreviousState
case ofCommitting
case ofNotEmpty
case ofInputting
case ofMarking
case ofChoosingCandidate
case ofSymbolTable
}
// InputState
public protocol InputStateProtocol {
var type: StateType { get }
}
/// ctlInputMethod
///
/// Finite State Machine/
/// 使
/// 使
///
///
/// InputState
/// 使
///
///
///
/// InputState
/// InputState.Marking InputState.Inputting
///
///
///
///
/// - .Deactivated: 使使
/// - .AssociatedPhrases:
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .EmptyIgnoringPreviousState: Empty
/// .Empty()
/// - .Committing:
/// - .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
public enum InputState {
/// .Deactivated: 使使
class Deactivated: InputStateProtocol {
public var type: StateType { .ofDeactivated }
var description: String {
"<InputState.Deactivated>"
}
}
// MARK: -
/// .Empty: 使
///
class Empty: InputStateProtocol {
public var type: StateType { .ofEmpty }
var composingBuffer: String {
""
}
var description: String {
"<InputState.Empty>"
}
}
// MARK: -
/// .EmptyIgnoringPreviousState: Empty
///
/// .Empty()
class EmptyIgnoringPreviousState: Empty {
override public var type: StateType { .ofEmptyIgnoringPreviousState }
override var description: String {
"<InputState.EmptyIgnoringPreviousState>"
}
}
// MARK: -
/// .Committing:
class Committing: InputStateProtocol {
public var type: StateType { .ofCommitting }
private(set) var textToCommit: String = ""
convenience init(textToCommit: String) {
self.init()
self.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit)
}
var description: String {
"<InputState.Committing textToCommit:\(textToCommit)>"
}
}
// MARK: -
/// .AssociatedPhrases:
/// 西 .NotEmpty
class AssociatedPhrases: InputStateProtocol {
public var type: StateType { .ofAssociatedPhrases }
private(set) var candidates: [(String, String)] = []
private(set) var isTypingVertical: Bool = false
init(candidates: [(String, String)], isTypingVertical: Bool) {
self.candidates = candidates
self.isTypingVertical = isTypingVertical
}
var attributedString: NSMutableAttributedString {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
}
var description: String {
"<InputState.AssociatedPhrases, candidates:\(candidates), isTypingVertical:\(isTypingVertical)>"
}
}
// MARK: -
/// .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
class NotEmpty: InputStateProtocol {
public var type: StateType { .ofNotEmpty }
private(set) var composingBuffer: String
private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
private(set) var reading: String = ""
private(set) var nodeValuesArray = [String]()
public var composingBufferConverted: String {
let converted = IME.kanjiConversionIfRequired(composingBuffer)
if converted.utf16.count != composingBuffer.utf16.count
|| converted.count != composingBuffer.count
{
return composingBuffer
}
return converted
}
public var committingBufferConverted: String { composingBufferConverted }
init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
self.composingBuffer = composingBuffer
self.reading = reading
// reading
if !reading.isEmpty {
var newNodeValuesArray = [String]()
var temporaryNode = ""
var charCounter = 0
for node in nodeValuesArray {
for char in node {
if charCounter == cursorIndex - reading.utf16.count {
newNodeValuesArray.append(temporaryNode)
temporaryNode = ""
newNodeValuesArray.append(reading)
}
temporaryNode += String(char)
charCounter += 1
}
newNodeValuesArray.append(temporaryNode)
temporaryNode = ""
}
self.nodeValuesArray = newNodeValuesArray
} else {
self.nodeValuesArray = nodeValuesArray
}
defer { self.cursorIndex = cursorIndex }
}
var attributedString: NSMutableAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
var newBegin = 0
for (i, neta) in nodeValuesArray.enumerated() {
attributedString.setAttributes(
[
/// .thick
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: i,
], range: NSRange(location: newBegin, length: neta.utf16.count)
)
newBegin += neta.utf16.count
}
return attributedString
}
var description: String {
"<InputState.NotEmpty, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
}
}
// MARK: -
/// .Inputting: 使Compositor
class Inputting: NotEmpty {
override public var type: StateType { .ofInputting }
var textToCommit: String = ""
var tooltip: String = ""
override public var committingBufferConverted: String {
let committingBuffer = nodeValuesArray.joined()
let converted = IME.kanjiConversionIfRequired(committingBuffer)
if converted.utf16.count != composingBuffer.utf16.count
|| converted.count != composingBuffer.count
{
return composingBuffer
}
return converted
}
override init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
super.init(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
}
override var description: String {
"<InputState.Inputting, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>, textToCommit:\(textToCommit)>"
}
}
// MARK: -
/// .Marking: 使
///
class Marking: NotEmpty {
override public var type: StateType { .ofMarking }
private var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
private(set) var markedRange: Range<Int>
private var literalMarkedRange: Range<Int> {
let lowerBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.lowerBound)
let upperBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.upperBound)
return lowerBoundLiteral..<upperBoundLiteral
}
var literalReadingThread: String {
var arrOutput = [String]()
for neta in readings[literalMarkedRange] {
var neta = neta
if neta.isEmpty { continue }
if neta.contains("_") {
arrOutput.append("??")
continue
}
if mgrPrefs.showHanyuPinyinInCompositionBuffer { // ->->調
neta = Tekkon.restoreToneOneInZhuyinKey(target: neta)
neta = Tekkon.cnvPhonaToHanyuPinyin(target: neta)
neta = Tekkon.cnvHanyuPinyinToTextbookStyle(target: neta)
} else {
neta = Tekkon.cnvZhuyinChainToTextbookReading(target: neta)
}
arrOutput.append(neta)
}
return arrOutput.joined(separator: " ")
}
private var markedTargetExists = false
var tooltip: String {
if composingBuffer.count != readings.count {
ctlInputMethod.tooltipController.setColor(state: .redAlert)
return NSLocalizedString(
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
)
}
if mgrPrefs.phraseReplacementEnabled {
ctlInputMethod.tooltipController.setColor(state: .warning)
return NSLocalizedString(
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry.", comment: ""
)
}
if markedRange.isEmpty {
return ""
}
let text = composingBuffer.utf16SubString(with: markedRange)
if literalMarkedRange.count < allowedMarkRange.lowerBound {
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
return String(
format: NSLocalizedString(
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""
) + "\n// " + literalReadingThread, text
)
} else if literalMarkedRange.count > allowedMarkRange.upperBound {
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
return String(
format: NSLocalizedString(
"\"%@\" length should ≤ %d for a user phrase.", comment: ""
) + "\n// " + literalReadingThread, text, allowedMarkRange.upperBound
)
}
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
if exist {
markedTargetExists = exist
ctlInputMethod.tooltipController.setColor(state: .prompt)
return String(
format: NSLocalizedString(
"\"%@\" already exists: ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude.",
comment: ""
) + "\n// " + literalReadingThread, text
)
}
ctlInputMethod.tooltipController.resetColor()
return String(
format: NSLocalizedString("\"%@\" selected. ENTER to add user phrase.", comment: "") + "\n// "
+ literalReadingThread,
text
)
}
var tooltipForInputting: String = ""
private(set) var readings: [String]
init(
composingBuffer: String, cursorIndex: Int, markerIndex: Int, readings: [String], nodeValuesArray: [String] = []
) {
let begin = min(cursorIndex, markerIndex)
let end = max(cursorIndex, markerIndex)
markedRange = begin..<end
self.readings = readings
super.init(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray
)
defer { self.markerIndex = markerIndex }
}
override var attributedString: NSMutableAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
let end = markedRange.upperBound
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
], range: NSRange(location: 0, length: markedRange.lowerBound)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1,
],
range: NSRange(
location: markedRange.lowerBound,
length: markedRange.upperBound - markedRange.lowerBound
)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 2,
],
range: NSRange(
location: end,
length: composingBuffer.utf16.count - end
)
)
return attributedString
}
override var description: String {
"<InputState.Marking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange)>"
}
var convertedToInputting: Inputting {
let state = Inputting(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
state.tooltip = tooltipForInputting
return state
}
var validToFilter: Bool { markedTargetExists ? allowedMarkRange.contains(literalMarkedRange.count) : false }
var bufferReadingCountMisMatch: Bool { composingBuffer.count != readings.count }
var chkIfUserPhraseExists: Bool {
let text = composingBuffer.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
}
var userPhrase: String {
let text = composingBuffer.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
return "\(text) \(joined)\(nerfedScore)"
}
var userPhraseConverted: String {
let text =
ChineseConverter.crossConvert(composingBuffer.utf16SubString(with: markedRange)) ?? ""
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
return "\(text) \(joined)\(nerfedScore)\t\(convertedMark)"
}
}
// MARK: -
/// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty {
override public var type: StateType { .ofChoosingCandidate }
private(set) var candidates: [(String, String)]
private(set) var isTypingVertical: Bool
// ctlInputMethod.candidateSelectionChanged()
public var chosenCandidateString: String = "" {
didSet {
// / JIS
if chosenCandidateString.contains("\u{17}") {
chosenCandidateString = String(chosenCandidateString.split(separator: "\u{17}")[0])
}
if !chosenCandidateString.contains("\u{1A}") { return }
chosenCandidateString = String(chosenCandidateString.split(separator: "\u{1A}").reversed()[0])
}
}
init(
composingBuffer: String, cursorIndex: Int, candidates: [(String, String)], isTypingVertical: Bool,
nodeValuesArray: [String] = []
) {
self.candidates = candidates
self.isTypingVertical = isTypingVertical
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray)
}
// 使 chosenCandidateString
// macOS
// ctlInputMethod.candidateSelectionChanged()
//
override var attributedString: NSMutableAttributedString {
guard !chosenCandidateString.isEmpty else { return super.attributedString }
let bufferTextRear = composingBuffer.utf16SubString(with: 0..<cursorIndex)
let bufferTextFront = composingBuffer.utf16SubString(with: cursorIndex..<(composingBuffer.utf16.count))
let cursorIndexU8 = bufferTextRear.count - 1
//
if (mgrPrefs.useRearCursorMode && bufferTextFront.count < chosenCandidateString.count)
|| (!mgrPrefs.useRearCursorMode && bufferTextRear.count < chosenCandidateString.count)
{
return super.attributedString
}
// u16Range NSAttributedString NSRange
let u16Range: Range<Int> = {
switch mgrPrefs.useRearCursorMode {
case false: return (max(0, cursorIndex - chosenCandidateString.utf16.count))..<cursorIndex
case true:
return
cursorIndex..<min(cursorIndex + chosenCandidateString.utf16.count, composingBuffer.utf16.count - 1)
}
}()
// u8Range
let u8Range: Range<Int> = {
switch mgrPrefs.useRearCursorMode {
case false: return (max(0, cursorIndexU8 - chosenCandidateString.count))..<cursorIndexU8
case true:
return cursorIndexU8..<min(cursorIndexU8 + chosenCandidateString.count, composingBuffer.count - 1)
}
}()
let strSegmentedRear = composingBuffer.charComponents[0..<u8Range.lowerBound].joined()
let strSegmentedFront = composingBuffer.charComponents[u8Range.upperBound...].joined()
let newBufferConverted: String = NotEmpty(
composingBuffer: strSegmentedRear + chosenCandidateString + strSegmentedFront, cursorIndex: 0
).composingBufferConverted
guard newBufferConverted.count == composingBuffer.count else { return super.attributedString }
///
/// JIS
let attributedStringResult = NSMutableAttributedString(string: newBufferConverted)
attributedStringResult.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
], range: NSRange(location: 0, length: u16Range.lowerBound)
)
attributedStringResult.setAttributes(
[
.underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1,
], range: NSRange(location: u16Range.lowerBound, length: u16Range.count)
)
attributedStringResult.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 2,
], range: NSRange(location: u16Range.upperBound, length: newBufferConverted.utf16.count)
)
return attributedStringResult
}
override var description: String {
"<InputState.ChoosingCandidate, candidates:\(candidates), isTypingVertical:\(isTypingVertical), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
}
}
// MARK: -
/// .SymbolTable:
class SymbolTable: ChoosingCandidate {
override public var type: StateType { .ofSymbolTable }
var node: SymbolNode
init(node: SymbolNode, previous: SymbolNode? = nil, isTypingVertical: Bool) {
self.node = node
if let previous = previous {
self.node.previous = previous
}
let candidates = node.children?.map(\.title) ?? [String]()
super.init(
composingBuffer: "", cursorIndex: 0, candidates: candidates.map { ("", $0) },
isTypingVertical: isTypingVertical
)
}
// InputState.SymbolTable
// MS Word
//
//
// Crediting Qwertyyb: https://github.com/qwertyyb/Fire/issues/55#issuecomment-1133497700
override var attributedString: NSMutableAttributedString {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
}
override var description: String {
"<InputState.SymbolTable, candidates:\(candidates), isTypingVertical:\(isTypingVertical)>"
}
}
}

View File

@ -25,7 +25,7 @@ protocol KeyHandlerDelegate {
_: KeyHandler, didSelectCandidateAt index: Int, _: KeyHandler, didSelectCandidateAt index: Int,
ctlCandidate controller: ctlCandidateProtocol ctlCandidate controller: ctlCandidateProtocol
) )
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol, addToFilter: Bool) func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: IMEStateProtocol, addToFilter: Bool)
-> Bool -> Bool
} }
@ -35,8 +35,6 @@ protocol KeyHandlerDelegate {
public class KeyHandler { public class KeyHandler {
/// ///
let kEpsilon: Double = 0.000001 let kEpsilon: Double = 0.000001
///
var isCursorCuttingChar = false
/// ///
var isTypingContentEmpty: Bool { composer.isEmpty && compositor.isEmpty } var isTypingContentEmpty: Bool { composer.isEmpty && compositor.isEmpty }
@ -88,6 +86,21 @@ public class KeyHandler {
// MARK: - Functions dealing with Megrez. // MARK: - Functions dealing with Megrez.
///
/// - Returns:
func currentMarkedRange() -> Range<Int> {
min(compositor.cursor, compositor.marker)..<max(compositor.cursor, compositor.marker)
}
///
func isCursorCuttingChar(isMarker: Bool = false) -> Bool {
let index = isMarker ? compositor.marker : compositor.cursor
var isBound = (index == compositor.walkedNodes.contextRange(ofGivenCursor: index).lowerBound)
if index == compositor.width { isBound = true }
let rawResult = compositor.walkedNodes.findNode(at: index)?.isReadingMismatched ?? false
return !isBound && rawResult
}
/// Megrez 使便 /// Megrez 使便
/// ///
/// 使 Node Crossing /// 使 Node Crossing
@ -107,11 +120,10 @@ public class KeyHandler {
// GraphViz // GraphViz
if mgrPrefs.isDebugModeEnabled { if mgrPrefs.isDebugModeEnabled {
let result = compositor.dumpDOT let result = compositor.dumpDOT
let appSupportPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].path.appending(
"/vChewing-visualization.dot")
do { do {
try result.write( try result.write(toFile: appSupportPath, atomically: true, encoding: .utf8)
toFile: "/private/var/tmp/vChewing-visualization.dot",
atomically: true, encoding: .utf8
)
} catch { } catch {
IME.prtDebugIntel("Failed from writing dumpDOT results.") IME.prtDebugIntel("Failed from writing dumpDOT results.")
} }
@ -122,7 +134,7 @@ public class KeyHandler {
/// - Parameter key: /// - Parameter key:
/// - Returns: /// - Returns:
/// nil /// nil
func buildAssociatePhraseArray(withPair pair: Megrez.Compositor.Candidate) -> [(String, String)] { func buildAssociatePhraseArray(withPair pair: Megrez.Compositor.KeyValuePaired) -> [(String, String)] {
var arrResult: [(String, String)] = [] var arrResult: [(String, String)] = []
if currentLM.hasAssociatedPhrasesFor(pair: pair) { if currentLM.hasAssociatedPhrasesFor(pair: pair) {
arrResult = currentLM.associatedPhrasesFor(pair: pair).map { ("", $0) } arrResult = currentLM.associatedPhrasesFor(pair: pair).map { ("", $0) }
@ -144,7 +156,7 @@ public class KeyHandler {
/// v1.9.3 SP2 Bug v1.9.4 /// v1.9.3 SP2 Bug v1.9.4
/// v2.0.2 /// v2.0.2
/// - Parameter theCandidate: /// - Parameter theCandidate:
func consolidateCursorContext(with theCandidate: Megrez.Compositor.Candidate) { func consolidateCursorContext(with theCandidate: Megrez.Compositor.KeyValuePaired) {
var grid = compositor var grid = compositor
var frontBoundaryEX = actualCandidateCursor + 1 var frontBoundaryEX = actualCandidateCursor + 1
var rearBoundaryEX = actualCandidateCursor var rearBoundaryEX = actualCandidateCursor
@ -195,7 +207,7 @@ public class KeyHandler {
let values = currentNode.currentPair.value.charComponents let values = currentNode.currentPair.value.charComponents
for (subPosition, key) in currentNode.keyArray.enumerated() { for (subPosition, key) in currentNode.keyArray.enumerated() {
guard values.count > subPosition else { break } // guard values.count > subPosition else { break } //
let thePair = Megrez.Compositor.Candidate( let thePair = Megrez.Compositor.KeyValuePaired(
key: key, value: values[subPosition] key: key, value: values[subPosition]
) )
compositor.overrideCandidate(thePair, at: position) compositor.overrideCandidate(thePair, at: position)
@ -214,15 +226,12 @@ public class KeyHandler {
/// - respectCursorPushing: true /// - respectCursorPushing: true
/// - consolidate: /// - consolidate:
func fixNode(candidate: (String, String), respectCursorPushing: Bool = true, preConsolidate: Bool = false) { func fixNode(candidate: (String, String), respectCursorPushing: Bool = true, preConsolidate: Bool = false) {
let theCandidate: Megrez.Compositor.Candidate = .init(key: candidate.0, value: candidate.1) let theCandidate: Megrez.Compositor.KeyValuePaired = .init(key: candidate.0, value: candidate.1)
/// ///
if preConsolidate { if preConsolidate { consolidateCursorContext(with: theCandidate) }
consolidateCursorContext(with: theCandidate)
}
// //
if !compositor.overrideCandidate(theCandidate, at: actualCandidateCursor) { return } if !compositor.overrideCandidate(theCandidate, at: actualCandidateCursor) { return }
let previousWalk = compositor.walkedNodes let previousWalk = compositor.walkedNodes
// //
@ -261,7 +270,7 @@ public class KeyHandler {
func getCandidatesArray(fixOrder: Bool = true) -> [(String, String)] { func getCandidatesArray(fixOrder: Bool = true) -> [(String, String)] {
/// 使 nodesCrossing macOS /// 使 nodesCrossing macOS
/// nodeCrossing /// nodeCrossing
var arrCandidates: [Megrez.Compositor.Candidate] = { var arrCandidates: [Megrez.Compositor.KeyValuePaired] = {
switch mgrPrefs.useRearCursorMode { switch mgrPrefs.useRearCursorMode {
case false: case false:
return compositor.fetchCandidates(at: actualCandidateCursor, filter: .endAt) return compositor.fetchCandidates(at: actualCandidateCursor, filter: .endAt)
@ -281,8 +290,8 @@ public class KeyHandler {
} }
let arrSuggestedUnigrams: [(String, Megrez.Unigram)] = fetchSuggestionsFromUOM(apply: false) let arrSuggestedUnigrams: [(String, Megrez.Unigram)] = fetchSuggestionsFromUOM(apply: false)
let arrSuggestedCandidates: [Megrez.Compositor.Candidate] = arrSuggestedUnigrams.map { let arrSuggestedCandidates: [Megrez.Compositor.KeyValuePaired] = arrSuggestedUnigrams.map {
Megrez.Compositor.Candidate(key: $0.0, value: $0.1.value) Megrez.Compositor.KeyValuePaired(key: $0.0, value: $0.1.value)
} }
arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates
arrCandidates = arrCandidates.deduplicate arrCandidates = arrCandidates.deduplicate
@ -307,7 +316,7 @@ public class KeyHandler {
if !suggestion.isEmpty, let newestSuggestedCandidate = suggestion.candidates.last { if !suggestion.isEmpty, let newestSuggestedCandidate = suggestion.candidates.last {
let overrideBehavior: Megrez.Compositor.Node.OverrideType = let overrideBehavior: Megrez.Compositor.Node.OverrideType =
suggestion.forceHighScoreOverride ? .withHighScore : .withTopUnigramScore suggestion.forceHighScoreOverride ? .withHighScore : .withTopUnigramScore
let suggestedPair: Megrez.Compositor.Candidate = .init( let suggestedPair: Megrez.Compositor.KeyValuePaired = .init(
key: newestSuggestedCandidate.0, value: newestSuggestedCandidate.1.value key: newestSuggestedCandidate.0, value: newestSuggestedCandidate.1.value
) )
IME.prtDebugIntel( IME.prtDebugIntel(

View File

@ -23,9 +23,9 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: IMK /// - Returns: IMK
func handleCandidate( func handleCandidate(
state: InputStateProtocol, state: IMEStateProtocol,
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard var ctlCandidateCurrent = delegate?.ctlCandidate() else { guard var ctlCandidateCurrent = delegate?.ctlCandidate() else {
@ -41,7 +41,7 @@ extension KeyHandler {
|| ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold) || ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold)
if cancelCandidateKey { if cancelCandidateKey {
if state is InputState.AssociatedPhrases if state.type == .ofAssociates
|| mgrPrefs.useSCPCTypingMode || mgrPrefs.useSCPCTypingMode
|| compositor.isEmpty || compositor.isEmpty
{ {
@ -49,13 +49,12 @@ extension KeyHandler {
// //
// 使 BackSpace // 使 BackSpace
// compositor.isEmpty // compositor.isEmpty
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
} else { } else {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} }
if let state = state as? InputState.SymbolTable, let nodePrevious = state.node.previous { if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, let _ = nodePrevious.children {
stateCallback(InputState.SymbolTable(node: nodePrevious, isTypingVertical: state.isTypingVertical)) stateCallback(IMEState.ofSymbolTable(node: nodePrevious))
} }
return true return true
} }
@ -63,9 +62,8 @@ extension KeyHandler {
// MARK: Enter // MARK: Enter
if input.isEnter { if input.isEnter {
if state is InputState.AssociatedPhrases, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter { if state.type == .ofAssociates, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
return true return true
} }
delegate?.keyHandler( delegate?.keyHandler(
@ -243,23 +241,15 @@ extension KeyHandler {
// MARK: End Key // MARK: End Key
var candidates: [(String, String)]! if state.candidates.isEmpty {
if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates
} else if let state = state as? InputState.AssociatedPhrases {
candidates = state.candidates
}
if candidates.isEmpty {
return false return false
} else { // count > 0!isEmpty滿 } else { // count > 0!isEmpty滿
if input.isEnd || input.emacsKey == EmacsKey.end { if input.isEnd || input.emacsKey == EmacsKey.end {
if ctlCandidateCurrent.selectedCandidateIndex == candidates.count - 1 { if ctlCandidateCurrent.selectedCandidateIndex == state.candidates.count - 1 {
IME.prtDebugIntel("9B69AAAD") IME.prtDebugIntel("9B69AAAD")
errorCallback() errorCallback()
} else { } else {
ctlCandidateCurrent.selectedCandidateIndex = candidates.count - 1 ctlCandidateCurrent.selectedCandidateIndex = state.candidates.count - 1
} }
return true return true
} }
@ -267,13 +257,13 @@ extension KeyHandler {
// MARK: (Associated Phrases) // MARK: (Associated Phrases)
if state is InputState.AssociatedPhrases { if state.type == .ofAssociates {
if !input.isShiftHold { return false } if !input.isShiftHold { return false }
} }
var index: Int = NSNotFound var index: Int = NSNotFound
let match: String = let match: String =
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : input.text (state.type == .ofAssociates) ? input.inputTextIgnoringModifiers ?? "" : input.text
for j in 0..<ctlCandidateCurrent.keyLabels.count { for j in 0..<ctlCandidateCurrent.keyLabels.count {
let label: CandidateKeyLabel = ctlCandidateCurrent.keyLabels[j] let label: CandidateKeyLabel = ctlCandidateCurrent.keyLabels[j]
@ -293,7 +283,7 @@ extension KeyHandler {
} }
} }
if state is InputState.AssociatedPhrases { return false } if state.type == .ofAssociates { return false }
// MARK: (SCPC Mode Processing) // MARK: (SCPC Mode Processing)
@ -333,10 +323,9 @@ extension KeyHandler {
didSelectCandidateAt: candidateIndex, didSelectCandidateAt: candidateIndex,
ctlCandidate: ctlCandidateCurrent ctlCandidate: ctlCandidateCurrent
) )
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
return handle( return handle(
input: input, state: InputState.Empty(), stateCallback: stateCallback, errorCallback: errorCallback input: input, state: IMEState.ofEmpty(), stateCallback: stateCallback, errorCallback: errorCallback
) )
} }
return true return true

View File

@ -19,7 +19,7 @@ extension KeyHandler {
/// - Returns: IMK /// - Returns: IMK
func handleComposition( func handleComposition(
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool? { ) -> Bool? {
// MARK: (Handle BPMF Keys) // MARK: (Handle BPMF Keys)
@ -62,7 +62,7 @@ extension KeyHandler {
composer.receiveKey(fromString: input.text) composer.receiveKey(fromString: input.text)
keyConsumedByReading = true keyConsumedByReading = true
// 調 updateClientComposingBuffer() return true // 調 updateClientdisplayedText() return true
// 調 // 調
if !composer.hasToneMarker() { if !composer.hasToneMarker() {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
@ -100,8 +100,7 @@ extension KeyHandler {
switch compositor.isEmpty { switch compositor.isEmpty {
case false: stateCallback(buildInputtingState) case false: stateCallback(buildInputtingState)
case true: case true:
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
} }
return true // IMK return true // IMK
} }
@ -118,37 +117,32 @@ extension KeyHandler {
// //
composer.clear() composer.clear()
// updateClientComposingBuffer() // updateClientdisplayedText()
let inputting = buildInputtingState let inputting = buildInputtingState
stateCallback(inputting) stateCallback(inputting)
/// ///
if mgrPrefs.useSCPCTypingMode { if mgrPrefs.useSCPCTypingMode {
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate( let candidateState: IMEState = buildCandidate(
state: inputting, state: inputting,
isTypingVertical: input.isTypingVertical isTypingVertical: input.isTypingVertical
) )
if choosingCandidates.candidates.count == 1, let firstCandidate = choosingCandidates.candidates.first { if candidateState.candidates.count == 1, let firstCandidate = candidateState.candidates.first {
let reading: String = firstCandidate.0 let reading: String = firstCandidate.0
let text: String = firstCandidate.1 let text: String = firstCandidate.1
stateCallback(InputState.Committing(textToCommit: text)) stateCallback(IMEState.ofCommitting(textToCommit: text))
if !mgrPrefs.associatedPhrasesEnabled { if !mgrPrefs.associatedPhrasesEnabled {
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
} else { } else {
if let associatedPhrases = let associatedPhrases =
buildAssociatePhraseState( buildAssociatePhraseState(
withPair: .init(key: reading, value: text), withPair: .init(key: reading, value: text)
isTypingVertical: input.isTypingVertical )
), !associatedPhrases.candidates.isEmpty stateCallback(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases)
{
stateCallback(associatedPhrases)
} else {
stateCallback(InputState.Empty())
}
} }
} else { } else {
stateCallback(choosingCandidates) stateCallback(candidateState)
} }
} }
// ctlInputMethod IMK // ctlInputMethod IMK
@ -157,7 +151,7 @@ extension KeyHandler {
/// 調 /// 調
if keyConsumedByReading { if keyConsumedByReading {
// updateClientComposingBuffer() // updateClientdisplayedText()
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
return true return true
} }

View File

@ -25,8 +25,8 @@ extension KeyHandler {
/// - Returns: IMK /// - Returns: IMK
func handle( func handle(
input: InputSignalProtocol, input: InputSignalProtocol,
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
// inputTest // inputTest
@ -38,8 +38,8 @@ extension KeyHandler {
// Megrez // Megrez
if input.isInvalid { if input.isInvalid {
// .Empty(IgnoringPreviousState) .Deactivated // .Empty(IgnoringPreviousState) .Deactivated
// .EmptyIgnoringPreviousState.Empty // .Abortion.Empty
if state is InputState.Empty || state is InputState.Deactivated { if state.type == .ofEmpty || state.type == .ofDeactivated {
return false return false
} }
IME.prtDebugIntel("550BCF7B: KeyHandler just refused an invalid input.") IME.prtDebugIntel("550BCF7B: KeyHandler just refused an invalid input.")
@ -51,7 +51,7 @@ extension KeyHandler {
// //
let isFunctionKey: Bool = let isFunctionKey: Bool =
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey) input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey)
if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey { if state.type != .ofAssociates, !state.hasComposition, !state.isCandidateContainer, isFunctionKey {
return false return false
} }
@ -68,7 +68,7 @@ extension KeyHandler {
// BackSpace // BackSpace
} else if input.isCapsLockOn || input.isASCIIModeInput { } else if input.isCapsLockOn || input.isASCIIModeInput {
// //
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
// Shift // Shift
if input.isUpperCaseASCIILetterKey { if input.isUpperCaseASCIILetterKey {
@ -82,8 +82,8 @@ extension KeyHandler {
} }
// //
stateCallback(InputState.Committing(textToCommit: inputText.lowercased())) stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
@ -94,19 +94,19 @@ extension KeyHandler {
// 使 Cocoa flags // 使 Cocoa flags
// //
if input.isNumericPadKey { if input.isNumericPadKey {
if !(state is InputState.ChoosingCandidate || state is InputState.AssociatedPhrases if !(state.type == .ofCandidates || state.type == .ofAssociates
|| state is InputState.SymbolTable) || state.type == .ofSymbolTable)
{ {
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
stateCallback(InputState.Committing(textToCommit: inputText.lowercased())) stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
} }
// MARK: (Handle Candidates) // MARK: (Handle Candidates)
if state is InputState.ChoosingCandidate { if [.ofCandidates, .ofSymbolTable].contains(state.type) {
return handleCandidate( return handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
) )
@ -114,26 +114,26 @@ extension KeyHandler {
// MARK: (Handle Associated Phrases) // MARK: (Handle Associated Phrases)
if state is InputState.AssociatedPhrases { if state.type == .ofAssociates {
if handleCandidate( if handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
) { ) {
return true return true
} else { } else {
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
} }
} }
// MARK: 便使() (Handle Marking) // MARK: 便使() (Handle Marking)
if let marking = state as? InputState.Marking { if state.type == .ofMarking {
if handleMarkingState( if handleMarkingState(
marking, input: input, stateCallback: stateCallback, state, input: input, stateCallback: stateCallback,
errorCallback: errorCallback errorCallback: errorCallback
) { ) {
return true return true
} }
state = marking.convertedToInputting state = state.convertedToInputting
stateCallback(state) stateCallback(state)
} }
@ -147,7 +147,7 @@ extension KeyHandler {
// MARK: (Calling candidate window using Up / Down or PageUp / PageDn.) // MARK: (Calling candidate window using Up / Down or PageUp / PageDn.)
if let currentState = state as? InputState.NotEmpty, composer.isEmpty, !input.isOptionHold, if state.hasComposition, composer.isEmpty, !input.isOptionHold,
input.isCursorClockLeft || input.isCursorClockRight || input.isSpace input.isCursorClockLeft || input.isCursorClockRight || input.isSpace
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior) || input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
{ {
@ -155,12 +155,12 @@ extension KeyHandler {
/// Space /// Space
if !mgrPrefs.chooseCandidateUsingSpace { if !mgrPrefs.chooseCandidateUsingSpace {
if compositor.cursor >= compositor.length { if compositor.cursor >= compositor.length {
let composingBuffer = currentState.composingBuffer let displayedText = state.displayedText
if !composingBuffer.isEmpty { if !displayedText.isEmpty {
stateCallback(InputState.Committing(textToCommit: composingBuffer)) stateCallback(IMEState.ofCommitting(textToCommit: displayedText))
} }
stateCallback(InputState.Committing(textToCommit: " ")) stateCallback(IMEState.ofCommitting(textToCommit: " "))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
} else if currentLM.hasUnigramsFor(key: " ") { } else if currentLM.hasUnigramsFor(key: " ") {
compositor.insertKey(" ") compositor.insertKey(" ")
walk() walk()
@ -175,7 +175,7 @@ extension KeyHandler {
) )
} }
} }
stateCallback(buildCandidate(state: currentState, isTypingVertical: input.isTypingVertical)) stateCallback(buildCandidate(state: state))
return true return true
} }
@ -236,7 +236,7 @@ extension KeyHandler {
// MARK: Clock-Left & Clock-Right // MARK: Clock-Left & Clock-Right
if input.isCursorClockLeft || input.isCursorClockRight { if input.isCursorClockLeft || input.isCursorClockRight {
if input.isOptionHold, state is InputState.Inputting { if input.isOptionHold, state.type == .ofInputting {
if input.isCursorClockRight { if input.isCursorClockRight {
return handleInlineCandidateRotation( return handleInlineCandidateRotation(
state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback
@ -277,7 +277,7 @@ extension KeyHandler {
// MARK: Punctuation list // MARK: Punctuation list
if input.isSymbolMenuPhysicalKey, !input.isShiftHold, !input.isControlHold { if input.isSymbolMenuPhysicalKey, !input.isShiftHold, !input.isControlHold, state.type != .ofDeactivated {
if input.isOptionHold { if input.isOptionHold {
if currentLM.hasUnigramsFor(key: "_punctuation_list") { if currentLM.hasUnigramsFor(key: "_punctuation_list") {
if composer.isEmpty { if composer.isEmpty {
@ -285,7 +285,7 @@ extension KeyHandler {
walk() walk()
let inputting = buildInputtingState let inputting = buildInputtingState
stateCallback(inputting) stateCallback(inputting)
stateCallback(buildCandidate(state: inputting, isTypingVertical: input.isTypingVertical)) stateCallback(buildCandidate(state: inputting))
} else { // } else { //
IME.prtDebugIntel("17446655") IME.prtDebugIntel("17446655")
errorCallback() errorCallback()
@ -297,14 +297,14 @@ extension KeyHandler {
// Enter 使 commit buffer // Enter 使 commit buffer
// bool _ = // bool _ =
_ = handleEnter(state: state, stateCallback: stateCallback) _ = handleEnter(state: state, stateCallback: stateCallback)
stateCallback(InputState.SymbolTable(node: SymbolNode.root, isTypingVertical: input.isTypingVertical)) stateCallback(IMEState.ofSymbolTable(node: SymbolNode.root))
return true return true
} }
} }
// MARK: / (FW / HW Arabic Numbers Input) // MARK: / (FW / HW Arabic Numbers Input)
if state is InputState.Empty { if state.type == .ofEmpty {
if input.isMainAreaNumKey, input.isShiftHold, input.isOptionHold, !input.isControlHold, !input.isCommandHold { if input.isMainAreaNumKey, input.isShiftHold, input.isOptionHold, !input.isControlHold, !input.isCommandHold {
// NOTE: macOS 10.11 El Capitan CFStringTransform StringTransform: // NOTE: macOS 10.11 El Capitan CFStringTransform StringTransform:
// https://developer.apple.com/documentation/foundation/stringtransform // https://developer.apple.com/documentation/foundation/stringtransform
@ -312,9 +312,9 @@ extension KeyHandler {
let string = NSMutableString(string: stringRAW) let string = NSMutableString(string: stringRAW)
CFStringTransform(string, nil, kCFStringTransformFullwidthHalfwidth, true) CFStringTransform(string, nil, kCFStringTransformFullwidthHalfwidth, true)
stateCallback( stateCallback(
InputState.Committing(textToCommit: mgrPrefs.halfWidthPunctuationEnabled ? stringRAW : string as String) IMEState.ofCommitting(textToCommit: mgrPrefs.halfWidthPunctuationEnabled ? stringRAW : string as String)
) )
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
} }
@ -357,10 +357,10 @@ extension KeyHandler {
// MARK: / (Full-Width / Half-Width Space) // MARK: / (Full-Width / Half-Width Space)
/// 使 /// 使
if state is InputState.Empty { if state.type == .ofEmpty {
if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold { if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
stateCallback(InputState.Committing(textToCommit: input.isShiftHold ? " " : " ")) stateCallback(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " "))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
} }
@ -371,14 +371,14 @@ extension KeyHandler {
if input.isShiftHold { // isOptionHold if input.isShiftHold { // isOptionHold
switch mgrPrefs.upperCaseLetterKeyBehavior { switch mgrPrefs.upperCaseLetterKeyBehavior {
case 1: case 1:
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
stateCallback(InputState.Committing(textToCommit: inputText.lowercased())) stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
case 2: case 2:
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
stateCallback(InputState.Committing(textToCommit: inputText.uppercased())) stateCallback(IMEState.ofCommitting(textToCommit: inputText.uppercased()))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
default: // case 0 default: // case 0
let letter = "_letter_\(inputText)" let letter = "_letter_\(inputText)"
@ -401,7 +401,7 @@ extension KeyHandler {
/// ///
/// F1-F12 /// F1-F12
/// 便 /// 便
if (state is InputState.NotEmpty) || !composer.isEmpty { if state.hasComposition || !composer.isEmpty {
IME.prtDebugIntel( IME.prtDebugIntel(
"Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode)") "Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode)")
IME.prtDebugIntel("A9BFF20E") IME.prtDebugIntel("A9BFF20E")

View File

@ -17,101 +17,73 @@ import Foundation
extension KeyHandler { extension KeyHandler {
// MARK: - State Building // MARK: - State Building
/// ///
var buildInputtingState: InputState.Inputting { var buildInputtingState: IMEState {
/// (Update the composing buffer) /// (Update the composing buffer)
/// NSAttributeString /// NSAttributeString
var tooltipParameterRef: [String] = ["", ""] var displayTextSegments: [String] = compositor.walkedNodes.values.map {
let nodeValuesArray: [String] = compositor.walkedNodes.values.map {
guard let delegate = delegate, delegate.isVerticalTyping else { return $0 } guard let delegate = delegate, delegate.isVerticalTyping else { return $0 }
guard mgrPrefs.hardenVerticalPunctuations else { return $0 } guard mgrPrefs.hardenVerticalPunctuations else { return $0 }
var neta = $0 var neta = $0
ChineseConverter.hardenVerticalPunctuations(target: &neta, convert: delegate.isVerticalTyping) ChineseConverter.hardenVerticalPunctuations(target: &neta, convert: delegate.isVerticalTyping)
return neta return neta
} }
var cursor = convertCursorForDisplay(compositor.cursor)
let reading = composer.getInlineCompositionForDisplay(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
if !reading.isEmpty {
var newDisplayTextSegments = [String]()
var temporaryNode = ""
var charCounter = 0
for node in displayTextSegments {
for char in node {
if charCounter == cursor {
newDisplayTextSegments.append(temporaryNode)
temporaryNode = ""
newDisplayTextSegments.append(reading)
}
temporaryNode += String(char)
charCounter += 1
}
newDisplayTextSegments.append(temporaryNode)
temporaryNode = ""
}
if newDisplayTextSegments == displayTextSegments { newDisplayTextSegments.append(reading) }
displayTextSegments = newDisplayTextSegments
cursor += reading.count
}
/// 使
return IMEState.ofInputting(displayTextSegments: displayTextSegments, cursor: cursor)
}
///
func convertCursorForDisplay(_ rawCursor: Int) -> Int {
var composedStringCursorIndex = 0 var composedStringCursorIndex = 0
var readingCursorIndex = 0 var readingCursorIndex = 0
/// IMK UTF8 emoji
/// Swift.utf16NSString.length()
///
for theNode in compositor.walkedNodes { for theNode in compositor.walkedNodes {
let strNodeValue = theNode.value let strNodeValue = theNode.value
let arrSplit: [String] = Array(strNodeValue).charComponents
let codepointCount = arrSplit.count
/// ///
/// NodeAnchorspanningLength /// NodeAnchorspanningLength
/// ///
let spanningLength: Int = theNode.spanLength let spanningLength: Int = theNode.keyArray.count
if readingCursorIndex + spanningLength <= compositor.cursor { if readingCursorIndex + spanningLength <= rawCursor {
composedStringCursorIndex += strNodeValue.utf16.count composedStringCursorIndex += strNodeValue.count
readingCursorIndex += spanningLength readingCursorIndex += spanningLength
continue continue
} }
if codepointCount == spanningLength { if !theNode.isReadingMismatched {
for i in 0..<codepointCount { for _ in 0..<strNodeValue.count {
guard readingCursorIndex < compositor.cursor else { continue } guard readingCursorIndex < rawCursor else { continue }
composedStringCursorIndex += arrSplit[i].utf16.count composedStringCursorIndex += 1
readingCursorIndex += 1 readingCursorIndex += 1
} }
continue continue
} }
guard readingCursorIndex < compositor.cursor else { continue } guard readingCursorIndex < rawCursor else { continue }
composedStringCursorIndex += strNodeValue.utf16.count composedStringCursorIndex += strNodeValue.count
readingCursorIndex += spanningLength readingCursorIndex += spanningLength
readingCursorIndex = min(readingCursorIndex, compositor.cursor) readingCursorIndex = min(readingCursorIndex, rawCursor)
///
///
///
///
///
switch compositor.cursor {
case compositor.keys.count...:
// compositor.cursor readings.count Megrez
tooltipParameterRef[0] = compositor.keys[compositor.cursor - 1]
case 0:
tooltipParameterRef[1] = compositor.keys[compositor.cursor]
default:
tooltipParameterRef[0] = compositor.keys[compositor.cursor - 1]
tooltipParameterRef[1] = compositor.keys[compositor.cursor]
} }
} return composedStringCursorIndex
isCursorCuttingChar = !tooltipParameterRef[0].isEmpty || !tooltipParameterRef[1].isEmpty
///
/// 便 composer
var arrHead = [String.UTF16View.Element]()
var arrTail = [String.UTF16View.Element]()
for (i, n) in nodeValuesArray.joined().utf16.enumerated() {
if i < composedStringCursorIndex {
arrHead.append(n)
} else {
arrTail.append(n)
}
}
/// stringview
///
let head = String(utf16CodeUnits: arrHead, count: arrHead.count)
let reading = composer.getInlineCompositionForDisplay(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
let tail = String(utf16CodeUnits: arrTail, count: arrTail.count)
let composedText = head + reading + tail
let cursorIndex = composedStringCursorIndex + reading.utf16.count
//
var cleanedComposition = ""
for theChar in composedText {
guard let charCode = theChar.utf16.first else { continue }
if !(theChar.isASCII && !(charCode.isPrintable)) {
cleanedComposition += String(theChar)
}
}
/// 使
return InputState.Inputting(
composingBuffer: cleanedComposition, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
} }
// MARK: - // MARK: -
@ -122,15 +94,13 @@ extension KeyHandler {
/// - isTypingVertical: /// - isTypingVertical:
/// - Returns: /// - Returns:
func buildCandidate( func buildCandidate(
state currentState: InputState.NotEmpty, state currentState: IMEStateProtocol,
isTypingVertical: Bool = false isTypingVertical _: Bool = false
) -> InputState.ChoosingCandidate { ) -> IMEState {
InputState.ChoosingCandidate( IMEState.ofCandidates(
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
candidates: getCandidatesArray(fixOrder: mgrPrefs.useFixecCandidateOrderOnSelection), candidates: getCandidatesArray(fixOrder: mgrPrefs.useFixecCandidateOrderOnSelection),
isTypingVertical: isTypingVertical, displayTextSegments: compositor.walkedNodes.values,
nodeValuesArray: compositor.walkedNodes.values cursor: currentState.data.cursor
) )
} }
@ -147,16 +117,13 @@ extension KeyHandler {
/// ///
/// - Parameters: /// - Parameters:
/// - key: /// - key:
/// - isTypingVertical:
/// - Returns: /// - Returns:
func buildAssociatePhraseState( func buildAssociatePhraseState(
withPair pair: Megrez.Compositor.Candidate, withPair pair: Megrez.Compositor.KeyValuePaired
isTypingVertical: Bool ) -> IMEState {
) -> InputState.AssociatedPhrases! {
//  Xcode //  Xcode
InputState.AssociatedPhrases( IMEState.ofAssociates(
candidates: buildAssociatePhraseArray(withPair: pair), isTypingVertical: isTypingVertical candidates: buildAssociatePhraseArray(withPair: pair))
)
} }
// MARK: - // MARK: -
@ -169,9 +136,9 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleMarkingState( func handleMarkingState(
_ state: InputState.Marking, _ state: IMEStateProtocol,
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
if input.isEsc { if input.isEsc {
@ -190,7 +157,7 @@ extension KeyHandler {
if input.isEnter { if input.isEnter {
if let keyHandlerDelegate = delegate { if let keyHandlerDelegate = delegate {
// //
if input.isShiftHold, input.isCommandHold, !state.validToFilter { if input.isShiftHold, input.isCommandHold, !state.isFilterable {
IME.prtDebugIntel("2EAC1F7A") IME.prtDebugIntel("2EAC1F7A")
errorCallback() errorCallback()
return true return true
@ -207,7 +174,7 @@ extension KeyHandler {
// BackSpace & Delete // BackSpace & Delete
if input.isBackSpace || input.isDelete { if input.isBackSpace || input.isDelete {
if let keyHandlerDelegate = delegate { if let keyHandlerDelegate = delegate {
if !state.validToFilter { if !state.isFilterable {
IME.prtDebugIntel("1F88B191") IME.prtDebugIntel("1F88B191")
errorCallback() errorCallback()
return true return true
@ -224,18 +191,19 @@ extension KeyHandler {
// Shift + Left // Shift + Left
if input.isCursorBackward || input.emacsKey == EmacsKey.backward, input.isShiftHold { if input.isCursorBackward || input.emacsKey == EmacsKey.backward, input.isShiftHold {
var index = state.markerIndex if compositor.marker > 0 {
if index > 0 { compositor.marker -= 1
index = state.composingBuffer.utf16PreviousPosition(for: index) if isCursorCuttingChar(isMarker: true) {
let marking = InputState.Marking( compositor.jumpCursorBySpan(to: .rear, isMarker: true)
composingBuffer: state.composingBuffer, }
cursorIndex: state.cursorIndex, var marking = IMEState.ofMarking(
markerIndex: index, displayTextSegments: state.data.displayTextSegments,
readings: state.readings, markedReadings: Array(compositor.keys[currentMarkedRange()]),
nodeValuesArray: compositor.walkedNodes.values cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
) )
marking.tooltipForInputting = state.tooltipForInputting marking.data.tooltipBackupForInputting = state.data.tooltipBackupForInputting
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking) stateCallback(marking.data.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else { } else {
IME.prtDebugIntel("1149908D") IME.prtDebugIntel("1149908D")
errorCallback() errorCallback()
@ -246,18 +214,19 @@ extension KeyHandler {
// Shift + Right // Shift + Right
if input.isCursorForward || input.emacsKey == EmacsKey.forward, input.isShiftHold { if input.isCursorForward || input.emacsKey == EmacsKey.forward, input.isShiftHold {
var index = state.markerIndex if compositor.marker < compositor.width {
if index < (state.composingBuffer.utf16.count) { compositor.marker += 1
index = state.composingBuffer.utf16NextPosition(for: index) if isCursorCuttingChar(isMarker: true) {
let marking = InputState.Marking( compositor.jumpCursorBySpan(to: .front, isMarker: true)
composingBuffer: state.composingBuffer, }
cursorIndex: state.cursorIndex, var marking = IMEState.ofMarking(
markerIndex: index, displayTextSegments: state.data.displayTextSegments,
readings: state.readings, markedReadings: Array(compositor.keys[currentMarkedRange()]),
nodeValuesArray: compositor.walkedNodes.values cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
) )
marking.tooltipForInputting = state.tooltipForInputting marking.data.tooltipBackupForInputting = state.data.tooltipBackupForInputting
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking) stateCallback(marking.data.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else { } else {
IME.prtDebugIntel("9B51408D") IME.prtDebugIntel("9B51408D")
errorCallback() errorCallback()
@ -280,9 +249,9 @@ extension KeyHandler {
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handlePunctuation( func handlePunctuation(
_ customPunctuation: String, _ customPunctuation: String,
state: InputStateProtocol, state: IMEStateProtocol,
usingVerticalTyping isTypingVertical: Bool, usingVerticalTyping isTypingVertical: Bool,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
if !currentLM.hasUnigramsFor(key: customPunctuation) { if !currentLM.hasUnigramsFor(key: customPunctuation) {
@ -312,8 +281,8 @@ extension KeyHandler {
if candidateState.candidates.count == 1 { if candidateState.candidates.count == 1 {
clear() // candidateState clear() // candidateState
if let candidateToCommit: (String, String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty { if let candidateToCommit: (String, String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty {
stateCallback(InputState.Committing(textToCommit: candidateToCommit.1)) stateCallback(IMEState.ofCommitting(textToCommit: candidateToCommit.1))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
} else { } else {
stateCallback(candidateState) stateCallback(candidateState)
} }
@ -331,13 +300,13 @@ extension KeyHandler {
/// - stateCallback: /// - stateCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleEnter( func handleEnter(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool { ) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
stateCallback(InputState.Committing(textToCommit: currentState.composingBuffer)) stateCallback(IMEState.ofCommitting(textToCommit: state.displayedText))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
@ -349,23 +318,23 @@ extension KeyHandler {
/// - stateCallback: /// - stateCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleCtrlCommandEnter( func handleCtrlCommandEnter(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
var composingBuffer = compositor.keys.joined(separator: "-") var displayedText = compositor.keys.joined(separator: "-")
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin { if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
composingBuffer = Tekkon.restoreToneOneInZhuyinKey(target: composingBuffer) // displayedText = Tekkon.restoreToneOneInZhuyinKey(target: displayedText) //
composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) // displayedText = Tekkon.cnvPhonaToHanyuPinyin(target: displayedText) //
} }
if let delegate = delegate, !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") { if let delegate = delegate, !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") {
composingBuffer = composingBuffer.replacingOccurrences(of: "-", with: " ") displayedText = displayedText.replacingOccurrences(of: "-", with: " ")
} }
stateCallback(InputState.Committing(textToCommit: composingBuffer)) stateCallback(IMEState.ofCommitting(textToCommit: displayedText))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
@ -377,10 +346,10 @@ extension KeyHandler {
/// - stateCallback: /// - stateCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleCtrlOptionCommandEnter( func handleCtrlOptionCommandEnter(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
var composed = "" var composed = ""
@ -400,8 +369,8 @@ extension KeyHandler {
composed += key.contains("_") ? value : "<ruby>\(value)<rp>(</rp><rt>\(key)</rt><rp>)</rp></ruby>" composed += key.contains("_") ? value : "<ruby>\(value)<rp>(</rp><rt>\(key)</rt><rp>)</rp></ruby>"
} }
stateCallback(InputState.Committing(textToCommit: composed)) stateCallback(IMEState.ofCommitting(textToCommit: composed))
stateCallback(InputState.Empty()) stateCallback(IMEState.ofEmpty())
return true return true
} }
@ -415,12 +384,12 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleBackSpace( func handleBackSpace(
state: InputStateProtocol, state: IMEStateProtocol,
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
// macOS Shift+BackSpace // macOS Shift+BackSpace
switch mgrPrefs.specifyShiftBackSpaceKeyBehavior { switch mgrPrefs.specifyShiftBackSpaceKeyBehavior {
@ -434,15 +403,13 @@ extension KeyHandler {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
return true return true
case 1: case 1:
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
return true return true
default: break default: break
} }
if input.isShiftHold, input.isOptionHold { if input.isShiftHold, input.isOptionHold {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
return true return true
} }
@ -465,8 +432,7 @@ extension KeyHandler {
switch composer.isEmpty && compositor.isEmpty { switch composer.isEmpty && compositor.isEmpty {
case false: stateCallback(buildInputtingState) case false: stateCallback(buildInputtingState)
case true: case true:
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
} }
return true return true
} }
@ -481,16 +447,15 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleDelete( func handleDelete(
state: InputStateProtocol, state: IMEStateProtocol,
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if input.isShiftHold { if input.isShiftHold {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
return true return true
} }
@ -510,11 +475,10 @@ extension KeyHandler {
let inputting = buildInputtingState let inputting = buildInputtingState
// count > 0!isEmpty滿 // count > 0!isEmpty滿
switch inputting.composingBuffer.isEmpty { switch inputting.displayedText.isEmpty {
case false: stateCallback(inputting) case false: stateCallback(inputting)
case true: case true:
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
} }
return true return true
} }
@ -528,11 +492,11 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleClockKey( func handleClockKey(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if !composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("9B6F908D") IME.prtDebugIntel("9B6F908D")
errorCallback() errorCallback()
@ -550,11 +514,11 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleHome( func handleHome(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if !composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("ABC44080") IME.prtDebugIntel("ABC44080")
@ -584,11 +548,11 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleEnd( func handleEnd(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if !composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("9B69908D") IME.prtDebugIntel("9B69908D")
@ -617,16 +581,15 @@ extension KeyHandler {
/// - stateCallback: /// - stateCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleEsc( func handleEsc(
state: InputStateProtocol, state: IMEStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if mgrPrefs.escToCleanInputBuffer { if mgrPrefs.escToCleanInputBuffer {
/// ///
/// macOS Windows 使 /// macOS Windows 使
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
} else { } else {
if composer.isEmpty { return true } if composer.isEmpty { return true }
/// ///
@ -634,8 +597,7 @@ extension KeyHandler {
switch compositor.isEmpty { switch compositor.isEmpty {
case false: stateCallback(buildInputtingState) case false: stateCallback(buildInputtingState)
case true: case true:
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(IMEState.ofAbortion())
stateCallback(InputState.Empty())
} }
} }
return true return true
@ -651,12 +613,12 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleForward( func handleForward(
state: InputStateProtocol, state: IMEStateProtocol,
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if !composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("B3BA5257") IME.prtDebugIntel("B3BA5257")
@ -667,16 +629,18 @@ extension KeyHandler {
if input.isShiftHold { if input.isShiftHold {
// Shift + Right // Shift + Right
if currentState.cursorIndex < currentState.composingBuffer.utf16.count { if compositor.cursor < compositor.width {
let nextPosition = currentState.composingBuffer.utf16NextPosition( compositor.marker = compositor.cursor + 1
for: currentState.cursorIndex) if isCursorCuttingChar(isMarker: true) {
let marking: InputState.Marking! = InputState.Marking( compositor.jumpCursorBySpan(to: .front, isMarker: true)
composingBuffer: currentState.composingBuffer, }
cursorIndex: currentState.cursorIndex, var marking = IMEState.ofMarking(
markerIndex: nextPosition, displayTextSegments: compositor.walkedNodes.values,
readings: compositor.keys markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
) )
marking.tooltipForInputting = currentState.tooltip marking.data.tooltipBackupForInputting = state.tooltip
stateCallback(marking) stateCallback(marking)
} else { } else {
IME.prtDebugIntel("BB7F6DB9") IME.prtDebugIntel("BB7F6DB9")
@ -684,7 +648,6 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
} else if input.isOptionHold { } else if input.isOptionHold {
isCursorCuttingChar = false
if input.isControlHold { if input.isControlHold {
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback) return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
@ -699,12 +662,10 @@ extension KeyHandler {
} else { } else {
if compositor.cursor < compositor.length { if compositor.cursor < compositor.length {
compositor.cursor += 1 compositor.cursor += 1
var inputtingState = buildInputtingState if isCursorCuttingChar() {
if isCursorCuttingChar == true {
compositor.jumpCursorBySpan(to: .front) compositor.jumpCursorBySpan(to: .front)
inputtingState = buildInputtingState
} }
stateCallback(inputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("A96AAD58") IME.prtDebugIntel("A96AAD58")
errorCallback() errorCallback()
@ -725,12 +686,12 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleBackward( func handleBackward(
state: InputStateProtocol, state: IMEStateProtocol,
input: InputSignalProtocol, input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false } guard state.type == .ofInputting else { return false }
if !composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("6ED95318") IME.prtDebugIntel("6ED95318")
@ -741,16 +702,18 @@ extension KeyHandler {
if input.isShiftHold { if input.isShiftHold {
// Shift + left // Shift + left
if currentState.cursorIndex > 0 { if compositor.cursor > 0 {
let previousPosition = currentState.composingBuffer.utf16PreviousPosition( compositor.marker = compositor.cursor - 1
for: currentState.cursorIndex) if isCursorCuttingChar(isMarker: true) {
let marking: InputState.Marking! = InputState.Marking( compositor.jumpCursorBySpan(to: .rear, isMarker: true)
composingBuffer: currentState.composingBuffer, }
cursorIndex: currentState.cursorIndex, var marking = IMEState.ofMarking(
markerIndex: previousPosition, displayTextSegments: compositor.walkedNodes.values,
readings: compositor.keys markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
) )
marking.tooltipForInputting = currentState.tooltip marking.data.tooltipBackupForInputting = state.tooltip
stateCallback(marking) stateCallback(marking)
} else { } else {
IME.prtDebugIntel("D326DEA3") IME.prtDebugIntel("D326DEA3")
@ -758,7 +721,6 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
} else if input.isOptionHold { } else if input.isOptionHold {
isCursorCuttingChar = false
if input.isControlHold { if input.isControlHold {
return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback) return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
@ -773,12 +735,10 @@ extension KeyHandler {
} else { } else {
if compositor.cursor > 0 { if compositor.cursor > 0 {
compositor.cursor -= 1 compositor.cursor -= 1
var inputtingState = buildInputtingState if isCursorCuttingChar() {
if isCursorCuttingChar == true {
compositor.jumpCursorBySpan(to: .rear) compositor.jumpCursorBySpan(to: .rear)
inputtingState = buildInputtingState
} }
stateCallback(inputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("7045E6F3") IME.prtDebugIntel("7045E6F3")
errorCallback() errorCallback()
@ -799,14 +759,14 @@ extension KeyHandler {
/// - errorCallback: /// - errorCallback:
/// - Returns: ctlInputMethod IMK /// - Returns: ctlInputMethod IMK
func handleInlineCandidateRotation( func handleInlineCandidateRotation(
state: InputStateProtocol, state: IMEStateProtocol,
reverseModifier: Bool, reverseModifier: Bool,
stateCallback: @escaping (InputStateProtocol) -> Void, stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
if composer.isEmpty, compositor.isEmpty || compositor.walkedNodes.isEmpty { return false } if composer.isEmpty, compositor.isEmpty || compositor.walkedNodes.isEmpty { return false }
guard state is InputState.Inputting else { guard state.type == .ofInputting else {
guard state is InputState.Empty else { guard state.type == .ofEmpty else {
IME.prtDebugIntel("6044F081") IME.prtDebugIntel("6044F081")
errorCallback() errorCallback()
return true return true

View File

@ -8,7 +8,36 @@
import Cocoa import Cocoa
// MARK: - NSEvent Extension // MARK: - NSEvent Extension - Reconstructors
extension NSEvent {
public func reinitiate(
with type: NSEvent.EventType? = nil,
location: NSPoint? = nil,
modifierFlags: NSEvent.ModifierFlags? = nil,
timestamp: TimeInterval? = nil,
windowNumber: Int? = nil,
characters: String? = nil,
charactersIgnoringModifiers: String? = nil,
isARepeat: Bool? = nil,
keyCode: UInt16? = nil
) -> NSEvent? {
NSEvent.keyEvent(
with: type ?? self.type,
location: location ?? locationInWindow,
modifierFlags: modifierFlags ?? self.modifierFlags,
timestamp: timestamp ?? self.timestamp,
windowNumber: windowNumber ?? self.windowNumber,
context: nil,
characters: characters ?? self.characters ?? "",
charactersIgnoringModifiers: charactersIgnoringModifiers ?? self.characters ?? "",
isARepeat: isARepeat ?? self.isARepeat,
keyCode: keyCode ?? self.keyCode
)
}
}
// MARK: - NSEvent Extension - InputSignalProtocol
extension NSEvent: InputSignalProtocol { extension NSEvent: InputSignalProtocol {
public var isASCIIModeInput: Bool { ctlInputMethod.isASCIIModeSituation } public var isASCIIModeInput: Bool { ctlInputMethod.isASCIIModeSituation }

View File

@ -1,62 +0,0 @@
// (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).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Foundation
/// Shiki's Notes: The cursor index in the IMK inline composition buffer
/// still uses UTF16 index measurements. This means that any attempt of
/// using Swift native UTF8 handlings to replace Zonble's NSString (or
/// .utf16) handlings below will still result in unavoidable necessities
/// of solving the UTF16->UTF8 conversions in another approach. Therefore,
/// I strongly advise against any attempt of such until the day that IMK is
/// capable of handling the cursor index in its inline composition buffer using
/// UTF8 measurements.
extension String {
/// Converts the index in an NSString or .utf16 to the index in a Swift string.
///
/// An Emoji might be compose by more than one UTF-16 code points. However,
/// the length of an NSString is only the sum of the UTF-16 code points. It
/// causes that the NSString and Swift string representation of the same
/// string have different lengths once the string contains such Emoji. The
/// method helps to find the index in a Swift string by passing the index
/// in an NSString (or .utf16).
public func charIndexLiteral(from utf16Index: Int) -> Int {
var length = 0
for (i, character) in enumerated() {
length += character.utf16.count
if length > utf16Index {
return (i)
}
}
return count
}
public func utf16NextPosition(for index: Int) -> Int {
let fixedIndex = min(charIndexLiteral(from: index) + 1, count)
return self[..<self.index(startIndex, offsetBy: fixedIndex)].utf16.count
}
public func utf16PreviousPosition(for index: Int) -> Int {
let fixedIndex = max(charIndexLiteral(from: index) - 1, 0)
return self[..<self.index(startIndex, offsetBy: fixedIndex)].utf16.count
}
internal func utf16SubString(with r: Range<Int>) -> String {
let arr = Array(utf16)[r].map { $0 }
return String(utf16CodeUnits: arr, count: arr.count)
}
public var charComponents: [String] { map { String($0) } }
}
extension Array where Element == String.Element {
public var charComponents: [String] { map { String($0) } }
}

View File

@ -31,11 +31,9 @@ extension ctlInputMethod {
if !shouldUseHandle || (!rencentKeyHandledByKeyHandler && shouldUseHandle) { if !shouldUseHandle || (!rencentKeyHandledByKeyHandler && shouldUseHandle) {
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n" message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
+ { + (toggleASCIIMode()
toggleASCIIMode()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
if shouldUseHandle { if shouldUseHandle {

View File

@ -32,16 +32,20 @@ class ctlInputMethod: IMKInputController {
// MARK: - // MARK: -
/// 調
var keyHandler: KeyHandler = .init()
///
var state: InputStateProtocol = InputState.Empty()
/// ctlInputMethod
var isASCIIMode: Bool = false
/// ctlInputMethod /// ctlInputMethod
static var isASCIIModeSituation: Bool = false static var isASCIIModeSituation: Bool = false
/// ctlInputMethod /// ctlInputMethod
static var isVerticalTypingSituation: Bool = false static var isVerticalTypingSituation: Bool = false
/// ctlInputMethod
var isASCIIMode: Bool = false
/// 調
var keyHandler: KeyHandler = .init()
///
var state: IMEStateProtocol = IMEState.ofEmpty() {
didSet {
IME.prtDebugIntel("Current State: \(state.type.rawValue)")
}
}
/// ctlInputMethod /// ctlInputMethod
func toggleASCIIMode() -> Bool { func toggleASCIIMode() -> Bool {
@ -65,15 +69,15 @@ class ctlInputMethod: IMKInputController {
/// 調 /// 調
func resetKeyHandler() { func resetKeyHandler() {
// //
if state is InputState.Inputting, mgrPrefs.trimUnfinishedReadingsOnCommit { if state.type == .ofInputting, mgrPrefs.trimUnfinishedReadingsOnCommit {
keyHandler.composer.clear() keyHandler.composer.clear()
handle(state: keyHandler.buildInputtingState) handle(state: keyHandler.buildInputtingState)
} }
if let state = state as? InputState.NotEmpty { if state.hasComposition {
/// 調 /// 調
handle(state: InputState.Committing(textToCommit: state.composingBufferConverted)) handle(state: IMEState.ofCommitting(textToCommit: state.displayedText))
} }
handle(state: InputState.Empty()) handle(state: IMEState.ofEmpty())
} }
// MARK: - IMKInputController // MARK: - IMKInputController
@ -115,11 +119,9 @@ class ctlInputMethod: IMKInputController {
} else { } else {
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n" message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
+ { + (isASCIIMode
isASCIIMode
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
} }
@ -129,7 +131,7 @@ class ctlInputMethod: IMKInputController {
if let client = client(), client.bundleIdentifier() != Bundle.main.bundleIdentifier { if let client = client(), client.bundleIdentifier() != Bundle.main.bundleIdentifier {
// 使 // 使
setKeyLayout() setKeyLayout()
handle(state: InputState.Empty()) handle(state: IMEState.ofEmpty())
} // } //
(NSApp.delegate as? AppDelegate)?.checkForUpdate() (NSApp.delegate as? AppDelegate)?.checkForUpdate()
} }
@ -139,7 +141,7 @@ class ctlInputMethod: IMKInputController {
override func deactivateServer(_ sender: Any!) { override func deactivateServer(_ sender: Any!) {
_ = sender // _ = sender //
resetKeyHandler() // Empty resetKeyHandler() // Empty
handle(state: InputState.Deactivated()) handle(state: IMEState.ofDeactivated())
} }
/// ///
@ -170,7 +172,7 @@ class ctlInputMethod: IMKInputController {
if let client = client(), client.bundleIdentifier() != Bundle.main.bundleIdentifier { if let client = client(), client.bundleIdentifier() != Bundle.main.bundleIdentifier {
// 使 // 使
setKeyLayout() setKeyLayout()
handle(state: InputState.Empty()) handle(state: IMEState.ofEmpty())
} // } //
} }
@ -224,20 +226,7 @@ class ctlInputMethod: IMKInputController {
// Shift+Enter delegate keyHandler // Shift+Enter delegate keyHandler
// Shift Flags // Shift Flags
if event.isShiftHold, event.isEnter { if event.isShiftHold, event.isEnter {
guard guard let newEvent = event.reinitiate(modifierFlags: []) else {
let newEvent = NSEvent.keyEvent(
with: event.type,
location: event.locationInWindow,
modifierFlags: [],
timestamp: event.timestamp,
windowNumber: event.windowNumber,
context: nil,
characters: event.characters ?? "",
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? event.characters ?? "",
isARepeat: event.isARepeat,
keyCode: event.keyCode
)
else {
NSSound.beep() NSSound.beep()
return true return true
} }
@ -247,20 +236,8 @@ class ctlInputMethod: IMKInputController {
// //
if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode], event.isShiftHold, if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode], event.isShiftHold,
isAssociatedPhrasesState isAssociatedPhrasesState, let newEvent = event.reinitiate(modifierFlags: [], characters: newChar)
{ {
let newEvent = NSEvent.keyEvent(
with: event.type,
location: event.locationInWindow,
modifierFlags: [],
timestamp: event.timestamp,
windowNumber: event.windowNumber,
context: nil,
characters: newChar,
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? event.characters ?? "",
isARepeat: event.isARepeat,
keyCode: event.keyCode
)
ctlCandidateCurrent.handleKeyboardEvent(newEvent) ctlCandidateCurrent.handleKeyboardEvent(newEvent)
} }
@ -288,8 +265,8 @@ class ctlInputMethod: IMKInputController {
/// - Returns: nil /// - Returns: nil
override func composedString(_ sender: Any!) -> Any! { override func composedString(_ sender: Any!) -> Any! {
_ = sender // _ = sender //
guard let state = state as? InputState.NotEmpty else { return "" } guard state.hasComposition else { return "" }
return state.committingBufferConverted return state.displayedText
} }
/// ///
@ -309,7 +286,7 @@ class ctlInputMethod: IMKInputController {
_ = sender // _ = sender //
var arrResult = [String]() var arrResult = [String]()
// 便 InputState // 便 IMEState
func handleCandidatesPrepared(_ candidates: [(String, String)], prefix: String = "") { func handleCandidatesPrepared(_ candidates: [(String, String)], prefix: String = "") {
for theCandidate in candidates { for theCandidate in candidates {
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1) let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
@ -325,12 +302,12 @@ class ctlInputMethod: IMKInputController {
} }
} }
if let state = state as? InputState.AssociatedPhrases { if state.type == .ofAssociates {
handleCandidatesPrepared(state.candidates, prefix: "") handleCandidatesPrepared(state.candidates, prefix: "")
} else if let state = state as? InputState.SymbolTable { } else if state.type == .ofSymbolTable {
// / JIS 使 // / JIS 使
arrResult = state.candidates.map(\.1) arrResult = state.candidates.map(\.1)
} else if let state = state as? InputState.ChoosingCandidate { } else if state.type == .ofCandidates {
guard !state.candidates.isEmpty else { return .init() } guard !state.candidates.isEmpty else { return .init() }
if state.candidates[0].0.contains("_punctuation") { if state.candidates[0].0.contains("_punctuation") {
arrResult = state.candidates.map(\.1) // arrResult = state.candidates.map(\.1) //
@ -363,17 +340,16 @@ class ctlInputMethod: IMKInputController {
/// - Parameter candidateString: /// - Parameter candidateString:
override open func candidateSelected(_ candidateString: NSAttributedString!) { override open func candidateSelected(_ candidateString: NSAttributedString!) {
let candidateString: NSAttributedString = candidateString ?? .init(string: "") let candidateString: NSAttributedString = candidateString ?? .init(string: "")
if state is InputState.AssociatedPhrases { if state.type == .ofAssociates {
if !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter { if !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
handle(state: InputState.EmptyIgnoringPreviousState()) handle(state: IMEState.ofAbortion())
handle(state: InputState.Empty())
return return
} }
} }
var indexDeducted = 0 var indexDeducted = 0
// 便 InputState // 便 IMEState
func handleCandidatesSelected(_ candidates: [(String, String)], prefix: String = "") { func handleCandidatesSelected(_ candidates: [(String, String)], prefix: String = "") {
for (i, neta) in candidates.enumerated() { for (i, neta) in candidates.enumerated() {
let theConverted = IME.kanjiConversionIfRequired(neta.1) let theConverted = IME.kanjiConversionIfRequired(neta.1)
@ -403,11 +379,11 @@ class ctlInputMethod: IMKInputController {
} }
} }
if let state = state as? InputState.AssociatedPhrases { if state.type == .ofAssociates {
handleCandidatesSelected(state.candidates, prefix: "") handleCandidatesSelected(state.candidates, prefix: "")
} else if let state = state as? InputState.SymbolTable { } else if state.type == .ofSymbolTable {
handleSymbolCandidatesSelected(state.candidates) handleSymbolCandidatesSelected(state.candidates)
} else if let state = state as? InputState.ChoosingCandidate { } else if state.type == .ofCandidates {
guard !state.candidates.isEmpty else { return } guard !state.candidates.isEmpty else { return }
if state.candidates[0].0.contains("_punctuation") { if state.candidates[0].0.contains("_punctuation") {
handleSymbolCandidatesSelected(state.candidates) // handleSymbolCandidatesSelected(state.candidates) //

View File

@ -37,21 +37,20 @@ extension ctlInputMethod: KeyHandlerDelegate {
ctlCandidate(controller, didSelectCandidateAtIndex: index) ctlCandidate(controller, didSelectCandidateAtIndex: index)
} }
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol, addToFilter: Bool) func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: IMEStateProtocol, addToFilter: Bool)
-> Bool -> Bool
{ {
guard let state = state as? InputState.Marking else { return false } guard state.type == .ofMarking else { return false }
if state.bufferReadingCountMisMatch { return false }
let refInputModeReversed: InputMode = let refInputModeReversed: InputMode =
(keyHandler.inputMode == InputMode.imeModeCHT) (keyHandler.inputMode == InputMode.imeModeCHT)
? InputMode.imeModeCHS : InputMode.imeModeCHT ? InputMode.imeModeCHS : InputMode.imeModeCHT
if !mgrLangModel.writeUserPhrase( if !mgrLangModel.writeUserPhrase(
state.userPhrase, inputMode: keyHandler.inputMode, state.data.userPhrase, inputMode: keyHandler.inputMode,
areWeDuplicating: state.chkIfUserPhraseExists, areWeDuplicating: state.data.chkIfUserPhraseExists,
areWeDeleting: addToFilter areWeDeleting: addToFilter
) )
|| !mgrLangModel.writeUserPhrase( || !mgrLangModel.writeUserPhrase(
state.userPhraseConverted, inputMode: refInputModeReversed, state.data.userPhraseConverted, inputMode: refInputModeReversed,
areWeDuplicating: false, areWeDuplicating: false,
areWeDeleting: addToFilter areWeDeleting: addToFilter
) )
@ -65,7 +64,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
// MARK: - Candidate Controller Delegate // MARK: - Candidate Controller Delegate
extension ctlInputMethod: ctlCandidateDelegate { extension ctlInputMethod: ctlCandidateDelegate {
var isAssociatedPhrasesState: Bool { state is InputState.AssociatedPhrases } var isAssociatedPhrasesState: Bool { state.type == .ofAssociates }
/// handle() IMK /// handle() IMK
/// handle() /// handle()
@ -78,9 +77,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int { func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
_ = controller // _ = controller //
if let state = state as? InputState.ChoosingCandidate { if state.isCandidateContainer {
return state.candidates.count
} else if let state = state as? InputState.AssociatedPhrases {
return state.candidates.count return state.candidates.count
} }
return 0 return 0
@ -91,9 +88,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
/// - Returns: /// - Returns:
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] { func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] {
_ = controller // _ = controller //
if let state = state as? InputState.ChoosingCandidate { if state.isCandidateContainer {
return state.candidates
} else if let state = state as? InputState.AssociatedPhrases {
return state.candidates return state.candidates
} }
return .init() return .init()
@ -103,9 +98,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
-> (String, String) -> (String, String)
{ {
_ = controller // _ = controller //
if let state = state as? InputState.ChoosingCandidate { if state.isCandidateContainer {
return state.candidates[index]
} else if let state = state as? InputState.AssociatedPhrases {
return state.candidates[index] return state.candidates[index]
} }
return ("", "") return ("", "")
@ -114,22 +107,20 @@ extension ctlInputMethod: ctlCandidateDelegate {
func ctlCandidate(_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int) { func ctlCandidate(_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int) {
_ = controller // _ = controller //
if let state = state as? InputState.SymbolTable, if state.type == .ofSymbolTable,
let node = state.node.children?[index] let node = state.node.children?[index]
{ {
if let children = node.children, !children.isEmpty { if let children = node.children, !children.isEmpty {
handle(state: InputState.Empty()) // handle(state: IMEState.ofEmpty()) //
handle( handle(state: IMEState.ofSymbolTable(node: node))
state: InputState.SymbolTable(node: node, previous: state.node, isTypingVertical: state.isTypingVertical)
)
} else { } else {
handle(state: InputState.Committing(textToCommit: node.title)) handle(state: IMEState.ofCommitting(textToCommit: node.title))
handle(state: InputState.Empty()) handle(state: IMEState.ofEmpty())
} }
return return
} }
if let state = state as? InputState.ChoosingCandidate { if [.ofCandidates, .ofSymbolTable].contains(state.type) {
let selectedValue = state.candidates[index] let selectedValue = state.candidates[index]
keyHandler.fixNode( keyHandler.fixNode(
candidate: selectedValue, respectCursorPushing: true, candidate: selectedValue, respectCursorPushing: true,
@ -139,17 +130,15 @@ extension ctlInputMethod: ctlCandidateDelegate {
let inputting = keyHandler.buildInputtingState let inputting = keyHandler.buildInputtingState
if mgrPrefs.useSCPCTypingMode { if mgrPrefs.useSCPCTypingMode {
handle(state: InputState.Committing(textToCommit: inputting.composingBufferConverted)) handle(state: IMEState.ofCommitting(textToCommit: inputting.displayedText))
// selectedValue.1 // selectedValue.1
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled {
let associatePhrases = keyHandler.buildAssociatePhraseState( let associates = keyHandler.buildAssociatePhraseState(
withPair: .init(key: selectedValue.0, value: selectedValue.1), withPair: .init(key: selectedValue.0, value: selectedValue.1)
isTypingVertical: state.isTypingVertical )
), !associatePhrases.candidates.isEmpty handle(state: associates.candidates.isEmpty ? IMEState.ofEmpty() : associates)
{
handle(state: associatePhrases)
} else { } else {
handle(state: InputState.Empty()) handle(state: IMEState.ofEmpty())
} }
} else { } else {
handle(state: inputting) handle(state: inputting)
@ -157,25 +146,25 @@ extension ctlInputMethod: ctlCandidateDelegate {
return return
} }
if let state = state as? InputState.AssociatedPhrases { if state.type == .ofAssociates {
let selectedValue = state.candidates[index] let selectedValue = state.candidates[index]
handle(state: InputState.Committing(textToCommit: selectedValue.1)) handle(state: IMEState.ofCommitting(textToCommit: selectedValue.1))
// selectedValue.1 // selectedValue.1
// //
guard let valueKept = selectedValue.1.last else { guard let valueKept = selectedValue.1.last else {
handle(state: InputState.Empty()) handle(state: IMEState.ofEmpty())
return return
} }
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled {
let associatePhrases = keyHandler.buildAssociatePhraseState( let associates = keyHandler.buildAssociatePhraseState(
withPair: .init(key: selectedValue.0, value: String(valueKept)), withPair: .init(key: selectedValue.0, value: String(valueKept))
isTypingVertical: state.isTypingVertical )
), !associatePhrases.candidates.isEmpty if !associates.candidates.isEmpty {
{ handle(state: associates)
handle(state: associatePhrases)
return return
} }
handle(state: InputState.Empty()) }
handle(state: IMEState.ofEmpty())
} }
} }
} }

View File

@ -13,11 +13,11 @@ import Cocoa
// MARK: - Tooltip Display and Candidate Display Methods // MARK: - Tooltip Display and Candidate Display Methods
extension ctlInputMethod { extension ctlInputMethod {
func show(tooltip: String, composingBuffer: String, cursorIndex: Int) { func show(tooltip: String, displayedText: String, u16Cursor: Int) {
guard let client = client() else { return } guard let client = client() else { return }
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor = cursorIndex var cursor = u16Cursor
if cursor == composingBuffer.count, cursor != 0 { if cursor == displayedText.count, cursor != 0 {
cursor -= 1 cursor -= 1
} }
while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 { while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
@ -38,24 +38,14 @@ extension ctlInputMethod {
ctlInputMethod.tooltipController.show(tooltip: tooltip, at: finalOrigin) ctlInputMethod.tooltipController.show(tooltip: tooltip, at: finalOrigin)
} }
func show(candidateWindowWith state: InputStateProtocol) { func show(candidateWindowWith state: IMEStateProtocol) {
guard let client = client() else { return } guard let client = client() else { return }
var isTypingVertical: Bool {
if let state = state as? InputState.ChoosingCandidate {
return state.isTypingVertical
} else if let state = state as? InputState.AssociatedPhrases {
return state.isTypingVertical
}
return false
}
var isCandidateWindowVertical: Bool { var isCandidateWindowVertical: Bool {
var candidates: [(String, String)] = .init() var candidates: [(String, String)] = .init()
if let state = state as? InputState.ChoosingCandidate { if state.isCandidateContainer {
candidates = state.candidates
} else if let state = state as? InputState.AssociatedPhrases {
candidates = state.candidates candidates = state.candidates
} }
if isTypingVertical { return true } if isVerticalTyping { return true }
// IMK // IMK
guard ctlInputMethod.ctlCandidateCurrent is ctlCandidateUniversal else { return false } guard ctlInputMethod.ctlCandidateCurrent is ctlCandidateUniversal else { return false }
// 使 // 使
@ -106,7 +96,7 @@ extension ctlInputMethod {
let candidateKeys = mgrPrefs.candidateKeys let candidateKeys = mgrPrefs.candidateKeys
let keyLabels = let keyLabels =
candidateKeys.count > 4 ? Array(candidateKeys) : Array(mgrPrefs.defaultCandidateKeys) candidateKeys.count > 4 ? Array(candidateKeys) : Array(mgrPrefs.defaultCandidateKeys)
let keyLabelSuffix = state is InputState.AssociatedPhrases ? "^" : "" let keyLabelSuffix = state.type == .ofAssociates ? "^" : ""
ctlInputMethod.ctlCandidateCurrent.keyLabels = keyLabels.map { ctlInputMethod.ctlCandidateCurrent.keyLabels = keyLabels.map {
CandidateKeyLabel(key: String($0), displayedText: String($0) + keyLabelSuffix) CandidateKeyLabel(key: String($0), displayedText: String($0) + keyLabelSuffix)
} }
@ -126,9 +116,9 @@ extension ctlInputMethod {
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor = 0 var cursor = 0
if let state = state as? InputState.ChoosingCandidate { if [.ofCandidates, .ofSymbolTable].contains(state.type) {
cursor = state.cursorIndex cursor = state.data.cursor
if cursor == state.composingBuffer.count, cursor != 0 { if cursor == state.displayedText.count, cursor != 0 {
cursor -= 1 cursor -= 1
} }
} }
@ -140,7 +130,7 @@ extension ctlInputMethod {
cursor -= 1 cursor -= 1
} }
if isTypingVertical { if isVerticalTyping {
ctlInputMethod.ctlCandidateCurrent.set( ctlInputMethod.ctlCandidateCurrent.set(
windowTopLeftPoint: NSPoint( windowTopLeftPoint: NSPoint(
x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0 x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0

View File

@ -18,29 +18,77 @@ extension ctlInputMethod {
/// ///
/// ///
/// - Parameter newState: /// - Parameter newState:
func handle(state newState: InputStateProtocol) { func handle(state newState: IMEStateProtocol) {
let prevState = state let previous = state
state = newState state = newState
switch state.type {
switch newState { case .ofDeactivated:
case let newState as InputState.Deactivated: ctlInputMethod.ctlCandidateCurrent.delegate = nil
handle(state: newState, previous: prevState) ctlInputMethod.ctlCandidateCurrent.visible = false
case let newState as InputState.Empty: ctlInputMethod.tooltipController.hide()
handle(state: newState, previous: prevState) if previous.hasComposition {
case let newState as InputState.EmptyIgnoringPreviousState: commit(text: previous.displayedText)
handle(state: newState, previous: prevState) }
case let newState as InputState.Committing: clearInlineDisplay()
handle(state: newState, previous: prevState) //
case let newState as InputState.Inputting: keyHandler.clear()
handle(state: newState, previous: prevState) case .ofEmpty, .ofAbortion:
case let newState as InputState.Marking: var previous = previous
handle(state: newState, previous: prevState) if state.type == .ofAbortion {
case let newState as InputState.ChoosingCandidate: state = IMEState.ofEmpty()
handle(state: newState, previous: prevState) previous = state
case let newState as InputState.AssociatedPhrases: }
handle(state: newState, previous: prevState) ctlInputMethod.ctlCandidateCurrent.visible = false
case let newState as InputState.SymbolTable: ctlInputMethod.tooltipController.hide()
handle(state: newState, previous: prevState) // .Abortion
if previous.hasComposition, state.type != .ofAbortion {
commit(text: previous.displayedText)
}
//
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
clearInlineDisplay()
//
keyHandler.clear()
case .ofCommitting:
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty { commit(text: textToCommit) }
clearInlineDisplay()
//
keyHandler.clear()
case .ofInputting:
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty { commit(text: textToCommit) }
setInlineDisplayWithCursor()
if !state.tooltip.isEmpty {
show(
tooltip: state.tooltip, displayedText: state.displayedText,
u16Cursor: state.data.u16Cursor
)
}
case .ofMarking:
ctlInputMethod.ctlCandidateCurrent.visible = false
setInlineDisplayWithCursor()
if state.tooltip.isEmpty {
ctlInputMethod.tooltipController.hide()
} else {
let cursorReference: Int = {
if state.data.marker >= state.data.cursor { return state.data.u16Cursor }
return state.data.u16Marker //
}()
show(
tooltip: state.tooltip, displayedText: state.displayedText,
u16Cursor: cursorReference
)
}
case .ofCandidates, .ofAssociates, .ofSymbolTable:
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
default: break default: break
} }
} }
@ -48,7 +96,7 @@ extension ctlInputMethod {
/// .NotEmpty() /// .NotEmpty()
func setInlineDisplayWithCursor() { func setInlineDisplayWithCursor() {
guard let client = client() else { return } guard let client = client() else { return }
if let state = state as? InputState.AssociatedPhrases { if state.type == .ofAssociates {
client.setMarkedText( client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: 0, length: 0), state.attributedString, selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
@ -56,49 +104,19 @@ extension ctlInputMethod {
return return
} }
guard let state = state as? InputState.NotEmpty else { if state.hasComposition || state.isCandidateContainer {
clearInlineDisplay()
return
}
var identifier: AnyObject {
switch IME.currentInputMode {
case InputMode.imeModeCHS:
if #available(macOS 12.0, *) {
return "zh-Hans" as AnyObject
}
case InputMode.imeModeCHT:
if #available(macOS 12.0, *) {
return (mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled)
? "ja" as AnyObject : "zh-Hant" as AnyObject
}
default:
break
}
return "" as AnyObject
}
// [Shiki's Note] This might needs to be bug-reported to Apple:
// The LanguageIdentifier attribute of an NSAttributeString designated to
// IMK Client().SetMarkedText won't let the actual font respect your languageIdentifier
// settings. Still, this might behaves as Apple's current expectation, I'm afraid.
if #available(macOS 12.0, *) {
state.attributedString.setAttributes(
[.languageIdentifier: identifier],
range: NSRange(
location: 0,
length: state.composingBuffer.utf16.count
)
)
}
/// selectionRange /// selectionRange
/// 0 replacementRangeNSNotFound /// 0 replacementRangeNSNotFound
/// ///
client.setMarkedText( client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0), state.attributedString, selectionRange: NSRange(location: state.data.u16Cursor, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
) )
return
}
//
clearInlineDisplay()
} }
/// .NotEmpty() /// .NotEmpty()
@ -123,109 +141,4 @@ extension ctlInputMethod {
buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound) buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
) )
} }
private func handle(state: InputState.Deactivated, previous: InputStateProtocol) {
_ = state //
ctlInputMethod.ctlCandidateCurrent.delegate = nil
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
if let previous = previous as? InputState.NotEmpty {
commit(text: previous.committingBufferConverted)
}
clearInlineDisplay()
//
keyHandler.clear()
}
private func handle(state: InputState.Empty, previous: InputStateProtocol) {
_ = state //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
// .EmptyIgnoringPreviousState
if let previous = previous as? InputState.NotEmpty,
!(state is InputState.EmptyIgnoringPreviousState)
{
commit(text: previous.committingBufferConverted)
}
//
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
clearInlineDisplay()
//
keyHandler.clear()
}
private func handle(
state: InputState.EmptyIgnoringPreviousState, previous: InputStateProtocol
) {
_ = state //
_ = previous //
// previous state 使西 commit
handle(state: InputState.Empty())
}
private func handle(state: InputState.Committing, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty {
commit(text: textToCommit)
}
clearInlineDisplay()
//
keyHandler.clear()
}
private func handle(state: InputState.Inputting, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty {
commit(text: textToCommit)
}
setInlineDisplayWithCursor()
if !state.tooltip.isEmpty {
show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.cursorIndex
)
}
}
private func handle(state: InputState.Marking, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.ctlCandidateCurrent.visible = false
setInlineDisplayWithCursor()
if state.tooltip.isEmpty {
ctlInputMethod.tooltipController.hide()
} else {
show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.markerIndex
)
}
}
private func handle(state: InputState.ChoosingCandidate, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
private func handle(state: InputState.SymbolTable, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
private func handle(state: InputState.AssociatedPhrases, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
} }

View File

@ -212,11 +212,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Per-Char Select Mode", comment: "") + "\n" message: NSLocalizedString("Per-Char Select Mode", comment: "") + "\n"
+ { + (mgrPrefs.toggleSCPCTypingModeEnabled()
mgrPrefs.toggleSCPCTypingModeEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -224,11 +222,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Force KangXi Writing", comment: "") + "\n" message: NSLocalizedString("Force KangXi Writing", comment: "") + "\n"
+ { + (mgrPrefs.toggleChineseConversionEnabled()
mgrPrefs.toggleChineseConversionEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -236,11 +232,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("JIS Shinjitai Output", comment: "") + "\n" message: NSLocalizedString("JIS Shinjitai Output", comment: "") + "\n"
+ { + (mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -248,11 +242,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Currency Numeral Output", comment: "") + "\n" message: NSLocalizedString("Currency Numeral Output", comment: "") + "\n"
+ { + (mgrPrefs.toggleCurrencyNumeralsEnabled()
mgrPrefs.toggleCurrencyNumeralsEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -260,11 +252,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Half-Width Punctuation Mode", comment: "") + "\n" message: NSLocalizedString("Half-Width Punctuation Mode", comment: "") + "\n"
+ { + (mgrPrefs.toggleHalfWidthPunctuationEnabled()
mgrPrefs.toggleHalfWidthPunctuationEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -272,11 +262,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("CNS11643 Mode", comment: "") + "\n" message: NSLocalizedString("CNS11643 Mode", comment: "") + "\n"
+ { + (mgrPrefs.toggleCNS11643Enabled()
mgrPrefs.toggleCNS11643Enabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -284,11 +272,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Symbol & Emoji Input", comment: "") + "\n" message: NSLocalizedString("Symbol & Emoji Input", comment: "") + "\n"
+ { + (mgrPrefs.toggleSymbolInputEnabled()
mgrPrefs.toggleSymbolInputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -296,11 +282,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Per-Char Associated Phrases", comment: "") + "\n" message: NSLocalizedString("Per-Char Associated Phrases", comment: "") + "\n"
+ { + (mgrPrefs.toggleAssociatedPhrasesEnabled()
mgrPrefs.toggleAssociatedPhrasesEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }
@ -308,11 +292,9 @@ extension ctlInputMethod {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: NSLocalizedString("Use Phrase Replacement", comment: "") + "\n" message: NSLocalizedString("Use Phrase Replacement", comment: "") + "\n"
+ { + (mgrPrefs.togglePhraseReplacementEnabled()
mgrPrefs.togglePhraseReplacementEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: ""))
}()
) )
} }

View File

@ -13,7 +13,7 @@ import InputMethodKit
public enum vChewing {} public enum vChewing {}
// The type of input modes. // The type of input modes.
public enum InputMode: String { public enum InputMode: String, CaseIterable {
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS" case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT" case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
case imeModeNULL = "" case imeModeNULL = ""
@ -131,11 +131,11 @@ public enum IME {
), ),
mgrLangModel.dataFolderPath(isDefaultFolder: false) mgrLangModel.dataFolderPath(isDefaultFolder: false)
) )
ctlNonModalAlertWindow.shared.show( let alert = NSAlert()
title: NSLocalizedString("Unable to create the user phrase file.", comment: ""), alert.messageText = NSLocalizedString("Unable to create the user phrase file.", comment: "")
content: content, confirmButtonTitle: NSLocalizedString("OK", comment: ""), alert.informativeText = content
cancelButtonTitle: nil, cancelAsDefault: false, delegate: nil alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
) alert.runModal()
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
return false return false
} }
@ -223,152 +223,6 @@ public enum IME {
} }
return 0 return 0
} }
// MARK: - Registering the input method.
@discardableResult static func registerInputMethod() -> Int32 {
guard let bundleID = Bundle.main.bundleIdentifier else {
return -1
}
let bundleUrl = Bundle.main.bundleURL
var maybeInputSource = InputSourceHelper.inputSource(for: bundleID)
if maybeInputSource == nil {
NSLog("Registering input source \(bundleID) at \(bundleUrl.absoluteString)")
// then register
let status = InputSourceHelper.registerTnputSource(at: bundleUrl)
if !status {
NSLog(
"Fatal error: Cannot register input source \(bundleID) at \(bundleUrl.absoluteString)."
)
return -1
}
maybeInputSource = InputSourceHelper.inputSource(for: bundleID)
}
guard let inputSource = maybeInputSource else {
NSLog("Fatal error: Cannot find input source \(bundleID) after registration.")
return -1
}
if !InputSourceHelper.inputSourceEnabled(for: inputSource) {
NSLog("Enabling input source \(bundleID) at \(bundleUrl.absoluteString).")
let status = InputSourceHelper.enable(inputSource: inputSource)
if !status {
NSLog("Fatal error: Cannot enable input source \(bundleID).")
return -1
}
if !InputSourceHelper.inputSourceEnabled(for: inputSource) {
NSLog("Fatal error: Cannot enable input source \(bundleID).")
return -1
}
}
if CommandLine.arguments.count > 2, CommandLine.arguments[2] == "--all" {
let enabled = InputSourceHelper.enableAllInputMode(for: bundleID)
NSLog(
enabled
? "All input sources enabled for \(bundleID)"
: "Cannot enable all input sources for \(bundleID), but this is ignored")
}
return 0
}
// MARK: - ASCII
struct CarbonKeyboardLayout {
var strName: String = ""
var strValue: String = ""
}
static let arrWhitelistedKeyLayoutsASCII: [String] = [
"com.apple.keylayout.ABC",
"com.apple.keylayout.ABC-AZERTY",
"com.apple.keylayout.ABC-QWERTZ",
"com.apple.keylayout.British",
"com.apple.keylayout.Colemak",
"com.apple.keylayout.Dvorak",
"com.apple.keylayout.Dvorak-Left",
"com.apple.keylayout.DVORAK-QWERTYCMD",
"com.apple.keylayout.Dvorak-Right",
]
static var arrEnumerateSystemKeyboardLayouts: [IME.CarbonKeyboardLayout] {
// macOS
var arrKeyLayouts: [IME.CarbonKeyboardLayout] = []
arrKeyLayouts += [
IME.CarbonKeyboardLayout(
strName: NSLocalizedString("Apple Chewing - Dachen", comment: ""),
strValue: "com.apple.keylayout.ZhuyinBopomofo"
),
IME.CarbonKeyboardLayout(
strName: NSLocalizedString("Apple Chewing - Eten Traditional", comment: ""),
strValue: "com.apple.keylayout.ZhuyinEten"
),
]
// ASCII
var arrKeyLayoutsMACV: [IME.CarbonKeyboardLayout] = []
var arrKeyLayoutsASCII: [IME.CarbonKeyboardLayout] = []
let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
for source in list {
if let ptrCategory = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) {
let category = Unmanaged<CFString>.fromOpaque(ptrCategory).takeUnretainedValue()
if category != kTISCategoryKeyboardInputSource {
continue
}
} else {
continue
}
if let ptrASCIICapable = TISGetInputSourceProperty(
source, kTISPropertyInputSourceIsASCIICapable
) {
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(ptrASCIICapable)
.takeUnretainedValue()
if asciiCapable != kCFBooleanTrue {
continue
}
} else {
continue
}
if let ptrSourceType = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) {
let sourceType = Unmanaged<CFString>.fromOpaque(ptrSourceType).takeUnretainedValue()
if sourceType != kTISTypeKeyboardLayout {
continue
}
} else {
continue
}
guard let ptrSourceID = TISGetInputSourceProperty(source, kTISPropertyInputSourceID),
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName)
else {
continue
}
let sourceID = String(Unmanaged<CFString>.fromOpaque(ptrSourceID).takeUnretainedValue())
let localizedName = String(
Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
if sourceID.contains("vChewing") {
arrKeyLayoutsMACV += [
IME.CarbonKeyboardLayout(strName: localizedName, strValue: sourceID)
]
}
if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) {
arrKeyLayoutsASCII += [
IME.CarbonKeyboardLayout(strName: localizedName, strValue: sourceID)
]
}
}
arrKeyLayouts += arrKeyLayoutsMACV
arrKeyLayouts += arrKeyLayoutsASCII
return arrKeyLayouts
}
} }
// MARK: - Root Extensions // MARK: - Root Extensions
@ -382,6 +236,16 @@ extension RangeReplaceableCollection where Element: Hashable {
} }
} }
// MARK: - String charComponents Extension
extension String {
public var charComponents: [String] { map { String($0) } }
}
extension Array where Element == String.Element {
public var charComponents: [String] { map { String($0) } }
}
// MARK: - String Tildes Expansion Extension // MARK: - String Tildes Expansion Extension
extension String { extension String {

View File

@ -0,0 +1,226 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Foundation
import InputMethodKit
// MARK: - IMKHelper by The vChewing Project (MIT License).
enum IMKHelper {
///
/// SwiftUI 便使
static let arrWhitelistedKeyLayoutsASCII: [String] = [
"com.apple.keylayout.ABC",
"com.apple.keylayout.ABC-AZERTY",
"com.apple.keylayout.ABC-QWERTZ",
"com.apple.keylayout.British",
"com.apple.keylayout.Colemak",
"com.apple.keylayout.Dvorak",
"com.apple.keylayout.Dvorak-Left",
"com.apple.keylayout.DVORAK-QWERTYCMD",
"com.apple.keylayout.Dvorak-Right",
]
static let arrDynamicBasicKeyLayouts: [String] = [
"com.apple.keylayout.ZhuyinBopomofo",
"com.apple.keylayout.ZhuyinEten",
"org.atelierInmu.vChewing.keyLayouts.vchewingdachen",
"org.atelierInmu.vChewing.keyLayouts.vchewingmitac",
"org.atelierInmu.vChewing.keyLayouts.vchewingibm",
"org.atelierInmu.vChewing.keyLayouts.vchewingseigyou",
"org.atelierInmu.vChewing.keyLayouts.vchewingeten",
"org.unknown.keylayout.vChewingDachen",
"org.unknown.keylayout.vChewingFakeSeigyou",
"org.unknown.keylayout.vChewingETen",
"org.unknown.keylayout.vChewingIBM",
"org.unknown.keylayout.vChewingMiTAC",
]
static var allowedBasicLayoutsAsTISInputSources: [TISInputSource?] {
//
var containerA: [TISInputSource?] = []
var containerB: [TISInputSource?] = []
var containerC: [TISInputSource?] = []
let rawDictionary = TISInputSource.rawTISInputSources(onlyASCII: false)
IMKHelper.arrWhitelistedKeyLayoutsASCII.forEach {
if let neta = rawDictionary[$0], !arrDynamicBasicKeyLayouts.contains(neta.identifier) {
containerC.append(neta)
}
}
IMKHelper.arrDynamicBasicKeyLayouts.forEach {
if let neta = rawDictionary[$0] {
if neta.identifier.contains("com.apple") {
containerA.append(neta)
} else {
containerB.append(neta)
}
}
}
// nil
if !containerA.isEmpty { containerA.append(nil) }
if !containerB.isEmpty { containerB.append(nil) }
return containerA + containerB + containerC
}
struct CarbonKeyboardLayout {
var strName: String = ""
var strValue: String = ""
}
}
// MARK: -
extension IMKHelper {
@discardableResult static func registerInputMethod() -> Int32 {
TISInputSource.registerInputMethod() ? 0 : -1
}
}
// MARK: - TISInputSource Extension by The vChewing Project (MIT License).
extension TISInputSource {
public static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] {
TISInputSource.modes.compactMap { TISInputSource.generate(from: $0) }
}
public static var modes: [String] {
guard let components = Bundle.main.infoDictionary?["ComponentInputModeDict"] as? [String: Any],
let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any]
else {
return []
}
return tsInputModeListKey.keys.map { $0 }
}
@discardableResult public static func registerInputMethod() -> Bool {
let instances = TISInputSource.allRegisteredInstancesOfThisInputMethod
if instances.isEmpty {
//
NSLog("Registering input source.")
if !TISInputSource.registerInputSource() {
NSLog("Input source registration failed.")
return false
}
}
var succeeded = true
instances.forEach {
NSLog("Enabling input source: \($0.identifier)")
if !$0.activate() {
NSLog("Failed from enabling input source: \($0.identifier)")
succeeded = false
}
}
return succeeded
}
@discardableResult public static func registerInputSource() -> Bool {
TISRegisterInputSource(Bundle.main.bundleURL as CFURL) == noErr
}
@discardableResult public func activate() -> Bool {
TISEnableInputSource(self) == noErr
}
@discardableResult public func select() -> Bool {
if !isSelectable {
NSLog("Non-selectable: \(identifier)")
return false
}
if TISSelectInputSource(self) != noErr {
NSLog("Failed from switching to \(identifier)")
return false
}
return true
}
@discardableResult public func deactivate() -> Bool {
TISDisableInputSource(self) == noErr
}
public var isActivated: Bool {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsEnabled), to: CFBoolean.self)
== kCFBooleanTrue
}
public var isSelectable: Bool {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsSelectCapable), to: CFBoolean.self)
== kCFBooleanTrue
}
public static func generate(from identifier: String) -> TISInputSource? {
TISInputSource.rawTISInputSources(onlyASCII: false)[identifier] ?? nil
}
public var inputModeID: String {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputModeID), to: NSString.self) as String
}
public var vChewingLocalizedName: String {
switch identifier {
case "com.apple.keylayout.ZhuyinBopomofo":
return NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "")
case "com.apple.keylayout.ZhuyinEten":
return NSLocalizedString("Apple Zhuyin Eten (Traditional)", comment: "")
default: return localizedName
}
}
}
// MARK: - TISInputSource Extension by Mizuno Hiroki (a.k.a. "Mzp") (MIT License)
// Ref: Original source codes are written in Swift 4 from Mzp's InputMethodKit textbook.
// Note: Slightly modified by vChewing Project: Using Dictionaries when necessary.
extension TISInputSource {
public var localizedName: String {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyLocalizedName), to: NSString.self) as String
}
public var identifier: String {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceID), to: NSString.self) as String
}
public var scriptCode: Int {
let r = TISGetInputSourceProperty(self, "TSMInputSourcePropertyScriptCode" as CFString)
return unsafeBitCast(r, to: NSString.self).integerValue
}
public static func rawTISInputSources(onlyASCII: Bool = false) -> [String: TISInputSource] {
// CFDictionary
//
let conditions = CFDictionaryCreateMutable(nil, 2, nil, nil)
if onlyASCII {
//
CFDictionaryAddValue(
conditions, unsafeBitCast(kTISPropertyInputSourceType, to: UnsafeRawPointer.self),
unsafeBitCast(kTISTypeKeyboardLayout, to: UnsafeRawPointer.self)
)
// ASCII
CFDictionaryAddValue(
conditions, unsafeBitCast(kTISPropertyInputSourceIsASCIICapable, to: UnsafeRawPointer.self),
unsafeBitCast(kCFBooleanTrue, to: UnsafeRawPointer.self)
)
}
//
var result = TISCreateInputSourceList(conditions, true).takeRetainedValue() as? [TISInputSource] ?? .init()
if onlyASCII {
result = result.filter { $0.scriptCode == 0 }
}
var resultDictionary: [String: TISInputSource] = [:]
result.forEach {
resultDictionary[$0.inputModeID] = $0
resultDictionary[$0.identifier] = $0
}
return resultDictionary
}
}

View File

@ -1,112 +0,0 @@
// (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).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Cocoa
import InputMethodKit
public class InputSourceHelper: NSObject {
@available(*, unavailable)
override public init() {
super.init()
}
public static func allInstalledInputSources() -> [TISInputSource] {
TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
}
public static func inputSource(for propertyKey: CFString, stringValue: String)
-> TISInputSource?
{
let stringID = CFStringGetTypeID()
for source in allInstalledInputSources() {
if let propertyPtr = TISGetInputSourceProperty(source, propertyKey) {
let property = Unmanaged<CFTypeRef>.fromOpaque(propertyPtr).takeUnretainedValue()
let typeID = CFGetTypeID(property)
if typeID != stringID {
continue
}
if stringValue == property as? String {
return source
}
}
}
return nil
}
public static func inputSource(for sourceID: String) -> TISInputSource? {
inputSource(for: kTISPropertyInputSourceID, stringValue: sourceID)
}
public static func inputSourceEnabled(for source: TISInputSource) -> Bool {
if let valuePts = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsEnabled) {
let value = Unmanaged<CFBoolean>.fromOpaque(valuePts).takeUnretainedValue()
return value == kCFBooleanTrue
}
return false
}
public static func enable(inputSource: TISInputSource) -> Bool {
let status = TISEnableInputSource(inputSource)
return status == noErr
}
public static func enableAllInputMode(for inputSourceBundleD: String) -> Bool {
var enabled = false
for source in allInstalledInputSources() {
guard let bundleIDPtr = TISGetInputSourceProperty(source, kTISPropertyBundleID),
let _ = TISGetInputSourceProperty(source, kTISPropertyInputModeID)
else {
continue
}
let bundleID = Unmanaged<CFString>.fromOpaque(bundleIDPtr).takeUnretainedValue()
if String(bundleID) == inputSourceBundleD {
let modeEnabled = enable(inputSource: source)
if !modeEnabled {
return false
}
enabled = true
}
}
return enabled
}
public static func enable(inputMode modeID: String, for bundleID: String) -> Bool {
for source in allInstalledInputSources() {
guard let bundleIDPtr = TISGetInputSourceProperty(source, kTISPropertyBundleID),
let modePtr = TISGetInputSourceProperty(source, kTISPropertyInputModeID)
else {
continue
}
let inputsSourceBundleID = Unmanaged<CFString>.fromOpaque(bundleIDPtr)
.takeUnretainedValue()
let inputsSourceModeID = Unmanaged<CFString>.fromOpaque(modePtr).takeUnretainedValue()
if modeID == String(inputsSourceModeID), bundleID == String(inputsSourceBundleID) {
let enabled = enable(inputSource: source)
print(
"Attempt to enable input source of mode: \(modeID), bundle ID: \(bundleID), result: \(enabled)"
)
return enabled
}
}
print("Failed to find any matching input source of mode: \(modeID), bundle ID: \(bundleID)")
return false
}
public static func disable(inputSource: TISInputSource) -> Bool {
let status = TISDisableInputSource(inputSource)
return status == noErr
}
public static func registerTnputSource(at url: URL) -> Bool {
let status = TISRegisterInputSource(url as CFURL)
return status == noErr
}
}

View File

@ -438,6 +438,7 @@ public enum mgrPrefs {
@UserDefault(key: UserDef.kTrimUnfinishedReadingsOnCommit.rawValue, defaultValue: true) @UserDefault(key: UserDef.kTrimUnfinishedReadingsOnCommit.rawValue, defaultValue: true)
static var trimUnfinishedReadingsOnCommit: Bool static var trimUnfinishedReadingsOnCommit: Bool
// MARK: - Settings (Tier 2) // MARK: - Settings (Tier 2)
@UserDefault(key: UserDef.kUseIMKCandidateWindow.rawValue, defaultValue: false) @UserDefault(key: UserDef.kUseIMKCandidateWindow.rawValue, defaultValue: false)
@ -458,6 +459,8 @@ public enum mgrPrefs {
@UserDefault(key: UserDef.kMaxCandidateLength.rawValue, defaultValue: 10) @UserDefault(key: UserDef.kMaxCandidateLength.rawValue, defaultValue: 10)
static var maxCandidateLength: Int static var maxCandidateLength: Int
static var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
@UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep.rawValue, defaultValue: true) @UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep.rawValue, defaultValue: true)
static var shouldNotFartInLieuOfBeep: Bool static var shouldNotFartInLieuOfBeep: Bool

View File

@ -245,11 +245,11 @@ extension vChewing {
return !unigramsFor(key: key).isEmpty return !unigramsFor(key: key).isEmpty
} }
public func associatedPhrasesFor(pair: Megrez.Compositor.Candidate) -> [String] { public func associatedPhrasesFor(pair: Megrez.Compositor.KeyValuePaired) -> [String] {
lmAssociates.valuesFor(pair: pair) lmAssociates.valuesFor(pair: pair)
} }
public func hasAssociatedPhrasesFor(pair: Megrez.Compositor.Candidate) -> Bool { public func hasAssociatedPhrasesFor(pair: Megrez.Compositor.KeyValuePaired) -> Bool {
lmAssociates.hasValuesFor(pair: pair) lmAssociates.hasValuesFor(pair: pair)
} }

View File

@ -10,24 +10,34 @@
import Foundation import Foundation
class SymbolNode { public class SymbolNode {
var title: String var title: String
var children: [SymbolNode]? var children: [SymbolNode]?
var previous: SymbolNode? var previous: SymbolNode?
init(_ title: String, _ children: [SymbolNode]? = nil) { init(_ title: String, _ children: [SymbolNode]? = nil, previous: SymbolNode? = nil) {
self.title = title self.title = title
self.children = children self.children = children
self.children?.forEach {
$0.previous = self
}
self.previous = previous
} }
init(_ title: String, symbols: String) { init(_ title: String, symbols: String) {
self.title = title self.title = title
children = Array(symbols).map { SymbolNode(String($0), nil) } children = Array(symbols).map { SymbolNode(String($0), nil) }
children?.forEach {
$0.previous = self
}
} }
init(_ title: String, symbols: [String]) { init(_ title: String, symbols: [String]) {
self.title = title self.title = title
children = symbols.map { SymbolNode($0, nil) } children = symbols.map { SymbolNode($0, nil) }
children?.forEach {
$0.previous = self
}
} }
static func parseUserSymbolNodeData() { static func parseUserSymbolNodeData() {

View File

@ -78,7 +78,7 @@ extension vChewing {
// This function will be implemented only if further hard-necessity comes. // This function will be implemented only if further hard-necessity comes.
} }
public func valuesFor(pair: Megrez.Compositor.Candidate) -> [String] { public func valuesFor(pair: Megrez.Compositor.KeyValuePaired) -> [String] {
var pairs: [String] = [] var pairs: [String] = []
if let arrRangeRecords: [(Range<String.Index>, Int)] = rangeMap[pair.toNGramKey] { if let arrRangeRecords: [(Range<String.Index>, Int)] = rangeMap[pair.toNGramKey] {
for (netaRange, index) in arrRangeRecords { for (netaRange, index) in arrRangeRecords {
@ -98,7 +98,7 @@ extension vChewing {
return pairs.filter { set.insert($0).inserted } return pairs.filter { set.insert($0).inserted }
} }
public func hasValuesFor(pair: Megrez.Compositor.Candidate) -> Bool { public func hasValuesFor(pair: Megrez.Compositor.KeyValuePaired) -> Bool {
if rangeMap[pair.toNGramKey] != nil { return true } if rangeMap[pair.toNGramKey] != nil { return true }
return rangeMap[pair.value] != nil return rangeMap[pair.value] != nil
} }

View File

@ -319,8 +319,8 @@ extension vChewing.LMUserOverride {
// //
let strCurrent = kvCurrent.key let strCurrent = kvCurrent.key
var kvPrevious = Megrez.Compositor.Candidate() var kvPrevious = Megrez.Compositor.KeyValuePaired()
var kvAnterior = Megrez.Compositor.Candidate() var kvAnterior = Megrez.Compositor.KeyValuePaired()
var readingStack = "" var readingStack = ""
var trigramKey: String { "(\(kvAnterior.toNGramKey),\(kvPrevious.toNGramKey),\(strCurrent))" } var trigramKey: String { "(\(kvAnterior.toNGramKey),\(kvPrevious.toNGramKey),\(strCurrent))" }
var result: String { var result: String {

View File

@ -4,9 +4,7 @@
// This code is released under the MIT license (SPDX-License-Identifier: MIT) // This code is released under the MIT license (SPDX-License-Identifier: MIT)
/// The namespace for this package. /// The namespace for this package.
public enum Megrez { public enum Megrez {}
public typealias KeyValuePaired = Compositor.Candidate //
}
// //
// Megrez Gramambular Lukhnos Liu // Megrez Gramambular Lukhnos Liu

View File

@ -23,7 +23,15 @@ extension Megrez {
/// - /// -
public static let kDefaultSeparator: String = "-" public static let kDefaultSeparator: String = "-"
/// ///
public var cursor: Int = 0 { didSet { cursor = max(0, min(cursor, length)) } } public var cursor: Int = 0 {
didSet {
cursor = max(0, min(cursor, length))
marker = cursor
}
}
///
public var marker: Int = 0 { didSet { marker = max(0, min(marker, length)) } }
/// - /// -
public var separator = kDefaultSeparator public var separator = kDefaultSeparator
/// ///
@ -88,37 +96,46 @@ extension Megrez {
} }
/// ///
/// - Parameter direction: /// - Parameters:
/// - direction:
/// - isMarker:
/// - Returns: /// - Returns:
@discardableResult public mutating func jumpCursorBySpan(to direction: TypingDirection) -> Bool { @discardableResult public mutating func jumpCursorBySpan(to direction: TypingDirection, isMarker: Bool = false)
-> Bool
{
var target = isMarker ? marker : cursor
switch direction { switch direction {
case .front: case .front:
if cursor == width { return false } if target == width { return false }
case .rear: case .rear:
if cursor == 0 { return false } if target == 0 { return false }
} }
guard let currentRegion = cursorRegionMap[cursor] else { return false } guard let currentRegion = cursorRegionMap[target] else { return false }
let aRegionForward = max(currentRegion - 1, 0) let aRegionForward = max(currentRegion - 1, 0)
let currentRegionBorderRear: Int = walkedNodes[0..<currentRegion].map(\.spanLength).reduce(0, +) let currentRegionBorderRear: Int = walkedNodes[0..<currentRegion].map(\.spanLength).reduce(0, +)
switch cursor { switch target {
case currentRegionBorderRear: case currentRegionBorderRear:
switch direction { switch direction {
case .front: case .front:
cursor = target =
(currentRegion > walkedNodes.count) (currentRegion > walkedNodes.count)
? keys.count : walkedNodes[0...currentRegion].map(\.spanLength).reduce(0, +) ? keys.count : walkedNodes[0...currentRegion].map(\.spanLength).reduce(0, +)
case .rear: case .rear:
cursor = walkedNodes[0..<aRegionForward].map(\.spanLength).reduce(0, +) target = walkedNodes[0..<aRegionForward].map(\.spanLength).reduce(0, +)
} }
default: default:
switch direction { switch direction {
case .front: case .front:
cursor = currentRegionBorderRear + walkedNodes[currentRegion].spanLength target = currentRegionBorderRear + walkedNodes[currentRegion].spanLength
case .rear: case .rear:
cursor = currentRegionBorderRear target = currentRegionBorderRear
} }
} }
switch isMarker {
case false: cursor = target
case true: marker = target
}
return true return true
} }

View File

@ -6,7 +6,7 @@
import Foundation import Foundation
extension Megrez.Compositor { extension Megrez.Compositor {
public struct Candidate: Equatable, Hashable, Comparable, CustomStringConvertible { public struct KeyValuePaired: Equatable, Hashable, Comparable, CustomStringConvertible {
/// ///
public var key: String public var key: String
/// ///
@ -32,23 +32,23 @@ extension Megrez.Compositor {
hasher.combine(value) hasher.combine(value)
} }
public static func == (lhs: Candidate, rhs: Candidate) -> Bool { public static func == (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
lhs.key == rhs.key && lhs.value == rhs.value lhs.key == rhs.key && lhs.value == rhs.value
} }
public static func < (lhs: Candidate, rhs: Candidate) -> Bool { public static func < (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count < rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value < rhs.value) (lhs.key.count < rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value < rhs.value)
} }
public static func > (lhs: Candidate, rhs: Candidate) -> Bool { public static func > (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count > rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value > rhs.value) (lhs.key.count > rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value > rhs.value)
} }
public static func <= (lhs: Candidate, rhs: Candidate) -> Bool { public static func <= (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count <= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value <= rhs.value) (lhs.key.count <= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value <= rhs.value)
} }
public static func >= (lhs: Candidate, rhs: Candidate) -> Bool { public static func >= (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count >= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value >= rhs.value) (lhs.key.count >= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value >= rhs.value)
} }
} }
@ -60,8 +60,8 @@ extension Megrez.Compositor {
/// location - 1 /// location - 1
/// - Parameter location: /// - Parameter location:
/// - Returns: /// - Returns:
public func fetchCandidates(at location: Int, filter: CandidateFetchFilter = .all) -> [Candidate] { public func fetchCandidates(at location: Int, filter: CandidateFetchFilter = .all) -> [KeyValuePaired] {
var result = [Candidate]() var result = [KeyValuePaired]()
guard !keys.isEmpty else { return result } guard !keys.isEmpty else { return result }
let location = max(min(location, keys.count - 1), 0) // let location = max(min(location, keys.count - 1), 0) //
let anchors: [NodeAnchor] = fetchOverlappingNodes(at: location).stableSorted { let anchors: [NodeAnchor] = fetchOverlappingNodes(at: location).stableSorted {
@ -96,7 +96,7 @@ extension Megrez.Compositor {
/// - overrideType: /// - overrideType:
/// - Returns: /// - Returns:
@discardableResult public func overrideCandidate( @discardableResult public func overrideCandidate(
_ candidate: Candidate, at location: Int, overrideType: Node.OverrideType = .withHighScore _ candidate: KeyValuePaired, at location: Int, overrideType: Node.OverrideType = .withHighScore
) )
-> Bool -> Bool
{ {
@ -154,7 +154,7 @@ extension Megrez.Compositor {
anchor.node.reset() anchor.node.reset()
continue continue
} }
anchor.node.overridingScore /= 2 anchor.node.overridingScore /= 4
} }
} }
return true return true

View File

@ -42,7 +42,7 @@ extension Megrez.Compositor {
didSet { currentUnigramIndex = min(max(0, currentUnigramIndex), unigrams.count - 1) } didSet { currentUnigramIndex = min(max(0, currentUnigramIndex), unigrams.count - 1) }
} }
public var currentPair: Megrez.Compositor.Candidate { .init(key: key, value: value) } public var currentPair: Megrez.Compositor.KeyValuePaired { .init(key: key, value: value) }
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
hasher.combine(key) hasher.combine(key)
@ -70,8 +70,12 @@ extension Megrez.Compositor {
overrideType = .withNoOverrides overrideType = .withNoOverrides
} }
///
public var isReadingMismatched: Bool {
keyArray.count != value.count
}
/// ///
/// - Returns:
public var currentUnigram: Megrez.Unigram { public var currentUnigram: Megrez.Unigram {
unigrams.isEmpty ? .init() : unigrams[currentUnigramIndex] unigrams.isEmpty ? .init() : unigrams[currentUnigramIndex]
} }

View File

@ -229,18 +229,7 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode] { if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode] {
/// KeyCode NSEvent Character /// KeyCode NSEvent Character
/// IMK /// IMK
let newEvent = NSEvent.keyEvent( let newEvent = event.reinitiate(characters: newChar)
with: event.type,
location: event.locationInWindow,
modifierFlags: event.modifierFlags,
timestamp: event.timestamp,
windowNumber: event.windowNumber,
context: nil,
characters: newChar,
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? event.characters ?? "",
isARepeat: event.isARepeat,
keyCode: event.keyCode
)
if let newEvent = newEvent { if let newEvent = newEvent {
if mgrPrefs.useSCPCTypingMode, delegate.isAssociatedPhrasesState { if mgrPrefs.useSCPCTypingMode, delegate.isAssociatedPhrasesState {
// input.isShiftHold ctlInputMethod.handle() // input.isShiftHold ctlInputMethod.handle()
@ -300,17 +289,6 @@ extension ctlCandidateIMK {
let mapNumPadKeyCodeTranslation: [UInt16: UInt16] = [ let mapNumPadKeyCodeTranslation: [UInt16: UInt16] = [
83: 18, 84: 19, 85: 20, 86: 21, 87: 23, 88: 22, 89: 26, 91: 28, 92: 25, 83: 18, 84: 19, 85: 20, 86: 21, 87: 23, 88: 22, 89: 26, 91: 28, 92: 25,
] ]
return NSEvent.keyEvent( return event.reinitiate(keyCode: mapNumPadKeyCodeTranslation[event.keyCode] ?? event.keyCode)
with: event.type,
location: event.locationInWindow,
modifierFlags: event.modifierFlags,
timestamp: event.timestamp,
windowNumber: event.windowNumber,
context: nil,
characters: event.characters ?? "",
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? event.characters ?? "",
isARepeat: event.isARepeat,
keyCode: mapNumPadKeyCodeTranslation[event.keyCode] ?? event.keyCode
)
} }
} }

View File

@ -93,12 +93,12 @@ struct suiPrefPaneKeyboard: View {
mgrPrefs.mandarinParser = value mgrPrefs.mandarinParser = value
switch value { switch value {
case 0: case 0:
if !AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { if !IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) {
mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ZhuyinBopomofo" mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ZhuyinBopomofo"
selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout
} }
default: default:
if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { if IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) {
mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ABC" mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ABC"
selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout
} }
@ -170,15 +170,19 @@ struct suiPrefPaneKeyboard: View {
selection: $selBasicKeyboardLayout.onChange { selection: $selBasicKeyboardLayout.onChange {
let value = selBasicKeyboardLayout let value = selBasicKeyboardLayout
mgrPrefs.basicKeyboardLayout = value mgrPrefs.basicKeyboardLayout = value
if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(value) { if IMKHelper.arrDynamicBasicKeyLayouts.contains(value) {
mgrPrefs.mandarinParser = 0 mgrPrefs.mandarinParser = 0
selMandarinParser = mgrPrefs.mandarinParser selMandarinParser = mgrPrefs.mandarinParser
} }
} }
) { ) {
ForEach(0...(IME.arrEnumerateSystemKeyboardLayouts.count - 1), id: \.self) { id in ForEach(0...(IMKHelper.allowedBasicLayoutsAsTISInputSources.count - 1), id: \.self) { id in
Text(IME.arrEnumerateSystemKeyboardLayouts[id].strName).tag( let theEntry = IMKHelper.allowedBasicLayoutsAsTISInputSources[id]
IME.arrEnumerateSystemKeyboardLayouts[id].strValue) if let theEntry = theEntry {
Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier)
} else {
Divider()
}
}.id(UUID()) }.id(UUID())
} }
.labelsHidden() .labelsHidden()

View File

@ -11,7 +11,11 @@
import Cocoa import Cocoa
import InputMethodKit import InputMethodKit
let kConnectionName = "org.atelierInmu.inputmethod.vChewing_Connection" guard let kConnectionName = Bundle.main.infoDictionary?["InputMethodConnectionName"] as? String
else {
NSLog("Fatal error: Failed from retrieving connection name from info.plist file.")
exit(-1)
}
switch max(CommandLine.arguments.count - 1, 0) { switch max(CommandLine.arguments.count - 1, 0) {
case 0: break case 0: break
@ -19,7 +23,7 @@ switch max(CommandLine.arguments.count - 1, 0) {
switch CommandLine.arguments[1] { switch CommandLine.arguments[1] {
case "install": case "install":
if CommandLine.arguments[1] == "install" { if CommandLine.arguments[1] == "install" {
let exitCode = IME.registerInputMethod() let exitCode = IMKHelper.registerInputMethod()
exit(exitCode) exit(exitCode)
} }
case "uninstall": case "uninstall":

View File

@ -52,7 +52,7 @@
<key>tsInputModePrimaryInScriptKey</key> <key>tsInputModePrimaryInScriptKey</key>
<true/> <true/>
<key>tsInputModeScriptKey</key> <key>tsInputModeScriptKey</key>
<string>smTradChinese</string> <string>smUnicode</string>
</dict> </dict>
<key>org.atelierInmu.inputmethod.vChewing.IMECHT</key> <key>org.atelierInmu.inputmethod.vChewing.IMECHT</key>
<dict> <dict>
@ -80,7 +80,7 @@
<key>tsInputModePrimaryInScriptKey</key> <key>tsInputModePrimaryInScriptKey</key>
<true/> <true/>
<key>tsInputModeScriptKey</key> <key>tsInputModeScriptKey</key>
<string>smTradChinese</string> <string>smUnicode</string>
</dict> </dict>
</dict> </dict>
<key>tsVisibleInputModeOrderedArrayKey</key> <key>tsVisibleInputModeOrderedArrayKey</key>

View File

@ -1,114 +0,0 @@
// (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).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
import Cocoa
protocol ctlNonModalAlertWindowDelegate: AnyObject {
func ctlNonModalAlertWindowDidConfirm(_ controller: ctlNonModalAlertWindow)
func ctlNonModalAlertWindowDidCancel(_ controller: ctlNonModalAlertWindow)
}
class ctlNonModalAlertWindow: NSWindowController {
static let shared = ctlNonModalAlertWindow(windowNibName: "frmNonModalAlertWindow")
@IBOutlet var titleTextField: NSTextField!
@IBOutlet var contentTextField: NSTextField!
@IBOutlet var confirmButton: NSButton!
@IBOutlet var cancelButton: NSButton!
weak var delegate: ctlNonModalAlertWindowDelegate?
func show(
title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?,
cancelAsDefault: Bool, delegate: ctlNonModalAlertWindowDelegate?
) {
guard let window = window else { return }
if window.isVisible == true {
self.delegate?.ctlNonModalAlertWindowDidCancel(self)
}
self.delegate = delegate
var oldFrame = confirmButton.frame
confirmButton.title = confirmButtonTitle
confirmButton.sizeToFit()
var newFrame = confirmButton.frame
newFrame.size.width = max(90, newFrame.size.width + 10)
newFrame.origin.x += oldFrame.size.width - newFrame.size.width
confirmButton.frame = newFrame
if let cancelButtonTitle = cancelButtonTitle {
cancelButton.title = cancelButtonTitle
cancelButton.sizeToFit()
var adjustFrame = cancelButton.frame
adjustFrame.size.width = max(90, adjustFrame.size.width + 10)
adjustFrame.origin.x = newFrame.origin.x - adjustFrame.size.width
confirmButton.frame = adjustFrame
cancelButton.isHidden = false
} else {
cancelButton.isHidden = true
}
cancelButton.nextKeyView = confirmButton
confirmButton.nextKeyView = cancelButton
if cancelButtonTitle != nil {
if cancelAsDefault {
window.defaultButtonCell = cancelButton.cell as? NSButtonCell
} else {
cancelButton.keyEquivalent = " "
window.defaultButtonCell = confirmButton.cell as? NSButtonCell
}
} else {
window.defaultButtonCell = confirmButton.cell as? NSButtonCell
}
titleTextField.stringValue = title
oldFrame = contentTextField.frame
contentTextField.stringValue = content
var infiniteHeightFrame = oldFrame
infiniteHeightFrame.size.width -= 4.0
infiniteHeightFrame.size.height = 10240
newFrame = (content as NSString).boundingRect(
with: infiniteHeightFrame.size, options: [.usesLineFragmentOrigin],
attributes: [.font: contentTextField.font!]
)
newFrame.size.width = max(newFrame.size.width, oldFrame.size.width)
newFrame.size.height += 4.0
newFrame.origin = oldFrame.origin
newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height)
contentTextField.frame = newFrame
var windowFrame = window.frame
windowFrame.size.height += (newFrame.size.height - oldFrame.size.height)
window.level = NSWindow.Level(Int(CGShieldingWindowLevel()) + 1)
window.setFrame(windowFrame, display: true)
window.center()
window.makeKeyAndOrderFront(self)
NSApp.activate(ignoringOtherApps: true)
}
@IBAction func confirmButtonAction(_: Any) {
delegate?.ctlNonModalAlertWindowDidConfirm(self)
window?.orderOut(self)
}
@IBAction func cancelButtonAction(_ sender: Any) {
cancel(sender)
}
func cancel(_: Any) {
delegate?.ctlNonModalAlertWindowDidCancel(self)
delegate = nil
window?.orderOut(self)
}
}

View File

@ -93,89 +93,25 @@ class ctlPrefWindow: NSWindowController {
currentLanguageSelectItem = chosenLanguageItem ?? autoMUISelectItem currentLanguageSelectItem = chosenLanguageItem ?? autoMUISelectItem
uiLanguageButton.select(currentLanguageSelectItem) uiLanguageButton.select(currentLanguageSelectItem)
let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
var usKeyboardLayoutItem: NSMenuItem? var usKeyboardLayoutItem: NSMenuItem?
var chosenBaseKeyboardLayoutItem: NSMenuItem? var chosenBaseKeyboardLayoutItem: NSMenuItem?
basicKeyboardLayoutButton.menu?.removeAllItems() basicKeyboardLayoutButton.menu?.removeAllItems()
let itmAppleZhuyinBopomofo = NSMenuItem()
itmAppleZhuyinBopomofo.title = NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "")
itmAppleZhuyinBopomofo.representedObject = String(
"com.apple.keylayout.ZhuyinBopomofo")
basicKeyboardLayoutButton.menu?.addItem(itmAppleZhuyinBopomofo)
let itmAppleZhuyinEten = NSMenuItem()
itmAppleZhuyinEten.title = NSLocalizedString("Apple Zhuyin Eten (Traditional)", comment: "")
itmAppleZhuyinEten.representedObject = String("com.apple.keylayout.ZhuyinEten")
basicKeyboardLayoutButton.menu?.addItem(itmAppleZhuyinEten)
let basicKeyboardLayoutID = mgrPrefs.basicKeyboardLayout let basicKeyboardLayoutID = mgrPrefs.basicKeyboardLayout
for source in list { for source in IMKHelper.allowedBasicLayoutsAsTISInputSources {
if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) { guard let source = source else {
let category = Unmanaged<CFString>.fromOpaque(categoryPtr).takeUnretainedValue() basicKeyboardLayoutButton.menu?.addItem(NSMenuItem.separator())
if category != kTISCategoryKeyboardInputSource {
continue continue
} }
} else {
continue
}
if let asciiCapablePtr = TISGetInputSourceProperty(
source, kTISPropertyInputSourceIsASCIICapable
) {
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr)
.takeUnretainedValue()
if asciiCapable != kCFBooleanTrue {
continue
}
} else {
continue
}
if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) {
let sourceType = Unmanaged<CFString>.fromOpaque(sourceTypePtr).takeUnretainedValue()
if sourceType != kTISTypeKeyboardLayout {
continue
}
} else {
continue
}
guard let sourceIDPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceID),
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName)
else {
continue
}
let sourceID = String(Unmanaged<CFString>.fromOpaque(sourceIDPtr).takeUnretainedValue())
let localizedName = String(
Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
let menuItem = NSMenuItem() let menuItem = NSMenuItem()
menuItem.title = localizedName menuItem.title = source.vChewingLocalizedName
menuItem.representedObject = sourceID menuItem.representedObject = source.identifier
if source.identifier == "com.apple.keylayout.US" { usKeyboardLayoutItem = menuItem }
if sourceID == "com.apple.keylayout.US" { if basicKeyboardLayoutID == source.identifier { chosenBaseKeyboardLayoutItem = menuItem }
usKeyboardLayoutItem = menuItem
}
if basicKeyboardLayoutID == sourceID {
chosenBaseKeyboardLayoutItem = menuItem
}
if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) || sourceID.contains("vChewing") {
basicKeyboardLayoutButton.menu?.addItem(menuItem) basicKeyboardLayoutButton.menu?.addItem(menuItem)
} }
}
switch basicKeyboardLayoutID {
case "com.apple.keylayout.ZhuyinBopomofo":
chosenBaseKeyboardLayoutItem = itmAppleZhuyinBopomofo
case "com.apple.keylayout.ZhuyinEten":
chosenBaseKeyboardLayoutItem = itmAppleZhuyinEten
default:
break // nothing to do
}
basicKeyboardLayoutButton.select(chosenBaseKeyboardLayoutItem ?? usKeyboardLayoutItem) basicKeyboardLayoutButton.select(chosenBaseKeyboardLayoutItem ?? usKeyboardLayoutItem)

View File

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ctlNonModalAlertWindow" customModule="vChewing" customModuleProvider="target">
<connections>
<outlet property="cancelButton" destination="71" id="83"/>
<outlet property="confirmButton" destination="5" id="82"/>
<outlet property="contentTextField" destination="59" id="79"/>
<outlet property="titleTextField" destination="39" id="78"/>
<outlet property="window" destination="1" id="3"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" visibleAtLaunch="NO" animationBehavior="default" titlebarAppearsTransparent="YES" id="1">
<windowStyleMask key="styleMask" titled="YES" fullSizeContentView="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="420" height="130"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
<view key="contentView" id="2" customClass="NSVisualEffectView">
<rect key="frame" x="0.0" y="0.0" width="420" height="130"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5">
<rect key="frame" x="314" y="13" width="92" height="32"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="78" id="OIg-RG-etq"/>
</constraints>
<buttonCell key="cell" type="push" title="Button" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="confirmButtonAction:" target="-2" id="85"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="71">
<rect key="frame" x="222" y="13" width="92" height="32"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="78" id="Ky0-Zb-Gla"/>
</constraints>
<buttonCell key="cell" type="push" title="Button" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="73">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="cancelButtonAction:" target="-2" id="86"/>
</connections>
</button>
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="12">
<rect key="frame" x="24" y="50" width="64" height="64"/>
<constraints>
<constraint firstAttribute="width" constant="64" id="A3W-eQ-z1A"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="AlertIcon" id="13"/>
</imageView>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="39">
<rect key="frame" x="103" y="92" width="300" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="17" id="P7H-ef-iqw"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Lorem ipsum" id="40">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" preferredMaxLayoutWidth="296" translatesAutoresizingMaskIntoConstraints="NO" id="59">
<rect key="frame" x="103" y="70" width="300" height="14"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Lorem ipsum" id="60">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="59" firstAttribute="top" secondItem="39" secondAttribute="bottom" constant="8" symbolic="YES" id="1wt-ne-dF2"/>
<constraint firstItem="71" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="2" secondAttribute="leading" constant="20" symbolic="YES" id="7bF-DG-vdn"/>
<constraint firstAttribute="bottom" secondItem="12" secondAttribute="bottom" constant="50" id="8xn-Ib-OoE"/>
<constraint firstItem="39" firstAttribute="leading" secondItem="12" secondAttribute="trailing" constant="17" id="Fby-g8-C6h"/>
<constraint firstAttribute="trailing" secondItem="39" secondAttribute="trailing" constant="19" id="MvK-Vm-t8m"/>
<constraint firstItem="39" firstAttribute="top" secondItem="2" secondAttribute="top" constant="21" id="QZ0-fh-VbK"/>
<constraint firstItem="71" firstAttribute="baseline" secondItem="5" secondAttribute="baseline" id="WzK-fU-3PM"/>
<constraint firstItem="39" firstAttribute="leading" secondItem="59" secondAttribute="leading" id="amF-fF-0aV"/>
<constraint firstAttribute="trailing" secondItem="5" secondAttribute="trailing" constant="21" id="b3T-ES-VrG"/>
<constraint firstItem="5" firstAttribute="leading" secondItem="71" secondAttribute="trailing" constant="14" id="kas-eq-M8s"/>
<constraint firstItem="39" firstAttribute="trailing" secondItem="59" secondAttribute="trailing" id="l6t-qJ-mLX"/>
<constraint firstAttribute="bottom" secondItem="71" secondAttribute="bottom" constant="20" symbolic="YES" id="mHa-mT-h87"/>
<constraint firstItem="12" firstAttribute="leading" secondItem="2" secondAttribute="leading" constant="24" id="oRs-Yb-Ysu"/>
<constraint firstItem="12" firstAttribute="top" secondItem="2" secondAttribute="top" constant="16" id="reW-NP-Ec0"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="4"/>
</connections>
<point key="canvasLocation" x="140" y="146"/>
</window>
</objects>
<resources>
<image name="AlertIcon" width="64" height="64"/>
</resources>
</document>

View File

@ -3,9 +3,9 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.3.1</string> <string>2.4.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2310</string> <string>2400</string>
<key>UpdateInfoEndpoint</key> <key>UpdateInfoEndpoint</key>
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string> <string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
<key>UpdateInfoSite</key> <key>UpdateInfoSite</key>

View File

@ -726,7 +726,7 @@
<key>USE_HFS+_COMPRESSION</key> <key>USE_HFS+_COMPRESSION</key>
<false/> <false/>
<key>VERSION</key> <key>VERSION</key>
<string>2.3.1</string> <string>2.4.0</string>
</dict> </dict>
<key>TYPE</key> <key>TYPE</key>
<integer>0</integer> <integer>0</integer>

View File

@ -10,6 +10,7 @@
5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; }; 5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; };
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; }; 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; }; 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; };
5B20430728BEE30900BFC6FD /* BookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B20430628BEE30900BFC6FD /* BookmarkManager.swift */; }; 5B20430728BEE30900BFC6FD /* BookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B20430628BEE30900BFC6FD /* BookmarkManager.swift */; };
5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */; }; 5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */; };
5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */; }; 5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */; };
@ -17,7 +18,7 @@
5B2170E0289FACAD00BE7304 /* 7_LangModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170D7289FACAC00BE7304 /* 7_LangModel.swift */; }; 5B2170E0289FACAD00BE7304 /* 7_LangModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170D7289FACAC00BE7304 /* 7_LangModel.swift */; };
5B2170E1289FACAD00BE7304 /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170D8289FACAC00BE7304 /* 0_Megrez.swift */; }; 5B2170E1289FACAD00BE7304 /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170D8289FACAC00BE7304 /* 0_Megrez.swift */; };
5B2170E2289FACAD00BE7304 /* 8_Unigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170D9289FACAC00BE7304 /* 8_Unigram.swift */; }; 5B2170E2289FACAD00BE7304 /* 8_Unigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170D9289FACAC00BE7304 /* 8_Unigram.swift */; };
5B2170E3289FACAD00BE7304 /* 3_Candidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DA289FACAC00BE7304 /* 3_Candidate.swift */; }; 5B2170E3289FACAD00BE7304 /* 3_KeyValuePaired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DA289FACAC00BE7304 /* 3_KeyValuePaired.swift */; };
5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DB289FACAC00BE7304 /* 2_Walker.swift */; }; 5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DB289FACAC00BE7304 /* 2_Walker.swift */; };
5B2170E5289FACAD00BE7304 /* 6_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DC289FACAC00BE7304 /* 6_Node.swift */; }; 5B2170E5289FACAD00BE7304 /* 6_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DC289FACAC00BE7304 /* 6_Node.swift */; };
5B2170E6289FACAD00BE7304 /* 4_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DD289FACAC00BE7304 /* 4_Span.swift */; }; 5B2170E6289FACAD00BE7304 /* 4_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2170DD289FACAC00BE7304 /* 4_Span.swift */; };
@ -32,14 +33,11 @@
5B5948CE289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5948CD289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift */; }; 5B5948CE289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5948CD289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift */; };
5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; }; 5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; };
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; }; 5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; }; 5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; };
5B62A33827AE79CD00A19448 /* StringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33727AE79CD00A19448 /* StringUtils.swift */; };
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; }; 5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; }; 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34327AE7CD900A19448 /* TooltipController.swift */; }; 5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34327AE7CD900A19448 /* TooltipController.swift */; };
5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34527AE7CD900A19448 /* NotifierController.swift */; }; 5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34527AE7CD900A19448 /* NotifierController.swift */; };
5B62A35327AE89C400A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
5B6C141228A9D4B30098ADF8 /* ctlInputMethod_Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */; }; 5B6C141228A9D4B30098ADF8 /* ctlInputMethod_Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */; };
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; }; 5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; }; 5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
@ -80,7 +78,6 @@
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */; }; 5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */; };
5BBBB75F27AED54C0023B93A /* Beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75D27AED54C0023B93A /* Beep.m4a */; }; 5BBBB75F27AED54C0023B93A /* Beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75D27AED54C0023B93A /* Beep.m4a */; };
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75E27AED54C0023B93A /* Fart.m4a */; }; 5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75E27AED54C0023B93A /* Fart.m4a */; };
5BBBB76B27AED5DB0023B93A /* frmNonModalAlertWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB76527AED5DB0023B93A /* frmNonModalAlertWindow.xib */; };
5BBBB76D27AED5DB0023B93A /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */; }; 5BBBB76D27AED5DB0023B93A /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */; };
5BBBB77327AED70B0023B93A /* MenuIcon-TCVIM@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB76F27AED70B0023B93A /* MenuIcon-TCVIM@2x.png */; }; 5BBBB77327AED70B0023B93A /* MenuIcon-TCVIM@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB76F27AED70B0023B93A /* MenuIcon-TCVIM@2x.png */; };
5BBBB77427AED70B0023B93A /* MenuIcon-SCVIM@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB77027AED70B0023B93A /* MenuIcon-SCVIM@2x.png */; }; 5BBBB77427AED70B0023B93A /* MenuIcon-SCVIM@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB77027AED70B0023B93A /* MenuIcon-SCVIM@2x.png */; };
@ -117,6 +114,9 @@
5BEDB724283B4C250078EB25 /* data-symbols.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71E283B4AEA0078EB25 /* data-symbols.plist */; }; 5BEDB724283B4C250078EB25 /* data-symbols.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71E283B4AEA0078EB25 /* data-symbols.plist */; };
5BEDB725283B4C250078EB25 /* data-chs.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71C283B4AEA0078EB25 /* data-chs.plist */; }; 5BEDB725283B4C250078EB25 /* data-chs.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71C283B4AEA0078EB25 /* data-chs.plist */; };
5BF0B84C28C070B000795FC6 /* NSEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */; }; 5BF0B84C28C070B000795FC6 /* NSEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */; };
5BF13B9428C627BB00E99EC1 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; };
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF56F9728C39A2700DD6839 /* IMEState.swift */; };
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF56F9928C39D1800DD6839 /* IMEStateData.swift */; };
5BF9DA2728840E6200DBD48E /* template-usersymbolphrases.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */; }; 5BF9DA2728840E6200DBD48E /* template-usersymbolphrases.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */; };
5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2328840E6200DBD48E /* template-exclusions.txt */; }; 5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2328840E6200DBD48E /* template-exclusions.txt */; };
5BF9DA2928840E6200DBD48E /* template-associatedPhrases-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2428840E6200DBD48E /* template-associatedPhrases-chs.txt */; }; 5BF9DA2928840E6200DBD48E /* template-associatedPhrases-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2428840E6200DBD48E /* template-associatedPhrases-chs.txt */; };
@ -134,10 +134,8 @@
6ACA41FD15FC1D9000935EF6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41F015FC1D9000935EF6 /* MainMenu.xib */; }; 6ACA41FD15FC1D9000935EF6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41F015FC1D9000935EF6 /* MainMenu.xib */; };
6ACA420215FC1E5200935EF6 /* vChewing.app in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */; }; 6ACA420215FC1E5200935EF6 /* vChewing.app in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */; };
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; }; D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; };
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; }; D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */; }; D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */; };
D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */; };
D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod_Core.swift */; }; D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod_Core.swift */; };
D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; }; D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; };
D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; }; D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; };
@ -213,6 +211,7 @@
5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = "<group>"; };
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; }; 5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; };
5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; }; 5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
5B18BA7127C7BD8B0056EB19 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 5B18BA7127C7BD8B0056EB19 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@ -229,7 +228,7 @@
5B2170D7289FACAC00BE7304 /* 7_LangModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 7_LangModel.swift; sourceTree = "<group>"; }; 5B2170D7289FACAC00BE7304 /* 7_LangModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 7_LangModel.swift; sourceTree = "<group>"; };
5B2170D8289FACAC00BE7304 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 0_Megrez.swift; sourceTree = "<group>"; }; 5B2170D8289FACAC00BE7304 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 0_Megrez.swift; sourceTree = "<group>"; };
5B2170D9289FACAC00BE7304 /* 8_Unigram.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 8_Unigram.swift; sourceTree = "<group>"; }; 5B2170D9289FACAC00BE7304 /* 8_Unigram.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 8_Unigram.swift; sourceTree = "<group>"; };
5B2170DA289FACAC00BE7304 /* 3_Candidate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 3_Candidate.swift; sourceTree = "<group>"; }; 5B2170DA289FACAC00BE7304 /* 3_KeyValuePaired.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 3_KeyValuePaired.swift; sourceTree = "<group>"; };
5B2170DB289FACAC00BE7304 /* 2_Walker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2_Walker.swift; sourceTree = "<group>"; }; 5B2170DB289FACAC00BE7304 /* 2_Walker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 2_Walker.swift; sourceTree = "<group>"; };
5B2170DC289FACAC00BE7304 /* 6_Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6_Node.swift; sourceTree = "<group>"; }; 5B2170DC289FACAC00BE7304 /* 6_Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6_Node.swift; sourceTree = "<group>"; };
5B2170DD289FACAC00BE7304 /* 4_Span.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 4_Span.swift; sourceTree = "<group>"; }; 5B2170DD289FACAC00BE7304 /* 4_Span.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 4_Span.swift; sourceTree = "<group>"; };
@ -248,9 +247,7 @@
5B5948CD289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMInstantiator_DateTimeExtension.swift; sourceTree = "<group>"; }; 5B5948CD289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMInstantiator_DateTimeExtension.swift; sourceTree = "<group>"; };
5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputSourceHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33527AE795800A19448 /* mgrPrefs.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B62A33527AE795800A19448 /* mgrPrefs.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33727AE79CD00A19448 /* StringUtils.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringUtils.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34327AE7CD900A19448 /* TooltipController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = TooltipController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B62A34327AE7CD900A19448 /* TooltipController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = TooltipController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -297,7 +294,6 @@
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod_Menu.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod_Menu.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; }; 5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; };
5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; }; 5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; };
5BBBB76627AED5DB0023B93A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmNonModalAlertWindow.xib; sourceTree = "<group>"; };
5BBBB76A27AED5DB0023B93A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; }; 5BBBB76A27AED5DB0023B93A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; };
5BBBB76F27AED70B0023B93A /* MenuIcon-TCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM@2x.png"; sourceTree = "<group>"; }; 5BBBB76F27AED70B0023B93A /* MenuIcon-TCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM@2x.png"; sourceTree = "<group>"; };
5BBBB77027AED70B0023B93A /* MenuIcon-SCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM@2x.png"; sourceTree = "<group>"; }; 5BBBB77027AED70B0023B93A /* MenuIcon-SCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM@2x.png"; sourceTree = "<group>"; };
@ -346,6 +342,8 @@
5BEDB720283B4AEA0078EB25 /* data-cht.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = "data-cht.plist"; path = "Data/data-cht.plist"; sourceTree = "<group>"; }; 5BEDB720283B4AEA0078EB25 /* data-cht.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = "data-cht.plist"; path = "Data/data-cht.plist"; sourceTree = "<group>"; };
5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEventExtension.swift; sourceTree = "<group>"; }; 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEventExtension.swift; sourceTree = "<group>"; };
5BF255CD28B2694E003ECB60 /* vChewing-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "vChewing-Bridging-Header.h"; sourceTree = "<group>"; }; 5BF255CD28B2694E003ECB60 /* vChewing-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "vChewing-Bridging-Header.h"; sourceTree = "<group>"; };
5BF56F9728C39A2700DD6839 /* IMEState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMEState.swift; sourceTree = "<group>"; };
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMEStateData.swift; sourceTree = "<group>"; };
5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-usersymbolphrases.txt"; sourceTree = "<group>"; usesTabs = 0; }; 5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-usersymbolphrases.txt"; sourceTree = "<group>"; usesTabs = 0; };
5BF9DA2328840E6200DBD48E /* template-exclusions.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-exclusions.txt"; sourceTree = "<group>"; usesTabs = 0; }; 5BF9DA2328840E6200DBD48E /* template-exclusions.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-exclusions.txt"; sourceTree = "<group>"; usesTabs = 0; };
5BF9DA2428840E6200DBD48E /* template-associatedPhrases-chs.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; name = "template-associatedPhrases-chs.txt"; path = "../Data/components/chs/template-associatedPhrases-chs.txt"; sourceTree = "<group>"; usesTabs = 0; }; 5BF9DA2428840E6200DBD48E /* template-associatedPhrases-chs.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; name = "template-associatedPhrases-chs.txt"; path = "../Data/components/chs/template-associatedPhrases-chs.txt"; sourceTree = "<group>"; usesTabs = 0; };
@ -366,10 +364,8 @@
6ACA41EF15FC1D9000935EF6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 6ACA41EF15FC1D9000935EF6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Installer-Info.plist"; path = "Installer/Installer-Info.plist"; sourceTree = SOURCE_ROOT; }; 6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Installer-Info.plist"; path = "Installer/Installer-Info.plist"; sourceTree = SOURCE_ROOT; };
D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputState.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = main.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = main.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlNonModalAlertWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D4A13D5927A59D5C003BE359 /* ctlInputMethod_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod_Core.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; D4A13D5927A59D5C003BE359 /* ctlInputMethod_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod_Core.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; }; D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; }; D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@ -498,14 +494,14 @@
5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */, 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */,
5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */, 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */,
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */, 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */,
D461B791279DAC010070E734 /* InputState.swift */, 5BF56F9728C39A2700DD6839 /* IMEState.swift */,
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */,
5BD0113C2818543900609769 /* KeyHandler_Core.swift */, 5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */, 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */, 5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */,
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */, 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */, 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */, 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */,
5B62A33727AE79CD00A19448 /* StringUtils.swift */,
5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */, 5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */,
); );
path = ControllerModules; path = ControllerModules;
@ -524,7 +520,7 @@
children = ( children = (
5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */, 5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */,
5B5E535127EF261400C6AA1E /* IME.swift */, 5B5E535127EF261400C6AA1E /* IME.swift */,
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */, 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */,
5B62A33527AE795800A19448 /* mgrPrefs.swift */, 5B62A33527AE795800A19448 /* mgrPrefs.swift */,
); );
path = IMEModules; path = IMEModules;
@ -586,7 +582,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */, 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */,
D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */,
D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */, D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */,
); );
path = WindowControllers; path = WindowControllers;
@ -596,7 +591,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */, 5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */,
5BBBB76527AED5DB0023B93A /* frmNonModalAlertWindow.xib */,
5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */, 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */,
6A187E2816004C5900466B2E /* MainMenu.xib */, 6A187E2816004C5900466B2E /* MainMenu.xib */,
); );
@ -867,7 +861,7 @@
5B2170D8289FACAC00BE7304 /* 0_Megrez.swift */, 5B2170D8289FACAC00BE7304 /* 0_Megrez.swift */,
5B2170DE289FACAC00BE7304 /* 1_Compositor.swift */, 5B2170DE289FACAC00BE7304 /* 1_Compositor.swift */,
5B2170DB289FACAC00BE7304 /* 2_Walker.swift */, 5B2170DB289FACAC00BE7304 /* 2_Walker.swift */,
5B2170DA289FACAC00BE7304 /* 3_Candidate.swift */, 5B2170DA289FACAC00BE7304 /* 3_KeyValuePaired.swift */,
5B2170DD289FACAC00BE7304 /* 4_Span.swift */, 5B2170DD289FACAC00BE7304 /* 4_Span.swift */,
5B2170DF289FACAC00BE7304 /* 5_Vertex.swift */, 5B2170DF289FACAC00BE7304 /* 5_Vertex.swift */,
5B2170DC289FACAC00BE7304 /* 6_Node.swift */, 5B2170DC289FACAC00BE7304 /* 6_Node.swift */,
@ -1070,7 +1064,6 @@
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */, 5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */,
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */, 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */,
D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */, D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */,
5BBBB76B27AED5DB0023B93A /* frmNonModalAlertWindow.xib in Resources */,
5BEDB723283B4C250078EB25 /* data-cht.plist in Resources */, 5BEDB723283B4C250078EB25 /* data-cht.plist in Resources */,
5BEDB721283B4C250078EB25 /* data-cns.plist in Resources */, 5BEDB721283B4C250078EB25 /* data-cns.plist in Resources */,
5BF9DA2D288427E000DBD48E /* template-associatedPhrases-cht.txt in Resources */, 5BF9DA2D288427E000DBD48E /* template-associatedPhrases-cht.txt in Resources */,
@ -1205,7 +1198,7 @@
5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */, 5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */,
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */, 5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */, 5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
D461B792279DAC010070E734 /* InputState.swift in Sources */, 5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */,
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */, 5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */,
D47B92C027972AD100458394 /* main.swift in Sources */, D47B92C027972AD100458394 /* main.swift in Sources */,
D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */, D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */,
@ -1217,7 +1210,6 @@
5B84579F2871AD2200C93B01 /* HotenkaChineseConverter.swift in Sources */, 5B84579F2871AD2200C93B01 /* HotenkaChineseConverter.swift in Sources */,
5B887F302826AEA400B6651E /* lmCoreEX.swift in Sources */, 5B887F302826AEA400B6651E /* lmCoreEX.swift in Sources */,
5BA9FD4627FEF3C9002DE248 /* Container.swift in Sources */, 5BA9FD4627FEF3C9002DE248 /* Container.swift in Sources */,
D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */,
5B2170E5289FACAD00BE7304 /* 6_Node.swift in Sources */, 5B2170E5289FACAD00BE7304 /* 6_Node.swift in Sources */,
5B949BD92816DC5400D87B5D /* LineReader.swift in Sources */, 5B949BD92816DC5400D87B5D /* LineReader.swift in Sources */,
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */, 5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */,
@ -1232,6 +1224,7 @@
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */, 5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */,
5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */, 5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */,
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */,
5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */, 5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */,
5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */, 5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */,
5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */, 5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */,
@ -1246,17 +1239,16 @@
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */, 5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */, 5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */, 5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
5B62A33827AE79CD00A19448 /* StringUtils.swift in Sources */, 5B2170E3289FACAD00BE7304 /* 3_KeyValuePaired.swift in Sources */,
5B2170E3289FACAD00BE7304 /* 3_Candidate.swift in Sources */,
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */, 5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
5B2170E6289FACAD00BE7304 /* 4_Span.swift in Sources */, 5B2170E6289FACAD00BE7304 /* 4_Span.swift in Sources */,
5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */,
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */, 5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */, 5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */,
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */, 5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */,
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */, 5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
5B8457A12871ADBE00C93B01 /* HotenkaCCBridge.swift in Sources */, 5B8457A12871ADBE00C93B01 /* HotenkaCCBridge.swift in Sources */,
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */, 5B40730D281672610023DFFF /* lmReplacements.swift in Sources */,
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */,
5B5E535227EF261400C6AA1E /* IME.swift in Sources */, 5B5E535227EF261400C6AA1E /* IME.swift in Sources */,
5B2170E0289FACAD00BE7304 /* 7_LangModel.swift in Sources */, 5B2170E0289FACAD00BE7304 /* 7_LangModel.swift in Sources */,
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */, 5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */,
@ -1285,7 +1277,7 @@
files = ( files = (
D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */, D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */,
D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */, D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */,
5B62A35327AE89C400A19448 /* InputSourceHelper.swift in Sources */, 5BF13B9428C627BB00E99EC1 /* IMKHelper.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1344,14 +1336,6 @@
name = frmPrefWindow.xib; name = frmPrefWindow.xib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5BBBB76527AED5DB0023B93A /* frmNonModalAlertWindow.xib */ = {
isa = PBXVariantGroup;
children = (
5BBBB76627AED5DB0023B93A /* Base */,
);
name = frmNonModalAlertWindow.xib;
sourceTree = "<group>";
};
5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */ = { 5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
@ -1471,7 +1455,7 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@ -1481,7 +1465,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
@ -1510,13 +1494,13 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
@ -1548,7 +1532,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1570,7 +1554,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1600,7 +1584,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -1618,7 +1602,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1734,7 +1718,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -1763,7 +1747,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1793,7 +1777,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -1816,7 +1800,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -1840,7 +1824,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@ -1861,7 +1845,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingInstaller; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingInstaller;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1884,7 +1868,7 @@
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2310; CURRENT_PROJECT_VERSION = 2400;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@ -1899,7 +1883,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.3.1; MARKETING_VERSION = 2.4.0;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingInstaller; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingInstaller;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";