SKPresentationOptionsSheetController.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. //
  2. // SKPresentationOptionsSheetController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by liujiajie on 2024/1/17.
  6. //
  7. import Cocoa
  8. typealias closePresentationControllerCallBack = (_ vc: SKPresentationOptionsSheetController) -> ()
  9. let RIGHTARROW_CHARACTER: unichar = 0x2192
  10. let PAGE_COLUMNID = "page"
  11. let IMAGE_COLUMNID = "image"
  12. let TRANSITIONSTYLE_KEY = "transitionStyle"
  13. let DURATION_KEY = "duration"
  14. let SHOULDRESTRICT_KEY = "shouldRestrict"
  15. let PROPERTIES_KEY = "properties"
  16. let CONTENTOBJECT_BINDINGNAME = "contentObject"
  17. let MAX_PAGE_COLUMN_WIDTH: CGFloat = 100.0
  18. let TABLE_OFFSET: CGFloat = 8.0
  19. var SKTransitionPropertiesObservationContext = UnsafeMutableRawPointer(mutating: "SKTransitionPropertiesObservationContext")
  20. var SKPDFViewTransitionsObservationContext: CChar?
  21. class SKPresentationOptionsSheetController: NSWindowController {
  22. @IBOutlet var notesDocumentPopUpButton: NSPopUpButton!
  23. @IBOutlet var tableView: KMBotaTableView!
  24. @IBOutlet var separateCheckButton: NSButton!
  25. @IBOutlet var boxes, transitionLabels, transitionControls, buttons: NSArray!
  26. // @IBOutlet var arrayController: NSArrayController!
  27. @IBOutlet var effectPopUpButton: NSPopUpButton!
  28. @IBOutlet var effectLabel: NSTextField!
  29. @IBOutlet var durationLabel: NSTextField!
  30. @IBOutlet var durationSlider: NSSlider!
  31. @IBOutlet var durationTF: NSTextField!
  32. @IBOutlet var extentLabel: NSTextField!
  33. @IBOutlet var screeBtn: NSButtonCell!
  34. @IBOutlet var pageBtn: NSButtonCell!
  35. @IBOutlet var synchoronizBox: NSBox!
  36. @IBOutlet var cancelBtn: NSButton!
  37. @IBOutlet var okBtn: NSButton!
  38. @IBOutlet var pageTransitionBox: NSBox!
  39. var closeCallBack: closePresentationControllerCallBack?
  40. var separate = false
  41. var transition: KMTransitionInfo = KMTransitionInfo()
  42. private var transitions_: NSArray?
  43. var transitions: NSArray? {
  44. get {
  45. return self.transitions_
  46. }
  47. set{
  48. if self.transitions_ != newValue {
  49. // (undoRedoManager?.prepare(withInvocationTarget: self) as AnyObject).transitions = transitions
  50. self.transitions_ = newValue
  51. stopObservingTransitions(infos: transitions as! [KMTransitionInfo])
  52. startObservingTransitions(transitions as! [KMTransitionInfo])
  53. }
  54. }
  55. }
  56. var currentTransitions: NSArray? {
  57. get{
  58. return self.separate ? self.transitions : [transition as Any]
  59. }
  60. }
  61. var pageTransitions: NSArray? {
  62. get{
  63. if self.separate && self.transitions?.count ?? 0 > 0 {
  64. return [transitions?.value(forKey: PROPERTIES_KEY) as Any]
  65. }
  66. return nil
  67. }
  68. }
  69. var notesDocument: NSDocument? {
  70. get{
  71. _ = self.window
  72. return self.notesDocumentPopUpButton.selectedItem?.representedObject as? NSDocument
  73. }
  74. }
  75. var isScrolling: Bool {
  76. get{
  77. let scroller = self.tableView?.enclosingScrollView?.verticalScroller as? KMScroller
  78. return scroller?.isScrolling ?? false
  79. }
  80. }
  81. var undoRedoManager = UndoManager()
  82. var controller: KMMainViewController?
  83. var transitionController: SKTransitionController!
  84. private weak var proxyDelegate_: SKTransitionControllerDelegate?
  85. class func keyPathsForValuesAffectingCurrentTransitions() -> Set<String> {
  86. return Set<String>(["separate", "transitions", "transition"])
  87. }
  88. // convenience init(for aController: KMMainViewController) {
  89. // super.init(window: window)
  90. // controller = aController
  91. // separate = false
  92. // transition = KMTransitionInfo()
  93. // transitions = nil
  94. // }
  95. override var windowNibName: NSNib.Name?{
  96. return "TransitionSheet"
  97. }
  98. override init(window: NSWindow?) {
  99. super.init(window: window)
  100. }
  101. required init?(coder: NSCoder) {
  102. super.init(coder: coder)
  103. }
  104. override func windowDidLoad() {
  105. super.windowDidLoad()
  106. let count = SKTransitionController.transitionNames().count
  107. for i in 0..<count {
  108. self.effectPopUpButton.addItem(withTitle: SKTransitionController.localizedName(for: SKAnimationTransitionStyle(rawValue: UInt(i)) ?? .noTransition))
  109. self.effectPopUpButton.lastItem?.tag = i
  110. }
  111. self.notesDocumentPopUpButton.item(at: 0)?.title = NSLocalizedString("None", comment: "Menu item title")
  112. // creatTransitionController()
  113. let transitionController = self.controller?.listView.transitionController
  114. self.proxyDelegate_ = transitionController?.delegate
  115. transitionController?.delegate = self
  116. self.transition.transitionStyle = transitionController?.transitionStyle ?? .noTransition
  117. self.transition.duration = Float(transitionController?.duration ?? 0)
  118. self.transition.shouldRestrict = transitionController?.shouldRestrict ?? false
  119. self.startObservingTransitions([self.transition])
  120. self.separateCheckButton.sizeToFit()
  121. // transitionControls.lastObject.sizeToFit()
  122. SKAutoSizeButtons(buttons as? [Any], true)
  123. let dw = SKAutoSizeLabelFields(transitionLabels as? [Any], transitionControls as? [Any], false)
  124. if abs(dw) > 0.0 {
  125. SKResizeWindow(self.window, dw)
  126. SKShiftAndResizeViews(self.boxes as? [Any], -dw, dw)
  127. SKShiftAndResizeView(self.separateCheckButton, -dw, 0.0)
  128. }
  129. // collapse the table
  130. self.window?.setFrame(NSInsetRect(window?.frame ?? .zero, 0.5 * (NSWidth(tableView.enclosingScrollView!.frame) + TABLE_OFFSET), 0.0), display: false)
  131. self.tableView.registerForDraggedTypes(KMTransitionInfo.readableTypesForPasteboard(pasteboard: NSPasteboard(name: NSPasteboard.Name.drag)) as! [NSPasteboard.PasteboardType])
  132. self.tableView.delegate = self
  133. self.tableView.dataSource = self
  134. self.tableView.botaDelegate = self
  135. self.tableView.setTypeSelectHelper(SKTypeSelectHelper(matchOption: .SKFullStringMatch))
  136. self.tableView.hasImageToolTips = true
  137. self.tableView.backgroundColor = NSColor.mainSourceListBackgroundColor()
  138. if (transitionController?.pageTransitions != nil && transitionController?.pageTransitions.count ?? 0 > 0) {
  139. undoManager?.disableUndoRegistration()
  140. self.separate = true
  141. undoManager?.enableUndoRegistration()
  142. }
  143. // set the current notes document and observe changes for the popup
  144. self.handleDocumentsDidChangeNotification(notification: nil)
  145. let docIndex = self.notesDocumentPopUpButton.indexOfItem(withRepresentedObject: self.controller?.myDocument/*presentationNotesDocument*/)
  146. self.notesDocumentPopUpButton.selectItem(at: docIndex > 0 ? docIndex : 0)
  147. NotificationCenter.default.addObserver(self, selector: #selector(handleDocumentsDidChangeNotification(notification:)), name: NSNotification.Name("SKDocumentDidShowNotification"), object: nil)
  148. NotificationCenter.default.addObserver(self, selector: #selector(handleDocumentsDidChangeNotification(notification:)), name: NSNotification.Name("SKDocumentControllerDidRemoveDocumentNotification"), object: nil)
  149. }
  150. func stopObservingTransitions(infos: [KMTransitionInfo]) {
  151. // for info in infos {
  152. // info.removeObserver(self, forKeyPath: TRANSITIONSTYLE_KEY)
  153. // info.removeObserver(self, forKeyPath: DURATION_KEY)
  154. // info.removeObserver(self, forKeyPath: SHOULDRESTRICT_KEY)
  155. // }
  156. }
  157. // func creatTransitionController() {
  158. // transitionController = SKTransitionController(for: controller?.listView)//controller?.mainViewController.listView.transitionController
  159. // let options: NSKeyValueObservingOptions = [.new, .old]
  160. // transitionController?.addObserver(self, forKeyPath: "transitionStyle", options: options, context: &SKPDFViewTransitionsObservationContext)
  161. // transitionController?.addObserver(self, forKeyPath: "duration", options: options, context: &SKPDFViewTransitionsObservationContext)
  162. // transitionController?.addObserver(self, forKeyPath: "shouldRestrict", options: options, context: &SKPDFViewTransitionsObservationContext)
  163. // transitionController?.addObserver(self, forKeyPath: "pageTransitions", options: options, context: &SKPDFViewTransitionsObservationContext)
  164. // }
  165. func startObservingTransitions(_ infos: [KMTransitionInfo]) {
  166. // for info in infos {
  167. // info.addObserver(self, forKeyPath: TRANSITIONSTYLE_KEY, options: [.new, .old], context: &SKTransitionPropertiesObservationContext)
  168. // info.addObserver(self, forKeyPath: DURATION_KEY, options: [.new, .old], context: &SKTransitionPropertiesObservationContext)
  169. // info.addObserver(self, forKeyPath: SHOULDRESTRICT_KEY, options: [.new, .old], context: &SKTransitionPropertiesObservationContext)
  170. // }
  171. }
  172. func setValue(_ value: Any?, forKey key: String, ofTransition info: KMTransitionInfo) {
  173. info.setValue(value, forKey: key)
  174. }
  175. override class func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
  176. if context == &SKTransitionPropertiesObservationContext {
  177. guard let info = object as? KMTransitionInfo else {
  178. super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
  179. return
  180. }
  181. let newValue = change?[.newKey]
  182. let oldValue = change?[.oldKey]
  183. let newNonNullValue = newValue as? NSNull == nil ? newValue : nil
  184. let oldNonNullValue = oldValue as? NSNull == nil ? oldValue : nil
  185. // if (newNonNullValue != nil || oldNonNullValue != nil) && newNonNullValue != oldNonNullValue {
  186. // (undoManager?.prepare(withInvocationTarget: self) as AnyObject).setValue(oldNonNullValue, forKey: keyPath, ofTransition: info)
  187. // }
  188. if (newNonNullValue != nil || oldNonNullValue != nil){
  189. }
  190. } else {
  191. super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
  192. }
  193. }
  194. // MARK: - Private
  195. private func makeTransitions() {
  196. if transitions != nil {
  197. return
  198. }
  199. let tableColumn = self.tableView?.tableColumn(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: PAGE_COLUMNID))
  200. let cell = tableColumn?.dataCell
  201. var labelWidth: CGFloat = 0.0
  202. var array = [Any]()
  203. let dictionary: NSDictionary? = self.transition.properties
  204. let arr: NSArray? = transitionController.pageTransitions as NSArray?
  205. let ptEnum = arr?.objectEnumerator()
  206. var tn: KMThumbnail? = nil
  207. for next in (controller?.leftSideViewController.thumbnails)! {
  208. if tn != nil {
  209. let info = KMTransitionInfo()
  210. info.thumbnail = tn
  211. info.label = "\(tn!.label)"+"→"+"\(next.label)"
  212. info.properties = ((ptEnum?.nextObject()) != nil) ? ptEnum?.nextObject() as? NSDictionary : dictionary
  213. array.append(info)
  214. (cell as AnyObject).setStringValue(info.label)
  215. labelWidth = max(labelWidth, ceil(((cell as AnyObject).cellSize?.width)!))
  216. }
  217. tn = next
  218. }
  219. labelWidth = min(labelWidth, MAX_PAGE_COLUMN_WIDTH)
  220. tableColumn?.minWidth = labelWidth
  221. tableColumn?.maxWidth = labelWidth
  222. tableColumn?.width = labelWidth
  223. var frame: NSRect = self.tableView?.enclosingScrollView?.frame ?? .zero
  224. let wi: CGFloat = 61//tableColumn?.value(forKeyPath: "@sum.width") as! CGFloat
  225. frame.size.width = 19.0 + wi
  226. self.tableView?.enclosingScrollView?.frame = frame
  227. self.transitions = array as NSArray
  228. }
  229. private func changePageTransitionType() {
  230. // SKImageToolTipWindow.sharedToolTipWindow().orderOut(nil)
  231. let window = self.window
  232. let isVisible = window?.isVisible ?? false
  233. var frame = window?.frame ?? NSRect.zero
  234. let scrollView = tableView?.enclosingScrollView
  235. var extraWidth: CGFloat
  236. let firstResponder = window?.firstResponder
  237. var editor: NSTextView? = nil
  238. if let textView = firstResponder as? NSTextView {
  239. editor = textView
  240. if textView.isFieldEditor {
  241. // firstResponder = textView.delegate
  242. }
  243. }
  244. if let editor = editor, window?.firstResponder != editor {
  245. window?.makeFirstResponder(firstResponder)
  246. }
  247. if separate {
  248. makeTransitions()
  249. extraWidth = (scrollView?.frame.width ?? 0) + TABLE_OFFSET
  250. frame.size.width += extraWidth
  251. frame.origin.x -= floor(0.5 * extraWidth)
  252. window?.setFrame(frame, display: isVisible, animate: isVisible)
  253. scrollView?.isHidden = false
  254. } else {
  255. scrollView?.isHidden = true
  256. extraWidth = (scrollView?.frame.width ?? 0) + TABLE_OFFSET
  257. frame.size.width -= extraWidth
  258. frame.origin.x += floor(0.5 * extraWidth)
  259. window?.setFrame(frame, display: isVisible, animate: isVisible)
  260. }
  261. // (undoRedoManager.prepare(withInvocationTarget: self) as AnyObject).separate = sparate == fasle
  262. }
  263. // Actions
  264. @IBAction func pageTransitionAction(_ sender: NSButton) {
  265. self.separate = sender.state == .on
  266. self.changePageTransitionType()
  267. }
  268. @IBAction func changeDurationSlider(_ sender: NSSlider) {
  269. self.durationTF.stringValue = sender.stringValue
  270. }
  271. @IBAction func cancelAction(_ sender: Any) {
  272. if let handle = self.closeCallBack {
  273. handle(self)
  274. }
  275. }
  276. @IBAction func okAction(_ sender: Any) {
  277. if let handle = self.closeCallBack {
  278. handle(self)
  279. }
  280. if ((undoManager?.canUndo) != nil) {
  281. self.transitionController?.transitionStyle = transition.transitionStyle
  282. self.transitionController?.duration = CGFloat(transition.duration)
  283. self.transitionController?.shouldRestrict = transition.shouldRestrict
  284. self.transitionController?.pageTransitions = pageTransitions as? [Any]
  285. (self.controller?.undoManager)?.setActionName(NSLocalizedString("Change Transitions", comment: "Undo action name"))
  286. }
  287. self.controller?.myDocument/*presentationNotesDocument*/ = notesDocument
  288. }
  289. // Noti Actions
  290. @objc private func handleDocumentsDidChangeNotification(notification: Notification?) {
  291. guard let currentDoc = notesDocumentPopUpButton.selectedItem?.representedObject else { return }
  292. while notesDocumentPopUpButton.numberOfItems > 1 {
  293. notesDocumentPopUpButton.removeItem(at: notesDocumentPopUpButton.numberOfItems - 1)
  294. }
  295. guard let document = controller?.document as? NSDocument,
  296. let pageCount = controller?.listView.document?.pageCount else { return }
  297. let documents = NSMutableArray()
  298. for doc in NSDocumentController.shared.documents {
  299. if let pdfDoc = doc as? CPDFDocument,
  300. doc != document,
  301. pdfDoc.pageCount == pageCount {
  302. documents.add(doc)
  303. }
  304. }
  305. let sortDescriptor = NSSortDescriptor(key: "displayName", ascending: true)
  306. documents.sort(using: [sortDescriptor])
  307. for doc in documents {
  308. notesDocumentPopUpButton.addItem(withTitle: (doc as AnyObject).displayName)
  309. notesDocumentPopUpButton.lastItem?.representedObject = doc
  310. }
  311. let docIndex = notesDocumentPopUpButton.indexOfItem(withRepresentedObject: currentDoc)
  312. notesDocumentPopUpButton.selectItem(at: docIndex == -1 ? 0 : docIndex)
  313. }
  314. }
  315. extension SKPresentationOptionsSheetController: NSWindowDelegate {
  316. func windowWillReturnUndoManager(_ window: NSWindow) -> UndoManager? {
  317. return self.undoRedoManager
  318. }
  319. }
  320. extension SKPresentationOptionsSheetController: SKTransitionControllerDelegate {
  321. func transitionController(_ controller: SKTransitionController!, valueDidChanged info: [AnyHashable : Any]!) {
  322. // 消息转发
  323. self.proxyDelegate_?.transitionController(controller, valueDidChanged: info)
  324. }
  325. }
  326. extension SKPresentationOptionsSheetController: NSTableViewDelegate, NSTableViewDataSource {
  327. func numberOfRows(in tableView: NSTableView) -> Int {
  328. return transitions?.count ?? 0
  329. }
  330. // func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  331. // let identifier = tableColumn!.identifier
  332. // let info = transitions?[row]
  333. // if identifier.rawValue == PAGE_COLUMNID {
  334. //
  335. // } else if identifier.rawValue == IMAGE_COLUMNID {
  336. //
  337. // }
  338. // return nil
  339. // }
  340. func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
  341. return nil
  342. }
  343. func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
  344. if rowIndexes.count == 1 {
  345. pboard.clearContents()
  346. pboard.writeObjects([transitions?[rowIndexes.first ?? 0] as! NSPasteboardWriting])
  347. return true
  348. } else {
  349. return false
  350. }
  351. }
  352. func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
  353. if info.draggingPasteboard.canReadObject(forClasses: [KMTransitionInfo.self], options: [:]) {
  354. if dropOperation == .above {
  355. tableView.setDropRow(-1, dropOperation: .on)
  356. }
  357. return .every
  358. }
  359. return []
  360. }
  361. func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
  362. let pboard = info.draggingPasteboard
  363. if dropOperation == .on {
  364. guard let infos = pboard.readObjects(forClasses: [KMTransitionInfo.self], options: [:]) as? [KMTransitionInfo], infos.count > 0 else {
  365. return false
  366. }
  367. let propertie: NSDictionary? = infos[0].properties
  368. if row == -1 {
  369. transitions?.setValue(propertie, forKey: PROPERTIES_KEY)
  370. } else {
  371. let transit: KMTransitionInfo = transitions?[row] as! KMTransitionInfo
  372. transit.properties = propertie
  373. }
  374. return true
  375. }
  376. return false
  377. }
  378. }
  379. extension SKPresentationOptionsSheetController: KMBotaTableViewDelegate {
  380. func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject? {
  381. return controller?.document?.page(at: UInt(rowIndex))
  382. }
  383. func tableView(_ aTableView: NSTableView, copyRowsWithIndexes rowIndexes: IndexSet) {
  384. let pboard = NSPasteboard.general
  385. pboard.clearContents()
  386. pboard.writeObjects([transitions?[rowIndexes.first ?? 0] as! NSPasteboardWriting])
  387. }
  388. func tableView(_ aTableView: NSTableView, canCopyRowsWithIndexes rowIndexes: IndexSet) -> Bool {
  389. return true
  390. }
  391. func tableView(_ aTableView: NSTableView, pasteFromPasteboard pboard: NSPasteboard) {
  392. guard let infos = pboard.readObjects(forClasses: [KMTransitionInfo.self], options: [:]) as? [KMTransitionInfo], infos.count > 0 else { return }
  393. let arr: NSArray = transitions?.objects(at: tableView.selectedRowIndexes) as! NSArray
  394. arr.setValue(infos[0].properties, forKey: PROPERTIES_KEY)
  395. }
  396. func tableView(_ aTableView: NSTableView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool {
  397. return (tableView.selectedRow != -1 && pboard.canReadObject(forClasses: [KMTransitionInfo.self], options: [:]))
  398. }
  399. func tableView(_ aTableView: NSTableView, typeSelectHelperSelectionStrings aTypeSelectHelper: SKTypeSelectHelper) -> NSArray {
  400. return transitions?.value(forKeyPath: "thumbnail.label") as! NSArray
  401. }
  402. }