SessionCtl // Refactor the menu structure.
This commit is contained in:
parent
a219b7881f
commit
bb9bc058cc
|
@ -0,0 +1,344 @@
|
|||
// (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 AppKit
|
||||
import CocoaExtension
|
||||
import NotifierUI
|
||||
import Shared
|
||||
import SwiftExtension
|
||||
|
||||
// MARK: - IME Menu Manager
|
||||
|
||||
// 因為選單部分的內容又臭又長,所以就單獨拉到一個檔案內管理了。
|
||||
|
||||
extension SessionCtl {
|
||||
var optionKeyPressed: Bool { NSEvent.keyModifierFlags.contains(.option) }
|
||||
var silentMode: Bool { clientBundleIdentifier == "com.apple.SecurityAgent" }
|
||||
|
||||
var currentRAMUsageDescription: String? {
|
||||
guard PrefMgr.shared.isDebugModeEnabled else { return nil }
|
||||
guard let currentMemorySizeInBytes = NSApplication.memoryFootprint else { return nil }
|
||||
let currentMemorySize: Double = (Double(currentMemorySizeInBytes) / 1024 / 1024).rounded(toPlaces: 1)
|
||||
return "Total RAM Usage: \(currentMemorySize)MB"
|
||||
}
|
||||
|
||||
override public func menu() -> NSMenu {
|
||||
.init().appendItems(self) {
|
||||
NSMenu.Item(verbatim: currentRAMUsageDescription)
|
||||
NSMenu.Item(
|
||||
verbatim: String(
|
||||
format: "Switch to %@ Input Mode".localized,
|
||||
IMEApp.currentInputMode.reversed.localizedDescription
|
||||
)
|
||||
)?.act(#selector(switchInputMode(_:)))
|
||||
.hotkey(PrefMgr.shared.usingHotKeyInputMode ? "D" : "", mask: [.command, .control])
|
||||
NSMenu.Item("Per-Char Select Mode")?
|
||||
.act(#selector(toggleSCPCTypingMode(_:)))
|
||||
.state(PrefMgr.shared.useSCPCTypingMode)
|
||||
.hotkey(PrefMgr.shared.usingHotKeySCPC ? "P" : "", mask: [.command, .control])
|
||||
NSMenu.Item("Associated Phrases")?
|
||||
.act(#selector(toggleAssociatedPhrasesEnabled(_:)))
|
||||
.state(PrefMgr.shared.associatedPhrasesEnabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyAssociates ? "O" : "", mask: [.command, .control])
|
||||
NSMenu.Item("CIN Cassette Mode")?
|
||||
.act(#selector(toggleCassetteMode(_:)))
|
||||
.state(PrefMgr.shared.cassetteEnabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyCassette ? "I" : "", mask: [.command, .control])
|
||||
NSMenu.Item("CNS11643 Mode")?
|
||||
.act(#selector(toggleCNS11643Enabled(_:)))
|
||||
.state(PrefMgr.shared.cns11643Enabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyCNS ? "L" : "", mask: [.command, .control])
|
||||
NSMenu.Item("Force KangXi Writing")?
|
||||
.act(#selector(toggleChineseConverter(_:)))
|
||||
.state(PrefMgr.shared.chineseConversionEnabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyKangXi ? "K" : "", mask: [.command, .control])
|
||||
.nulled(IMEApp.currentInputMode != .imeModeCHT)
|
||||
NSMenu.Item("JIS Shinjitai Output")?
|
||||
.act(#selector(toggleShiftJISShinjitaiOutput(_:)))
|
||||
.state(PrefMgr.shared.shiftJISShinjitaiOutputEnabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyJIS ? "J" : "", mask: [.command, .control])
|
||||
.nulled(IMEApp.currentInputMode != .imeModeCHT)
|
||||
NSMenu.Item("Currency Numeral Output")?
|
||||
.act(#selector(toggleCurrencyNumerals(_:)))
|
||||
.state(PrefMgr.shared.currencyNumeralsEnabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyCurrencyNumerals ? "M" : "", mask: [.command, .control])
|
||||
NSMenu.Item("Half-Width Punctuation Mode")?
|
||||
.act(#selector(toggleHalfWidthPunctuation(_:)))
|
||||
.state(PrefMgr.shared.halfWidthPunctuationEnabled)
|
||||
.hotkey(PrefMgr.shared.usingHotKeyHalfWidthASCII ? "H" : "", mask: [.command, .control])
|
||||
NSMenu.Item("Use Phrase Replacement")?
|
||||
.act(#selector(togglePhraseReplacement(_:)))
|
||||
.state(PrefMgr.shared.phraseReplacementEnabled)
|
||||
.nulled(!optionKeyPressed && !PrefMgr.shared.phraseReplacementEnabled)
|
||||
NSMenu.Item("Symbol & Emoji Input")?
|
||||
.act(#selector(toggleSymbolEnabled(_:)))
|
||||
.state(PrefMgr.shared.symbolInputEnabled)
|
||||
.nulled(!optionKeyPressed)
|
||||
|
||||
NSMenu.Item.separator() // ---------------------
|
||||
NSMenu.Item("Open User Dictionary Folder")?.act(#selector(openUserDataFolder(_:))).nulled(silentMode)
|
||||
NSMenu.Item("Edit vChewing User Phrases…")?.act(#selector(openUserPhrases(_:))).nulled(silentMode)
|
||||
NSMenu.Item("Edit Excluded Phrases…")?.act(#selector(openExcludedPhrases(_:))).nulled(silentMode)
|
||||
NSMenu.Item("Edit Associated Phrases…")?.act(#selector(openAssociatedPhrases(_:))).nulled(
|
||||
!(!silentMode && (optionKeyPressed || PrefMgr.shared.associatedPhrasesEnabled))
|
||||
)
|
||||
NSMenu.Item("Edit Phrase Replacement Table…")?.act(#selector(openPhraseReplacement(_:))).nulled(silentMode || !optionKeyPressed)
|
||||
NSMenu.Item("Edit User Symbol & Emoji Data…")?.act(#selector(openUserSymbols(_:))).nulled(silentMode || !optionKeyPressed)
|
||||
NSMenu.Item("Open App Support Folder")?.act(#selector(openAppSupportFolderFromContainer(_:))).nulled(silentMode || !optionKeyPressed)
|
||||
|
||||
NSMenu.Item("Reload User Phrases")?.act(#selector(reloadUserPhrasesData(_:))).nulled(PrefMgr.shared.shouldAutoReloadUserDataFiles && !optionKeyPressed)
|
||||
NSMenu.Item(verbatim: "Reverse Lookup (Phonabets)".localized.withEllipsis)?
|
||||
.act(#selector(callReverseLookupWindow(_:))).hotkey(PrefMgr.shared.usingHotKeyRevLookup ? "/" : "", mask: [.command, .control])
|
||||
|
||||
NSMenu.Item("Optimize Memorized Phrases")?.act(#selector(removeUnigramsFromUOM(_:)))
|
||||
NSMenu.Item("Clear Memorized Phrases")?.act(#selector(clearUOM(_:)))
|
||||
|
||||
NSMenu.Item.separator() // ---------------------
|
||||
if #unavailable(macOS 13) {
|
||||
NSMenu.Item("vChewing Preferences…")?.act(#selector(showPreferences(_:))).nulled(silentMode)
|
||||
} else {
|
||||
NSMenu.Item(
|
||||
verbatim: "vChewing Preferences…".localized + " (SwiftUI)"
|
||||
)?.act(#selector(showSettingsSwiftUI(_:))).nulled(silentMode)
|
||||
NSMenu.Item(
|
||||
verbatim: "vChewing Preferences…".localized + " (AppKit)"
|
||||
)?.act(#selector(showSettingsAppKit(_:))).nulled(silentMode)
|
||||
}
|
||||
NSMenu.Item(verbatim: "Client Manager".localized.withEllipsis)?.act(#selector(showClientListMgr(_:))).nulled(silentMode)
|
||||
NSMenu.Item("Check for Updates…")?.act(#selector(checkForUpdate(_:))).nulled(silentMode)
|
||||
NSMenu.Item("Reboot vChewing…")?.act(#selector(selfTerminate(_:)))
|
||||
NSMenu.Item("About vChewing…")?.act(#selector(showAbout(_:))).nulled(silentMode)
|
||||
NSMenu.Item("CheatSheet")?.act(#selector(showCheatSheet(_:))).nulled(silentMode)
|
||||
NSMenu.Item("Uninstall vChewing…")?.act(#selector(selfUninstall(_:))).nulled(silentMode || !optionKeyPressed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - IME Menu Items
|
||||
|
||||
public extension SessionCtl {
|
||||
@objc override func showPreferences(_: Any? = nil) {
|
||||
osCheck: if #available(macOS 13, *) {
|
||||
switch NSEvent.keyModifierFlags {
|
||||
case .option: break osCheck
|
||||
default: CtlSettingsUI.show()
|
||||
}
|
||||
NSApp.popup()
|
||||
return
|
||||
}
|
||||
CtlSettingsCocoa.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
@objc func showSettingsAppKit(_: Any? = nil) {
|
||||
CtlSettingsCocoa.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
@available(macOS 13, *)
|
||||
@objc func showSettingsSwiftUI(_: Any? = nil) {
|
||||
CtlSettingsUI.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
@objc func showCheatSheet(_: Any? = nil) {
|
||||
guard let url = Bundle.main.url(forResource: "shortcuts", withExtension: "html") else { return }
|
||||
FileOpenMethod.safari.open(url: url)
|
||||
}
|
||||
|
||||
@objc func showClientListMgr(_: Any? = nil) {
|
||||
CtlClientListMgr.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
@objc func toggleCassetteMode(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
if !PrefMgr.shared.cassetteEnabled, !LMMgr.checkCassettePathValidity(PrefMgr.shared.cassettePath) {
|
||||
DispatchQueue.main.async {
|
||||
IMEApp.buzz()
|
||||
let alert = NSAlert(error: "Path invalid or file access error.".localized)
|
||||
let informativeText = "Please reconfigure the cassette path to a valid one before enabling this mode."
|
||||
alert.informativeText = informativeText.localized
|
||||
let result = alert.runModal()
|
||||
NSApp.popup()
|
||||
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
|
||||
LMMgr.resetCassettePath()
|
||||
PrefMgr.shared.cassetteEnabled = false
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Notifier.notify(
|
||||
message: "CIN Cassette Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.cassetteEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
if !inputMode.langModel.isCassetteDataLoaded {
|
||||
LMMgr.loadCassetteData()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggleSCPCTypingMode(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Per-Char Select Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.useSCPCTypingMode.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleChineseConverter(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Force KangXi Writing".localized + "\n"
|
||||
+ (PrefMgr.shared.chineseConversionEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleShiftJISShinjitaiOutput(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "JIS Shinjitai Output".localized + "\n"
|
||||
+ (PrefMgr.shared.shiftJISShinjitaiOutputEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleCurrencyNumerals(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Currency Numeral Output".localized + "\n"
|
||||
+ (PrefMgr.shared.currencyNumeralsEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleHalfWidthPunctuation(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Half-Width Punctuation Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.halfWidthPunctuationEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleCNS11643Enabled(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "CNS11643 Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.cns11643Enabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleSymbolEnabled(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Symbol & Emoji Input".localized + "\n"
|
||||
+ (PrefMgr.shared.symbolInputEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleAssociatedPhrasesEnabled(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Associated Phrases".localized + "\n"
|
||||
+ (PrefMgr.shared.associatedPhrasesEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func togglePhraseReplacement(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Use Phrase Replacement".localized + "\n"
|
||||
+ (PrefMgr.shared.phraseReplacementEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func selfUninstall(_: Any? = nil) {
|
||||
AppDelegate.shared.selfUninstall()
|
||||
}
|
||||
|
||||
@objc func selfTerminate(_: Any? = nil) {
|
||||
NSApp.popup()
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
|
||||
@objc func checkForUpdate(_: Any? = nil) {
|
||||
AppDelegate.shared.checkUpdate(forced: true) { [weak self] in
|
||||
self?.clientBundleIdentifier == "com.apple.SecurityAgent"
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openUserDataFolder(_: Any? = nil) {
|
||||
guard LMMgr.userDataFolderExists else { return }
|
||||
let url = URL(fileURLWithPath: LMMgr.dataFolderPath(isDefaultFolder: false))
|
||||
FileOpenMethod.finder.open(url: url)
|
||||
}
|
||||
|
||||
@objc func openAppSupportFolderFromContainer(_: Any? = nil) {
|
||||
FileOpenMethod.finder.open(url: LMMgr.appSupportURL)
|
||||
}
|
||||
|
||||
@objc func openUserPhrases(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .thePhrases, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openExcludedPhrases(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theFilter, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openUserSymbols(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theSymbols, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openPhraseReplacement(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theReplacements, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openAssociatedPhrases(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theAssociates, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func reloadUserPhrasesData(_: Any? = nil) {
|
||||
LMMgr.initUserLangModels()
|
||||
}
|
||||
|
||||
@objc func callReverseLookupWindow(_: Any? = nil) {
|
||||
CtlRevLookupWindow.show()
|
||||
}
|
||||
|
||||
@objc func removeUnigramsFromUOM(_: Any? = nil) {
|
||||
LMMgr.removeUnigramsFromUserOverrideModel(IMEApp.currentInputMode)
|
||||
LMMgr.removeUnigramsFromUserOverrideModel(IMEApp.currentInputMode.reversed)
|
||||
}
|
||||
|
||||
@objc func clearUOM(_: Any? = nil) {
|
||||
LMMgr.clearUserOverrideModelData(IMEApp.currentInputMode)
|
||||
LMMgr.clearUserOverrideModelData(IMEApp.currentInputMode.reversed)
|
||||
}
|
||||
|
||||
@objc func showAbout(_: Any? = nil) {
|
||||
CtlAboutUI.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
}
|
|
@ -1,445 +0,0 @@
|
|||
// (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 AppKit
|
||||
import MainAssembly
|
||||
import NotifierUI
|
||||
|
||||
private extension Bool {
|
||||
var state: NSControl.StateValue {
|
||||
self ? .on : .off
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - IME Menu Manager
|
||||
|
||||
// 因為選單部分的內容又臭又長,所以就單獨拉到一個檔案內管理了。
|
||||
|
||||
extension SessionCtl {
|
||||
var optionKeyPressed: Bool { NSEvent.keyModifierFlags.contains(.option) }
|
||||
|
||||
override public func menu() -> NSMenu! {
|
||||
let menu = NSMenu(title: "Input Method Menu")
|
||||
var silentMode: Bool { clientBundleIdentifier == "com.apple.SecurityAgent" }
|
||||
|
||||
if PrefMgr.shared.isDebugModeEnabled, let currentMemorySizeInBytes = NSApplication.memoryFootprint {
|
||||
let currentMemorySize: Double = (Double(currentMemorySizeInBytes) / 1024 / 1024).rounded(toPlaces: 1)
|
||||
menu.addItem(withTitle: "Total RAM Usage: \(currentMemorySize)MB", action: nil, keyEquivalent: "")
|
||||
}
|
||||
|
||||
let switchInputModeItem = menu.addItem(
|
||||
withTitle: String(
|
||||
format: "Switch to %@ Input Mode".localized,
|
||||
IMEApp.currentInputMode.reversed.localizedDescription
|
||||
),
|
||||
action: #selector(switchInputMode(_:)), keyEquivalent: PrefMgr.shared.usingHotKeyInputMode ? "D" : ""
|
||||
)
|
||||
switchInputModeItem.keyEquivalentModifierMask = [.command, .control]
|
||||
|
||||
let useSCPCTypingModeItem = menu.addItem(
|
||||
withTitle: "Per-Char Select Mode".localized,
|
||||
action: #selector(toggleSCPCTypingMode(_:)), keyEquivalent: PrefMgr.shared.usingHotKeySCPC ? "P" : ""
|
||||
)
|
||||
useSCPCTypingModeItem.keyEquivalentModifierMask = [.command, .control]
|
||||
useSCPCTypingModeItem.state = PrefMgr.shared.useSCPCTypingMode.state
|
||||
|
||||
let userAssociatedPhrasesItem = menu.addItem(
|
||||
withTitle: "Associated Phrases".localized,
|
||||
action: #selector(toggleAssociatedPhrasesEnabled(_:)),
|
||||
keyEquivalent: PrefMgr.shared.usingHotKeyAssociates ? "O" : ""
|
||||
)
|
||||
userAssociatedPhrasesItem.keyEquivalentModifierMask = [.command, .control]
|
||||
userAssociatedPhrasesItem.state = PrefMgr.shared.associatedPhrasesEnabled.state
|
||||
|
||||
let cassetteModeItem = menu.addItem(
|
||||
withTitle: "CIN Cassette Mode".localized,
|
||||
action: #selector(toggleCassetteMode(_:)),
|
||||
keyEquivalent: PrefMgr.shared.usingHotKeyCassette ? "I" : ""
|
||||
)
|
||||
cassetteModeItem.keyEquivalentModifierMask = [.command, .control]
|
||||
cassetteModeItem.state = PrefMgr.shared.cassetteEnabled.state
|
||||
|
||||
let useCNS11643SupportItem = menu.addItem(
|
||||
withTitle: "CNS11643 Mode".localized,
|
||||
action: #selector(toggleCNS11643Enabled(_:)), keyEquivalent: PrefMgr.shared.usingHotKeyCNS ? "L" : ""
|
||||
)
|
||||
useCNS11643SupportItem.keyEquivalentModifierMask = [.command, .control]
|
||||
useCNS11643SupportItem.state = PrefMgr.shared.cns11643Enabled.state
|
||||
|
||||
if IMEApp.currentInputMode == .imeModeCHT {
|
||||
let chineseConversionItem = menu.addItem(
|
||||
withTitle: "Force KangXi Writing".localized,
|
||||
action: #selector(toggleChineseConverter(_:)), keyEquivalent: PrefMgr.shared.usingHotKeyKangXi ? "K" : ""
|
||||
)
|
||||
chineseConversionItem.keyEquivalentModifierMask = [.command, .control]
|
||||
chineseConversionItem.state = PrefMgr.shared.chineseConversionEnabled.state
|
||||
let shiftJISConversionItem = menu.addItem(
|
||||
withTitle: "JIS Shinjitai Output".localized,
|
||||
action: #selector(toggleShiftJISShinjitaiOutput(_:)), keyEquivalent: PrefMgr.shared.usingHotKeyJIS ? "J" : ""
|
||||
)
|
||||
shiftJISConversionItem.keyEquivalentModifierMask = [.command, .control]
|
||||
shiftJISConversionItem.state = PrefMgr.shared.shiftJISShinjitaiOutputEnabled.state
|
||||
}
|
||||
|
||||
let currencyNumeralsItem = menu.addItem(
|
||||
withTitle: "Currency Numeral Output".localized,
|
||||
action: #selector(toggleCurrencyNumerals(_:)),
|
||||
keyEquivalent: PrefMgr.shared.usingHotKeyCurrencyNumerals ? "M" : ""
|
||||
)
|
||||
currencyNumeralsItem.keyEquivalentModifierMask = [.command, .control]
|
||||
currencyNumeralsItem.state = PrefMgr.shared.currencyNumeralsEnabled.state
|
||||
|
||||
let halfWidthPunctuationItem = menu.addItem(
|
||||
withTitle: "Half-Width Punctuation Mode".localized,
|
||||
action: #selector(toggleHalfWidthPunctuation(_:)),
|
||||
keyEquivalent: PrefMgr.shared.usingHotKeyHalfWidthASCII ? "H" : ""
|
||||
)
|
||||
halfWidthPunctuationItem.keyEquivalentModifierMask = [.command, .control]
|
||||
halfWidthPunctuationItem.state = PrefMgr.shared.halfWidthPunctuationEnabled.state
|
||||
|
||||
if optionKeyPressed || PrefMgr.shared.phraseReplacementEnabled {
|
||||
let phaseReplacementItem = menu.addItem(
|
||||
withTitle: "Use Phrase Replacement".localized,
|
||||
action: #selector(togglePhraseReplacement(_:)), keyEquivalent: ""
|
||||
)
|
||||
phaseReplacementItem.state = PrefMgr.shared.phraseReplacementEnabled.state
|
||||
}
|
||||
|
||||
if optionKeyPressed {
|
||||
let toggleSymbolInputItem = menu.addItem(
|
||||
withTitle: "Symbol & Emoji Input".localized,
|
||||
action: #selector(toggleSymbolEnabled(_:)), keyEquivalent: ""
|
||||
)
|
||||
toggleSymbolInputItem.state = PrefMgr.shared.symbolInputEnabled.state
|
||||
}
|
||||
|
||||
menu.addItem(NSMenuItem.separator()) // ---------------------
|
||||
|
||||
if !silentMode {
|
||||
menu.addItem(
|
||||
withTitle: "Open User Dictionary Folder".localized,
|
||||
action: #selector(openUserDataFolder(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "Edit vChewing User Phrases…".localized,
|
||||
action: #selector(openUserPhrases(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "Edit Excluded Phrases…".localized,
|
||||
action: #selector(openExcludedPhrases(_:)), keyEquivalent: ""
|
||||
)
|
||||
|
||||
if optionKeyPressed || PrefMgr.shared.associatedPhrasesEnabled {
|
||||
menu.addItem(
|
||||
withTitle: "Edit Associated Phrases…".localized,
|
||||
action: #selector(openAssociatedPhrases(_:)), keyEquivalent: ""
|
||||
)
|
||||
}
|
||||
|
||||
if optionKeyPressed {
|
||||
menu.addItem(
|
||||
withTitle: "Edit Phrase Replacement Table…".localized,
|
||||
action: #selector(openPhraseReplacement(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "Edit User Symbol & Emoji Data…".localized,
|
||||
action: #selector(openUserSymbols(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "Open App Support Folder".localized.withEllipsis,
|
||||
action: #selector(openAppSupportFolderFromContainer(_:)), keyEquivalent: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if optionKeyPressed || !PrefMgr.shared.shouldAutoReloadUserDataFiles {
|
||||
menu.addItem(
|
||||
withTitle: "Reload User Phrases".localized,
|
||||
action: #selector(reloadUserPhrasesData(_:)), keyEquivalent: ""
|
||||
)
|
||||
}
|
||||
|
||||
let revLookupMenuItem = menu.addItem(
|
||||
withTitle: "Reverse Lookup (Phonabets)".localized.withEllipsis,
|
||||
action: #selector(callReverseLookupWindow(_:)),
|
||||
keyEquivalent: PrefMgr.shared.usingHotKeyRevLookup ? "/" : ""
|
||||
)
|
||||
revLookupMenuItem.keyEquivalentModifierMask = [.command, .control]
|
||||
|
||||
menu.addItem(
|
||||
withTitle: "Optimize Memorized Phrases".localized,
|
||||
action: #selector(removeUnigramsFromUOM(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "Clear Memorized Phrases".localized,
|
||||
action: #selector(clearUOM(_:)), keyEquivalent: ""
|
||||
)
|
||||
|
||||
if !silentMode {
|
||||
menu.addItem(NSMenuItem.separator()) // ---------------------
|
||||
menu.addItem(
|
||||
withTitle: "vChewing Preferences…".localized,
|
||||
action: #selector(showPreferences(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "Client Manager".localized.withEllipsis,
|
||||
action: #selector(showClientListMgr(_:)), keyEquivalent: ""
|
||||
)
|
||||
if !optionKeyPressed {
|
||||
menu.addItem(
|
||||
withTitle: "Check for Updates…".localized,
|
||||
action: #selector(checkForUpdate(_:)), keyEquivalent: ""
|
||||
)
|
||||
}
|
||||
menu.addItem(
|
||||
withTitle: "Reboot vChewing…".localized,
|
||||
action: #selector(selfTerminate(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "About vChewing…".localized,
|
||||
action: #selector(showAbout(_:)), keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(
|
||||
withTitle: "CheatSheet".localized.withEllipsis,
|
||||
action: #selector(showCheatSheet(_:)), keyEquivalent: ""
|
||||
)
|
||||
if optionKeyPressed {
|
||||
menu.addItem(
|
||||
withTitle: "Uninstall vChewing…".localized,
|
||||
action: #selector(selfUninstall(_:)), keyEquivalent: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return menu
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - IME Menu Items
|
||||
|
||||
public extension SessionCtl {
|
||||
@objc override func showPreferences(_: Any? = nil) {
|
||||
osCheck: if #available(macOS 13, *) {
|
||||
switch NSEvent.keyModifierFlags {
|
||||
case .option: break osCheck
|
||||
default: CtlSettingsUI.show()
|
||||
}
|
||||
NSApp.popup()
|
||||
return
|
||||
}
|
||||
CtlSettingsCocoa.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
@objc func showCheatSheet(_: Any? = nil) {
|
||||
guard let url = Bundle.main.url(forResource: "shortcuts", withExtension: "html") else { return }
|
||||
guard let safariURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Safari") else { return }
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
configuration.promptsUserIfNeeded = true
|
||||
NSWorkspace.shared.open([url], withApplicationAt: safariURL, configuration: configuration)
|
||||
}
|
||||
|
||||
@objc func showClientListMgr(_: Any? = nil) {
|
||||
CtlClientListMgr.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
|
||||
@objc func toggleCassetteMode(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
if !PrefMgr.shared.cassetteEnabled, !LMMgr.checkCassettePathValidity(PrefMgr.shared.cassettePath) {
|
||||
DispatchQueue.main.async {
|
||||
IMEApp.buzz()
|
||||
let alert = NSAlert(error: "Path invalid or file access error.".localized)
|
||||
let informativeText = "Please reconfigure the cassette path to a valid one before enabling this mode."
|
||||
alert.informativeText = informativeText.localized
|
||||
let result = alert.runModal()
|
||||
NSApp.popup()
|
||||
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
|
||||
LMMgr.resetCassettePath()
|
||||
PrefMgr.shared.cassetteEnabled = false
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Notifier.notify(
|
||||
message: "CIN Cassette Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.cassetteEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
if !inputMode.langModel.isCassetteDataLoaded {
|
||||
LMMgr.loadCassetteData()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggleSCPCTypingMode(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Per-Char Select Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.useSCPCTypingMode.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleChineseConverter(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Force KangXi Writing".localized + "\n"
|
||||
+ (PrefMgr.shared.chineseConversionEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleShiftJISShinjitaiOutput(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "JIS Shinjitai Output".localized + "\n"
|
||||
+ (PrefMgr.shared.shiftJISShinjitaiOutputEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleCurrencyNumerals(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Currency Numeral Output".localized + "\n"
|
||||
+ (PrefMgr.shared.currencyNumeralsEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleHalfWidthPunctuation(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Half-Width Punctuation Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.halfWidthPunctuationEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleCNS11643Enabled(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "CNS11643 Mode".localized + "\n"
|
||||
+ (PrefMgr.shared.cns11643Enabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleSymbolEnabled(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Symbol & Emoji Input".localized + "\n"
|
||||
+ (PrefMgr.shared.symbolInputEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func toggleAssociatedPhrasesEnabled(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Associated Phrases".localized + "\n"
|
||||
+ (PrefMgr.shared.associatedPhrasesEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func togglePhraseReplacement(_: Any? = nil) {
|
||||
resetInputHandler(forceComposerCleanup: true)
|
||||
Notifier.notify(
|
||||
message: "Use Phrase Replacement".localized + "\n"
|
||||
+ (PrefMgr.shared.phraseReplacementEnabled.toggled()
|
||||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
}
|
||||
|
||||
@objc func selfUninstall(_: Any? = nil) {
|
||||
AppDelegate.shared.selfUninstall()
|
||||
}
|
||||
|
||||
@objc func selfTerminate(_: Any? = nil) {
|
||||
NSApp.popup()
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
|
||||
@objc func checkForUpdate(_: Any? = nil) {
|
||||
AppDelegate.shared.checkUpdate(forced: true) { [weak self] in
|
||||
self?.clientBundleIdentifier == "com.apple.SecurityAgent"
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openUserDataFolder(_: Any? = nil) {
|
||||
if !LMMgr.userDataFolderExists {
|
||||
return
|
||||
}
|
||||
let url = URL(fileURLWithPath: LMMgr.dataFolderPath(isDefaultFolder: false))
|
||||
guard let finderURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.finder") else { return }
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
configuration.promptsUserIfNeeded = true
|
||||
NSWorkspace.shared.open([url], withApplicationAt: finderURL, configuration: configuration)
|
||||
}
|
||||
|
||||
@objc func openAppSupportFolderFromContainer(_: Any? = nil) {
|
||||
guard let finderURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.finder") else { return }
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
configuration.promptsUserIfNeeded = true
|
||||
NSWorkspace.shared.open([LMMgr.appSupportURL], withApplicationAt: finderURL, configuration: configuration)
|
||||
}
|
||||
|
||||
@objc func openUserPhrases(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .thePhrases, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openExcludedPhrases(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theFilter, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openUserSymbols(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theSymbols, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openPhraseReplacement(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theReplacements, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func openAssociatedPhrases(_: Any? = nil) {
|
||||
LMMgr.openUserDictFile(type: .theAssociates, dual: optionKeyPressed, alt: optionKeyPressed)
|
||||
}
|
||||
|
||||
@objc func reloadUserPhrasesData(_: Any? = nil) {
|
||||
LMMgr.initUserLangModels()
|
||||
}
|
||||
|
||||
@objc func callReverseLookupWindow(_: Any? = nil) {
|
||||
CtlRevLookupWindow.show()
|
||||
}
|
||||
|
||||
@objc func removeUnigramsFromUOM(_: Any? = nil) {
|
||||
LMMgr.removeUnigramsFromUserOverrideModel(IMEApp.currentInputMode)
|
||||
LMMgr.removeUnigramsFromUserOverrideModel(IMEApp.currentInputMode.reversed)
|
||||
}
|
||||
|
||||
@objc func clearUOM(_: Any? = nil) {
|
||||
LMMgr.clearUserOverrideModelData(IMEApp.currentInputMode)
|
||||
LMMgr.clearUserOverrideModelData(IMEApp.currentInputMode.reversed)
|
||||
}
|
||||
|
||||
@objc func showAbout(_: Any? = nil) {
|
||||
CtlAboutUI.show()
|
||||
NSApp.popup()
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@
|
|||
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */ = {isa = PBXBuildFile; productRef = 5B98114728D6198700CBC605 /* PinyinPhonaConverter */; };
|
||||
5BA8C30328DF0360004C5CC4 /* CandidateWindow in Frameworks */ = {isa = PBXBuildFile; productRef = 5BA8C30228DF0360004C5CC4 /* CandidateWindow */; };
|
||||
5BAD0CD527D701F6003D127F /* vChewingKeyLayout.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */; };
|
||||
5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB802D927FABA8300CF1C19 /* SessionCtl_Menu.swift */; };
|
||||
5BBBB75F27AED54C0023B93A /* Beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75D27AED54C0023B93A /* Beep.m4a */; };
|
||||
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75E27AED54C0023B93A /* Fart.m4a */; };
|
||||
5BBC2D9E28F51C0400C986F6 /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B18BA7227C7BD8B0056EB19 /* LICENSE.txt */; };
|
||||
|
@ -134,7 +133,6 @@
|
|||
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PinyinPhonaConverter; path = Packages/vChewing_PinyinPhonaConverter; sourceTree = "<group>"; };
|
||||
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CandidateWindow; path = Packages/vChewing_CandidateWindow; sourceTree = "<group>"; };
|
||||
5BAD67E12ADAB62D005A4842 /* HangarRash_SwiftyCapsLockToggler */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = HangarRash_SwiftyCapsLockToggler; path = Packages/HangarRash_SwiftyCapsLockToggler; sourceTree = "<group>"; };
|
||||
5BB802D927FABA8300CF1C19 /* SessionCtl_Menu.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = SessionCtl_Menu.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; };
|
||||
5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; };
|
||||
5BBC2DA228F5212100C986F6 /* RelocationDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelocationDetector.swift; sourceTree = "<group>"; };
|
||||
|
@ -430,7 +428,6 @@
|
|||
children = (
|
||||
D427F76B278CA1BA004A2160 /* AppDelegateImpl.swift */,
|
||||
D47B92BF27972AC800458394 /* main.swift */,
|
||||
5BB802D927FABA8300CF1C19 /* SessionCtl_Menu.swift */,
|
||||
);
|
||||
path = Modules;
|
||||
sourceTree = "<group>";
|
||||
|
@ -709,7 +706,6 @@
|
|||
files = (
|
||||
D427F76C278CA2B0004A2160 /* AppDelegateImpl.swift in Sources */,
|
||||
D47B92C027972AD100458394 /* main.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue