Zonble: Pref Module + Interface to choose Conv Engine
- We removed the VXHanConvert from the codes inherited from upstream. - OpenCC in vChewing is for converting things to KangXi. It doesn't convert Simplified Chinese to Traditional (nor vise versa). - Fixed a bug to ensure that the candidate keys won't be niled in the preferencesWindowController. - Fixed a bug to ensure that the customized candidate keys will always consist of unique characters. Entries like "1145141919" will be recognized as "1459" prior to the validity check process. Co-Authored-By: Weizhong Yang a.k.a zonble <zonble@gmail.com>
This commit is contained in:
parent
a541117ae3
commit
0bc695d081
|
@ -203,7 +203,7 @@
|
|||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="o6K-VN-uxe"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Check for updates automatically" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" state="on" inset="2" id="Z9t-P0-BLF">
|
||||
<buttonCell key="cell" type="check" title="Check for updates automatically" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="Z9t-P0-BLF">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// EmacsKeyHelper.swift
|
||||
//
|
||||
// Copyright (c) 2011-2022 The OpenVanilla Project.
|
||||
//
|
||||
// Contributors:
|
||||
// Weizhong Yang (@zonble) @ OpenVanilla
|
||||
//
|
||||
// Based on the Syrup Project and the Formosana Library
|
||||
// by Lukhnos Liu (@lukhnos).
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
@objc enum vChewingEmacsKey: UInt16 {
|
||||
case none = 0
|
||||
case forward = 6 // F
|
||||
case backward = 2 // B
|
||||
case home = 1 // A
|
||||
case end = 5 // E
|
||||
case delete = 4 // D
|
||||
case nextPage = 22 // V
|
||||
}
|
||||
|
||||
class EmacsKeyHelper: NSObject {
|
||||
@objc static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> vChewingEmacsKey {
|
||||
if flags.contains(.control) {
|
||||
return vChewingEmacsKey(rawValue: charCode) ?? .none
|
||||
}
|
||||
return .none;
|
||||
}
|
||||
}
|
|
@ -80,11 +80,5 @@
|
|||
|
||||
// current input mode
|
||||
NSString *_inputMode;
|
||||
|
||||
// if Chinese conversion is enabled
|
||||
BOOL _chineseConversionEnabled;
|
||||
|
||||
// if half-width punctuation is enabled
|
||||
BOOL _halfWidthPunctuationEnabled;
|
||||
}
|
||||
@end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// PreferencesWindowController.swift
|
||||
// OpenCCBridge.swift
|
||||
//
|
||||
// Copyright (c) 2011-2022 The OpenVanilla Project.
|
||||
//
|
||||
|
@ -34,22 +34,23 @@
|
|||
import Foundation
|
||||
import OpenCC
|
||||
|
||||
// Since SwiftyLibreCC only provide Swift classes, we create an NSObject subclass
|
||||
// in Swift in order to bridge the Swift classes into our Objective-C++ project.
|
||||
class OpenCCBridge : NSObject {
|
||||
/// A bridge to let Objctive-C code to access SwiftyOpenCC.
|
||||
///
|
||||
/// Since SwiftyOpenCC only provide Swift classes, we create an NSObject subclass
|
||||
/// in Swift in order to bridge the Swift classes into our Objective-C++ project.
|
||||
public class OpenCCBridge: NSObject {
|
||||
private static let shared = OpenCCBridge()
|
||||
private var converter: ChineseConverter?
|
||||
|
||||
private override init() {
|
||||
try? converter = ChineseConverter(options: .twStandardRev)
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc static func convert(_ string: String) -> String? {
|
||||
/// Converts to Simplified Chinese.
|
||||
///
|
||||
/// - Parameter string: Text in Traditional Chinese.
|
||||
/// - Returns: Text in Simplified Chinese.
|
||||
@objc public static func convertToKangXi(_ string: String) -> String? {
|
||||
shared.converter?.convert(string)
|
||||
}
|
||||
|
||||
private func convert(_ string: String) -> String? {
|
||||
converter?.convert(string)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
//
|
||||
// Preferences.swift
|
||||
//
|
||||
// Copyright (c) 2011-2022 The OpenVanilla Project.
|
||||
//
|
||||
// Contributors:
|
||||
// Weizhong Yang (@zonble) @ OpenVanilla
|
||||
//
|
||||
// Based on the Syrup Project and the Formosana Library
|
||||
// by Lukhnos Liu (@lukhnos).
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
private let kKeyboardLayoutPreferenceKey = "KeyboardLayout"
|
||||
private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout"; // alphanumeric ("ASCII") input basi
|
||||
private let kFunctionKeyKeyboardLayoutPreferenceKey = "FunctionKeyKeyboardLayout"; // alphanumeric ("ASCII") input basi
|
||||
private let kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey = "FunctionKeyKeyboardLayoutOverrideIncludeShift"; // whether include shif
|
||||
private let kCandidateListTextSizeKey = "CandidateListTextSize"
|
||||
private let kSelectPhraseAfterCursorAsCandidatePreferenceKey = "SelectPhraseAfterCursorAsCandidate"
|
||||
private let kUseHorizontalCandidateListPreferenceKey = "UseHorizontalCandidateList"
|
||||
private let kComposingBufferSizePreferenceKey = "ComposingBufferSize"
|
||||
private let kChooseCandidateUsingSpaceKey = "ChooseCandidateUsingSpaceKey"
|
||||
private let kChineseConversionEnabledKey = "ChineseConversionEnabled"
|
||||
private let kHalfWidthPunctuationEnabledKey = "HalfWidthPunctuationEnable"
|
||||
private let kEscToCleanInputBufferKey = "EscToCleanInputBuffer"
|
||||
|
||||
private let kCandidateTextFontName = "CandidateTextFontName"
|
||||
private let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
|
||||
private let kCandidateKeys = "CandidateKeys"
|
||||
private let kChineseConversionEngineKey = "ChineseConversionEngine"
|
||||
|
||||
private let kDefaultCandidateListTextSize: CGFloat = 18
|
||||
private let kMinKeyLabelSize: CGFloat = 10
|
||||
private let kMinCandidateListTextSize: CGFloat = 12
|
||||
private let kMaxCandidateListTextSize: CGFloat = 196
|
||||
|
||||
// default, min and max composing buffer size (in codepoints)
|
||||
// modern Macs can usually work up to 16 codepoints when the builder still
|
||||
// walks the grid with good performance; slower Macs (like old PowerBooks)
|
||||
// will start to sputter beyond 12; such is the algorithmatic complexity
|
||||
// of the Viterbi algorithm used in the builder library (at O(N^2))
|
||||
private let kDefaultComposingBufferSize = 10
|
||||
private let kMinComposingBufferSize = 4
|
||||
private let kMaxComposingBufferSize = 20
|
||||
|
||||
private let kDefaultKeys = "123456789"
|
||||
|
||||
// MARK: Property wrappers
|
||||
@propertyWrapper
|
||||
struct UserDefault<Value> {
|
||||
let key: String
|
||||
let defaultValue: Value
|
||||
var container: UserDefaults = .standard
|
||||
|
||||
var wrappedValue: Value {
|
||||
get {
|
||||
return container.object(forKey: key) as? Value ?? defaultValue
|
||||
}
|
||||
set {
|
||||
container.set(newValue, forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
struct CandidateListTextSize {
|
||||
let key: String
|
||||
let defaultValue: CGFloat = kDefaultCandidateListTextSize
|
||||
lazy var container: UserDefault = {
|
||||
UserDefault(key: key, defaultValue: defaultValue) }()
|
||||
|
||||
var wrappedValue: CGFloat {
|
||||
mutating get {
|
||||
var value = container.wrappedValue
|
||||
if value < kMinCandidateListTextSize {
|
||||
value = kMinCandidateListTextSize
|
||||
} else if value > kMaxCandidateListTextSize {
|
||||
value = kMaxCandidateListTextSize
|
||||
}
|
||||
return value
|
||||
}
|
||||
set {
|
||||
var value = newValue
|
||||
if value < kMinCandidateListTextSize {
|
||||
value = kMinCandidateListTextSize
|
||||
} else if value > kMaxCandidateListTextSize {
|
||||
value = kMaxCandidateListTextSize
|
||||
}
|
||||
container.wrappedValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
struct ComposingBufferSize {
|
||||
let key: String
|
||||
let defaultValue: Int = kDefaultComposingBufferSize
|
||||
lazy var container: UserDefault = {
|
||||
UserDefault(key: key, defaultValue: defaultValue) }()
|
||||
|
||||
var wrappedValue: Int {
|
||||
mutating get {
|
||||
let currentValue = container.wrappedValue
|
||||
if currentValue < kMinComposingBufferSize {
|
||||
return kMinComposingBufferSize
|
||||
} else if currentValue > kMaxComposingBufferSize {
|
||||
return kMaxComposingBufferSize
|
||||
}
|
||||
return currentValue
|
||||
}
|
||||
set {
|
||||
var value = newValue
|
||||
if value < kMinComposingBufferSize {
|
||||
value = kMinComposingBufferSize
|
||||
} else if value > kMaxComposingBufferSize {
|
||||
value = kMaxComposingBufferSize
|
||||
}
|
||||
container.wrappedValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
struct ComposingKeys {
|
||||
let key: String
|
||||
let defaultValue: String? = kCandidateKeys
|
||||
lazy var container: UserDefault = {
|
||||
UserDefault(key: key, defaultValue: defaultValue) }()
|
||||
|
||||
var wrappedValue: String? {
|
||||
mutating get {
|
||||
let value = container.wrappedValue
|
||||
if let value = value {
|
||||
if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
set {
|
||||
let value = newValue
|
||||
if let value = value {
|
||||
if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
container.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
}
|
||||
container.wrappedValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@objc enum KeyboardLayout: Int {
|
||||
case standard
|
||||
case eten
|
||||
case eten26
|
||||
case hsu
|
||||
case hanyuPinyin
|
||||
case IBM
|
||||
|
||||
var name: String {
|
||||
switch (self) {
|
||||
case .standard:
|
||||
return "Standard"
|
||||
case .eten:
|
||||
return "ETen"
|
||||
case .eten26:
|
||||
return "ETen26"
|
||||
case .hsu:
|
||||
return "Hsu"
|
||||
case .hanyuPinyin:
|
||||
return "HanyuPinyin"
|
||||
case .IBM:
|
||||
return "IBM"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc enum ChineseConversionEngine: Int {
|
||||
case openCC
|
||||
case vxHanConvert
|
||||
|
||||
var name: String {
|
||||
switch (self) {
|
||||
case .openCC:
|
||||
return "OpenCC"
|
||||
case .vxHanConvert:
|
||||
return "VXHanConvert"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
class Preferences: NSObject {
|
||||
@UserDefault(key: kKeyboardLayoutPreferenceKey, defaultValue: 0)
|
||||
@objc static var keyboardLayout: Int
|
||||
|
||||
@objc static var keyboardLayoutName: String {
|
||||
(KeyboardLayout(rawValue: self.keyboardLayout) ?? KeyboardLayout.standard).name
|
||||
}
|
||||
|
||||
@UserDefault(key: kBasisKeyboardLayoutPreferenceKey, defaultValue: "com.apple.keylayout.US")
|
||||
@objc static var basisKeyboardLayout: String
|
||||
|
||||
@UserDefault(key: kFunctionKeyKeyboardLayoutPreferenceKey, defaultValue: "com.apple.keylayout.US")
|
||||
@objc static var functionKeyboardLayout: String
|
||||
|
||||
@UserDefault(key: kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey, defaultValue: false)
|
||||
@objc static var functionKeyKeyboardLayoutOverrideIncludeShiftKey: Bool
|
||||
|
||||
@CandidateListTextSize(key: kCandidateListTextSizeKey)
|
||||
@objc static var candidateListTextSize: CGFloat
|
||||
|
||||
@UserDefault(key: kSelectPhraseAfterCursorAsCandidatePreferenceKey, defaultValue: false)
|
||||
@objc static var selectPhraseAfterCursorAsCandidate: Bool
|
||||
|
||||
@UserDefault(key: kUseHorizontalCandidateListPreferenceKey, defaultValue: false)
|
||||
@objc static var useHorizontalCandidateList: Bool
|
||||
|
||||
@ComposingBufferSize(key: kComposingBufferSizePreferenceKey)
|
||||
@objc static var composingBufferSize: Int
|
||||
|
||||
@UserDefault(key: kChooseCandidateUsingSpaceKey, defaultValue: true)
|
||||
@objc static var chooseCandidateUsingSpace: Bool
|
||||
|
||||
@UserDefault(key: kChineseConversionEnabledKey, defaultValue: false)
|
||||
@objc static var chineseConversionEnabled: Bool
|
||||
|
||||
@objc static func toggleChineseConversionEnabled() -> Bool {
|
||||
chineseConversionEnabled = !chineseConversionEnabled
|
||||
return chineseConversionEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: kHalfWidthPunctuationEnabledKey, defaultValue: false)
|
||||
@objc static var halfWidthPunctuationEnabled: Bool
|
||||
|
||||
@objc static func toogleHalfWidthPunctuationEnabled() -> Bool {
|
||||
halfWidthPunctuationEnabled = !halfWidthPunctuationEnabled
|
||||
return halfWidthPunctuationEnabled;
|
||||
}
|
||||
|
||||
@UserDefault(key: kEscToCleanInputBufferKey, defaultValue: false)
|
||||
@objc static var escToCleanInputBuffer: Bool
|
||||
|
||||
// MARK: Optional settings
|
||||
@UserDefault(key: kCandidateTextFontName, defaultValue: nil)
|
||||
@objc static var candidateTextFontName: String?
|
||||
|
||||
@UserDefault(key: kCandidateKeyLabelFontName, defaultValue: nil)
|
||||
@objc static var candidateKeyLabelFontName: String?
|
||||
|
||||
@ComposingKeys(key: kCandidateKeys)
|
||||
@objc static var candidateKeys: String?
|
||||
|
||||
@objc static var defaultKeys: String {
|
||||
kDefaultKeys
|
||||
}
|
||||
|
||||
@UserDefault(key: kChineseConversionEngineKey, defaultValue: 0)
|
||||
@objc static var chineseConversionEngine: Int
|
||||
|
||||
@objc static var chineseConversionEngineName: String? {
|
||||
return ChineseConversionEngine(rawValue: chineseConversionEngine)?.name
|
||||
}
|
||||
|
||||
}
|
|
@ -36,9 +36,13 @@
|
|||
import Cocoa
|
||||
import Carbon
|
||||
|
||||
private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout"
|
||||
private let kCandidateKeys = "CandidateKeys"
|
||||
private let kDefaultKeys = "123456789"
|
||||
// Extend the RangeReplaceableCollection to allow it clean duplicated characters.
|
||||
extension RangeReplaceableCollection where Element: Hashable {
|
||||
var charDeDuplicate: Self {
|
||||
var set = Set<Element>()
|
||||
return filter{ set.insert($0).inserted }
|
||||
}
|
||||
}
|
||||
|
||||
// Please note that the class should be exposed as "PreferencesWindowController"
|
||||
// in Objective-C in order to let IMK to see the same class name as
|
||||
|
@ -47,55 +51,78 @@ private let kDefaultKeys = "123456789"
|
|||
@IBOutlet weak var fontSizePopUpButton: NSPopUpButton!
|
||||
@IBOutlet weak var basisKeyboardLayoutButton: NSPopUpButton!
|
||||
@IBOutlet weak var selectionKeyComboBox: NSComboBox!
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
|
||||
var usKeyboardLayoutItem: NSMenuItem? = nil
|
||||
var chosenItem: NSMenuItem? = nil
|
||||
|
||||
|
||||
basisKeyboardLayoutButton.menu?.removeAllItems()
|
||||
|
||||
let basisKeyboardLayoutID = UserDefaults.standard.string(forKey: kBasisKeyboardLayoutPreferenceKey)
|
||||
|
||||
let basisKeyboardLayoutID = Preferences.basisKeyboardLayout
|
||||
for source in list {
|
||||
if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) {
|
||||
let category = Unmanaged<CFString>.fromOpaque(categoryPtr).takeUnretainedValue()
|
||||
if category != kTISCategoryKeyboardInputSource {
|
||||
|
||||
func getString(_ key: CFString) -> String? {
|
||||
if let ptr = TISGetInputSourceProperty(source, key) {
|
||||
return String(Unmanaged<CFString>.fromOpaque(ptr).takeUnretainedValue())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBool(_ key: CFString) -> Bool? {
|
||||
if let ptr = TISGetInputSourceProperty(source, key) {
|
||||
return Unmanaged<CFBoolean>.fromOpaque(ptr).takeUnretainedValue() == kCFBooleanTrue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if let category = getString(kTISPropertyInputSourceCategory) {
|
||||
if category != String(kTISCategoryKeyboardInputSource) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let asciiCapablePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsASCIICapable) {
|
||||
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr).takeUnretainedValue()
|
||||
if asciiCapable != kCFBooleanTrue {
|
||||
|
||||
if let asciiCapable = getBool(kTISPropertyInputSourceIsASCIICapable) {
|
||||
if !asciiCapable {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) {
|
||||
let sourceType = Unmanaged<CFString>.fromOpaque(sourceTypePtr).takeUnretainedValue()
|
||||
if sourceType != kTISTypeKeyboardLayout {
|
||||
|
||||
if let sourceType = getString(kTISPropertyInputSourceType) {
|
||||
if sourceType != String(kTISTypeKeyboardLayout) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard let sourceIDPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceID),
|
||||
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName) else {
|
||||
continue
|
||||
}
|
||||
|
||||
let sourceID = String(Unmanaged<CFString>.fromOpaque(sourceIDPtr).takeUnretainedValue())
|
||||
let localizedName = String(Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
|
||||
|
||||
|
||||
guard let sourceID = getString(kTISPropertyInputSourceID),
|
||||
let localizedName = getString(kTISPropertyLocalizedName) else {
|
||||
continue
|
||||
}
|
||||
|
||||
let menuItem = NSMenuItem()
|
||||
menuItem.title = localizedName
|
||||
menuItem.representedObject = sourceID
|
||||
|
||||
|
||||
if let iconPtr = TISGetInputSourceProperty(source, kTISPropertyIconRef) {
|
||||
let icon = IconRef(iconPtr)
|
||||
let image = NSImage(iconRef: icon)
|
||||
|
||||
func resize( _ image: NSImage) -> NSImage {
|
||||
let newImage = NSImage(size: NSSize(width: 16, height: 16))
|
||||
newImage.lockFocus()
|
||||
image.draw(in: NSRect(x: 0, y: 0, width: 16, height: 16))
|
||||
newImage.unlockFocus()
|
||||
return newImage
|
||||
}
|
||||
menuItem.image = resize(image)
|
||||
}
|
||||
|
||||
if sourceID == "com.apple.keylayout.US" {
|
||||
usKeyboardLayoutItem = menuItem
|
||||
}
|
||||
|
@ -104,41 +131,41 @@ private let kDefaultKeys = "123456789"
|
|||
}
|
||||
basisKeyboardLayoutButton.menu?.addItem(menuItem)
|
||||
}
|
||||
|
||||
|
||||
basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem)
|
||||
selectionKeyComboBox.usesDataSource = false
|
||||
selectionKeyComboBox.addItems(withObjectValues: [kDefaultKeys, "asdfghjkl", "asdfzxcvb"])
|
||||
|
||||
var candidateSelectionKeys = (UserDefaults.standard.string(forKey: kCandidateKeys) ?? kDefaultKeys)
|
||||
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
selectionKeyComboBox.removeAllItems()
|
||||
selectionKeyComboBox.addItems(withObjectValues: [Preferences.defaultKeys, "ASDFGHJKL", "ASDFZXCVB"])
|
||||
|
||||
var candidateSelectionKeys = Preferences.candidateKeys ?? Preferences.defaultKeys
|
||||
if candidateSelectionKeys.isEmpty {
|
||||
candidateSelectionKeys = kDefaultKeys
|
||||
candidateSelectionKeys = Preferences.defaultKeys
|
||||
}
|
||||
|
||||
|
||||
selectionKeyComboBox.stringValue = candidateSelectionKeys
|
||||
}
|
||||
|
||||
@IBAction func updateBasisKeyboardLayoutAction(_ sender:Any) {
|
||||
if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject {
|
||||
UserDefaults.standard.set(sourceID, forKey: kBasisKeyboardLayoutPreferenceKey)
|
||||
|
||||
@IBAction func updateBasisKeyboardLayoutAction(_ sender: Any) {
|
||||
if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject as? String {
|
||||
Preferences.basisKeyboardLayout = sourceID
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func changeSelectionKeyAction(_ sender: Any) {
|
||||
let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).charDeDuplicate
|
||||
if keys.count != 9 || !keys.canBeConverted(to: .ascii) {
|
||||
selectionKeyComboBox.stringValue = kDefaultKeys
|
||||
UserDefaults.standard.removeObject(forKey: kCandidateKeys)
|
||||
selectionKeyComboBox.stringValue = Preferences.defaultKeys
|
||||
Preferences.candidateKeys = nil
|
||||
NSSound.beep()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
selectionKeyComboBox.stringValue = keys
|
||||
if keys == kDefaultKeys {
|
||||
UserDefaults.standard.removeObject(forKey: kCandidateKeys)
|
||||
if keys == Preferences.defaultKeys {
|
||||
Preferences.candidateKeys = nil
|
||||
} else {
|
||||
UserDefaults.standard.set(keys, forKey: kCandidateKeys)
|
||||
Preferences.candidateKeys = keys
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
"Visit Website" = "Visit Website";
|
||||
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@";
|
||||
"Chinese Conversion" = "Convert zh-TW Kanji to KangXi Variants";
|
||||
"NotificationSwitchON" = " ON";
|
||||
"NotificationSwitchOFF" = " OFF";
|
||||
"NotificationSwitchON" = "✔ ON";
|
||||
"NotificationSwitchOFF" = "✘ OFF";
|
||||
"Edit User Phrases" = "Edit User Phrases";
|
||||
"Reload User Phrases" = "Reload User Phrases";
|
||||
"Unable to create the user phrase file." = "Unable to create the user phrase file.";
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
"Visit Website" = "前往网站";
|
||||
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "目前使用的威注音官方版本是 %1$@ (%2$@),网路上有更新版本 %3$@ (%4$@) 可供下载。是否要前往威注音网站下载新版来安装?%5$@";
|
||||
"Chinese Conversion" = "优先使用康熙繁体字";
|
||||
"NotificationSwitchON" = " 已启用";
|
||||
"NotificationSwitchOFF" = " 已停用";
|
||||
"NotificationSwitchON" = "✔ 已启用";
|
||||
"NotificationSwitchOFF" = "✘ 已停用";
|
||||
"Edit User Phrases" = "编辑自订语汇";
|
||||
"Reload User Phrases" = "重载自订语汇";
|
||||
"Unable to create the user phrase file." = "无法创建自订语汇档案。";
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
"Visit Website" = "前往網站";
|
||||
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@" = "目前使用的威注音官方版本是 %1$@ (%2$@),網路上有更新版本 %3$@ (%4$@) 可供下載。是否要前往威注音網站下載新版來安裝?%5$@";
|
||||
"Chinese Conversion" = "優先使用康熙繁體字";
|
||||
"NotificationSwitchON" = " 已啟用";
|
||||
"NotificationSwitchOFF" = " 已停用";
|
||||
"NotificationSwitchON" = "✔ 已啟用";
|
||||
"NotificationSwitchOFF" = "✘ 已停用";
|
||||
"Edit User Phrases" = "編輯自訂語彙";
|
||||
"Reload User Phrases" = "重載自訂語彙";
|
||||
"Unable to create the user phrase file." = "無法創建自訂語彙檔案。";
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
5B5F4F972792A4EA00922DC2 /* UserPhrasesLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B5F4F962792A4EA00922DC2 /* UserPhrasesLM.cpp */; };
|
||||
5BC2D2872793B434002C0BEC /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BC2D2852793B434002C0BEC /* CMakeLists.txt */; };
|
||||
5BC2D2882793B434002C0BEC /* KeyValueBlobReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D2862793B434002C0BEC /* KeyValueBlobReader.cpp */; };
|
||||
5BC2D28B2793B8FB002C0BEC /* EmacsKeyHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D28A2793B8FB002C0BEC /* EmacsKeyHelper.swift */; };
|
||||
5BC2D28D2793B98F002C0BEC /* PreferencesModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2D28C2793B98F002C0BEC /* PreferencesModule.swift */; };
|
||||
5BC3EE1B278FC48C00F5E44C /* VerticalCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE18278FC48C00F5E44C /* VerticalCandidateController.swift */; };
|
||||
5BC3EE1C278FC48C00F5E44C /* VTCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE19278FC48C00F5E44C /* VTCandidateController.swift */; };
|
||||
5BC3EE1D278FC48C00F5E44C /* HorizontalCandidateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3EE1A278FC48C00F5E44C /* HorizontalCandidateController.swift */; };
|
||||
|
@ -105,6 +107,8 @@
|
|||
5BC2D2842793B434002C0BEC /* KeyValueBlobReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyValueBlobReader.h; sourceTree = "<group>"; };
|
||||
5BC2D2852793B434002C0BEC /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
|
||||
5BC2D2862793B434002C0BEC /* KeyValueBlobReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyValueBlobReader.cpp; sourceTree = "<group>"; };
|
||||
5BC2D28A2793B8FB002C0BEC /* EmacsKeyHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmacsKeyHelper.swift; sourceTree = "<group>"; };
|
||||
5BC2D28C2793B98F002C0BEC /* PreferencesModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesModule.swift; sourceTree = "<group>"; };
|
||||
5BC3EE18278FC48C00F5E44C /* VerticalCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticalCandidateController.swift; sourceTree = "<group>"; };
|
||||
5BC3EE19278FC48C00F5E44C /* VTCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VTCandidateController.swift; sourceTree = "<group>"; };
|
||||
5BC3EE1A278FC48C00F5E44C /* HorizontalCandidateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalCandidateController.swift; sourceTree = "<group>"; };
|
||||
|
@ -272,6 +276,14 @@
|
|||
path = vChewing;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5BC2D2892793B8DB002C0BEC /* Keyboard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BC2D28A2793B8FB002C0BEC /* EmacsKeyHelper.swift */,
|
||||
);
|
||||
path = Keyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5BE798A12792E50F00337FF9 /* UI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -336,7 +348,6 @@
|
|||
6A0D4F1215FC0EB100ABF4B3 /* Engine */,
|
||||
6ACA41E715FC1D9000935EF6 /* Installer */,
|
||||
6A0D4F4715FC0EB900ABF4B3 /* Resources */,
|
||||
5BDF2D052791DA6700838ADB /* AppDelegate.swift */,
|
||||
5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */,
|
||||
5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */,
|
||||
6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */,
|
||||
|
@ -345,10 +356,12 @@
|
|||
5B5F4F92279294A300922DC2 /* LanguageModelManager.mm */,
|
||||
6A0D4EC815FC0D6400ABF4B3 /* main.m */,
|
||||
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */,
|
||||
5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */,
|
||||
5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */,
|
||||
D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */,
|
||||
5BDF2D052791DA6700838ADB /* AppDelegate.swift */,
|
||||
5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */,
|
||||
5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */,
|
||||
D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */,
|
||||
5BC2D28C2793B98F002C0BEC /* PreferencesModule.swift */,
|
||||
5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */,
|
||||
D427A9BF25ED28CC005D43E0 /* vChewing-Bridging-Header.h */,
|
||||
5BA923AC2791B7C20001323A /* vChewingInstaller-Bridging-Header.h */,
|
||||
);
|
||||
|
@ -380,6 +393,7 @@
|
|||
6A0D4F1215FC0EB100ABF4B3 /* Engine */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BC2D2892793B8DB002C0BEC /* Keyboard */,
|
||||
5BC2D2832793B434002C0BEC /* vChewing */,
|
||||
5BA8DAFE27928120009C9FFF /* LanguageModel */,
|
||||
6A0D4F1315FC0EB100ABF4B3 /* Gramambular */,
|
||||
|
@ -688,6 +702,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5BDF2CFE2791BE4400838ADB /* InputSourceHelper.swift in Sources */,
|
||||
5BC2D28B2793B8FB002C0BEC /* EmacsKeyHelper.swift in Sources */,
|
||||
5BE798A72793280C00337FF9 /* NotifierController.swift in Sources */,
|
||||
5B5F4F93279294A300922DC2 /* LanguageModelManager.mm in Sources */,
|
||||
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
|
||||
|
@ -704,6 +719,7 @@
|
|||
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
|
||||
6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */,
|
||||
5BDF2D012791C03B00838ADB /* PreferencesWindowController.swift in Sources */,
|
||||
5BC2D28D2793B98F002C0BEC /* PreferencesModule.swift in Sources */,
|
||||
5BC3EE1C278FC48C00F5E44C /* VTCandidateController.swift in Sources */,
|
||||
5BDF2D032791C71200838ADB /* NonModalAlertWindowController.swift in Sources */,
|
||||
5BC3EE1D278FC48C00F5E44C /* HorizontalCandidateController.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue