KMOutlineViewController.swift 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. //
  2. // KMOutlineViewController.swift
  3. // PDF Office
  4. //
  5. // Created by lxy on 2022/10/10.
  6. //
  7. import Cocoa
  8. class KMOutlineViewController: NSViewController,NSMenuItemValidation {
  9. @IBOutlet var contendView: NSView!
  10. @IBOutlet weak var outlineView: NSOutlineView!
  11. @IBOutlet weak var topView: NSView!
  12. @IBOutlet weak var titleLabel: NSTextField!
  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. var dragPDFOutline : CPDFOutline!
  20. var renameTextField : NSTextField!
  21. var listView : CPDFListView!
  22. var renamePDFOutline : CPDFOutline!
  23. let moreMenu = NSMenu()
  24. func dealloc() {
  25. NotificationCenter.default.removeObserver(self)
  26. }
  27. override func viewDidLoad() {
  28. super.viewDidLoad()
  29. self.emptyView.wantsLayer = true
  30. // self.emptyView.layer?.backgroundColor = NSColor.red.cgColor
  31. self.outlineView.registerForDraggedTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")])
  32. self.outlineView.doubleAction = #selector(outlineViewDoubleAction)
  33. self.outlineView.target = self
  34. self.outlineView.allowsMultipleSelection = true
  35. self.outlineView.selectionHighlightStyle = NSTableView.SelectionHighlightStyle.none;
  36. self.addOutlineMenu()
  37. self.titleLabel.stringValue = NSLocalizedString("Outline", comment: "")
  38. self.topView.wantsLayer = true
  39. // self.topView.layer?.backgroundColor = NSColor.cl.cgColor
  40. // self.outlineView.backgroundColor = NSColor.white
  41. self.refreshUI()
  42. self.initNotification()
  43. }
  44. func refreshUI() {
  45. self.contendView.wantsLayer = true
  46. self.contendView.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
  47. self.topSepline.wantsLayer = true
  48. self.topSepline.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor
  49. self.titleLabel.textColor = NSColor(hex: "#252629")
  50. self.bigTipLabel.font = NSFont.SFProTextRegular(14.0)
  51. self.bigTipLabel.textColor = NSColor(hex: "#616469")
  52. self.bigTipLabel.stringValue = NSLocalizedString("No Outlines", comment: "")
  53. 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: "")
  54. let paragraphStyle = NSMutableParagraphStyle()
  55. paragraphStyle.lineHeightMultiple = 1.32
  56. paragraphStyle.alignment = .center
  57. self.tipLabel.attributedStringValue = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor(hex: "#94989C")])
  58. self.addButton.toolTip = NSLocalizedString("Add Outline", comment: "")
  59. self.moreButton.toolTip = NSLocalizedString("More", comment: "")
  60. }
  61. private func addOutlineMenu(){
  62. let menus : NSMenu = NSMenu(title: "")
  63. let addItem = self.menuItemWithTitle(title: NSLocalizedString("Add Item", comment: ""), action: #selector(addItemAction))
  64. let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Add Sub-Item", comment: ""), action: #selector(addChildItemAction))
  65. let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Add A Higher Level", comment: ""), action: #selector(addHigherItemAction))
  66. let deleteItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction))
  67. // let editItem = self.menuItemWithTitle(title: NSLocalizedString("Edit", comment: ""), action: #selector(editItemAction))
  68. let renameItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameItemAction))
  69. let changeItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeItemAction))
  70. let promoteItem = self.menuItemWithTitle(title: NSLocalizedString("Promote", comment: ""), action: #selector(promoteItemAction))
  71. let demoteItem = self.menuItemWithTitle(title: NSLocalizedString("Demote", comment: ""), action: #selector(demoteItemAction))
  72. menus.addItem(addItem)
  73. menus.addItem(addChildItem)
  74. menus.addItem(addHigherItem)
  75. menus.addItem(NSMenuItem.separator())
  76. menus.addItem(deleteItem)
  77. menus.addItem(NSMenuItem.separator())
  78. // menus.addItem(editItem)
  79. menus.addItem(renameItem)
  80. menus.addItem(changeItem)
  81. menus.addItem(NSMenuItem.separator())
  82. menus.addItem(promoteItem)
  83. menus.addItem(demoteItem)
  84. self.outlineView.menu = menus
  85. moreMenu.addItem(withTitle: NSLocalizedString("Expand All", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
  86. moreMenu.addItem(withTitle: NSLocalizedString("Collapse All", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 1)
  87. moreMenu.addItem(withTitle: NSLocalizedString("Remove All Outlines", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 2)
  88. }
  89. func initNotification() {
  90. NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil)
  91. }
  92. //MARK: Public Method
  93. @objc func outlineViewDoubleAction() {
  94. if(self.outlineView.clickedRow >= 0) {
  95. self.renameItemAction()
  96. } else {
  97. __NSBeep()
  98. }
  99. }
  100. @IBAction func addNewOutline(_ sender: Any) {
  101. self.addItemAction()
  102. }
  103. @IBAction func moreButton_click(_ sender: NSButton) {
  104. let rect = sender.convert(sender.bounds, to: self.view)
  105. moreMenu.popUp(positioning: nil, at: NSPoint(x: rect.origin.x, y: rect.origin.y-10), in: self.view)
  106. }
  107. @IBAction func escButtonAction(_ sender: Any) {
  108. self.outlineView.deselectAll(nil)
  109. }
  110. /// 右键菜单
  111. @objc private func toc_expandAllComments(item:NSMenuItem) {
  112. if item.tag == 0 {
  113. self.outlineView.reloadData()
  114. self.outlineView.expandItem(nil, expandChildren: true)
  115. } else if item.tag == 1 {
  116. self.outlineView.reloadData()
  117. self.outlineView.collapseItem(nil, collapseChildren: true)
  118. } else if item.tag == 2 {
  119. let alter = NSAlert()
  120. alter.alertStyle = NSAlert.Style.informational
  121. alter.messageText = NSLocalizedString("This will permanently remove all outlines. Are you sure to continue?", comment: "")
  122. alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
  123. alter.addButton(withTitle: NSLocalizedString("No", comment:""))
  124. let modlres = alter.runModal()
  125. if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
  126. self.removeAllOutline()
  127. }
  128. }
  129. }
  130. @objc private func removeAllOutline() {
  131. var selectedPDFOutlineArr = [CPDFOutline]();
  132. for index in 0 ... self.listView.document.outlineRoot().numberOfChildren-1 {
  133. let outline : CPDFOutline = self.listView.document.outlineRoot().child(at: index)
  134. selectedPDFOutlineArr.append(outline)
  135. }
  136. for tOutline in selectedPDFOutlineArr {
  137. self.removePDFOutline(outline: tOutline, toIndex: NSInteger(tOutline.index), atParent: tOutline.parent)
  138. }
  139. }
  140. @objc private func addItemAction() {
  141. let selectRowIndexs = self.selectedRowIndexs()
  142. if (selectRowIndexs.count == 0) {
  143. var lastPDFOuline : CPDFOutline = CPDFOutline()
  144. if(self.outlineView.numberOfRows == 0) {
  145. lastPDFOuline = self.listView.document.setNewOutlineRoot()
  146. } else {
  147. lastPDFOuline = self.outlineView.item(atRow: self.outlineView.numberOfRows - 1) as! CPDFOutline
  148. }
  149. var rootPDFOutline : CPDFOutline!
  150. if(lastPDFOuline != nil) {
  151. while ((lastPDFOuline.parent) != nil) {
  152. lastPDFOuline = lastPDFOuline.parent
  153. }
  154. rootPDFOutline = lastPDFOuline
  155. } else {
  156. rootPDFOutline = self.listView.document.outlineRoot()
  157. if(rootPDFOutline == nil) {
  158. rootPDFOutline = self.listView.document.setNewOutlineRoot()
  159. }
  160. }
  161. let index = rootPDFOutline.numberOfChildren
  162. self.addPDFOutlineToIndex(index: NSInteger(index), atParent: rootPDFOutline)
  163. } else {
  164. let currentPDFOutline : CPDFOutline = self.outlineView.item(atRow: selectRowIndexs.last ?? 0) as! CPDFOutline
  165. let parent = currentPDFOutline.parent
  166. let index = currentPDFOutline.index + 1
  167. self.addPDFOutlineToIndex(index: NSInteger(index), atParent: parent)
  168. }
  169. }
  170. @objc func addChildItemAction() {
  171. let selectedRowIndexes : IndexSet! = self.selectedRowIndexs()
  172. if selectedRowIndexes.count == 0 {
  173. __NSBeep()
  174. } else {
  175. let parent = self.outlineView.item(atRow: selectedRowIndexes.last!) as! CPDFOutline
  176. let index = parent.numberOfChildren
  177. self.addPDFOutlineToIndex(index: NSInteger(index), atParent: parent)
  178. }
  179. }
  180. @objc func addHigherItemAction() {
  181. let selectedRowIndexes : IndexSet! = self.selectedRowIndexs()
  182. if selectedRowIndexes.count == 0 {
  183. __NSBeep()
  184. } else {
  185. let currentPDFOutline = self.outlineView.item(atRow: selectedRowIndexes.last!) as! CPDFOutline
  186. var parent = currentPDFOutline.parent
  187. let index = NSInteger(parent!.index) + 1
  188. parent = parent?.parent
  189. if parent != nil {
  190. self.addPDFOutlineToIndex(index: index, atParent: parent)
  191. } else {
  192. __NSBeep()
  193. }
  194. }
  195. }
  196. @objc func deleteItemAction() {
  197. let selectedRowIndexes : IndexSet! = self.selectedRowIndexs()
  198. if selectedRowIndexes.count == 0 {
  199. __NSBeep()
  200. } else {
  201. var selectedPDFOutlineArr = [CPDFOutline]();
  202. for index in selectedRowIndexes {
  203. var outline : CPDFOutline = self.outlineView.item(atRow: index) as! CPDFOutline
  204. selectedPDFOutlineArr.append(outline)
  205. }
  206. for tOutline in selectedPDFOutlineArr {
  207. self.removePDFOutline(outline: tOutline, toIndex: NSInteger(tOutline.index), atParent: tOutline.parent)
  208. }
  209. }
  210. }
  211. @objc func editItemAction() {
  212. if self.outlineView.clickedRow >= 0 {
  213. let cell = self.outlineView.rowView(atRow: self.outlineView.clickedRow, makeIfNecessary: true)
  214. let outline = self.outlineView.item(atRow: self.outlineView.clickedRow)
  215. let outlineEditViewController = KMOutlineEditViewController.init(outline: outline as! CPDFOutline, document: self.listView)
  216. let popover = NSPopover()
  217. popover.delegate = self
  218. popover.contentViewController = outlineEditViewController
  219. popover.animates = true
  220. popover.behavior = .transient
  221. popover.show(relativeTo: cell!.bounds, of: cell!, preferredEdge: .minX)
  222. } else {
  223. __NSBeep()
  224. }
  225. }
  226. @objc func renameItemAction() {
  227. if self.outlineView.clickedRow >= 0 {
  228. self.renameOutlineWithRow(row: self.outlineView.clickedRow)
  229. } else {
  230. __NSBeep()
  231. }
  232. }
  233. @objc func changeItemAction() {
  234. if self.outlineView.clickedRow >= 0 {
  235. let outline = self.outlineView.item(atRow: self.outlineView.clickedRow)
  236. let alter = NSAlert()
  237. alter.alertStyle = NSAlert.Style.informational
  238. alter.messageText = NSLocalizedString("Are you sure you want to set the target location of the selected outline to the current page?", comment: "")
  239. alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
  240. alter.addButton(withTitle: NSLocalizedString("No", comment:""))
  241. let modlres = alter.runModal()
  242. if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
  243. self.setPDFOutline(outline: outline as? CPDFOutline, destination: self.listView.currentDestination)
  244. }
  245. } else {
  246. __NSBeep()
  247. }
  248. }
  249. @objc func promoteItemAction() {
  250. if self.outlineView.clickedRow >= 0 {
  251. let outline = self.outlineView.item(atRow: self.outlineView.clickedRow) as! CPDFOutline
  252. var parent = outline.parent
  253. let index = NSInteger(parent!.index) + 1
  254. parent = parent?.parent
  255. if parent != nil {
  256. self.movePDFOutline(outline: outline, toIndex: index, atParent: parent)
  257. } else {
  258. __NSBeep()
  259. }
  260. } else {
  261. __NSBeep()
  262. }
  263. }
  264. @objc func demoteItemAction() {
  265. if self.outlineView.clickedRow >= 0 {
  266. let outline = self.outlineView.item(atRow: self.outlineView.clickedRow) as! CPDFOutline
  267. var parent = outline.parent
  268. let newParent = parent?.child(at: UInt(outline.index - 1))
  269. let index = newParent?.numberOfChildren
  270. if(index != nil) {
  271. self.movePDFOutline(outline: outline, toIndex: NSInteger(index ?? 0), atParent: newParent)
  272. } else {
  273. __NSBeep()
  274. }
  275. } else {
  276. __NSBeep()
  277. }
  278. }
  279. //MARK: - Outline
  280. func renameOutlineWithRow(row:NSInteger) {
  281. self.renamePDFOutline = self.outlineView.item(atRow: row) as? CPDFOutline
  282. let cell : KMOutlineCellView = self.outlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as! KMOutlineCellView
  283. self.renameTextField = cell.titleTextField
  284. self.renameTextField.delegate = self
  285. self.renameTextField.isEditable = true
  286. self.renameTextField.becomeFirstResponder()
  287. }
  288. func renamePDFOutline(outline : CPDFOutline! , label:String) {
  289. self.renameTextField.isEditable = false
  290. if outline.label == label {
  291. return
  292. }
  293. outline.label = label
  294. self.outlineView.reloadData()
  295. var indexSet = IndexSet()
  296. indexSet.insert(self.outlineView.row(forItem: outline))
  297. self.outlineView.selectRowIndexes(indexSet, byExtendingSelection: false)
  298. }
  299. func movePDFOutline(outline : CPDFOutline! , toIndex index : NSInteger , atParent parent : CPDFOutline!) {
  300. outline.removeFromParent()
  301. parent.insertChild(outline, at: UInt(index))
  302. self.outlineView.reloadData()
  303. self.outlineView.expandItem(parent)
  304. }
  305. func addPDFOutlineCurrentSelections() {
  306. var string : String = ""
  307. for currentSelection in self.listView.currentSelection.selectionsByLine {
  308. if string.count == 0 {
  309. string = "\(string)+\("\n")"
  310. string = "\(string)+\(String(describing: currentSelection.string()))"
  311. } else {
  312. string = currentSelection.string()
  313. }
  314. }
  315. var parent : CPDFOutline?
  316. var index : Int = 0
  317. let selectRowIndexes = self.selectedRowIndexs()
  318. if selectRowIndexes.count == 0 {
  319. var lastOutline = self.outlineView.item(atRow: self.outlineView.numberOfRows - 1) as? CPDFOutline
  320. if lastOutline != nil {
  321. while ((lastOutline?.parent) != nil) {
  322. lastOutline = lastOutline?.parent
  323. }
  324. parent = lastOutline
  325. } else {
  326. parent = self.listView.document.outlineRoot()
  327. if parent == nil {
  328. parent = self.listView.document.setNewOutlineRoot()
  329. }
  330. }
  331. index = Int(parent?.numberOfChildren ?? 0)
  332. } else {
  333. let currentPDFOutline = self.outlineView.item(atRow: selectRowIndexes.last ?? 0) as! CPDFOutline
  334. parent = currentPDFOutline.parent
  335. index = Int(currentPDFOutline.index + 1)
  336. }
  337. if parent != nil {
  338. let outline = parent?.insertChild(at: UInt(index))
  339. outline?.label = string
  340. outline?.destination = self.listView.currentDestination
  341. self.addPDFOutline(outline: outline, toIndex: index, atParent: parent)
  342. } else {
  343. __NSBeep()
  344. }
  345. }
  346. func updateOutlineSelection() {
  347. let currentPageIndex = self.listView.currentPageIndex
  348. let currentPage : CPDFPage = self.listView.document.page(at: UInt(currentPageIndex))
  349. let numRows = self.outlineView.numberOfRows
  350. var arr = [[String : CPDFPage]]()
  351. for i in 0..<numRows {
  352. let tPDFOutline = self.outlineView.item(atRow: i) as! CPDFOutline
  353. let tPage = tPDFOutline.destination.pageIndex
  354. if tPage < 0 {
  355. continue
  356. }
  357. var page = self.listView.document.page(at: UInt(tPage))!
  358. var tDict : [String : CPDFPage] = ["\(i)":page]
  359. arr.append(tDict)
  360. }
  361. DispatchQueue.global(qos: .utility).async{
  362. var hasExistInOutlineView = false
  363. var selectIndex = 0
  364. for (index,value) in arr.enumerated() {
  365. let dict : [String : CPDFPage] = value
  366. let page: CPDFPage = dict.values.first ?? CPDFPage()
  367. let index = Int(index)
  368. if page.isEqual(currentPage) {
  369. selectIndex = index
  370. hasExistInOutlineView = true
  371. break
  372. }
  373. }
  374. DispatchQueue.main.async {
  375. if !hasExistInOutlineView {
  376. self.outlineView.deselectRow(self.outlineView.selectedRow)
  377. } else {
  378. self.outlineView.selectRowIndexes([selectIndex], byExtendingSelection: false)
  379. self.outlineView.scrollRowToVisible(selectIndex)
  380. }
  381. }
  382. }
  383. }
  384. //MARK: - Accessors
  385. func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem {
  386. let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "")
  387. menuItem.target = self
  388. return menuItem
  389. }
  390. func selectedRowIndexs() -> IndexSet {
  391. let clickRow = self.outlineView.clickedRow
  392. var selectedRowIndexs = self.outlineView.selectedRowIndexes
  393. if(clickRow != -1 && !selectedRowIndexs.contains(clickRow)) {
  394. selectedRowIndexs = [clickRow]
  395. }
  396. return selectedRowIndexs
  397. }
  398. func reloadData() {
  399. self.outlineView.reloadData()
  400. }
  401. func addPDFOutlineToIndex(index:NSInteger, atParent parent:CPDFOutline!) {
  402. let currentPageIndex = self.listView.currentPageIndex
  403. var outline = parent.insertChild(at: UInt(index))
  404. outline?.label = "\(NSLocalizedString("Page", comment: ""))\(currentPageIndex+1)"
  405. let des = self.listView.currentDestination
  406. if "\(des?.point.x ?? 0)" != "nan" {
  407. outline?.destination = self.listView.currentDestination
  408. } else {
  409. let destination : CPDFDestination = CPDFDestination(document: self.listView.document, pageIndex: currentPageIndex, at: CGPoint(x: 0, y: 0), zoom: self.listView.scaleFactor)
  410. outline?.destination = destination
  411. }
  412. self.addPDFOutline(outline:outline,toIndex: index,atParent: parent)
  413. }
  414. func addPDFOutline(outline:CPDFOutline? , toIndex index : NSInteger , atParent parent : CPDFOutline!) {
  415. self.view.window?.makeFirstResponder(self.outlineView)
  416. if outline?.parent == nil {
  417. parent.insertChild(outline, at: UInt(index))
  418. }
  419. self.outlineView.reloadData()
  420. self.outlineView.expandItem(parent)
  421. let indexSet : IndexSet = [self.outlineView.row(forItem: outline)]
  422. self.outlineView.selectRowIndexes(indexSet, byExtendingSelection: false)
  423. if(self.outlineView.selectedRow >= 0) {
  424. self.renameOutlineWithRow(row: self.outlineView.selectedRow)
  425. }
  426. if Thread.current.isMainThread {
  427. self.outlineView.scrollToVisible(self.outlineView.rect(ofRow: index))
  428. } else {
  429. DispatchQueue.main.async {
  430. self.outlineView.scrollToVisible(self.outlineView.rect(ofRow: index))
  431. }
  432. }
  433. }
  434. func removePDFOutline(outline:CPDFOutline! , toIndex index : NSInteger , atParent parent : CPDFOutline!) {
  435. outline.removeFromParent()
  436. self.outlineView.reloadData()
  437. self.outlineView.expandItem(parent)
  438. }
  439. func setPDFOutline(outline:CPDFOutline! , destination : CPDFDestination!) {
  440. outline.destination = destination
  441. self.outlineView.reloadData()
  442. }
  443. func editOutlineUI(editVC : KMOutlineEditViewController!) {
  444. if editVC.pageButton.state == NSControl.StateValue.on {
  445. let index = Int(editVC.outlineTargetPageIndexTextField.stringValue)!
  446. if editVC.originalDestination.pageIndex != index {
  447. let page = editVC.pdfView.document.page(at: UInt(index))
  448. if page != nil {
  449. let destination = CPDFDestination.init(document: editVC.pdfView.document, pageIndex: index)
  450. editVC.outline.destination = destination
  451. } else {
  452. __NSBeep()
  453. }
  454. }
  455. } else if editVC.urlButton.state == NSControl.StateValue.on {
  456. if editVC.originalURLString != editVC.outlineURLTextField.stringValue {
  457. var urlString = editVC.outlineURLTextField.stringValue
  458. let tLowerUrl = urlString.lowercased()
  459. if !tLowerUrl.hasPrefix("https://") && !tLowerUrl.hasPrefix("pf]://") && !urlString.hasPrefix("https://") &&
  460. urlString.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) > 0 {
  461. urlString = "http://\(urlString)"
  462. }
  463. let action = CPDFURLAction.init(url: urlString)
  464. editVC.outline.action = action
  465. }
  466. } else if editVC.mailButton.state == NSControl.StateValue.on {
  467. var mailString = editVC.mailAddressTextField.stringValue
  468. let tLowerStr = mailString.lowercased()
  469. if !tLowerStr.hasPrefix("mailto:") {
  470. mailString = "mailto:\(mailString)"
  471. }
  472. if mailString != editVC.originalURLString {
  473. var action = CPDFURLAction.init(url: mailString)
  474. if action?.url == nil {
  475. action = CPDFURLAction.init(url: "mailto:")
  476. }
  477. editVC.outline.action = action
  478. }
  479. }
  480. // self.mainWindowController.document?.undo()
  481. if editVC.outlineNameTextView.string != editVC.originalLabel {
  482. self.renamePDFOutline(outline: editVC.outline, label: editVC.outlineNameTextView.string)
  483. }
  484. }
  485. //MARK: NSMenuItemValidation
  486. func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  487. let action = menuItem.action
  488. if action == #selector(addItemAction) ||
  489. action == #selector(addChildItemAction) ||
  490. action == #selector(addHigherItemAction) ||
  491. action == #selector(deleteItemAction) ||
  492. action == #selector(editItemAction) ||
  493. action == #selector(changeItemAction) ||
  494. action == #selector(renameItemAction) ||
  495. action == #selector(promoteItemAction) ||
  496. action == #selector(demoteItemAction) {
  497. if self.outlineView.selectedRowIndexes.count > 1 {
  498. if action == #selector(deleteItemAction) {
  499. return true
  500. }
  501. return false
  502. } else if self.outlineView.selectedRowIndexes.count > 0 {
  503. if action == #selector(addChildItemAction) || action == #selector(changeItemAction) {
  504. return true
  505. }
  506. }
  507. if self.outlineView.clickedRow == -1 {
  508. if action == #selector(addItemAction) {
  509. return true
  510. } else {
  511. return false
  512. }
  513. } else {
  514. let outline : CPDFOutline = self.outlineView.item(atRow: self.outlineView.clickedRow) as! CPDFOutline
  515. if outline.index > 0 {
  516. if action == #selector(demoteItemAction) {
  517. return true
  518. }
  519. } else {
  520. if action == #selector(demoteItemAction) {
  521. return false
  522. }
  523. }
  524. let parentOutline = outline.parent
  525. let grandparentOutline = parentOutline?.parent
  526. if grandparentOutline != nil {
  527. if action == #selector(addHigherItemAction) {
  528. return true
  529. } else if action == #selector(promoteItemAction) {
  530. return true
  531. }
  532. } else {
  533. if action == #selector(addHigherItemAction) {
  534. return false
  535. } else if action == #selector(promoteItemAction) {
  536. return false
  537. }
  538. }
  539. }
  540. return true
  541. }
  542. return true
  543. }
  544. //MARK: Noti
  545. @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) {
  546. let pdfdocument : CPDFDocument = notification.object as! CPDFDocument
  547. if pdfdocument.isEqual(self.listView.document) {
  548. self.updateOutlineSelection()
  549. }
  550. }
  551. }
  552. //MARK: NSOutlineViewDataSource,NSOutlineViewDelegate
  553. extension KMOutlineViewController : NSOutlineViewDataSource,NSOutlineViewDelegate {
  554. func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
  555. var rootOutline = self.listView.document.outlineRoot()
  556. if rootOutline == nil {
  557. rootOutline = CPDFOutline()
  558. }
  559. var newItem : CPDFOutline = item as? CPDFOutline ?? rootOutline!
  560. if(newItem.numberOfChildren == 0) { //无数据时的图
  561. self.emptyView.isHidden = false
  562. } else {
  563. self.emptyView.isHidden = true
  564. }
  565. return Int(newItem.numberOfChildren)
  566. }
  567. func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
  568. var newItem : CPDFOutline = item as? CPDFOutline ?? self.listView.document.outlineRoot()
  569. var child : CPDFOutline? = newItem.child(at: UInt(index))
  570. return child ?? CPDFOutline()
  571. }
  572. func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
  573. var newItem : CPDFOutline = item as? CPDFOutline ?? self.listView.document.outlineRoot()
  574. return newItem.numberOfChildren > 0
  575. }
  576. func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
  577. let cell : KMOutlineCellView = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMOutlineCellView"), owner: self) as! KMOutlineCellView
  578. let outline : CPDFOutline? = item as? CPDFOutline
  579. cell.titleTextField.stringValue = outline?.label ?? ""
  580. var destination = outline?.destination
  581. if destination == nil {
  582. let action = outline?.action
  583. if (action != nil) && (action is CPDFGoToAction) {
  584. destination = (action as? CPDFGoToAction)?.destination()
  585. }
  586. }
  587. if destination != nil {
  588. cell.pageTextField.stringValue = "\((destination?.pageIndex ?? 0)+1)"
  589. } else {
  590. cell.pageTextField.stringValue = ""
  591. }
  592. let font : NSFont = NSFont.systemFont(ofSize: CGFloat(UserDefaults.standard.float(forKey: "SKOfficeTableFontSize")))
  593. if (font != nil) {
  594. cell.titleTextField.font = NSFont.init(name: cell.titleTextField.font!.fontName, size: font.pointSize)
  595. cell.pageTextField.font = NSFont.init(name: cell.pageTextField.font!.fontName, size: font.pointSize)
  596. }
  597. return cell
  598. }
  599. func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
  600. let rowView = KMOutlineRowView()
  601. rowView.selectionHighlightStyle = NSTableView.SelectionHighlightStyle.none;
  602. return rowView
  603. }
  604. func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
  605. let font : NSFont = NSFont.systemFont(ofSize: CGFloat(UserDefaults.standard.float(forKey: "SKOfficeTableFontSize")))
  606. return 27 + font.pointSize
  607. }
  608. func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
  609. return true
  610. }
  611. func outlineViewSelectionDidChange(_ notification: Notification) {
  612. if self.outlineView.selectedRowIndexes.count == 0 {
  613. return
  614. }
  615. if self.self.outlineView.selectedRowIndexes.count > 1 {
  616. } else {
  617. let outline : CPDFOutline = self.outlineView .item(atRow:self.outlineView.selectedRow) as! CPDFOutline
  618. if outline.destination != nil {
  619. self.listView.go(to: outline.destination)
  620. } else if outline.action != nil {
  621. self.listView.perform(outline.action)
  622. }
  623. }
  624. }
  625. func outlineView(_ outlineView: NSOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool {
  626. if self.outlineView.selectedRowIndexes.count > 1 {
  627. return true
  628. }
  629. self.dragPDFOutline = items.first as? CPDFOutline
  630. let indexSet = [self.outlineView.clickedRow]
  631. let indexSetData : Data = NSKeyedArchiver.archivedData(withRootObject: indexSet) as Data
  632. pasteboard.declareTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")], owner: self)
  633. pasteboard.setData(indexSetData, forType: NSPasteboard.PasteboardType(rawValue: NSPasteboard.PasteboardType.RawValue("kKMPDFViewOutlineDragDataType")))
  634. return true
  635. }
  636. func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
  637. var dragOperation = NSDragOperation.init(rawValue: 0)
  638. if index > 0 {
  639. dragOperation = NSDragOperation.move
  640. }
  641. return dragOperation
  642. }
  643. func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
  644. if ((self.dragPDFOutline == nil) || (index < 0)) {
  645. return false
  646. }
  647. var outline : CPDFOutline = CPDFOutline()
  648. if item != nil {
  649. outline = item as! CPDFOutline
  650. }
  651. if outline.parent == nil {
  652. var root = self.dragPDFOutline
  653. while root?.parent != nil {
  654. root = root?.parent
  655. }
  656. if self.dragPDFOutline.parent.isEqual(root) {
  657. if self.dragPDFOutline.index > index {
  658. self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: root)
  659. } else {
  660. self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index - 1, atParent: root)
  661. }
  662. } else {
  663. self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: root)
  664. }
  665. } else {
  666. if self.dragPDFOutline.parent.isEqual(item) {
  667. if self.dragPDFOutline.index != 0 {
  668. if self.dragPDFOutline.index > index {
  669. self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: outline)
  670. } else {
  671. self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index - 1, atParent: outline)
  672. }
  673. } else {
  674. return false
  675. }
  676. } else {
  677. var tOutline = outline
  678. var isContains = false
  679. while (tOutline != nil && tOutline.parent != nil) {
  680. if tOutline.isEqual(self.dragPDFOutline) {
  681. isContains = true
  682. break
  683. }
  684. tOutline = tOutline.parent
  685. }
  686. if isContains == false {
  687. self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: outline)
  688. }
  689. }
  690. }
  691. return true
  692. }
  693. }
  694. // MARK - NSTextFieldDelegate
  695. extension KMOutlineViewController : NSTextFieldDelegate {
  696. func controlTextDidEndEditing(_ obj: Notification) {
  697. if (self.renameTextField.isEqual(obj.object)) {
  698. let textField : NSTextField = obj.object as! NSTextField
  699. self.renamePDFOutline(outline: self.renamePDFOutline, label: textField.stringValue)
  700. }
  701. }
  702. }
  703. // MARK - NSPopoverDelegate
  704. extension KMOutlineViewController : NSPopoverDelegate {
  705. func popoverWillClose(_ notification: Notification) {
  706. let popover : NSPopover = notification.object as! NSPopover
  707. if popover.contentViewController!.isKind(of: KMOutlineEditViewController.self) {
  708. }
  709. }
  710. }