KMPreferenceController.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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. /*
  32. @interface SKPreferenceController : NSWindowController <NSWindowDelegate, NSTabViewDelegate, NSToolbarDelegate> {
  33. NSArray *resetButtons;
  34. }
  35. @property (nonatomic, retain) IBOutlet NSArray *resetButtons;
  36. + (id)sharedPrefenceController;
  37. - (IBAction)resetAll:(id)sender;
  38. - (IBAction)resetCurrent:(id)sender;
  39. - (IBAction)doGoToNextPage:(id)sender;
  40. - (IBAction)doGoToPreviousPage:(id)sender;
  41. - (IBAction)doGoToFirstPage:(id)sender;
  42. - (IBAction)doGoToLastPage:(id)sender;
  43. - (IBAction)doGoBack:(id)sender;
  44. - (IBAction)doGoForward:(id)sender;
  45. - (IBAction)changeFont:(id)sender;
  46. - (IBAction)changeAttributes:(id)sender;
  47. - (void)selectPaneWithIdentifier:(NSString *)itemIdentifier;
  48. @end
  49. */
  50. deinit {
  51. KMPrint("KMPreferenceController deinit.")
  52. // currentPane = nil;
  53. }
  54. static let shared = KMPreferenceController(windowNibName: "PreferenceWindow")
  55. override func windowDidLoad() {
  56. super.windowDidLoad()
  57. self.preferencePanes = [KMGeneralPreferences(), KMDisplayPreferences(), KMNotesPreferences(), KMSyncPreferences()]
  58. self.history = []
  59. self.historyIndex = 0
  60. if #available(macOS 11.0,*) {
  61. self.window?.toolbarStyle = .expanded
  62. }
  63. resetBtn.title = KMLocalizedString("Reset")
  64. resetAllBtn.title = KMLocalizedString("Reset All")
  65. let window = self.window
  66. let toolbar = NSToolbar(identifier: SKPreferencesToolbarIdentifier)
  67. toolbar.allowsUserCustomization = false
  68. toolbar.autosavesConfiguration = false
  69. toolbar.isVisible = true
  70. toolbar.delegate = self
  71. window?.toolbar = toolbar
  72. window?.showsToolbarButton = false
  73. window?.contentView?.wantsLayer = true
  74. // we want to restore the top of the window, while without the force it restores the bottom position without the size
  75. window?.setFrameUsingName(SKPreferenceWindowFrameAutosaveName, force: true)
  76. self.windowFrameAutosaveName = SKPreferenceWindowFrameAutosaveName
  77. // [NSGraphicsContext SKAutoSizeButtons:resetButtons rightAlign:false];
  78. var width = 0.0
  79. var frame: NSRect = .zero
  80. var pane: NSViewController?
  81. var view: NSView?
  82. for pane in self.preferencePanes ?? [] {
  83. let _pane = pane as? NSViewController
  84. width = fmax(width, NSWidth(_pane?.view.frame ?? .zero))
  85. }
  86. for pane in self.preferencePanes ?? [] {
  87. let _pane = pane as? NSViewController
  88. view = _pane?.view
  89. frame = view?.frame ?? .zero
  90. if let data = view?.autoresizingMask.contains(.width), data {
  91. frame.size.width = width
  92. } else {
  93. frame.origin.x = floor(0.5 * (width - NSWidth(frame)))
  94. }
  95. frame.origin.y = BOTTOM_MARGIN
  96. view?.frame = frame
  97. }
  98. self.currentPane = self._preferencePane(forItemIdentifier: UserDefaults.standard.string(forKey: SKLastSelectedPreferencePaneKey) ?? "") ?? self.preferencePanes?.first
  99. toolbar.selectedItemIdentifier = NSToolbarItem.Identifier((self.currentPane as? NSViewController)?.nibName ?? "")
  100. if let _currentPane = self.currentPane as? NSViewController {
  101. window?.title = _currentPane.title ?? ""
  102. self.history?.append(_currentPane)
  103. }
  104. view = (self.currentPane as? NSViewController)?.view
  105. frame = window?.frame ?? .zero
  106. frame.size.width = width
  107. frame = KMShrinkRect(rect: frame, amount: NSHeight(window?.contentView?.frame ?? .zero) - NSMaxY(view?.frame ?? .zero), edge: .minY)
  108. window?.setFrame(frame, display: false)
  109. if let data = view {
  110. window?.contentView?.addSubview(data)
  111. }
  112. NotificationCenter.default.addObserver(self, selector: #selector(preferenceDidChangeNotification), name: KMPreferenceManager.didChangeNotification, object: nil)
  113. self.resetBtn.toolTip = NSLocalizedString("Revert all currently shown preferences to their original values", comment: "")
  114. self.resetAllBtn.toolTip = NSLocalizedString("Revert all preferences to their original values", comment: "")
  115. }
  116. @IBAction func changeFont(_ sender: AnyObject?) {
  117. self.window?.contentView?.activeFontWell()?.changeFontFromFontManager(sender)
  118. }
  119. /*
  120. - (void)selectPaneWithIdentifier:(NSString *)itemIdentifier {
  121. [self selectPane:[self preferencePaneForItemIdentifier:itemIdentifier]];
  122. }
  123. #pragma mark Actions
  124. - (IBAction)doGoToNextPage:(id)sender {
  125. NSUInteger itemIndex = [preferencePanes indexOfObject:currentPane];
  126. if (itemIndex != NSNotFound && ++itemIndex < [preferencePanes count])
  127. [self selectPane:[preferencePanes objectAtIndex:itemIndex]];
  128. }
  129. - (IBAction)doGoToPreviousPage:(id)sender {
  130. NSUInteger itemIndex = [preferencePanes indexOfObject:currentPane];
  131. if (itemIndex != NSNotFound && itemIndex-- > 0)
  132. [self selectPane:[preferencePanes objectAtIndex:itemIndex]];
  133. }
  134. - (IBAction)doGoToFirstPage:(id)sender {
  135. [self selectPane:[preferencePanes objectAtIndex:0]];
  136. }
  137. - (IBAction)doGoToLastPage:(id)sender {
  138. [self selectPane:[preferencePanes lastObject]];
  139. }
  140. - (IBAction)doGoBack:(id)sender {
  141. if (historyIndex > 0) {
  142. historyIndex--;
  143. [self selectPane:nil];
  144. }
  145. }
  146. - (IBAction)doGoForward:(id)sender {
  147. if (historyIndex + 1 < [history count]) {
  148. historyIndex++;
  149. [self selectPane:nil];
  150. }
  151. }
  152. - (IBAction)changeAttributes:(id)sender {
  153. // [[[[self window] contentView] activeFontWell] changeAttributesFromFontManager:sender];
  154. }
  155. - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
  156. if ([menuItem action] == @selector(doGoToNextPage:) || [menuItem action] == @selector(doGoToLastPage:))
  157. return [currentPane isEqual:[preferencePanes lastObject]] == NO;
  158. else if ([menuItem action] == @selector(doGoToPreviousPage:) || [menuItem action] == @selector(doGoToFirstPage:))
  159. return [currentPane isEqual:[preferencePanes objectAtIndex:0]] == NO;
  160. else if ([menuItem action] == @selector(doGoBack:))
  161. return historyIndex > 0;
  162. else if ([menuItem action] == @selector(doGoForward:))
  163. return historyIndex + 1 < [history count];
  164. return YES;
  165. }
  166. #pragma mark Toolbar
  167. @end
  168. */
  169. @IBAction func resetCurrent(_ sender: AnyObject?) {
  170. if (self.currentPane == nil) {
  171. return
  172. }
  173. let label = (self.currentPane as? NSViewController)?.title ?? ""
  174. let alert = NSAlert()
  175. alert.messageText = String(format: KMLocalizedString("Reset %@ preferences to their original values?"), label)
  176. alert.informativeText = String(format: KMLocalizedString("Choosing Reset will restore all settings in this pane to the state they were in when PDF Reader Pro Edition was first installed."), label)
  177. alert.addButton(withTitle: KMLocalizedString("Reset"))
  178. alert.addButton(withTitle: KMLocalizedString("Cancel"))
  179. alert.beginSheetModal(for: self.window!, modalDelegate: self, didEnd: #selector(_resetCurrentSheetDidEnd), contextInfo: nil)
  180. }
  181. @IBAction func resetAll(_ sender: AnyObject?) {
  182. let alert = NSAlert()
  183. alert.messageText = KMLocalizedString("Reset all preferences to their original values?")
  184. alert.informativeText = KMLocalizedString("Choosing Reset will restore all settings to the state they were in when PDF Reader Pro Edition was first installed.")
  185. alert.addButton(withTitle: KMLocalizedString("Reset"))
  186. alert.addButton(withTitle: KMLocalizedString("Cancel"))
  187. let response = alert.runModal()
  188. if (response == .alertFirstButtonReturn) {
  189. NSUserDefaultsController.shared.revertToInitialValues(forKeys: nil)
  190. for pane in self.preferencePanes ?? [] {
  191. pane.defaultsDidRevert?()
  192. }
  193. KMPreference.shared.resetAllData()
  194. }
  195. }
  196. @objc private func preferenceDidChangeNotification(sender: Notification) {
  197. self.currentPane?.reloadData?()
  198. }
  199. }
  200. // MARK: - Private Methods
  201. extension KMPreferenceController {
  202. private func _preferencePane(forItemIdentifier itemIdent: String) -> KMPreferencePane? {
  203. self._panelControlSelectColor(itemIdent)
  204. for pane in self.preferencePanes ?? [] {
  205. let _pane = pane as? NSViewController
  206. if _pane?.nibName == itemIdent {
  207. return pane
  208. }
  209. }
  210. return nil
  211. }
  212. private func _panelControlSelectColor(_ identifier: String) {
  213. for item in self.window?.toolbar?.items ?? [] {
  214. if item.itemIdentifier.rawValue == identifier {
  215. if item.itemIdentifier.rawValue == "GeneralPreferences" {
  216. item.image = NSImage(named: "KMImageNameElseSettingsGeneralSel")
  217. } else if item.itemIdentifier.rawValue == "DisplayPreferences" {
  218. item.image = NSImage(named: "KMImageNameElseSettingsViewSel")
  219. } else if item.itemIdentifier.rawValue == "NotesPreferences" {
  220. item.image = NSImage(named: "KMImageNameElseSettingsAnnotationSel")
  221. } else if item.itemIdentifier.rawValue == "SyncPreferences" {
  222. item.image = NSImage(named: "KMImageNameElseSettingsSynchronizeSel")
  223. } else if item.itemIdentifier.rawValue == "DropboxPreferences" {
  224. item.image = NSImage(named: "KMImageNameElseSettingsDropboxSel")
  225. }
  226. } else {
  227. if item.itemIdentifier.rawValue == "GeneralPreferences" {
  228. item.image = NSImage(named: "KMImageNameElseSettingsGeneralNor")
  229. } else if item.itemIdentifier.rawValue == "DisplayPreferences" {
  230. item.image = NSImage(named: "KMImageNameElseSettingsViewNor")
  231. } else if item.itemIdentifier.rawValue == "NotesPreferences" {
  232. item.image = NSImage(named: "KMImageNameElseSettingsAnnotationNor")
  233. } else if item.itemIdentifier.rawValue == "SyncPreferences" {
  234. item.image = NSImage(named: "KMImageNameElseSettingsSynchronizeNor")
  235. } else if item.itemIdentifier.rawValue == "DropboxPreferences" {
  236. item.image = NSImage(named: "KMImageNameElseSettingsDropboxNor")
  237. }
  238. }
  239. }
  240. }
  241. @objc private func _selectPaneAction(_ sender: AnyObject?) {
  242. let _item = sender as? NSToolbarItem
  243. self._panelControlSelectColor(_item?.itemIdentifier.rawValue ?? "")
  244. if let pane = self._preferencePane(forItemIdentifier: _item?.itemIdentifier.rawValue ?? "") {
  245. self._selectPane(pane)
  246. }
  247. }
  248. private func _selectPane(_ pane: KMPreferencePane) {
  249. var _pane = pane as? NSViewController
  250. let _currentPane = self.currentPane as? NSViewController
  251. if _pane != _currentPane {
  252. if let data = _pane {
  253. self.historyIndex += 1
  254. if let cnt = self.history?.count, cnt > self.historyIndex {
  255. let range = self.historyIndex ..< cnt-self.historyIndex
  256. self.history?.removeSubrange(range)
  257. }
  258. self.history?.append(data)
  259. } else {
  260. _pane = self.history?[self.historyIndex]
  261. }
  262. let window = self.window
  263. let contentView = window?.contentView
  264. let oldView = _currentPane?.view
  265. let view = _pane?.view
  266. var frame = window?.frame ?? .zero
  267. frame = KMShrinkRect(rect: frame, amount: NSHeight(contentView?.frame ?? .zero) - NSMaxY(view?.frame ?? .zero), edge: .minY)
  268. // make sure edits are committed
  269. _currentPane?.commitEditing()
  270. NSUserDefaultsController.shared.commitEditing()
  271. self.currentPane = pane
  272. window?.title = (self.currentPane as? NSViewController)?.title ?? ""
  273. if let data = (self.currentPane as? NSViewController)?.nibName {
  274. UserDefaults.standard.setValue(data, forKey: SKLastSelectedPreferencePaneKey)
  275. window?.toolbar?.selectedItemIdentifier = NSToolbarItem.Identifier(data)
  276. }
  277. if UserDefaults.standard.bool(forKey: SKDisableAnimationsKey) {
  278. if oldView != nil && view != nil {
  279. contentView?.replaceSubview(oldView!, with: view!)
  280. }
  281. window?.setFrame(frame, display: true)
  282. } else {
  283. let duration = window?.animationResizeTime(frame) ?? 0
  284. contentView?.displayIfNeeded()
  285. NSAnimationContext.runAnimationGroup { context in
  286. context.duration = duration
  287. if oldView != nil && view != nil {
  288. contentView?.animator().replaceSubview(oldView!, with: view!)
  289. }
  290. window?.animator().setFrame(frame, display: true)
  291. }
  292. }
  293. }
  294. }
  295. @objc private func _resetCurrentSheetDidEnd(_ alert: NSAlert, _ returnCode: Int, _ contextInfo: Any) {
  296. if (returnCode == NSApplication.ModalResponse.alertFirstButtonReturn.rawValue) {
  297. if let initialUserDefaultsURL = Bundle.main.url(forResource: INITIALUSERDEFAULTS_KEY, withExtension: "plist") {
  298. if let _currentPane = self.currentPane as? NSViewController {
  299. let resettableKeys = (NSDictionary(contentsOf: initialUserDefaultsURL)?.object(forKey: RESETTABLEKEYS_KEY) as? NSDictionary)?.object(forKey: _currentPane.nibName ?? "")
  300. if let data = resettableKeys as? [String] {
  301. NSUserDefaultsController.shared.revertToInitialValues(forKeys: data)
  302. }
  303. self.currentPane?.defaultsDidRevert?()
  304. if _currentPane is KMGeneralPreferences {
  305. KMPreference.shared.resetData(.general)
  306. } else if _currentPane is KMDisplayPreferences {
  307. KMPreference.shared.resetData(.display)
  308. } else if _currentPane is KMNotesPreferences {
  309. KMPreference.shared.resetData(.markup)
  310. }
  311. }
  312. }
  313. }
  314. }
  315. }
  316. extension KMPreferenceController: NSToolbarDelegate {
  317. func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
  318. let pane = self._preferencePane(forItemIdentifier: itemIdentifier.rawValue) as? NSViewController
  319. let item = NSToolbarItem(itemIdentifier: itemIdentifier)
  320. item.label = pane?.title ?? ""
  321. var ima: NSImage?
  322. if itemIdentifier.rawValue == "GeneralPreferences" {
  323. ima = NSImage(named: "KMImageNameElseSettingsGeneralNor")
  324. } else if itemIdentifier.rawValue == "DisplayPreferences" {
  325. ima = NSImage(named: "KMImageNameElseSettingsViewNor")
  326. } else if itemIdentifier.rawValue == "NotesPreferences" {
  327. ima = NSImage(named: "KMImageNameElseSettingsAnnotationNor")
  328. } else if itemIdentifier.rawValue == "SyncPreferences" {
  329. ima = NSImage(named: "KMImageNameElseSettingsSynchronizeNor")
  330. } else if itemIdentifier.rawValue == "DropboxPreferences" {
  331. ima = NSImage(named: "KMImageNameElseSettingsDropboxNor")
  332. }
  333. item.image = ima
  334. item.target = self
  335. item.action = #selector(_selectPaneAction)
  336. return item;
  337. }
  338. func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  339. var items: [NSToolbarItem.Identifier] = []
  340. for item in self.preferencePanes ?? [] {
  341. if let name = (item as? NSViewController)?.nibName {
  342. items.append(NSToolbarItem.Identifier(rawValue: name))
  343. }
  344. }
  345. return items
  346. }
  347. func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  348. return self.toolbarDefaultItemIdentifiers(toolbar)
  349. }
  350. func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  351. return self.toolbarDefaultItemIdentifiers(toolbar)
  352. }
  353. }
  354. extension KMPreferenceController: NSWindowDelegate {
  355. func windowDidResignMain(_ notification: Notification) {
  356. self.window?.contentView?.deactivateWellSubcontrols()
  357. }
  358. func windowWillClose(_ notification: Notification) {
  359. (self.currentPane as? NSViewController)?.commitEditing()
  360. NSUserDefaultsController.shared.commitEditing()
  361. }
  362. }