vChewing-macOS/Packages/vChewing_MainAssembly/Sources/MainAssembly/SessionController/SessionCtl_Menu.swift

373 lines
14 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// (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("Edit Associated Phrases…")?
.act(#selector(openAssociatedPhrases(_:)))
.alternated()
.hotkey(PrefMgr.shared.usingHotKeyAssociates ? "O" : "", mask: [.command, .option, .control])
.nulled(silentMode)
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)
NSMenu.Item("Edit Phrase Replacement Table…")?
.act(#selector(openPhraseReplacement(_:)))
.alternated().nulled(silentMode)
NSMenu.Item("Symbol & Emoji Input")?
.act(#selector(toggleSymbolEnabled(_:)))
.state(PrefMgr.shared.symbolInputEnabled)
NSMenu.Item("Edit User Symbol & Emoji Data…")?
.act(#selector(openUserSymbols(_:)))
.alternated().nulled(silentMode)
NSMenu.Item.separator() // ---------------------
NSMenu.Item("Open User Dictionary Folder")?
.act(#selector(openUserDataFolder(_:)))
.nulled(silentMode)
NSMenu.Item("Open App Support Folder")?
.act(#selector(openAppSupportFolderFromContainer(_:)))
.alternated().nulled(silentMode)
NSMenu.Item("Edit vChewing User Phrases…")?
.act(#selector(openUserPhrases(_:)))
.nulled(silentMode)
NSMenu.Item("Reload User Phrases")?
.act(#selector(reloadUserPhrasesData(_:)))
NSMenu.Item("Edit Excluded Phrases…")?
.act(#selector(openExcludedPhrases(_:)))
.alternated().nulled(silentMode)
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(_:)))
.alternated()
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(_:)))
.alternated().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("CheatSheet")?
.act(#selector(showCheatSheet(_:)))
.nulled(silentMode)
NSMenu.Item("About vChewing…")?
.act(#selector(showAbout(_:)))
.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()
}
}