SKPresentationOptionsSheetController.swift 19 KB

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