KMOutlineViewController.swift 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. //
  2. // KMOutlineViewController.swift
  3. // PDF Master
  4. //
  5. // Created by lxy on 2022/10/10.
  6. //
  7. import Cocoa
  8. class KMOutlineViewController: NSViewController {
  9. @IBOutlet var contendView: NSView!
  10. @IBOutlet weak var topView: NSView!
  11. @IBOutlet weak var titleLabel: NSTextField!
  12. @IBOutlet weak var lineView: NSView!
  13. @IBOutlet weak var addButton: NSButton!
  14. @IBOutlet weak var moreButton: NSButton!
  15. @IBOutlet var topSepline: NSView!
  16. @IBOutlet weak var emptyView: NSView!
  17. @IBOutlet weak var bigTipLabel: NSTextField!
  18. @IBOutlet weak var tipLabel: NSTextField!
  19. @IBOutlet weak var BOTAOutlineView: KMBOTAOutlineView!
  20. var dragPDFOutline : KMBOTAOutlineItem!
  21. var renameTextField : NSTextField!
  22. var listView : CPDFListView!
  23. var renamePDFOutline : KMBOTAOutlineItem!
  24. let moreMenu = NSMenu()
  25. var isLocalEvent = false
  26. func dealloc() {
  27. NotificationCenter.default.removeObserver(self)
  28. }
  29. deinit {
  30. NotificationCenter.default.removeObserver(self)
  31. self.BOTAOutlineView.delegate = nil
  32. }
  33. override func viewDidLoad() {
  34. super.viewDidLoad()
  35. self.emptyView.wantsLayer = true
  36. // self.emptyView.layer?.backgroundColor = NSColor.red.cgColor
  37. self.addOutlineMenu()
  38. self.titleLabel.stringValue = NSLocalizedString("Outline", comment: "")
  39. self.topView.wantsLayer = true
  40. self.BOTAOutlineView.delegate = self
  41. self.BOTAOutlineView.inputData = self.listView.document.outlineRoot()
  42. self.BOTAOutlineView.outlineView.doubleAction = #selector(outlineViewDoubleAction)
  43. self.refreshUI()
  44. self.initNotification()
  45. }
  46. func refreshUI() {
  47. self.contendView.wantsLayer = true
  48. self.contendView.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
  49. self.topSepline.wantsLayer = true
  50. self.topSepline.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor
  51. self.titleLabel.textColor = NSColor(hex: "#252629")
  52. self.titleLabel.font = NSFont.SFProTextSemibold(14.0)
  53. self.bigTipLabel.font = NSFont.SFProTextRegular(14.0)
  54. self.bigTipLabel.textColor = NSColor(hex: "#616469")
  55. self.bigTipLabel.stringValue = NSLocalizedString("No Outlines", comment: "")
  56. self.lineView.backgroundColor(NSColor(hex: "#EDEEF0"))
  57. let title = NSLocalizedString("To create an outline, please right-click on the selected page and choose \"Add Outline\", or click \"Add\" in the upper right corner.", comment: "")
  58. let paragraphStyle = NSMutableParagraphStyle()
  59. paragraphStyle.lineHeightMultiple = 1.32
  60. paragraphStyle.alignment = .center
  61. self.tipLabel.attributedStringValue = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor(hex: "#94989C")])
  62. self.addButton.toolTip = NSLocalizedString("Add Outline", comment: "")
  63. self.moreButton.toolTip = NSLocalizedString("More", comment: "")
  64. }
  65. private func addOutlineMenu(){
  66. moreMenu.addItem(withTitle: NSLocalizedString("Expand All", comment: ""), action: #selector(expandAllComments), target: self, tag: 0)
  67. moreMenu.addItem(withTitle: NSLocalizedString("Collapse All", comment: ""), action: #selector(collapseAllComments), target: self, tag: 1)
  68. moreMenu.addItem(withTitle: NSLocalizedString("Remove All Outlines", comment: ""), action: #selector(removeAllOutlineItem), target: self, tag: 2)
  69. }
  70. func addRightMenu(view: NSView, event: NSEvent) {
  71. let menus : NSMenu = NSMenu(title: "")
  72. let addItem = self.menuItemWithTitle(title: NSLocalizedString("Add Item", comment: ""), action: #selector(addItemAction))
  73. let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Add Sub-Item", comment: ""), action: #selector(addChildItemAction))
  74. let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Add A Higher Level", comment: ""), action: #selector(addHigherItemAction))
  75. let deleteItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction))
  76. // let editItem = self.menuItemWithTitle(title: NSLocalizedString("Edit", comment: ""), action: #selector(editItemAction))
  77. let renameItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameItemAction))
  78. let changeItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeItemAction))
  79. let promoteItem = self.menuItemWithTitle(title: NSLocalizedString("Promote", comment: ""), action: #selector(promoteItemAction))
  80. let demoteItem = self.menuItemWithTitle(title: NSLocalizedString("Demote", comment: ""), action: #selector(demoteItemAction))
  81. menus.addItem(addItem)
  82. menus.addItem(addChildItem)
  83. menus.addItem(addHigherItem)
  84. menus.addItem(NSMenuItem.separator())
  85. menus.addItem(deleteItem)
  86. menus.addItem(NSMenuItem.separator())
  87. // menus.addItem(editItem)
  88. menus.addItem(renameItem)
  89. menus.addItem(changeItem)
  90. menus.addItem(NSMenuItem.separator())
  91. menus.addItem(promoteItem)
  92. menus.addItem(demoteItem)
  93. let point = view.convert(event.locationInWindow, from: nil)
  94. menus.popUp(positioning: nil, at: point, in: view)
  95. }
  96. func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem {
  97. let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "")
  98. menuItem.target = self
  99. return menuItem
  100. }
  101. func initNotification() {
  102. NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil)
  103. }
  104. func reloadData() {
  105. self.BOTAOutlineView.reloadData(expandItemType: .none)
  106. }
  107. func editOutlineUI(editVC : KMOutlineEditViewController!) {
  108. if editVC.pageButton.state == NSControl.StateValue.on {
  109. let index = Int(editVC.outlineTargetPageIndexTextField.stringValue)!
  110. if editVC.originalDestination.pageIndex != index {
  111. let page = editVC.pdfView.document.page(at: UInt(index))
  112. if page != nil {
  113. let destination = CPDFDestination.init(document: editVC.pdfView.document, pageIndex: index)
  114. editVC.outline.destination = destination
  115. } else {
  116. __NSBeep()
  117. }
  118. }
  119. } else if editVC.urlButton.state == NSControl.StateValue.on {
  120. if editVC.originalURLString != editVC.outlineURLTextField.stringValue {
  121. var urlString = editVC.outlineURLTextField.stringValue
  122. let tLowerUrl = urlString.lowercased()
  123. if !tLowerUrl.hasPrefix("https://") && !tLowerUrl.hasPrefix("pf]://") && !urlString.hasPrefix("https://") &&
  124. urlString.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) > 0 {
  125. urlString = "http://\(urlString)"
  126. }
  127. let action = CPDFURLAction.init(url: urlString)
  128. editVC.outline.action = action
  129. }
  130. } else if editVC.mailButton.state == NSControl.StateValue.on {
  131. var mailString = editVC.mailAddressTextField.stringValue
  132. let tLowerStr = mailString.lowercased()
  133. if !tLowerStr.hasPrefix("mailto:") {
  134. mailString = "mailto:\(mailString)"
  135. }
  136. if mailString != editVC.originalURLString {
  137. var action = CPDFURLAction.init(url: mailString)
  138. if action?.url == nil {
  139. action = CPDFURLAction.init(url: "mailto:")
  140. }
  141. editVC.outline.action = action
  142. }
  143. }
  144. // self.mainWindowController.document?.undo()
  145. // if editVC.outlineNameTextView.string != editVC.originalLabel {
  146. // self.renamePDFOutline(outline: editVC.outline, label: editVC.outlineNameTextView.string)
  147. // }
  148. }
  149. }
  150. //Data
  151. extension KMOutlineViewController {
  152. func updateExtempViewState() {
  153. if(self.listView.document.outlineRoot() == nil || self.listView.document.outlineRoot().numberOfChildren == 0) { //无数据时的图
  154. self.emptyView.isHidden = false
  155. } else {
  156. self.emptyView.isHidden = true
  157. }
  158. }
  159. }
  160. //MARK: - Notification
  161. extension KMOutlineViewController {
  162. @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) {
  163. let pdfdocument : CPDFDocument = notification.object as! CPDFDocument
  164. if pdfdocument.isEqual(self.listView.document) {
  165. if !isLocalEvent {
  166. self.updateOutlineSelection()
  167. }
  168. self.isLocalEvent = false
  169. }
  170. }
  171. }
  172. //MARK: - Menu 右键菜单
  173. extension KMOutlineViewController {
  174. @objc func outlineViewDoubleAction() {
  175. if(self.BOTAOutlineView.outlineView.clickedRow >= 0) {
  176. self.renameItemAction()
  177. }
  178. }
  179. @objc func addItemAction() {
  180. let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
  181. let selectRowIndexs = outlineView.selectedRowIndexes
  182. let dataCount = self.BOTAOutlineView.data?.children.count
  183. var index: Int
  184. var parent: KMBOTAOutlineItem = KMBOTAOutlineItem()
  185. var outlineItem: KMBOTAOutlineItem = KMBOTAOutlineItem()
  186. if selectRowIndexs.count == 0 {
  187. var lastOulineItem = KMBOTAOutlineItem()
  188. if dataCount == nil || dataCount == 0 {
  189. let item = KMBOTAOutlineItem()
  190. item.outline = self.listView.document.setNewOutlineRoot()
  191. item.parent = nil
  192. parent = item
  193. lastOulineItem = item
  194. } else {
  195. outlineItem = outlineView.item(atRow: outlineView.numberOfRows - 1) as! KMBOTAOutlineItem
  196. lastOulineItem = outlineItem
  197. while lastOulineItem.parent != nil {
  198. lastOulineItem = lastOulineItem.parent!
  199. }
  200. parent = lastOulineItem
  201. }
  202. index = Int(lastOulineItem.outline.numberOfChildren)
  203. } else {
  204. outlineItem = outlineView.item(atRow: selectRowIndexs.last ?? 0) as! KMBOTAOutlineItem
  205. parent = outlineItem.parent ?? KMBOTAOutlineItem()
  206. index = Int(outlineItem.outline.index + 1)
  207. }
  208. self.addOutlineToIndex(index: index, parent: parent)
  209. }
  210. @objc func addChildItemAction() {
  211. let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
  212. let selectRowIndexs = outlineView.selectedRowIndexes
  213. if selectRowIndexs.count != 0 {
  214. let outlineItem: KMBOTAOutlineItem = outlineView.item(atRow: selectRowIndexs.last!) as! KMBOTAOutlineItem
  215. let index = outlineItem.outline.numberOfChildren
  216. self.addOutlineToIndex(index: NSInteger(index), parent: outlineItem)
  217. }
  218. }
  219. @objc func addHigherItemAction() {
  220. let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
  221. let selectRowIndexs = outlineView.selectedRowIndexes
  222. if selectRowIndexs.count != 0 {
  223. let outlineItem: KMBOTAOutlineItem = outlineView.item(atRow: selectRowIndexs.last!) as! KMBOTAOutlineItem
  224. var parent = outlineItem.parent
  225. let index = NSInteger(parent!.outline.index) + 1
  226. parent = parent?.parent
  227. if parent != nil {
  228. self.addOutlineToIndex(index: index, parent: parent!)
  229. }
  230. }
  231. }
  232. @objc func deleteItemAction() {
  233. let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
  234. let selectRowIndexs = outlineView.selectedRowIndexes
  235. if selectRowIndexs.count != 0 {
  236. var outlineItems: [KMBOTAOutlineItem] = []
  237. for index in selectRowIndexs {
  238. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: index) as! KMBOTAOutlineItem
  239. outlineItem.toIndex = index
  240. outlineItem.parent = outlineItem.parent ?? KMBOTAOutlineItem()
  241. outlineItems.append(outlineItem)
  242. }
  243. self.deleteOutline(outlineItems: outlineItems)
  244. }
  245. }
  246. @objc func editItemAction() {
  247. if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
  248. if self.BOTAOutlineView.outlineView.rowView(atRow: self.BOTAOutlineView.outlineView.clickedRow, makeIfNecessary: true) != nil {
  249. let cell = self.BOTAOutlineView.outlineView.rowView(atRow: self.BOTAOutlineView.outlineView.clickedRow, makeIfNecessary: true)
  250. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
  251. let outlineEditViewController = KMOutlineEditViewController.init(outline: outlineItem.outline, document: self.listView)
  252. let popover = NSPopover()
  253. popover.delegate = self
  254. popover.contentViewController = outlineEditViewController
  255. popover.animates = true
  256. popover.behavior = .transient
  257. popover.show(relativeTo: cell!.bounds, of: cell!, preferredEdge: .minX)
  258. }
  259. } else {
  260. __NSBeep()
  261. }
  262. }
  263. @objc func renameItemAction() {
  264. if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
  265. self.renameOutlineWithRow(row: self.BOTAOutlineView.outlineView.clickedRow)
  266. } else {
  267. __NSBeep()
  268. }
  269. }
  270. @objc func changeItemAction() {
  271. if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
  272. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
  273. let alter = NSAlert()
  274. alter.alertStyle = NSAlert.Style.informational
  275. alter.messageText = NSLocalizedString("Are you sure you want to set the target location of the selected outline to the current page?", comment: "")
  276. alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
  277. alter.addButton(withTitle: NSLocalizedString("No", comment:""))
  278. let modlres = alter.runModal()
  279. if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
  280. self.changeLocation(outlineItem: outlineItem, destination: self.fetchCurrentDestination())
  281. }
  282. } else {
  283. __NSBeep()
  284. }
  285. }
  286. @objc func promoteItemAction() {
  287. if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
  288. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
  289. var parent = outlineItem.parent
  290. let index = NSInteger(parent!.outline.index) + 1
  291. parent = parent?.parent
  292. if parent != nil {
  293. self.moveOutline(outlineItem: outlineItem, index: index, parent: parent)
  294. }
  295. }
  296. }
  297. @objc func demoteItemAction() {
  298. if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
  299. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
  300. let parent = outlineItem.parent
  301. let newParent = parent?.children[Int(outlineItem.outline.index) - 1]
  302. let index = newParent?.children.count
  303. if (index != nil) {
  304. self.moveOutline(outlineItem: outlineItem, index: NSInteger(index ?? 0), parent: newParent)
  305. }
  306. }
  307. }
  308. @objc private func expandAllComments(item: NSMenuItem) {
  309. self.BOTAOutlineView.expandAllComments(item: item)
  310. }
  311. @objc private func collapseAllComments(item: NSMenuItem) {
  312. self.BOTAOutlineView.collapseAllComments(item: item)
  313. }
  314. @objc private func removeAllOutlineItem(item: NSMenuItem) {
  315. let alter = NSAlert()
  316. alter.alertStyle = NSAlert.Style.informational
  317. alter.messageText = NSLocalizedString("This will permanently remove all outlines. Are you sure to continue?", comment: "")
  318. alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
  319. alter.addButton(withTitle: NSLocalizedString("No", comment:""))
  320. let modlres = alter.runModal()
  321. if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
  322. self.removeAllOutline()
  323. }
  324. }
  325. @objc private func removeAllOutline() {
  326. guard let data = self.BOTAOutlineView.data else { return }
  327. for item in data.children {
  328. item.toIndex = Int(item.outline.index)
  329. }
  330. self.deleteOutline(outlineItems: data.children)
  331. self.BOTAOutlineView.reloadData(expandItemType: .none)
  332. }
  333. }
  334. //MARK: - Action
  335. extension KMOutlineViewController {
  336. @IBAction func addNewOutline(_ sender: Any) {
  337. self.addItemAction()
  338. }
  339. @IBAction func moreButton_click(_ sender: NSButton) {
  340. let rect = sender.convert(sender.bounds, to: self.view)
  341. moreMenu.popUp(positioning: nil, at: NSPoint(x: rect.origin.x, y: rect.origin.y-10), in: self.view)
  342. }
  343. @IBAction func escButtonAction(_ sender: Any) {
  344. self.cancelSelect()
  345. }
  346. func cancelSelect() {
  347. self.BOTAOutlineView.cancelSelect()
  348. }
  349. func renameOutlineWithRow(row: NSInteger) {
  350. DispatchQueue.main.async {
  351. self.renamePDFOutline = self.BOTAOutlineView.outlineView.item(atRow: row) as? KMBOTAOutlineItem
  352. let cell : KMBOTAOutlineCellView = self.BOTAOutlineView.outlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as! KMBOTAOutlineCellView
  353. self.renameTextField = cell.titleLabel
  354. self.renameTextField.delegate = self
  355. self.renameTextField.isEditable = true
  356. self.renameTextField.becomeFirstResponder()
  357. }
  358. }
  359. func addOutlineToIndex(index: Int, parent: KMBOTAOutlineItem) {
  360. let pageIndex: Int = self.listView.currentPageIndex
  361. let label: String = self.fetchCurrentLabel(pageIndex: pageIndex)
  362. let destination: CPDFDestination = self.fetchCurrentDestination()
  363. self.addOutlineToIndex(index: index, pageIndex: pageIndex, destination: destination, lable: label, parent: parent)
  364. }
  365. func addOutlineToIndex(index: Int, pageIndex: Int, destination: CPDFDestination, lable: String, parent: KMBOTAOutlineItem) {
  366. let outlineItem = KMBOTAOutlineItem()
  367. outlineItem.destination = destination
  368. outlineItem.label = lable
  369. outlineItem.parent = parent
  370. outlineItem.toIndex = index
  371. self.addOutline(outlineItems: [outlineItem])
  372. let tempOutlineView = self.BOTAOutlineView!
  373. var index = -1
  374. if tempOutlineView.outlineView.numberOfRows == 1 || tempOutlineView.data == nil {
  375. index = 0
  376. } else {
  377. index = tempOutlineView.outlineView.row(forItem: outlineItem)
  378. }
  379. tempOutlineView.selectIndex(index: index)
  380. //滑动到指定位置
  381. if(tempOutlineView.outlineView.selectedRow >= 0) {
  382. self.renameOutlineWithRow(row: tempOutlineView.outlineView.selectedRow)
  383. }
  384. let row = tempOutlineView.outlineView.row(forItem: outlineItem)
  385. if Thread.current.isMainThread {
  386. tempOutlineView.outlineView.scrollToVisible(tempOutlineView.outlineView.rect(ofRow: row))
  387. } else {
  388. DispatchQueue.main.async {
  389. tempOutlineView.outlineView.scrollToVisible(tempOutlineView.outlineView.rect(ofRow: row))
  390. }
  391. }
  392. }
  393. func updateOutlineSelection() {
  394. print("updateOutlineSelection")
  395. let currentPageIndex = self.listView.currentPageIndex
  396. let numRows = self.BOTAOutlineView.outlineView.numberOfRows
  397. if numRows > 0 {
  398. for i in 0...numRows - 1 {
  399. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: i) as! KMBOTAOutlineItem
  400. if (outlineItem.outline.destination == nil) {
  401. continue
  402. }
  403. if outlineItem.outline.destination.pageIndex == currentPageIndex {
  404. self.BOTAOutlineView.selectIndex(index: currentPageIndex)
  405. break
  406. }
  407. }
  408. }
  409. }
  410. func fetchCurrentDestination() -> CPDFDestination {
  411. //destination
  412. var destination: CPDFDestination
  413. let pageIndex: Int
  414. if self.listView.currentSelection != nil {
  415. let des :CPDFDestination = CPDFDestination.init(document: self.listView.document, pageIndex: Int(self.listView.currentSelection.page.pageIndex()), at: CGPoint(x: self.listView.currentSelection.bounds.origin.x, y: self.listView.currentSelection.bounds.origin.y + self.listView.currentSelection.bounds.size.height), zoom: self.listView.scaleFactor)
  416. destination = des
  417. pageIndex = Int(self.listView.currentSelection.page.pageIndex())
  418. } else {
  419. let des :CPDFDestination = CPDFDestination.init(document: self.listView.document, pageIndex: Int(self.listView.currentPageIndex), at: CGPoint(x: 0, y: self.listView.currentPage().size.height), zoom: self.listView.scaleFactor)
  420. destination = des
  421. pageIndex = Int(self.listView.currentPageIndex)
  422. }
  423. if "\(destination.point.x )" == "nan" {
  424. destination = CPDFDestination(document: self.listView.document, pageIndex: pageIndex, at: CGPoint(x: 0, y: 0), zoom: self.listView.scaleFactor)
  425. }
  426. return destination
  427. }
  428. func fetchCurrentLabel(pageIndex: Int) -> String{
  429. //label
  430. var label = "\(NSLocalizedString("Page", comment: ""))\(pageIndex + 1)"
  431. if self.listView.currentSelection != nil && self.listView.currentSelection.selectionsByLine != nil {
  432. for currentSelection in self.listView.currentSelection.selectionsByLine {
  433. label = currentSelection.string()
  434. }
  435. }
  436. return label
  437. }
  438. }
  439. //MARK: - KMBOTAOutlineViewDelegate
  440. extension KMOutlineViewController: KMBOTAOutlineViewDelegate {
  441. func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, rightDidMoseDown: KMBOTAOutlineItem, event: NSEvent) {
  442. let row = outlineView.outlineView.row(forItem: rightDidMoseDown)
  443. if outlineView.outlineView.rowView(atRow: row, makeIfNecessary: false) != nil {
  444. let rowView = outlineView.outlineView.rowView(atRow: row, makeIfNecessary: false)
  445. self.addRightMenu(view: rowView!, event: event)
  446. }
  447. }
  448. func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, didReloadData: KMBOTAOutlineItem) {
  449. self.updateExtempViewState()
  450. }
  451. func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, didSelectItem: [KMBOTAOutlineItem]) {
  452. if self.BOTAOutlineView.outlineView.selectedRowIndexes.count == 1 {
  453. self.isLocalEvent = true
  454. let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow:self.BOTAOutlineView.outlineView.selectedRow) as! KMBOTAOutlineItem
  455. if outlineItem.outline.destination != nil {
  456. self.listView.go(toTargetPoint: outlineItem.outline.destination.point, on: outlineItem.outline.destination.page() , at: .top)
  457. } else if outlineItem.outline.action != nil {
  458. self.listView.perform(outlineItem.outline.action)
  459. }
  460. }
  461. }
  462. func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool {
  463. if outlineView.outlineView.selectedRowIndexes.count > 1 || outlineView.outlineView.selectedRow == -1 {
  464. return false
  465. }
  466. self.dragPDFOutline = items.first as? KMBOTAOutlineItem
  467. let indexSet = [outlineView.outlineView.clickedRow]
  468. let indexSetData: Data = try!NSKeyedArchiver.archivedData(withRootObject: indexSet, requiringSecureCoding: true)
  469. pasteboard.declareTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")], owner: self)
  470. pasteboard.setData(indexSetData, forType: NSPasteboard.PasteboardType(rawValue: NSPasteboard.PasteboardType.RawValue("kKMPDFViewOutlineDragDataType")))
  471. return true
  472. }
  473. func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
  474. var dragOperation = NSDragOperation.init(rawValue: 0)
  475. if index >= 0 {
  476. dragOperation = NSDragOperation.move
  477. }
  478. return dragOperation
  479. }
  480. func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
  481. guard let dragOutlineItem = self.dragPDFOutline else { return false }
  482. let outlineItem: KMBOTAOutlineItem = (item ?? KMBOTAOutlineItem()) as! KMBOTAOutlineItem
  483. if index < 0 {
  484. return false
  485. }
  486. if outlineItem.parent == nil {
  487. var root = dragOutlineItem.parent
  488. while root?.parent?.children != nil {
  489. root = root?.parent!
  490. }
  491. if dragOutlineItem.parent!.isEqual(root) {
  492. if dragOutlineItem.outline.index > index {
  493. self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: root)
  494. } else {
  495. self.moveOutline(outlineItem: dragOutlineItem, index: index - 1, parent: root)
  496. }
  497. } else {
  498. self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: root)
  499. }
  500. } else {
  501. if dragOutlineItem.parent!.isEqual(item) {
  502. if dragOutlineItem.outline.index != 0 {
  503. if dragOutlineItem.outline.index > index {
  504. self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: item as? KMBOTAOutlineItem)
  505. } else {
  506. self.moveOutline(outlineItem: dragOutlineItem, index: index - 1, parent: item as? KMBOTAOutlineItem)
  507. }
  508. } else {
  509. return false
  510. }
  511. } else {
  512. var tOutline = outlineItem
  513. var isContains = false
  514. while (tOutline.parent != nil) {
  515. if tOutline.outline.isEqual(dragOutlineItem.outline) {
  516. isContains = true
  517. break
  518. }
  519. tOutline = tOutline.parent!
  520. }
  521. if isContains == false {
  522. self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: item as? KMBOTAOutlineItem)
  523. }
  524. }
  525. }
  526. return true
  527. }
  528. }
  529. //MARK: - NSTextFieldDelegate
  530. extension KMOutlineViewController: NSTextFieldDelegate {
  531. func controlTextDidEndEditing(_ obj: Notification) {
  532. if (self.renameTextField.isEqual(obj.object)) {
  533. let textField : NSTextField = obj.object as! NSTextField
  534. self.renamePDFOutline(outlineItem: self.renamePDFOutline, label: textField.stringValue)
  535. }
  536. }
  537. }
  538. //MARK: - NSPopoverDelegate
  539. extension KMOutlineViewController: NSPopoverDelegate {
  540. func popoverWillClose(_ notification: Notification) {
  541. let popover : NSPopover = notification.object as! NSPopover
  542. if popover.contentViewController!.isKind(of: KMOutlineEditViewController.self) {
  543. }
  544. }
  545. }
  546. //MARK: - NSMenuItemValidation
  547. extension KMOutlineViewController: NSMenuDelegate, NSMenuItemValidation {
  548. func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  549. let action = menuItem.action
  550. if action == #selector(addItemAction) ||
  551. action == #selector(addChildItemAction) ||
  552. action == #selector(addHigherItemAction) ||
  553. action == #selector(deleteItemAction) ||
  554. action == #selector(editItemAction) ||
  555. action == #selector(changeItemAction) ||
  556. action == #selector(renameItemAction) ||
  557. action == #selector(promoteItemAction) ||
  558. action == #selector(demoteItemAction) {
  559. if self.BOTAOutlineView.outlineView.selectedRowIndexes.count > 1 {
  560. if action == #selector(deleteItemAction) {
  561. return true
  562. }
  563. return false
  564. } else if self.BOTAOutlineView.outlineView.selectedRowIndexes.count > 0 {
  565. if action == #selector(addChildItemAction) || action == #selector(changeItemAction) {
  566. return true
  567. }
  568. }
  569. if self.BOTAOutlineView.outlineView.clickedRow == -1 {
  570. if action == #selector(addItemAction) {
  571. return true
  572. } else {
  573. return false
  574. }
  575. } else {
  576. let outlineItem : KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
  577. if outlineItem.outline.index > 0 {
  578. if action == #selector(demoteItemAction) {
  579. return true
  580. }
  581. } else {
  582. if action == #selector(demoteItemAction) {
  583. return false
  584. }
  585. }
  586. let parentOutline = outlineItem.outline.parent
  587. let grandparentOutline = parentOutline?.parent
  588. if grandparentOutline != nil {
  589. if action == #selector(addHigherItemAction) {
  590. return true
  591. } else if action == #selector(promoteItemAction) {
  592. return true
  593. }
  594. } else {
  595. if action == #selector(addHigherItemAction) {
  596. return false
  597. } else if action == #selector(promoteItemAction) {
  598. return false
  599. }
  600. }
  601. }
  602. return true
  603. }
  604. if (action == #selector(undo)) {
  605. return self.listView.undoManager?.canUndo ?? false
  606. }
  607. if (action == #selector(redo)) {
  608. return self.listView.undoManager?.canRedo ?? false
  609. }
  610. if (action == #selector(expandAllComments)) {
  611. var canExpand = false
  612. for row in 0..<self.BOTAOutlineView.outlineView.numberOfRows {
  613. // 检查当前项目是否可以展开
  614. let item = self.BOTAOutlineView.outlineView.item(atRow: row)
  615. if self.BOTAOutlineView.outlineView.isExpandable(item) {
  616. if !self.BOTAOutlineView.outlineView.isItemExpanded(item) {
  617. canExpand = true
  618. break
  619. }
  620. }
  621. }
  622. return canExpand
  623. }
  624. if (action == #selector(collapseAllComments)) {
  625. var canCollapse = false
  626. for row in 0..<self.BOTAOutlineView.outlineView.numberOfRows {
  627. let item = self.BOTAOutlineView.outlineView.item(atRow: row)
  628. if self.BOTAOutlineView.outlineView.isExpandable(item) {
  629. if self.BOTAOutlineView.outlineView.isItemExpanded(item) {
  630. canCollapse = true
  631. break
  632. }
  633. }
  634. }
  635. return canCollapse
  636. }
  637. if (action == #selector(removeAllOutlineItem)) {
  638. if self.BOTAOutlineView.outlineView.item(atRow: 0) != nil {
  639. return true
  640. } else {
  641. return false
  642. }
  643. }
  644. return true
  645. }
  646. }
  647. //MARK: - 快捷键
  648. extension KMOutlineViewController {
  649. @IBAction func delete(_ sender: Any) {
  650. self.deleteItemAction()
  651. }
  652. }
  653. //MARK: - undoRedo
  654. extension KMOutlineViewController {
  655. func moveOutline(outlineItem: KMBOTAOutlineItem, index: NSInteger, parent: KMBOTAOutlineItem!) {
  656. let tempOutlineView = self.BOTAOutlineView!
  657. let indexTemp = outlineItem.outline.index
  658. let parentTemp = outlineItem.parent
  659. //元数据移除
  660. outlineItem.outline.removeFromParent()
  661. parent.outline.insertChild(outlineItem.outline, at: UInt(index))
  662. //显示数据刷新
  663. outlineItem.parent?.children.removeObject(outlineItem)
  664. parent?.children.insert(outlineItem, at: index)
  665. outlineItem.parent = parent
  666. tempOutlineView.outlineView.reloadData()
  667. tempOutlineView.cancelSelect()
  668. //展开
  669. outlineItem.isItemExpanded = true
  670. outlineItem.parent?.isItemExpanded = true
  671. tempOutlineView.outlineView.expandItem(outlineItem)
  672. tempOutlineView.outlineView.expandItem(outlineItem.parent)
  673. self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
  674. self.moveOutline(outlineItem: outlineItem, index: NSInteger(indexTemp), parent: parentTemp)
  675. }
  676. }
  677. func changeLocation(outlineItem: KMBOTAOutlineItem, destination: CPDFDestination) {
  678. let tempOutlineView = self.BOTAOutlineView!
  679. let temp = outlineItem.outline.destination
  680. outlineItem.outline.destination = CPDFDestination(document: destination.document, pageIndex: destination.pageIndex, at: destination.point, zoom: destination.zoom)
  681. tempOutlineView.outlineView.reloadItem(outlineItem)
  682. self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
  683. self.changeLocation(outlineItem: outlineItem, destination: temp!)
  684. }
  685. }
  686. func renamePDFOutline(outlineItem: KMBOTAOutlineItem!, label: String) {
  687. let tempOutlineView = self.BOTAOutlineView!
  688. self.view.window?.makeFirstResponder(tempOutlineView.outlineView)
  689. self.renameTextField.isEditable = false
  690. if outlineItem.outline.label == label {
  691. return
  692. }
  693. let temp: String = outlineItem.outline.label
  694. outlineItem.outline.label = label
  695. tempOutlineView.outlineView.reloadItem(outlineItem)
  696. self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
  697. self.renamePDFOutline(outlineItem: outlineItem, label: temp)
  698. }
  699. }
  700. func deleteOutline(outlineItems: [KMBOTAOutlineItem]) {
  701. NSApp.mainWindow?.makeFirstResponder(self.BOTAOutlineView)
  702. let tempOutlineView = self.BOTAOutlineView!
  703. var tempOutlineItems: [KMBOTAOutlineItem] = outlineItems
  704. tempOutlineItems.sort(){$0.toIndex > $1.toIndex}
  705. for outlineItem in tempOutlineItems {
  706. outlineItem.outline.removeFromParent()
  707. let index = outlineItem.parent?.children.firstIndex(of: outlineItem)
  708. outlineItem.toIndex = index!
  709. outlineItem.parent?.children.removeObject(outlineItem)
  710. }
  711. //展开
  712. for outlineItem in tempOutlineItems {
  713. outlineItem.parent?.isItemExpanded = true
  714. tempOutlineView.outlineView.expandItem(outlineItem.parent)
  715. }
  716. tempOutlineView.outlineView.reloadData()
  717. //删除需要取消选中
  718. tempOutlineView.cancelSelect()
  719. //刷新nil数据
  720. self.updateExtempViewState()
  721. self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
  722. self.addOutline(outlineItems: tempOutlineItems)
  723. }
  724. }
  725. func addOutline(outlineItems: [KMBOTAOutlineItem]) {
  726. NSApp.mainWindow?.makeFirstResponder(self.BOTAOutlineView)
  727. let tempOutlineView = self.BOTAOutlineView!
  728. //先取消选中
  729. tempOutlineView.cancelSelect()
  730. var tempOutlineItems: [KMBOTAOutlineItem] = outlineItems
  731. tempOutlineItems.sort(){$0.toIndex < $1.toIndex}
  732. for outlineItem in tempOutlineItems {
  733. if outlineItem.outline.label != nil {
  734. outlineItem.parent?.outline.insertChild(outlineItem.outline, at: UInt(outlineItem.toIndex))
  735. } else {
  736. let outline = outlineItem.parent?.outline.insertChild(at: UInt(outlineItem.toIndex))
  737. outline?.label = outlineItem.label
  738. outline?.destination = outlineItem.destination
  739. outlineItem.outline = outline!
  740. }
  741. outlineItem.parent?.children.insert(outlineItem, at: outlineItem.toIndex)
  742. }
  743. if tempOutlineView.data?.children.count == 0 || tempOutlineView.data == nil {
  744. tempOutlineView.inputData = self.listView.document.outlineRoot()
  745. } else {
  746. tempOutlineView.outlineView.reloadData()
  747. }
  748. //展开
  749. // DispatchQueue.main.async {
  750. for outlineItem in tempOutlineItems {
  751. var tempParent = outlineItem
  752. while tempParent.parent != nil {
  753. tempParent.isItemExpanded = true
  754. tempParent = tempParent.parent!
  755. tempOutlineView.outlineView.expandItem(tempParent)
  756. }
  757. tempOutlineView.outlineView.expandItem(tempParent.parent)
  758. }
  759. // }
  760. self.updateExtempViewState()
  761. self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
  762. self.deleteOutline(outlineItems: tempOutlineItems)
  763. }
  764. }
  765. @IBAction func undo(_ sender: Any) {
  766. if (self.listView.undoManager?.canUndo ?? false) {
  767. self.listView.undoManager?.undo()
  768. }
  769. }
  770. @IBAction func redo(_ sender: Any) {
  771. if (self.listView.undoManager?.canRedo ?? false) {
  772. self.listView.undoManager?.redo()
  773. }
  774. }
  775. }