123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- //
- // KMTTSWindowController.swift
- // PDF Reader Pro
- //
- // Created by liujiajie on 2023/12/12.
- //
- import Cocoa
- typealias TTSCloseWindowCallback = (_ isCloseWindow: Bool) -> Void
- let minSpeed: Float = 0.6
- let maxSpeed: Float = 5.0
- let standardSpeed: Float = 175.0
- class KMTTSWindowController: NSWindowController, KMTTSManagerDelegate, NSWindowDelegate, NSTextFieldDelegate{
- var pdfView: CPDFView!
- var closeWindowCallback: TTSCloseWindowCallback?
- @IBOutlet var sontinuouButton: NSButton!
- @IBOutlet var speedLabel: NSTextField!
- @IBOutlet var languageLabel: NSTextField!
-
- @IBOutlet var languageComboBox: NSPopUpButton!
- @IBOutlet var speedTextField: NSTextField!
- @IBOutlet var speedSlider: NSSlider!
-
- @IBOutlet var speedStepper: NSStepper!
- @IBOutlet var nextButton: KMToolbarItem!
- @IBOutlet var forwardButton: KMToolbarItem!
-
- @IBOutlet var playButton: KMToolbarItem!
-
- @IBOutlet var speedBox: NSBox!
-
-
- @IBOutlet var sppendCountLabel: NSTextField!
-
-
- var pdfSelection: CPDFSelection?
-
- var currentPageIndex: Int = 0
-
- var isChangePage = false
-
- var voiceArrays = NSMutableArray()
-
- static let share = KMTTSWindowController()
-
- convenience init() {
- self.init(windowNibName: "KMTTSWindowController")
- }
- deinit {
- DistributedNotificationCenter.default().removeObserver(self)
- NotificationCenter.default.removeObserver(self)
- }
- override func windowDidLoad() {
- super.windowDidLoad()
- self.window?.title = NSLocalizedString("TTS", comment: "")
-
- self.speedStepper.minValue = Double(minSpeed)
- self.speedStepper.maxValue = Double(maxSpeed);
- self.speedSlider.minValue = Double(minSpeed);
- self.speedSlider.maxValue = Double(maxSpeed);
-
- self.nextButton.toolTip = KMLocalizedString("Next Page", nil)
- self.forwardButton.toolTip = KMLocalizedString("Previous Page", nil)
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.playButton.toolTip = KMLocalizedString("Play", nil)
- self.sppendCountLabel.stringValue = KMLocalizedString("SpeedX", nil)
- let str = String(format: "%.1f", KMTTSManager.defalutManager.rate/Float(standardSpeed))
- self.speedStepper.stringValue = str
- self.speedTextField.stringValue = str
- self.speedSlider.stringValue = str
-
- self.sontinuouButton.title = KMLocalizedString("Continuous Reading", nil);
- self.speedLabel.stringValue = KMLocalizedString("Speed", nil) + ":"
- self.languageLabel.stringValue = KMLocalizedString("Language", nil) + ":"
-
- let array = KMTTSManager.defalutManager.availableVoices()
- let currentVoicName = KMTTSManager.defalutManager.voice()
- var currentIndex = 0
- let menu = NSMenu()
-
- for voiceType: NSSpeechSynthesizer.VoiceName in array {
- let voiceDic = NSMutableDictionary()
- // let dic = KMTTSManager.defalutManager.attributesForVoice(voiceType)
- let voiceLocaleIdentifier = ( NSSpeechSynthesizer.attributes( forVoice: voiceType )[ NSSpeechSynthesizer.VoiceAttributeKey.localeIdentifier ] as! String )
-
- let name = self.switchLanguage(withCode: voiceLocaleIdentifier)
- voiceDic["voiceType"] = voiceType
- voiceDic["voiceName"] = name
- voiceArrays.add(voiceDic)
- }
- voiceArrays = self.sortArray(voiceArrays)
- for i in 0..<voiceArrays.count {
- let voiceDic = voiceArrays[i] as! NSDictionary
- let voiceType = voiceDic.object(forKey: "voiceType") as! NSSpeechSynthesizer.VoiceName
- if currentVoicName == voiceType {
- currentIndex = i
- }
-
- let dic = KMTTSManager.defalutManager.attributesForVoice(voiceType) as NSDictionary
- let code = ( NSSpeechSynthesizer.attributes( forVoice: voiceType )[ NSSpeechSynthesizer.VoiceAttributeKey.localeIdentifier ] as! String )
- var languageStaring: String? = nil
- let voiceName = ( NSSpeechSynthesizer.attributes( forVoice: voiceType )[ NSSpeechSynthesizer.VoiceAttributeKey.name ] as! String )
- let name = self.switchLanguage(withCode: code)
- if voiceName.count > 0 {
- languageStaring = "\(name)(\(voiceName))"
- }
-
- let font = NSFont.systemFont(ofSize: 12.0)
- let namefont = NSFont.systemFont(ofSize: 10.0)
-
- let textRowSpace = NSMutableAttributedString(string: languageStaring ?? "")
- textRowSpace.addAttribute(.font, value: font, range: NSRange(location: 0, length: name.count))
- textRowSpace.addAttribute(.font, value: namefont, range: NSRange(location: name.count, length: languageStaring!.count - name.count))
- let item = NSMenuItem()
- item.attributedTitle = textRowSpace
- menu.addItem(item)
- }
- self.languageComboBox.menu = menu
- self.languageComboBox.selectItem(at: currentIndex)
- KMTTSManager.defalutManager.delegate = self
- self.updateViewColor()
- self.window?.standardWindowButton(.zoomButton)?.isHidden = true
- if KMTTSManager.defalutManager.isContinue {
- self.sontinuouButton.state = .on
- } else {
- self.sontinuouButton.state = .off
- }
- DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil)
-
- NotificationCenter.default.addObserver(self, selector: #selector(handlePageChangedNotification), name: NSNotification.Name.CPDFViewPageChanged, object: self.pdfView)
- }
-
- func updateViewColor() {
- var viewBackcolor = NSColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0)
- var textColor = NSColor(red: 51.0/255.0, green: 51.0/255.0, blue: 51.0/255.0, alpha: 1.0)
- var contentColor = NSColor(red: 102.0/255.0, green: 102.0/255.0, blue: 102.0/255.0, alpha: 1.0)
- var fillColor = NSColor.white
- if #available(macOS 10.14, *) {
- let appearanceName = NSApp.effectiveAppearance.bestMatch(from: [.aqua, .darkAqua])
- if appearanceName == .darkAqua {
- viewBackcolor = NSColor(red: 69.0/255.0, green: 69.0/255.0, blue: 71.0/255.0, alpha: 1.0)
- textColor = NSColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0)
- contentColor = NSColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 0.6)
- fillColor = NSColor(red: 101.0/255.0, green: 101.0/255.0, blue: 101.0/255.0, alpha: 1.0)
- }
- }
-
- self.window?.backgroundColor = viewBackcolor
- self.window?.isMovableByWindowBackground = true
- self.languageLabel.textColor = contentColor
- self.speedLabel.textColor = contentColor
- self.speedTextField.textColor = textColor
- self.speedBox.fillColor = fillColor
- self.sontinuouButton.setTitleColor(contentColor)
- }
-
-
- @objc func themeChanged(notification: NSNotification) {
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
- self.updateViewColor()
- }
- }
- @objc func handlePageChangedNotification(notification: NSNotification) {
- guard let PDFView = notification.object as? CPDFView else { return }
- if PDFView == self.pdfView {
- if self.pdfView.canGoToNextPage() {
- self.nextButton.isEnabled = true
- } else{
- self.nextButton.isEnabled = false
- }
- if self.pdfView.canGoToPreviousPage() {
- self.forwardButton.isEnabled = true
- } else {
- self.forwardButton.isEnabled = false
- }
- }
- }
- func sortArray(_ array: NSArray) -> NSMutableArray {
- let sortDesc = [NSSortDescriptor(key: "voiceName", ascending: true)]
- let sortedArr = array.sortedArray(using: sortDesc) as NSArray
- let tArray = NSMutableArray(array: sortedArr)
- return tArray
- }
-
- func windowShouldClose(_ sender: NSWindow) -> Bool {
- self.stopSpeaking()
- if let callBlack = closeWindowCallback {
- callBlack(true)
- }
- return true
- }
-
- override func showWindow(_ sender: Any?) {
- super.showWindow(sender)
- self.isChangePage = false
- if self.pdfView.canGoToNextPage() {
- self.nextButton.isEnabled = true
- } else{
- self.nextButton.isEnabled = false
- }
- if self.pdfView.canGoToPreviousPage() {
- self.forwardButton.isEnabled = true
- } else {
- self.forwardButton.isEnabled = false
- }
- }
-
- func stopSpeaking() {
- if KMTTSManager.defalutManager.isSpeaking() || KMTTSManager.defalutManager.isPaused {
- self.isChangePage = true
- KMTTSManager.defalutManager.stopSpeaking()
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.playButton.toolTip = NSLocalizedString("Play", comment: "")
- self.pdfView.setHighlightedSelections([])
- }
- self.pdfSelection = nil
- self.pdfView = nil
- }
-
- func quikeStartSpeakingPDFPage(_ page: CPDFPage) {
- if KMTTSManager.defalutManager.isSpeaking() || KMTTSManager.defalutManager.isPaused {
- KMTTSManager.defalutManager.stopSpeaking()
- self.isChangePage = true
- }
- self.currentPageIndex = Int(self.pdfView.document?.index(for: page) ?? 0)
- self.startSpeakingPDFPage(page)
- }
- func startSpeakingPDFPage(_ page: CPDFPage) {
- let dex = self.languageComboBox.indexOfSelectedItem
- if dex >= 0 && dex < self.voiceArrays.count {
- let dic: NSMutableDictionary = self.voiceArrays[dex] as! NSMutableDictionary
- if let voiceName = dic["voiceType"] {
- KMTTSManager.defalutManager.setVoice(voice: voiceName as! NSSpeechSynthesizer.VoiceName)
- }
- }
- KMTTSManager.defalutManager.rate = self.speedSlider.floatValue * Float(standardSpeed)
- let isSuccess = KMTTSManager.defalutManager.startSpeakingPDFPage(page)
- if isSuccess {
- self.playButton.image = NSImage(named: "KMImageNameTTSPause")
- self.playButton.toolTip = NSLocalizedString("Pause", comment: "")
- self.speedSlider.isEnabled = false
- self.speedStepper.isEnabled = false
- } else {
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.playButton.toolTip = NSLocalizedString("Play", comment: "")
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- }
- }
- func startSpeakingPDFSelection(_ selection: CPDFSelection) {
- if KMTTSManager.defalutManager.isSpeaking() || KMTTSManager.defalutManager.isPaused {
- KMTTSManager.defalutManager.stopSpeaking()
- self.isChangePage = true
- }
- self.pdfSelection = selection
- self.pdfView?.setCurrentSelection(nil, animate: false)
- let dex = self.languageComboBox.indexOfSelectedItem
- if dex >= 0 && dex < self.voiceArrays.count {
- let dic: NSMutableDictionary = self.voiceArrays[dex] as! NSMutableDictionary
- if let voiceName = dic["voiceType"] as? NSSpeechSynthesizer.VoiceName {
- KMTTSManager.defalutManager.setVoice(voice: voiceName)
- }
- }
- KMTTSManager.defalutManager.rate = self.speedSlider.floatValue * Float(standardSpeed)
- let isSuccess = KMTTSManager.defalutManager.startSpeakingPDFSelection(selection)
- if isSuccess {
- self.speedSlider.isEnabled = false
- self.speedStepper.isEnabled = false
- self.playButton.image = NSImage(named: "KMImageNameTTSPause")
- self.playButton.toolTip = NSLocalizedString("Pause", comment: "")
- } else {
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.playButton.toolTip = NSLocalizedString("Play", comment: "")
- }
- }
- @IBAction func buttonItemClick_Next(_ sender: NSButton) {
- if (KMTTSManager.defalutManager.isSpeaking()) {
- var currentPageNum = self.currentPageIndex;
- var page: CPDFPage?
- if (currentPageNum + 1 < self.pdfView.document.pageCount) {
- page = self.pdfView.document.page(at: UInt(currentPageNum+1))
- self.currentPageIndex = currentPageNum + 1;
-
- } else {
- page = self.pdfView.document.page(at: self.pdfView.document.pageCount - 1)
- self.currentPageIndex = Int(self.pdfView.document.pageCount - 1)
- }
- self.pdfView.go(to: page)
- self.isChangePage = true
- self.startSpeakingPDFPage(page!)
- }
- }
- @IBAction func buttonItemClick_Forward(_ sender: NSButton) {
- if (KMTTSManager.defalutManager.isSpeaking()) {
- var currentPageNum = self.currentPageIndex;
- var page: CPDFPage?
- if (currentPageNum - 1 < 0) {
- page = self.pdfView.document.page(at: 0)
- self.currentPageIndex = 0;
-
- } else {
- page = self.pdfView.document.page(at: UInt(currentPageNum - 1))
- self.currentPageIndex = currentPageNum - 1
- }
- self.pdfView.go(to: page)
- self.isChangePage = true
- self.startSpeakingPDFPage(page!)
- }
- }
- @IBAction func buttonItemClick_Play(_ sender: KMToolbarItem) {
- if (KMTTSManager.defalutManager.isPaused) {
- self.playButton.image = NSImage(named: "KMImageNameTTSPause")
- self.playButton.toolTip = KMLocalizedString("Pause", nil)
- self.speedSlider.isEnabled = false
- self.speedStepper.isEnabled = false
- KMTTSManager.defalutManager.continueSpeaking()
- } else if (KMTTSManager.defalutManager.isSpeaking()) {
- self.isChangePage = true
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.playButton.toolTip = KMLocalizedString("Play", nil)
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.pdfView.setHighlightedSelection(nil, animated: false)
- KMTTSManager.defalutManager.pauseSpeaking()
- } else {
- if ((self.pdfSelection) != nil) {
- self.startSpeakingPDFSelection(self.pdfSelection!)
- } else {
- let page = self.pdfView.currentPage()
- self.currentPageIndex = self.pdfView.currentPageIndex
- if page != nil {
- self.startSpeakingPDFPage(page!)
- }
- }
- }
- }
- @IBAction func buttonItemClick_Continue(_ sender: NSButton) {
- KMTTSManager.defalutManager.isContinue = !(KMTTSManager.defalutManager.isContinue)
- }
- @IBAction func stepperItemClick_Speed(_ sender: NSButton) {
- var rate = self.speedStepper.floatValue
- let str = String(format: "%.1f", rate)
- self.speedStepper.stringValue = str
- self.speedTextField.stringValue = str
- self.speedSlider.stringValue = str
- rate = Float(standardSpeed) * rate
- KMTTSManager.defalutManager.rate = rate
- }
- @IBAction func sliderItemClick_Speed(_ sender: NSButton) {
- var rate: CGFloat = CGFloat(self.speedSlider.floatValue)
- let str = String(format: "%.1f", rate)
- self.speedStepper.stringValue = str
- self.speedTextField.stringValue = str
- self.speedSlider.stringValue = str
- rate = CGFloat(standardSpeed) * rate
- KMTTSManager.defalutManager.rate = Float(rate)
- }
- @IBAction func buttonItemClick_Language(_ sender: NSButton) {
- if KMTTSManager.defalutManager.isSpeaking() {
- self.isChangePage = true
- if let pdfSelection = self.pdfSelection {
- self.startSpeakingPDFSelection(pdfSelection)
- } else {
- let currentPageInd = self.currentPageIndex
- if currentPageInd < (self.pdfView.document.pageCount - 1) {
- let page = self.pdfView.document.page(at: UInt(currentPageInd))
- self.startSpeakingPDFPage(page!)
- }
- }
- }
- }
- override func mouseDown(with event: NSEvent) {
- super.mouseDown(with: event)
- self.window?.makeFirstResponder(self)
- }
- //MARK: KMTTSManagerDelegate
- func ttsMananger(_ tts: KMTTSManager, willSpeak selection: CPDFSelection) {
- // if (selection != nil) {
- self.pdfView?.setHighlightedSelection(selection, animated: false)
- // }
- }
- func ttsManangerDidFinishSpeech(_ tts: KMTTSManager) -> Bool {
- var isFinish = true
- if self.pdfSelection != nil {
- self.pdfSelection = nil
- self.pdfView.setHighlightedSelections(nil)
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.playButton.toolTip = KMLocalizedString("Play", nil)
- } else {
- if KMTTSManager.defalutManager.isContinue {
- if !isChangePage {
- if let pdfSelection = self.pdfSelection {
- self.pdfView.setHighlightedSelections(nil)
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.playButton.toolTip = KMLocalizedString("Play", nil)
- } else {
- let currentPageIndex = self.currentPageIndex
- if currentPageIndex + 1 < self.pdfView.document.pageCount {
- let page = self.pdfView.document.page(at: UInt(currentPageIndex+1))
- self.currentPageIndex = currentPageIndex+1
- self.pdfView.go(to: page)
- self.startSpeakingPDFPage(page!)
- isFinish = false
- } else {
- self.pdfView.setHighlightedSelections(nil)
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.playButton.toolTip = KMLocalizedString("Play", nil)
- }
- }
- }
- } else {
- if !isChangePage {
- self.speedSlider.isEnabled = true
- self.speedStepper.isEnabled = true
- self.pdfView.setHighlightedSelections(nil)
- self.playButton.image = NSImage(named: "KMImageNameTTSStop")
- self.playButton.toolTip = KMLocalizedString("Play", nil)
- }
- }
- if isChangePage {
- isChangePage = false
- }
- }
- return isFinish
- }
- func ttsManangerdidErrorSpeech(_ tts: KMTTSManager, message: String) {
-
- }
- //MARK: NSTextFieldDelegate
- func controlTextDidEndEditing(_ obj: Notification) {
- if let textField = obj.object as? NSTextField {
- if textField == self.speedTextField {
- var rate: Float = Float(textField.stringValue) ?? 0
- if maxSpeed - rate < 0 {
- rate = maxSpeed
- } else if rate - minSpeed < 0 {
- rate = minSpeed
- }
- self.speedStepper.stringValue = String(format: "%.1f", rate)
- self.speedTextField.stringValue = String(format: "%.1f", rate)
- self.speedSlider.stringValue = String(format: "%.1f", rate)
- rate = standardSpeed * rate
- KMTTSManager.defalutManager.rate = rate
- }
- }
- }
-
- func switchLanguage(withCode code: String) -> String {
- var language = code
- if code == "ar_SA" {
- language = "Arabic (Saudi Arabia)"
- } else if code == "cs_CZ" {
- language = "Czech (Czech republic)"
- } else if code == "da_DK" {
- language = "Danish (Denmark)"
- } else if code == "de_DE" {
- language = "German (Germany)"
- } else if code == "el_GR" {
- language = "Greek (Greece)"
- } else if code == "en_AU" {
- language = "English (Australia)"
- } else if code == "en_GB" {
- language = "English (UK)"
- } else if code == "en_IE" {
- language = "English (Ireland)"
- } else if code == "en_US" {
- language = "English"
- } else if code == "en_ZA" {
- language = "English (South Africa)"
- } else if code == "es_ES" {
- language = "Spanish (Spain)"
- } else if code == "es_MX" {
- language = "Spanish (Mexico)"
- } else if code == "fi_FI" {
- language = "Finnish (Finland)"
- } else if code == "fr_CA" {
- language = "French (Canada)"
- } else if code == "fr_FR" {
- language = "French (France)"
- } else if code == "he_IL" {
- language = "Hebrew"
- } else if code == "hi_IN" {
- language = "Hindi (India)"
- } else if code == "hu_HU" {
- language = "Hungarian (Hungary)"
- } else if code == "id_ID" {
- language = "Indonesian (Indonesia)"
- } else if code == "it_IT" {
- language = "Italian (Italy)"
- } else if code == "ja_JP" {
- language = "日本語"
- } else if code == "ko_KR" {
- language = "Korean (South Korea)"
- } else if code == "nl_BE" {
- language = "Dutch (Belgium)"
- } else if code == "nl_NL" {
- language = "Dutch (Holland)"
- } else if code == "nb_NO" {
- language = "Norwegian (Norway)"
- } else if code == "pl_PL" {
- language = "Polish (Poland)"
- } else if code == "pt_BR" {
- language = "Portuguese (Brazil)"
- } else if code == "pt_PT" {
- language = "Portuguese (Portugal)"
- } else if code == "ro_RO" {
- language = "Romanian (Romania)"
- } else if code == "ru_RU" {
- language = "Russian (Russia)"
- } else if code == "sk_SK" {
- language = "Slovakia (Slovakia)"
- } else if code == "sv_SE" {
- language = "Swe (Sweden)"
- } else if code == "th_TH" {
- language = "Thai (Thailand)"
- } else if code == "tr_TR" {
- language = "Turkish (Turkey)"
- } else if code == "zh_CN" {
- language = "简体中文"
- } else if code == "zh_HK" {
- language = "繁體中文 (香港)"
- } else if code == "zh_TW" {
- language = "繁體中文 (台灣)"
- } else if code == "en_IN" {
- language = "English"
- }
- return language
- }
- }
|