KMPreferenceController.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. //
  2. // KMPreferenceController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/11/7.
  6. //
  7. import Cocoa
  8. class KMPreferenceWindow: NSWindow {
  9. // override func responds(to aSelector: Selector!) -> Bool {
  10. // return aSelector != #selector(toggleToolbarShow) && aSelector != #selector(runToolbarCustomizationPalette) && super.responds(to: aSelector)
  11. // }
  12. }
  13. private let SKPreferencesToolbarIdentifier = "SKPreferencesToolbarIdentifier"
  14. private let SKPreferenceWindowFrameAutosaveName = "SKPreferenceWindow"
  15. private let BOTTOM_MARGIN = 27.0
  16. private let SKLastSelectedPreferencePaneKey = "SKLastSelectedPreferencePane"
  17. private let NIBNAME_KEY = "nibName"
  18. @objc protocol KMPreferencePane: NSObjectProtocol {
  19. @objc optional func defaultsDidRevert()
  20. @objc optional func reloadData()
  21. }
  22. @objcMembers class KMPreferenceController: NSWindowController {
  23. @IBOutlet var resetBtn: NSButton!
  24. @IBOutlet var resetAllBtn: NSButton!
  25. var preferencePanes: [KMPreferencePane]?
  26. var history: [NSViewController]?
  27. var historyIndex: Int = 0
  28. var currentPane: KMPreferencePane?
  29. private let INITIALUSERDEFAULTS_KEY = "InitialUserDefaults"
  30. private let RESETTABLEKEYS_KEY = "ResettableKeys"
  31. deinit {
  32. KMPrint("KMPreferenceController deinit.")
  33. }
  34. static let shared = KMPreferenceController(windowNibName: "PreferenceWindow")
  35. override func windowDidLoad() {
  36. super.windowDidLoad()
  37. self.preferencePanes = [KMGeneralPreferences(), KMDisplayPreferences(), KMNotesPreferences(), KMSyncPreferences(), IdentifyPreferense()]
  38. self.history = []
  39. self.historyIndex = 0
  40. if #available(macOS 11.0,*) {
  41. self.window?.toolbarStyle = .expanded
  42. }
  43. resetBtn.title = KMLocalizedString("Reset", nil)
  44. resetAllBtn.title = KMLocalizedString("Reset All", nil)
  45. let window = self.window
  46. let toolbar = NSToolbar(identifier: SKPreferencesToolbarIdentifier)
  47. toolbar.allowsUserCustomization = false
  48. toolbar.autosavesConfiguration = false
  49. toolbar.isVisible = true
  50. toolbar.delegate = self
  51. window?.toolbar = toolbar
  52. window?.showsToolbarButton = false
  53. window?.contentView?.wantsLayer = true
  54. // we want to restore the top of the window, while without the force it restores the bottom position without the size
  55. window?.setFrameUsingName(SKPreferenceWindowFrameAutosaveName, force: true)
  56. self.windowFrameAutosaveName = SKPreferenceWindowFrameAutosaveName
  57. var width = 0.0
  58. var frame: NSRect = .zero
  59. var pane: NSViewController?
  60. var view: NSView?
  61. for pane in self.preferencePanes ?? [] {
  62. let _pane = pane as? NSViewController
  63. width = fmax(width, NSWidth(_pane?.view.frame ?? .zero))
  64. }
  65. for pane in self.preferencePanes ?? [] {
  66. let _pane = pane as? NSViewController
  67. view = _pane?.view
  68. frame = view?.frame ?? .zero
  69. if let data = view?.autoresizingMask.contains(.width), data {
  70. frame.size.width = width
  71. } else {
  72. frame.origin.x = floor(0.5 * (width - NSWidth(frame)))
  73. }
  74. frame.origin.y = BOTTOM_MARGIN
  75. view?.frame = frame
  76. }
  77. self.currentPane = self._preferencePane(forItemIdentifier: UserDefaults.standard.string(forKey: SKLastSelectedPreferencePaneKey) ?? "") ?? self.preferencePanes?.first
  78. toolbar.selectedItemIdentifier = NSToolbarItem.Identifier((self.currentPane as? NSViewController)?.nibName ?? "")
  79. if let _currentPane = self.currentPane as? NSViewController {
  80. window?.title = _currentPane.title ?? ""
  81. self.history?.append(_currentPane)
  82. }
  83. view = (self.currentPane as? NSViewController)?.view
  84. frame = window?.frame ?? .zero
  85. frame.size.width = width
  86. frame = KMShrinkRect(rect: frame, amount: NSHeight(window?.contentView?.frame ?? .zero) - NSMaxY(view?.frame ?? .zero), edge: .minY)
  87. window?.setFrame(frame, display: false)
  88. if let data = view {
  89. window?.contentView?.addSubview(data)
  90. }
  91. NotificationCenter.default.addObserver(self, selector: #selector(preferenceDidChangeNotification), name: KMPreferenceManager.didChangeNotification, object: nil)
  92. self.resetBtn.toolTip = NSLocalizedString("Revert all currently shown preferences to their original values", comment: "")
  93. self.resetAllBtn.toolTip = NSLocalizedString("Revert all preferences to their original values", comment: "")
  94. }
  95. override func close() {
  96. super.close()
  97. }
  98. @IBAction func changeFont(_ sender: AnyObject?) {
  99. self.window?.contentView?.activeFontWell()?.changeFontFromFontManager(sender)
  100. }
  101. @IBAction func resetCurrent(_ sender: AnyObject?) {
  102. if (self.currentPane == nil) {
  103. return
  104. }
  105. let label = (self.currentPane as? NSViewController)?.title ?? ""
  106. let alert = NSAlert()
  107. alert.messageText = String(format: KMLocalizedString("Reset %@ preferences to their original values?", "Message in alert dialog when pressing Reset All button"), label)
  108. alert.informativeText = String(format: KMLocalizedString("Choosing Reset will restore all settings in this pane to the state they were in when LynxPDF Editor was first installed.", "Informative text in alert dialog when pressing Reset All button"), label)
  109. alert.addButton(withTitle: KMLocalizedString("Reset", "Button title"))
  110. alert.addButton(withTitle: KMLocalizedString("Cancel", "Button title"))
  111. alert.beginSheetModal(for: self.window!, modalDelegate: self, didEnd: #selector(_resetCurrentSheetDidEnd), contextInfo: nil)
  112. }
  113. @IBAction func resetAll(_ sender: AnyObject?) {
  114. let alert = NSAlert()
  115. alert.messageText = KMLocalizedString("Reset all preferences to their original values?", "Message in alert dialog when pressing Reset All button")
  116. alert.informativeText = KMLocalizedString("Choosing Reset will restore all settings to the state they were in when LynxPDF Editor was first installed.", "Informative text in alert dialog when pressing Reset All button")
  117. alert.addButton(withTitle: KMLocalizedString("Reset", "Button title"))
  118. alert.addButton(withTitle: KMLocalizedString("Cancel", "Button title"))
  119. let response = alert.runModal()
  120. if (response == .alertFirstButtonReturn) {
  121. NSUserDefaultsController.shared.revertToInitialValues(forKeys: nil)
  122. KMPreference.shared.resetAllData()
  123. for pane in self.preferencePanes ?? [] {
  124. pane.defaultsDidRevert?()
  125. }
  126. }
  127. }
  128. @objc private func preferenceDidChangeNotification(sender: Notification) {
  129. self.currentPane?.reloadData?()
  130. }
  131. }
  132. // MARK: - Private Methods
  133. extension KMPreferenceController {
  134. private func _preferencePane(forItemIdentifier itemIdent: String) -> KMPreferencePane? {
  135. self._panelControlSelectColor(itemIdent)
  136. for pane in self.preferencePanes ?? [] {
  137. let _pane = pane as? NSViewController
  138. if _pane?.nibName == itemIdent {
  139. return pane
  140. }
  141. }
  142. return nil
  143. }
  144. private func _panelControlSelectColor(_ identifier: String) {
  145. for item in self.window?.toolbar?.items ?? [] {
  146. if item.itemIdentifier.rawValue == identifier {
  147. if item.itemIdentifier.rawValue == "GeneralPreferences" {
  148. item.image = NSImage(named: "KMImageNameElseSettingsGeneralSel")
  149. } else if item.itemIdentifier.rawValue == "DisplayPreferences" {
  150. item.image = NSImage(named: "KMImageNameElseSettingsViewSel")
  151. } else if item.itemIdentifier.rawValue == "NotesPreferences" {
  152. item.image = NSImage(named: "KMImageNameElseSettingsAnnotationSel")
  153. } else if item.itemIdentifier.rawValue == "SyncPreferences" {
  154. item.image = NSImage(named: "KMImageNameElseSettingsSynchronizeSel")
  155. } else if item.itemIdentifier.rawValue == "DropboxPreferences" {
  156. item.image = NSImage(named: "KMImageNameElseSettingsDropboxSel")
  157. } else if item.itemIdentifier.rawValue == "IdentifyPreferense" {
  158. item.image = NSImage(named: "KMImageNameIdentifyPrefenceSel")
  159. }
  160. } else {
  161. if item.itemIdentifier.rawValue == "GeneralPreferences" {
  162. item.image = NSImage(named: "KMImageNameElseSettingsGeneralNor")
  163. } else if item.itemIdentifier.rawValue == "DisplayPreferences" {
  164. item.image = NSImage(named: "KMImageNameElseSettingsViewNor")
  165. } else if item.itemIdentifier.rawValue == "NotesPreferences" {
  166. item.image = NSImage(named: "KMImageNameElseSettingsAnnotationNor")
  167. } else if item.itemIdentifier.rawValue == "SyncPreferences" {
  168. item.image = NSImage(named: "KMImageNameElseSettingsSynchronizeNor")
  169. } else if item.itemIdentifier.rawValue == "DropboxPreferences" {
  170. item.image = NSImage(named: "KMImageNameElseSettingsDropboxNor")
  171. } else if item.itemIdentifier.rawValue == "IdentifyPreferense" {
  172. item.image = NSImage(named: "KMImageNameIdentifyPrefenceNor")
  173. }
  174. }
  175. }
  176. }
  177. @objc private func _selectPaneAction(_ sender: AnyObject?) {
  178. let _item = sender as? NSToolbarItem
  179. self._panelControlSelectColor(_item?.itemIdentifier.rawValue ?? "")
  180. if let pane = self._preferencePane(forItemIdentifier: _item?.itemIdentifier.rawValue ?? "") {
  181. self._selectPane(pane)
  182. }
  183. }
  184. private func _selectPane(_ pane: KMPreferencePane) {
  185. var _pane = pane as? NSViewController
  186. let _currentPane = self.currentPane as? NSViewController
  187. if _pane != _currentPane {
  188. if let data = _pane {
  189. self.historyIndex += 1
  190. if let cnt = self.history?.count, cnt > self.historyIndex {
  191. let range = self.historyIndex ..< cnt-self.historyIndex
  192. self.history?.removeSubrange(range)
  193. }
  194. self.history?.append(data)
  195. } else {
  196. _pane = self.history?[self.historyIndex]
  197. }
  198. let window = self.window
  199. let contentView = window?.contentView
  200. let oldView = _currentPane?.view
  201. let view = _pane?.view
  202. var frame = window?.frame ?? .zero
  203. frame.size.width = fmax(frame.size.width, NSWidth(_pane?.view.frame ?? .zero))
  204. frame = KMShrinkRect(rect: frame, amount: NSHeight(contentView?.frame ?? .zero) - NSMaxY(view?.frame ?? .zero), edge: .minY)
  205. // make sure edits are committed
  206. _currentPane?.commitEditing()
  207. NSUserDefaultsController.shared.commitEditing()
  208. self.currentPane = pane
  209. window?.title = (self.currentPane as? NSViewController)?.title ?? ""
  210. if let data = (self.currentPane as? NSViewController)?.nibName {
  211. UserDefaults.standard.setValue(data, forKey: SKLastSelectedPreferencePaneKey)
  212. window?.toolbar?.selectedItemIdentifier = NSToolbarItem.Identifier(data)
  213. }
  214. if UserDefaults.standard.bool(forKey: SKDisableAnimationsKey) {
  215. if oldView != nil && view != nil {
  216. contentView?.replaceSubview(oldView!, with: view!)
  217. }
  218. window?.setFrame(frame, display: true)
  219. } else {
  220. let duration = window?.animationResizeTime(frame) ?? 0
  221. contentView?.displayIfNeeded()
  222. NSAnimationContext.runAnimationGroup { context in
  223. context.duration = duration
  224. if oldView != nil && view != nil {
  225. contentView?.animator().replaceSubview(oldView!, with: view!)
  226. }
  227. window?.animator().setFrame(frame, display: true)
  228. }
  229. }
  230. }
  231. }
  232. @objc private func _resetCurrentSheetDidEnd(_ alert: NSAlert, _ returnCode: Int, _ contextInfo: Any) {
  233. if (returnCode == NSApplication.ModalResponse.alertFirstButtonReturn.rawValue) {
  234. if let initialUserDefaultsURL = Bundle.main.url(forResource: INITIALUSERDEFAULTS_KEY, withExtension: "plist") {
  235. if let _currentPane = self.currentPane as? NSViewController {
  236. let resettableKeys = (NSDictionary(contentsOf: initialUserDefaultsURL)?.object(forKey: RESETTABLEKEYS_KEY) as? NSDictionary)?.object(forKey: _currentPane.nibName ?? "")
  237. if let data = resettableKeys as? [String] {
  238. NSUserDefaultsController.shared.revertToInitialValues(forKeys: data)
  239. }
  240. self.currentPane?.defaultsDidRevert?()
  241. if _currentPane is KMGeneralPreferences {
  242. KMPreference.shared.resetData(.general)
  243. } else if _currentPane is KMDisplayPreferences {
  244. KMPreference.shared.resetData(.display)
  245. } else if _currentPane is KMNotesPreferences {
  246. KMPreference.shared.resetData(.markup)
  247. } else if _currentPane is IdentifyPreferense {
  248. KMProfileInfo.shared().fullName = ""
  249. KMProfileInfo.shared().headName = ""
  250. KMProfileInfo.shared().OrganizeName = ""
  251. KMProfileInfo.shared().OrganizePart = ""
  252. KMProfileInfo.shared().email = ""
  253. self.currentPane?.defaultsDidRevert?()
  254. }
  255. }
  256. }
  257. }
  258. }
  259. }
  260. extension KMPreferenceController: NSToolbarDelegate {
  261. func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
  262. let pane = self._preferencePane(forItemIdentifier: itemIdentifier.rawValue) as? NSViewController
  263. let item = NSToolbarItem(itemIdentifier: itemIdentifier)
  264. item.label = pane?.title ?? ""
  265. var ima: NSImage?
  266. if itemIdentifier.rawValue == "GeneralPreferences" {
  267. ima = NSImage(named: "KMImageNameElseSettingsGeneralNor")
  268. } else if itemIdentifier.rawValue == "DisplayPreferences" {
  269. ima = NSImage(named: "KMImageNameElseSettingsViewNor")
  270. } else if itemIdentifier.rawValue == "NotesPreferences" {
  271. ima = NSImage(named: "KMImageNameElseSettingsAnnotationNor")
  272. } else if itemIdentifier.rawValue == "SyncPreferences" {
  273. ima = NSImage(named: "KMImageNameElseSettingsSynchronizeNor")
  274. } else if itemIdentifier.rawValue == "DropboxPreferences" {
  275. ima = NSImage(named: "KMImageNameElseSettingsDropboxNor")
  276. } else if itemIdentifier.rawValue == "IdentifyPreferense" {
  277. ima = NSImage(named: "KMImageNameElseSettingsDropboxNor")
  278. }
  279. item.image = ima
  280. item.target = self
  281. item.action = #selector(_selectPaneAction)
  282. return item;
  283. }
  284. func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  285. var items: [NSToolbarItem.Identifier] = []
  286. for item in self.preferencePanes ?? [] {
  287. if let name = (item as? NSViewController)?.nibName {
  288. items.append(NSToolbarItem.Identifier(rawValue: name))
  289. }
  290. }
  291. return items
  292. }
  293. func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  294. return self.toolbarDefaultItemIdentifiers(toolbar)
  295. }
  296. func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  297. return self.toolbarDefaultItemIdentifiers(toolbar)
  298. }
  299. }
  300. extension KMPreferenceController: NSWindowDelegate {
  301. func windowDidResignMain(_ notification: Notification) {
  302. self.window?.contentView?.deactivateWellSubcontrols()
  303. }
  304. func windowWillClose(_ notification: Notification) {
  305. (self.currentPane as? NSViewController)?.commitEditing()
  306. NSUserDefaultsController.shared.commitEditing()
  307. self.window?.makeFirstResponder(nil)
  308. }
  309. }