From 790463d6403b59bcd87f6bf599fbaf00cd9f41a4 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 5 Sep 2022 14:39:00 +0800 Subject: [PATCH] Repo // Introducing IMKHelper. --- .../InputSourceHelper.swift | 0 .../AppleKeyboardConverter.swift | 17 +- .../Modules/ControllerModules/IMEState.swift | 3 +- Source/Modules/IMEModules/IME.swift | 148 +----------- Source/Modules/IMEModules/IMKHelper.swift | 226 ++++++++++++++++++ .../PrefUI/suiPrefPaneKeyboard.swift | 16 +- Source/Modules/main.swift | 2 +- Source/WindowControllers/ctlPrefWindow.swift | 80 +------ vChewing.xcodeproj/project.pbxproj | 10 +- 9 files changed, 256 insertions(+), 246 deletions(-) rename {Source/Modules/IMEModules => Installer}/InputSourceHelper.swift (100%) create mode 100644 Source/Modules/IMEModules/IMKHelper.swift diff --git a/Source/Modules/IMEModules/InputSourceHelper.swift b/Installer/InputSourceHelper.swift similarity index 100% rename from Source/Modules/IMEModules/InputSourceHelper.swift rename to Installer/InputSourceHelper.swift diff --git a/Source/Modules/ControllerModules/AppleKeyboardConverter.swift b/Source/Modules/ControllerModules/AppleKeyboardConverter.swift index c8731c68..45061982 100644 --- a/Source/Modules/ControllerModules/AppleKeyboardConverter.swift +++ b/Source/Modules/ControllerModules/AppleKeyboardConverter.swift @@ -7,23 +7,8 @@ // requirements defined in MIT License. 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 { - AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) + IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) } static func cnvStringApple2ABC(_ strProcessed: String) -> String { diff --git a/Source/Modules/ControllerModules/IMEState.swift b/Source/Modules/ControllerModules/IMEState.swift index 27361e0f..a8d732e1 100644 --- a/Source/Modules/ControllerModules/IMEState.swift +++ b/Source/Modules/ControllerModules/IMEState.swift @@ -134,7 +134,8 @@ extension IMEState { return result } - public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int) -> IMEState + public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int) + -> IMEState { var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor) result.type = .ofCandidates diff --git a/Source/Modules/IMEModules/IME.swift b/Source/Modules/IMEModules/IME.swift index d1dd1ea5..42685365 100644 --- a/Source/Modules/IMEModules/IME.swift +++ b/Source/Modules/IMEModules/IME.swift @@ -13,7 +13,7 @@ import InputMethodKit public enum vChewing {} // The type of input modes. -public enum InputMode: String { +public enum InputMode: String, CaseIterable { case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS" case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT" case imeModeNULL = "" @@ -223,152 +223,6 @@ public enum IME { } 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.fromOpaque(ptrCategory).takeUnretainedValue() - if category != kTISCategoryKeyboardInputSource { - continue - } - } else { - continue - } - - if let ptrASCIICapable = TISGetInputSourceProperty( - source, kTISPropertyInputSourceIsASCIICapable - ) { - let asciiCapable = Unmanaged.fromOpaque(ptrASCIICapable) - .takeUnretainedValue() - if asciiCapable != kCFBooleanTrue { - continue - } - } else { - continue - } - - if let ptrSourceType = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) { - let sourceType = Unmanaged.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.fromOpaque(ptrSourceID).takeUnretainedValue()) - let localizedName = String( - Unmanaged.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 diff --git a/Source/Modules/IMEModules/IMKHelper.swift b/Source/Modules/IMEModules/IMKHelper.swift new file mode 100644 index 00000000..62873e88 --- /dev/null +++ b/Source/Modules/IMEModules/IMKHelper.swift @@ -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 + } +} diff --git a/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift b/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift index 362ba82f..d75bea8b 100644 --- a/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift +++ b/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift @@ -93,12 +93,12 @@ struct suiPrefPaneKeyboard: View { mgrPrefs.mandarinParser = value switch value { case 0: - if !AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { + if !IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) { mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ZhuyinBopomofo" selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout } default: - if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { + if IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) { mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ABC" selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout } @@ -170,15 +170,19 @@ struct suiPrefPaneKeyboard: View { selection: $selBasicKeyboardLayout.onChange { let value = selBasicKeyboardLayout mgrPrefs.basicKeyboardLayout = value - if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(value) { + if IMKHelper.arrDynamicBasicKeyLayouts.contains(value) { mgrPrefs.mandarinParser = 0 selMandarinParser = mgrPrefs.mandarinParser } } ) { - ForEach(0...(IME.arrEnumerateSystemKeyboardLayouts.count - 1), id: \.self) { id in - Text(IME.arrEnumerateSystemKeyboardLayouts[id].strName).tag( - IME.arrEnumerateSystemKeyboardLayouts[id].strValue) + ForEach(0...(IMKHelper.allowedBasicLayoutsAsTISInputSources.count - 1), id: \.self) { id in + let theEntry = IMKHelper.allowedBasicLayoutsAsTISInputSources[id] + if let theEntry = theEntry { + Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier) + } else { + Divider() + } }.id(UUID()) } .labelsHidden() diff --git a/Source/Modules/main.swift b/Source/Modules/main.swift index 59d2a28c..3fb131c1 100644 --- a/Source/Modules/main.swift +++ b/Source/Modules/main.swift @@ -23,7 +23,7 @@ switch max(CommandLine.arguments.count - 1, 0) { switch CommandLine.arguments[1] { case "install": if CommandLine.arguments[1] == "install" { - let exitCode = IME.registerInputMethod() + let exitCode = IMKHelper.registerInputMethod() exit(exitCode) } case "uninstall": diff --git a/Source/WindowControllers/ctlPrefWindow.swift b/Source/WindowControllers/ctlPrefWindow.swift index f777c66e..5b5e4ac7 100644 --- a/Source/WindowControllers/ctlPrefWindow.swift +++ b/Source/WindowControllers/ctlPrefWindow.swift @@ -93,88 +93,24 @@ class ctlPrefWindow: NSWindowController { currentLanguageSelectItem = chosenLanguageItem ?? autoMUISelectItem uiLanguageButton.select(currentLanguageSelectItem) - let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource] var usKeyboardLayoutItem: NSMenuItem? var chosenBaseKeyboardLayoutItem: NSMenuItem? 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 - for source in list { - if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) { - let category = Unmanaged.fromOpaque(categoryPtr).takeUnretainedValue() - if category != kTISCategoryKeyboardInputSource { - continue - } - } else { + for source in IMKHelper.allowedBasicLayoutsAsTISInputSources { + guard let source = source else { + basicKeyboardLayoutButton.menu?.addItem(NSMenuItem.separator()) continue } - - if let asciiCapablePtr = TISGetInputSourceProperty( - source, kTISPropertyInputSourceIsASCIICapable - ) { - let asciiCapable = Unmanaged.fromOpaque(asciiCapablePtr) - .takeUnretainedValue() - if asciiCapable != kCFBooleanTrue { - continue - } - } else { - continue - } - - if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) { - let sourceType = Unmanaged.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.fromOpaque(sourceIDPtr).takeUnretainedValue()) - let localizedName = String( - Unmanaged.fromOpaque(localizedNamePtr).takeUnretainedValue()) - let menuItem = NSMenuItem() - menuItem.title = localizedName - menuItem.representedObject = sourceID - - if sourceID == "com.apple.keylayout.US" { - usKeyboardLayoutItem = menuItem - } - if basicKeyboardLayoutID == sourceID { - chosenBaseKeyboardLayoutItem = menuItem - } - if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) || sourceID.contains("vChewing") { - 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 + menuItem.title = source.vChewingLocalizedName + menuItem.representedObject = source.identifier + if source.identifier == "com.apple.keylayout.US" { usKeyboardLayoutItem = menuItem } + if basicKeyboardLayoutID == source.identifier { chosenBaseKeyboardLayoutItem = menuItem } + basicKeyboardLayoutButton.menu?.addItem(menuItem) } basicKeyboardLayoutButton.select(chosenBaseKeyboardLayoutItem ?? usKeyboardLayoutItem) diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 532e26c6..a4724f9b 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; }; 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.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 */; }; 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 */; }; @@ -32,7 +33,6 @@ 5B5948CE289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5948CD289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift */; }; 5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.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 */; }; 5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; }; 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; }; @@ -116,6 +116,7 @@ 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 */; }; 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 */; }; @@ -213,6 +214,7 @@ 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; + 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = ""; }; 5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = ""; }; 5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; 5B18BA7127C7BD8B0056EB19 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -524,7 +526,7 @@ children = ( 5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */, 5B5E535127EF261400C6AA1E /* IME.swift */, - 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */, + 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */, 5B62A33527AE795800A19448 /* mgrPrefs.swift */, ); path = IMEModules; @@ -884,6 +886,7 @@ 5BBBB77827AEDB330023B93A /* Resources */, D4F0BBE0279AF8B30071253C /* AppDelegate.swift */, D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */, + 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */, 6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */, 5BC0AACA27F58472002D33E9 /* pkgPostInstall.sh */, 5BC0AAC927F58472002D33E9 /* pkgPreInstall.sh */, @@ -1250,13 +1253,13 @@ 5B2170E3289FACAD00BE7304 /* 3_KeyValuePaired.swift in Sources */, 5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */, 5B2170E6289FACAD00BE7304 /* 4_Span.swift in Sources */, + 5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */, 5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */, 5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */, 5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */, 5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */, 5B8457A12871ADBE00C93B01 /* HotenkaCCBridge.swift in Sources */, 5B40730D281672610023DFFF /* lmReplacements.swift in Sources */, - 5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */, 5B5E535227EF261400C6AA1E /* IME.swift in Sources */, 5B2170E0289FACAD00BE7304 /* 7_LangModel.swift in Sources */, 5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */, @@ -1286,6 +1289,7 @@ D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */, D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */, 5B62A35327AE89C400A19448 /* InputSourceHelper.swift in Sources */, + 5BF13B9428C627BB00E99EC1 /* IMKHelper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };