KMOutlineViewController.swift 39 KB

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