KMAnnotationViewController.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. //
  2. // KMAnnotationViewController.swift
  3. // PDF Office
  4. //
  5. // Created by lxy on 2022/10/10.
  6. //
  7. import Cocoa
  8. class KMAnnotationViewController: KMSideViewController {
  9. @IBOutlet weak var tableView: NSTableView!
  10. @IBOutlet weak var topView: NSView!
  11. @IBOutlet weak var filtrateButton: NSButton!
  12. @IBOutlet weak var moreButton: NSButton!
  13. @IBOutlet weak var markupTitleLabel: NSTextField!
  14. @IBOutlet weak var emptyView: NSView!
  15. @IBOutlet weak var bigTipLabel: NSTextField!
  16. @IBOutlet weak var tipLabel: NSTextField!
  17. let pages = "page"
  18. let label = "label"
  19. var annotations : [Any] = [Any]()
  20. var selectedRowIndexs : IndexSet = []
  21. var allAnnotations : [Any] = [Any]()
  22. let moreMenu = NSMenu()
  23. //MARK: View
  24. override func viewDidLoad() {
  25. super.viewDidLoad()
  26. self.setup()
  27. self.updateUI()
  28. self.updateLanguage()
  29. self.initMenu()
  30. NotificationCenter.default.addObserver(self, selector: #selector(PDFViewActiveAnnotationDidChangeNotification), name: NSNotification.Name.init(rawValue: "KMHomeFileRectChange"), object: nil)
  31. NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: NSNotification.Name.init(rawValue: "CPDFPageDidAddAnnotationNotification"), object: nil)
  32. NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: NSNotification.Name.init(rawValue: "CPDFPageDidRemoveAnnotationNotification"), object: nil)
  33. }
  34. func setup() {
  35. self.view.wantsLayer = true
  36. self.view.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
  37. self.topView.wantsLayer = true
  38. self.topView.layer?.backgroundColor = NSColor.clear.cgColor
  39. self.tableView.doubleAction = #selector(tableViewDoubleAction)
  40. self.tableView.allowsMultipleSelection = true
  41. self.tableView.wantsLayer = true
  42. self.tableView.delegate = self
  43. self.tableView.dataSource = self
  44. let menu = NSMenu()
  45. menu.delegate = self
  46. self.tableView.menu = menu
  47. }
  48. func updateUI() {
  49. self.markupTitleLabel.font = NSFont.SFProTextSemibold(14.0)
  50. self.markupTitleLabel.textColor = NSColor(hex: "#252629")
  51. // self.tableView.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
  52. // self.tableView.border(NSColor(hex: "#000000", alpha: 0.1), 1, 0)
  53. self.bigTipLabel.font = NSFont.SFProTextRegular(14.0)
  54. self.bigTipLabel.textColor = NSColor(hex: "#616469")
  55. }
  56. func updateLanguage() {
  57. self.markupTitleLabel.stringValue = NSLocalizedString("Annotation", comment: "")
  58. self.bigTipLabel.stringValue = NSLocalizedString("No markup found", comment: "")
  59. self.filtrateButton.toolTip = NSLocalizedString("Sort", comment: "")
  60. self.moreButton.toolTip = NSLocalizedString("More", comment: "")
  61. let title = NSLocalizedString("All comments for this document will be displayed here.", 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(hex: "#94989C")])
  66. }
  67. private func initMenu() {
  68. moreMenu.addItem(withTitle: NSLocalizedString("Expand All", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
  69. moreMenu.addItem(withTitle: NSLocalizedString("Collapse All", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 1)
  70. let soreItem = moreMenu.addItem(withTitle: NSLocalizedString("Sort", comment: ""), action: nil, target: self)
  71. let soreMenu = NSMenu()
  72. soreMenu.addItem(withTitle: NSLocalizedString("Page", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
  73. // soreMenu.addItem(withTitle: NSLocalizedString("Chronologically - ascending", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 1)
  74. // soreMenu.addItem(withTitle: NSLocalizedString("Chronologically - reverse", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
  75. soreItem?.submenu = soreMenu
  76. moreMenu.addItem(withTitle: NSLocalizedString("Import annotations", comment: ""), action: #selector(importItemAction), target: self)
  77. moreMenu.addItem(withTitle: NSLocalizedString("Export annotations to XFDF", comment: ""), action: #selector(exportItemAction), target: self)
  78. moreMenu.addItem(withTitle: NSLocalizedString("Remove All Annotations", comment: ""), action: #selector(deleteAllAnonationAction), target: self)
  79. }
  80. @objc private func toc_expandAllComments(sender:NSMenuItem) {
  81. // if sender.tag == 0 {
  82. // self.outlineView.reloadData()
  83. // self.outlineView.expandItem(nil, expandChildren: true)
  84. // } else if sender.tag == 1 {
  85. // self.outlineView.reloadData()
  86. // self.outlineView.collapseItem(nil, collapseChildren: true)
  87. // } else if sender.tag == 2 {
  88. // let alter = NSAlert()
  89. // alter.alertStyle = NSAlert.Style.informational
  90. // alter.messageText = NSLocalizedString("This will permanently remove all outlines. Are you sure to continue?", comment: "")
  91. // alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
  92. // alter.addButton(withTitle: NSLocalizedString("No", comment:""))
  93. // let modlres = alter.runModal()
  94. // if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
  95. // self.removeAllOutline()
  96. // }
  97. // }
  98. }
  99. @objc public func reloadData() {
  100. self.annotations = [CPDFAnnotation]()
  101. var index = 0
  102. for i in 0 ..< self.listView.document.pageCount {
  103. let page = self.listView.document.page(at: i)
  104. let types = ["Highlight","Underline","Strikeout","Freehand","FreeText","Note","Square","Circle","Line","Stamp","Arrow","Image","Redact","Sign"]
  105. var annotations = KMOCToolClass.filterAnnotation(page!.annotations,types: types)!
  106. for annotation in page!.annotations {
  107. if annotation.isKind(of: CPDFSignatureAnnotation.self) {
  108. annotations.append(annotation)
  109. }
  110. }
  111. if(annotations.count > 0) {
  112. var dic : [String:Any] = [:]
  113. dic[pages] = page
  114. dic[label] = annotations
  115. self.annotations.append(dic)
  116. index += 1
  117. }
  118. for annotation in annotations {
  119. self.annotations.append(annotation)
  120. }
  121. }
  122. self.allAnnotations = annotations
  123. if self.annotations.count < 1 {
  124. self.filtrateButton.isEnabled = false
  125. } else {
  126. self.filtrateButton.isEnabled = true
  127. }
  128. self.tableView.reloadData()
  129. }
  130. public func clear() {
  131. self.annotations.removeAll()
  132. self.tableView.reloadData()
  133. }
  134. //MARK: Accessors
  135. @IBAction func tableViewDoubleAction(_ sender: Any) {
  136. let selectedRow = self.tableView.selectedRow
  137. if selectedRow >= 0 && selectedRow < self.annotations.count {
  138. let annotation = self.annotations[selectedRow]
  139. if (annotation as AnyObject).isKind(of: CPDFAnnotation.self) {
  140. self.listView.go(to: (annotation as! CPDFAnnotation).bounds, on: (annotation as! CPDFAnnotation).page, animated: true)
  141. // self.mainWindowController.listView.seta
  142. }
  143. }
  144. }
  145. @IBAction func moreButtonAction(_ sender: NSButton) {
  146. let rect = sender.convert(sender.bounds, to: self.view)
  147. moreMenu.popUp(positioning: nil, at: NSPoint(x: rect.origin.x, y: rect.origin.y-10), in: self.view)
  148. }
  149. @IBAction func filtrateButtonAction(_ sender: NSButton) {
  150. let menu = NSMenu()
  151. let filterVC = KMNoteOutlineFilterViewController()
  152. filterVC.notesArray = self.allAnnotations
  153. filterVC.applyFilterCallback = { typeArr, colorArr,authArr,isEmpty in
  154. menu.cancelTracking()
  155. self.annotationSort(sortArray: [typeArr!,colorArr!,authArr!])
  156. }
  157. filterVC.cancelCallback = { isCancel in
  158. if isCancel {
  159. menu.cancelTracking()
  160. }
  161. }
  162. let item = menu.addItem(withTitle: "", action: nil, keyEquivalent: "")
  163. item.target = self
  164. item.representedObject = filterVC
  165. item.view = filterVC.view
  166. menu.popUp(positioning: nil, at: CGPoint(x: -130, y: 30), in: sender)
  167. }
  168. func annotationSort(sortArray:[[Any]]) {
  169. let typeArr = sortArray[0]
  170. let colorArr = sortArray[1]
  171. let authorArr = sortArray[2]
  172. if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 {
  173. } else {
  174. self.annotations = [CPDFAnnotation]()
  175. for i in 0 ..< self.listView.document.pageCount {
  176. let page = self.listView.document.page(at: i)
  177. if page!.annotations.count > 0 {
  178. var filterAnnotations = [CPDFAnnotation]()
  179. if typeArr.count > 0 {
  180. filterAnnotations = (KMOCToolClass.filterAnnotation(page!.annotations,types: typeArr) as! [CPDFAnnotation])
  181. }
  182. if (colorArr.count > 0) {
  183. filterAnnotations = (KMOCToolClass.filterAnnotation(filterAnnotations,colors: colorArr) as! [CPDFAnnotation])
  184. }
  185. if (authorArr.count > 0) {
  186. filterAnnotations = (KMOCToolClass.filterAnnotation(filterAnnotations,authors: authorArr) as! [CPDFAnnotation])
  187. }
  188. if(filterAnnotations.count > 0) {
  189. var dic : [String:Any] = [:]
  190. dic[self.pages] = page
  191. dic[self.label] = filterAnnotations
  192. self.annotations.append(dic)
  193. }
  194. for annotation in filterAnnotations {
  195. self.annotations.append(annotation)
  196. }
  197. }
  198. }
  199. self.tableView.reloadData()
  200. }
  201. }
  202. @IBAction func deleteButtonAction(_ sender: Any) {
  203. let alert = NSAlert()
  204. alert.alertStyle = .critical
  205. alert.messageText = NSLocalizedString("This will permanently remove all annotations. Are you sure to continue?", comment: "")
  206. alert.informativeText = NSLocalizedString("You cannot undo this operation.", comment: "")
  207. alert.addButton(withTitle: NSLocalizedString("Yes", comment: ""))
  208. alert.addButton(withTitle: NSLocalizedString("No", comment: ""))
  209. alert.beginSheetModal(for: self.view.window!, completionHandler: { result in
  210. if result == .OK {
  211. for i in 0 ..< self.listView.document.pageCount {
  212. let page = self.listView.document.page(at: i)
  213. page?.removeAllAnnotations()
  214. }
  215. // let undoManager : UndoManager = self.mainWindowController.document?.undoManager ?? UndoManager()
  216. // undoManager.setActionName("")
  217. self.listView.updateActiveAnnotations([CPDFAnnotation()])
  218. self.listView.setNeedsDisplayForVisiblePages()
  219. self.reloadData()
  220. }
  221. })
  222. }
  223. @IBAction func flattenButtonAction(_ sender: NSMenuItem) {
  224. let selects = sender.representedObject as! NSIndexSet
  225. var indexs : [Int] = []
  226. for index in selects {
  227. indexs.append(index)
  228. }
  229. if selects.count == 1 {
  230. let index = selects.firstIndex
  231. let annotation : CPDFAnnotation = annotations[index] as! CPDFAnnotation
  232. if annotation.contents?.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) ?? 0 > 0 {
  233. let content = annotation.contents! as NSString
  234. let pasteBoard = NSPasteboard.general
  235. pasteBoard.clearContents()
  236. pasteBoard.setString(content as String, forType: .string)
  237. }
  238. }
  239. }
  240. @IBAction func exportItemAction(_ sender: Any) {
  241. let panel = NSSavePanel()
  242. panel.nameFieldStringValue = "\(NSLocalizedString("Untitled", comment: "")).xfdf"
  243. panel.isExtensionHidden = true
  244. let response = panel.runModal()
  245. if response == .OK {
  246. let url = panel.url
  247. let result = self.listView.document.exportAnnotation(toXFDFPath: url!.path)
  248. if result {
  249. NSWorkspace.shared.openFile(url!.path)
  250. }
  251. }
  252. }
  253. @IBAction func importItemAction(_ sender: Any) {
  254. let panel = NSOpenPanel()
  255. panel.allowsMultipleSelection = false
  256. panel.allowedFileTypes = ["xfdf"]
  257. panel.beginSheetModal(for: NSApp.mainWindow!) { response in
  258. if response == .OK {
  259. let openPath = panel.url?.path
  260. let result = self.listView.document.importAnnotation(fromXFDFPath: openPath!)
  261. if result {
  262. self.listView.setNeedsDisplayAnnotationViewForVisiblePages()
  263. self.reloadData()
  264. }
  265. }
  266. }
  267. }
  268. @IBAction func deleteItemAction(_ sender: NSMenuItem) {
  269. let selects = sender.representedObject as! NSIndexSet
  270. var indexs : [Int] = []
  271. for index in selects {
  272. indexs.append(index)
  273. }
  274. indexs.sort(){$0 > $1}
  275. self.tableView(tableView: self.tableView, deleteRowsWithIndexes: indexs)
  276. }
  277. @IBAction func deleteAllAnonationAction(_ sender: NSMenuItem) {
  278. for i in 0 ..< self.listView.document.pageCount {
  279. let page = self.listView.document.page(at: i)
  280. for annotation in page!.annotations {
  281. page?.removeAnnotation(annotation)
  282. self.listView.setNeedsDisplayForVisiblePages()
  283. }
  284. }
  285. }
  286. //MARK: NSNotification
  287. @IBAction func PDFViewActiveAnnotationDidChangeNotification(notification: NSNotification) {
  288. let pdfView : CPDFListView = notification.object as! CPDFListView
  289. if pdfView.isEqual(self.listView) {
  290. let annotion = pdfView.activeAnnotations.firstObject
  291. if (annotion != nil && KMOCToolClass.arrayContains(self.annotations, annotation: annotion)) {
  292. let index = KMOCToolClass.arrayIndex(of: self.annotations, annotation: annotion)
  293. let indexset = IndexSet.init(integer: index)
  294. self.tableView.selectRowIndexes(indexset, byExtendingSelection: false)
  295. self.tableView.scrollRowToVisible(index)
  296. }
  297. }
  298. }
  299. @IBAction func escButtonAction(_ sender: Any) {
  300. self.tableView.deselectAll(nil)
  301. }
  302. private func tableView(tableView:NSTableView,canDeleteRowsWithIndexes rowindexs : IndexSet) -> Bool {
  303. return true
  304. }
  305. private func tableView(tableView:NSTableView, deleteRowsWithIndexes rowindexs : [Int]) {
  306. var removeAnnotations: [Any] = []
  307. for index in rowindexs {
  308. if index < self.annotations.count {
  309. let annotation = self.annotations[index] as? CPDFAnnotation
  310. let page = annotation?.page
  311. if ((page?.annotations.contains(annotation!)) != nil) {
  312. page?.removeAnnotation(annotation!)
  313. } else {
  314. print("不存在")
  315. }
  316. if self.listView.activeAnnotations.contains(annotation) {
  317. removeAnnotations.append(annotation)
  318. }
  319. }
  320. }
  321. if removeAnnotations.count != 0 {
  322. self.listView.activeAnnotations.remove(removeAnnotations)
  323. self.listView.setNeedsDisplayForVisiblePages()
  324. }
  325. self.reloadData()
  326. self.selectedRowIndexs = []
  327. }
  328. }
  329. //MARK: NSTableViewDelegate,NSTableViewDataSource
  330. extension KMAnnotationViewController: NSTableViewDelegate,NSTableViewDataSource {
  331. func numberOfRows(in tableView: NSTableView) -> Int {
  332. if self.annotations.count > 0 {
  333. self.emptyView.isHidden = true
  334. } else {
  335. self.emptyView.isHidden = false
  336. }
  337. return self.annotations.count
  338. }
  339. func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  340. if self.annotations[row] is [String:Any] {
  341. let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMAnnotationHeaderCellView"), owner: self) as! KMAnnotationHeaderCellView
  342. let dic : [String:Any] = self.annotations[row] as! [String : Any]
  343. let page : CPDFPage = dic[pages] as! CPDFPage
  344. cell.currentPage = Int(page.document.index(for: page))
  345. cell.notes = dic[label] as! [CPDFAnnotation]
  346. cell.updateCellInfo()
  347. return cell
  348. } else if self.annotations[row] is CPDFAnnotation {
  349. let cell : KMAnnotationTableCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMAnnotationTableCellView"), owner: self) as! KMAnnotationTableCellView
  350. cell.annotation = self.annotations[row] as? CPDFAnnotation
  351. cell.updateAnnotation(annotation: self.annotations[row] as! CPDFAnnotation)
  352. return cell
  353. }
  354. return nil
  355. }
  356. func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
  357. let tableRowView = KMAnnotationTableRowView()
  358. return tableRowView
  359. }
  360. func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
  361. if self.annotations[row] is [String:Any] {
  362. return 32
  363. } else if self.annotations[row] is CPDFAnnotation {
  364. var height: CGFloat = 40
  365. let paragraphStyle = NSMutableParagraphStyle()
  366. paragraphStyle.lineHeightMultiple = 1.32
  367. paragraphStyle.alignment = .left
  368. let attributes = [NSAttributedString.Key.paragraphStyle: paragraphStyle,
  369. NSAttributedString.Key.font : NSFont.SFProTextRegular(14.0)]
  370. let annotation = self.annotations[row] as! CPDFAnnotation
  371. if annotation.isKind(of: CPDFMarkupAnnotation.self) {
  372. var text : String = (annotation as! CPDFMarkupAnnotation).markupText()
  373. if text.count == 0 {
  374. let exproString = KMOCToolClass.exproString(self.annotations[row] as? CPDFAnnotation)!
  375. if exproString.count > 0 {
  376. text = exproString
  377. }
  378. }
  379. if text.hasPrefix("\n") {
  380. text = text.filter{ $0 != "\n" }
  381. }
  382. if text.count > 0 {
  383. let size = NSString(string: text).boundingRect(with: NSSize(width: tableView.frame.size.width-28, height: 300), options: NSString.DrawingOptions(rawValue: 3), attributes: attributes).size
  384. height += 4
  385. height = height + size.height
  386. }
  387. } else if (annotation.isKind(of: CPDFInkAnnotation.self)) ||
  388. (annotation.isKind(of: CPDFSquareAnnotation.self)) ||
  389. (annotation.isKind(of: CPDFLineAnnotation.self)) ||
  390. (annotation.isKind(of: CPDFCircleAnnotation.self)) {
  391. height += 4
  392. height += 58
  393. }
  394. var contentsString : String = annotation.contents ?? ""
  395. if contentsString.count > 0 {
  396. if contentsString.hasSuffix("\n") {
  397. contentsString = contentsString.filter{ $0 != "\n" }
  398. }
  399. let size = NSString(string: contentsString).boundingRect(with: NSSize(width: tableView.frame.size.width-28, height: 300), options: NSString.DrawingOptions(rawValue: 3), attributes: attributes).size
  400. height = height + 4
  401. height = height + 4
  402. height = height + size.height
  403. height = height + 4
  404. }
  405. return height
  406. }
  407. return 52
  408. }
  409. func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
  410. if self.annotations[row] is [String:Any] {
  411. return false
  412. }
  413. return true
  414. }
  415. func tableViewSelectionDidChange(_ notification: Notification) {
  416. for index in self.selectedRowIndexs {
  417. if self.tableView.selectedRowIndexes.contains(index) {
  418. let rowView: KMAnnotationTableRowView = self.tableView.rowView(atRow: index, makeIfNecessary: false) as! KMAnnotationTableRowView
  419. rowView.itemSelect = true
  420. } else {
  421. let rowView: KMAnnotationTableRowView = self.tableView.rowView(atRow: index, makeIfNecessary: false) as! KMAnnotationTableRowView
  422. rowView.itemSelect = false
  423. }
  424. }
  425. // if self.tableView.selectedRowIndexes.count > 1 {
  426. // var newAnnonations : [CPDFAnnotation] = []
  427. // for itemIndex in self.tableView.selectedRowIndexes {
  428. // let annotation = self.annotations[itemIndex]as! CPDFAnnotation
  429. // if annotation != nil {
  430. // newAnnonations.append(annotation)
  431. // }
  432. // }
  433. // self.listView.updateActiveAnnotations(newAnnonations)
  434. // self.listView.setNeedsDisplayAnnotationViewForVisiblePages()
  435. // } else {
  436. // let selectedRow = self.tableView.selectedRow
  437. // if selectedRow >= 0 && selectedRow < self.annotations.count {
  438. // let annotation = self.annotations[selectedRow]
  439. // if (annotation as AnyObject).isKind(of: CPDFAnnotation.self) {
  440. // self.listView.go(to: (annotation as! CPDFAnnotation).bounds, on: (annotation as! CPDFAnnotation).page, animated: true)
  441. //
  442. // self.listView.updateActiveAnnotations([annotation as! CPDFAnnotation])
  443. // self.listView.setNeedsDisplayAnnotationViewForVisiblePages()
  444. // }
  445. // }
  446. // }
  447. self.selectedRowIndexs = self.tableView.selectedRowIndexes
  448. }
  449. }
  450. extension KMAnnotationViewController : NSMenuDelegate {
  451. func menuNeedsUpdate(_ menu: NSMenu) {
  452. menu.removeAllItems()
  453. var selectedRowIndexes = self.tableView.selectedRowIndexes
  454. let clickRow = self.tableView.clickedRow
  455. if clickRow >= 0 {
  456. if !selectedRowIndexes.contains(clickRow) {
  457. selectedRowIndexes = [clickRow]
  458. }
  459. var item = NSMenuItem()
  460. if selectedRowIndexes.count == 1 {
  461. let annotation : CPDFAnnotation = annotations[selectedRowIndexes.first ?? 0] as! CPDFAnnotation
  462. if annotation.contents?.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) ?? 0 > 0 {
  463. item = menu.addItem(withTitle: NSLocalizedString("Copy Text", comment: ""), action: #selector(flattenButtonAction), target: self)
  464. item.representedObject = selectedRowIndexes
  465. menu.addItem(NSMenuItem.separator())
  466. }
  467. }
  468. item = menu.addItem(withTitle: NSLocalizedString("Export Annotation", comment: ""), action: #selector(exportItemAction), target: self)
  469. item = menu.addItem(withTitle: NSLocalizedString("Import Annotation", comment: ""), action: #selector(importItemAction), target: self)
  470. menu.addItem(NSMenuItem.separator())
  471. if self.tableView(tableView: self.tableView, canDeleteRowsWithIndexes: selectedRowIndexes) {
  472. item = menu.addItem(withTitle: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction), target: self)
  473. item.representedObject = selectedRowIndexes
  474. menu.addItem(NSMenuItem.separator())
  475. }
  476. }
  477. }
  478. }