From bb9bc058ccd0180b139a841b01d124432a972713 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Thu, 8 Feb 2024 22:51:18 +0800 Subject: [PATCH] SessionCtl // Refactor the menu structure. --- .../MainAssembly/SessionCtl_Menu.swift | 344 ++++++++++++++ Source/Modules/SessionCtl_Menu.swift | 445 ------------------ vChewing.xcodeproj/project.pbxproj | 4 - 3 files changed, 344 insertions(+), 449 deletions(-) create mode 100644 Packages/vChewing_MainAssembly/Sources/MainAssembly/SessionCtl_Menu.swift delete mode 100644 Source/Modules/SessionCtl_Menu.swift diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/SessionCtl_Menu.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/SessionCtl_Menu.swift new file mode 100644 index 00000000..a50ac5a3 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/SessionCtl_Menu.swift @@ -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() + } +} diff --git a/Source/Modules/SessionCtl_Menu.swift b/Source/Modules/SessionCtl_Menu.swift deleted file mode 100644 index f3abde6c..00000000 --- a/Source/Modules/SessionCtl_Menu.swift +++ /dev/null @@ -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() - } -} diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 312eaa5e..42f359dc 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -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 = ""; }; 5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CandidateWindow; path = Packages/vChewing_CandidateWindow; sourceTree = ""; }; 5BAD67E12ADAB62D005A4842 /* HangarRash_SwiftyCapsLockToggler */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = HangarRash_SwiftyCapsLockToggler; path = Packages/HangarRash_SwiftyCapsLockToggler; sourceTree = ""; }; - 5BB802D927FABA8300CF1C19 /* SessionCtl_Menu.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = SessionCtl_Menu.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = ""; }; 5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = ""; }; 5BBC2DA228F5212100C986F6 /* RelocationDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelocationDetector.swift; sourceTree = ""; }; @@ -430,7 +428,6 @@ children = ( D427F76B278CA1BA004A2160 /* AppDelegateImpl.swift */, D47B92BF27972AC800458394 /* main.swift */, - 5BB802D927FABA8300CF1C19 /* SessionCtl_Menu.swift */, ); path = Modules; sourceTree = ""; @@ -709,7 +706,6 @@ files = ( D427F76C278CA2B0004A2160 /* AppDelegateImpl.swift in Sources */, D47B92C027972AD100458394 /* main.swift in Sources */, - 5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };