123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- //
- // KMAnnotationViewController.swift
- // PDF Office
- //
- // Created by lxy on 2022/10/10.
- //
- import Cocoa
- class KMAnnotationViewController: KMSideViewController {
- @IBOutlet weak var tableView: NSTableView!
- @IBOutlet weak var topView: NSView!
- @IBOutlet weak var filtrateButton: NSButton!
- @IBOutlet weak var moreButton: NSButton!
- @IBOutlet weak var markupTitleLabel: NSTextField!
- @IBOutlet weak var emptyView: NSView!
- @IBOutlet weak var bigTipLabel: NSTextField!
- @IBOutlet weak var tipLabel: NSTextField!
-
- let pages = "page"
- let label = "label"
-
- var annotations : [Any] = [Any]()
- var selectedRowIndexs : IndexSet = []
-
- var allAnnotations : [Any] = [Any]()
- let moreMenu = NSMenu()
- //MARK: View
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- self.setup()
- self.updateUI()
- self.updateLanguage()
-
- self.initMenu()
-
- NotificationCenter.default.addObserver(self, selector: #selector(PDFViewActiveAnnotationDidChangeNotification), name: NSNotification.Name.init(rawValue: "KMHomeFileRectChange"), object: nil)
- NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: NSNotification.Name.init(rawValue: "CPDFPageDidAddAnnotationNotification"), object: nil)
- NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: NSNotification.Name.init(rawValue: "CPDFPageDidRemoveAnnotationNotification"), object: nil)
- }
-
- func setup() {
- self.view.wantsLayer = true
- self.view.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
-
- self.topView.wantsLayer = true
- self.topView.layer?.backgroundColor = NSColor.clear.cgColor
-
- self.tableView.doubleAction = #selector(tableViewDoubleAction)
- self.tableView.allowsMultipleSelection = true
- self.tableView.wantsLayer = true
- self.tableView.delegate = self
- self.tableView.dataSource = self
-
- let menu = NSMenu()
- menu.delegate = self
- self.tableView.menu = menu
- }
-
- func updateUI() {
- self.markupTitleLabel.font = NSFont.SFProTextSemibold(14.0)
- self.markupTitleLabel.textColor = NSColor(hex: "#252629")
-
- // self.tableView.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
- // self.tableView.border(NSColor(hex: "#000000", alpha: 0.1), 1, 0)
-
- self.bigTipLabel.font = NSFont.SFProTextRegular(14.0)
- self.bigTipLabel.textColor = NSColor(hex: "#616469")
- }
-
- func updateLanguage() {
- self.markupTitleLabel.stringValue = NSLocalizedString("Annotation", comment: "")
- self.bigTipLabel.stringValue = NSLocalizedString("No markup found", comment: "")
- self.filtrateButton.toolTip = NSLocalizedString("Sort", comment: "")
- self.moreButton.toolTip = NSLocalizedString("More", comment: "")
-
-
- let title = NSLocalizedString("All comments for this document will be displayed here.", comment: "")
- let paragraphStyle = NSMutableParagraphStyle()
- paragraphStyle.lineHeightMultiple = 1.32
- paragraphStyle.alignment = .center
- self.tipLabel.attributedStringValue = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor(hex: "#94989C")])
- }
-
- private func initMenu() {
- moreMenu.addItem(withTitle: NSLocalizedString("Expand All", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
- moreMenu.addItem(withTitle: NSLocalizedString("Collapse All", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 1)
- let soreItem = moreMenu.addItem(withTitle: NSLocalizedString("Sort", comment: ""), action: nil, target: self)
- let soreMenu = NSMenu()
- soreMenu.addItem(withTitle: NSLocalizedString("Page", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
- // soreMenu.addItem(withTitle: NSLocalizedString("Chronologically - ascending", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 1)
- // soreMenu.addItem(withTitle: NSLocalizedString("Chronologically - reverse", comment: ""), action: #selector(toc_expandAllComments), target: self, tag: 0)
- soreItem?.submenu = soreMenu
- moreMenu.addItem(withTitle: NSLocalizedString("Import annotations", comment: ""), action: #selector(importItemAction), target: self)
- moreMenu.addItem(withTitle: NSLocalizedString("Export annotations to XFDF", comment: ""), action: #selector(exportItemAction), target: self)
- moreMenu.addItem(withTitle: NSLocalizedString("Remove All Annotations", comment: ""), action: #selector(deleteAllAnonationAction), target: self)
- }
-
- @objc private func toc_expandAllComments(sender:NSMenuItem) {
- // if sender.tag == 0 {
- // self.outlineView.reloadData()
- // self.outlineView.expandItem(nil, expandChildren: true)
- // } else if sender.tag == 1 {
- // self.outlineView.reloadData()
- // self.outlineView.collapseItem(nil, collapseChildren: true)
- // } else if sender.tag == 2 {
- // let alter = NSAlert()
- // alter.alertStyle = NSAlert.Style.informational
- // alter.messageText = NSLocalizedString("This will permanently remove all outlines. Are you sure to continue?", comment: "")
- // alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
- // alter.addButton(withTitle: NSLocalizedString("No", comment:""))
- // let modlres = alter.runModal()
- // if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
- // self.removeAllOutline()
- // }
- // }
- }
-
- @objc public func reloadData() {
- self.annotations = [CPDFAnnotation]()
- var index = 0
- for i in 0 ..< self.listView.document.pageCount {
- let page = self.listView.document.page(at: i)
- let types = ["Highlight","Underline","Strikeout","Freehand","FreeText","Note","Square","Circle","Line","Stamp","Arrow","Image","Redact","Sign"]
- var annotations = KMOCToolClass.filterAnnotation(page!.annotations,types: types)!
- for annotation in page!.annotations {
- if annotation.isKind(of: CPDFSignatureAnnotation.self) {
- annotations.append(annotation)
- }
- }
- if(annotations.count > 0) {
- var dic : [String:Any] = [:]
- dic[pages] = page
- dic[label] = annotations
- self.annotations.append(dic)
- index += 1
- }
- for annotation in annotations {
- self.annotations.append(annotation)
- }
- }
- self.allAnnotations = annotations
- if self.annotations.count < 1 {
- self.filtrateButton.isEnabled = false
- } else {
- self.filtrateButton.isEnabled = true
- }
- self.tableView.reloadData()
- }
-
- public func clear() {
- self.annotations.removeAll()
- self.tableView.reloadData()
- }
-
- //MARK: Accessors
-
- @IBAction func tableViewDoubleAction(_ sender: Any) {
- let selectedRow = self.tableView.selectedRow
- if selectedRow >= 0 && selectedRow < self.annotations.count {
- let annotation = self.annotations[selectedRow]
- if (annotation as AnyObject).isKind(of: CPDFAnnotation.self) {
- self.listView.go(to: (annotation as! CPDFAnnotation).bounds, on: (annotation as! CPDFAnnotation).page, animated: true)
- // self.mainWindowController.listView.seta
- }
- }
- }
-
-
- @IBAction func moreButtonAction(_ sender: NSButton) {
- let rect = sender.convert(sender.bounds, to: self.view)
- moreMenu.popUp(positioning: nil, at: NSPoint(x: rect.origin.x, y: rect.origin.y-10), in: self.view)
- }
-
- @IBAction func filtrateButtonAction(_ sender: NSButton) {
- let menu = NSMenu()
- let filterVC = KMNoteOutlineFilterViewController()
- filterVC.notesArray = self.allAnnotations
- filterVC.applyFilterCallback = { typeArr, colorArr,authArr,isEmpty in
- menu.cancelTracking()
- self.annotationSort(sortArray: [typeArr!,colorArr!,authArr!])
- }
- filterVC.cancelCallback = { isCancel in
- if isCancel {
- menu.cancelTracking()
- }
- }
- let item = menu.addItem(withTitle: "", action: nil, keyEquivalent: "")
- item.target = self
- item.representedObject = filterVC
- item.view = filterVC.view
- menu.popUp(positioning: nil, at: CGPoint(x: -130, y: 30), in: sender)
- }
-
- func annotationSort(sortArray:[[Any]]) {
- let typeArr = sortArray[0]
- let colorArr = sortArray[1]
- let authorArr = sortArray[2]
- if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 {
-
- } else {
- self.annotations = [CPDFAnnotation]()
- for i in 0 ..< self.listView.document.pageCount {
- let page = self.listView.document.page(at: i)
- if page!.annotations.count > 0 {
- var filterAnnotations = [CPDFAnnotation]()
- if typeArr.count > 0 {
- filterAnnotations = (KMOCToolClass.filterAnnotation(page!.annotations,types: typeArr) as! [CPDFAnnotation])
- }
- if (colorArr.count > 0) {
- filterAnnotations = (KMOCToolClass.filterAnnotation(filterAnnotations,colors: colorArr) as! [CPDFAnnotation])
- }
- if (authorArr.count > 0) {
- filterAnnotations = (KMOCToolClass.filterAnnotation(filterAnnotations,authors: authorArr) as! [CPDFAnnotation])
- }
- if(filterAnnotations.count > 0) {
- var dic : [String:Any] = [:]
- dic[self.pages] = page
- dic[self.label] = filterAnnotations
- self.annotations.append(dic)
- }
- for annotation in filterAnnotations {
- self.annotations.append(annotation)
- }
- }
- }
- self.tableView.reloadData()
- }
- }
-
- @IBAction func deleteButtonAction(_ sender: Any) {
- let alert = NSAlert()
- alert.alertStyle = .critical
- alert.messageText = NSLocalizedString("This will permanently remove all annotations. Are you sure to continue?", comment: "")
- alert.informativeText = NSLocalizedString("You cannot undo this operation.", comment: "")
- alert.addButton(withTitle: NSLocalizedString("Yes", comment: ""))
- alert.addButton(withTitle: NSLocalizedString("No", comment: ""))
- alert.beginSheetModal(for: self.view.window!, completionHandler: { result in
- if result == .OK {
- for i in 0 ..< self.listView.document.pageCount {
- let page = self.listView.document.page(at: i)
- page?.removeAllAnnotations()
- }
- // let undoManager : UndoManager = self.mainWindowController.document?.undoManager ?? UndoManager()
- // undoManager.setActionName("")
- self.listView.updateActiveAnnotations([CPDFAnnotation()])
- self.listView.setNeedsDisplayForVisiblePages()
- self.reloadData()
- }
- })
- }
-
- @IBAction func flattenButtonAction(_ sender: NSMenuItem) {
- let selects = sender.representedObject as! NSIndexSet
- var indexs : [Int] = []
- for index in selects {
- indexs.append(index)
- }
-
- if selects.count == 1 {
- let index = selects.firstIndex
- let annotation : CPDFAnnotation = annotations[index] as! CPDFAnnotation
- if annotation.contents?.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) ?? 0 > 0 {
- let content = annotation.contents! as NSString
-
- let pasteBoard = NSPasteboard.general
- pasteBoard.clearContents()
- pasteBoard.setString(content as String, forType: .string)
- }
- }
- }
-
- @IBAction func exportItemAction(_ sender: Any) {
- let panel = NSSavePanel()
- panel.nameFieldStringValue = "\(NSLocalizedString("Untitled", comment: "")).xfdf"
- panel.isExtensionHidden = true
- let response = panel.runModal()
- if response == .OK {
- let url = panel.url
- let result = self.listView.document.exportAnnotation(toXFDFPath: url!.path)
- if result {
- NSWorkspace.shared.openFile(url!.path)
- }
- }
- }
-
- @IBAction func importItemAction(_ sender: Any) {
- let panel = NSOpenPanel()
- panel.allowsMultipleSelection = false
- panel.allowedFileTypes = ["xfdf"]
- panel.beginSheetModal(for: NSApp.mainWindow!) { response in
- if response == .OK {
- let openPath = panel.url?.path
- let result = self.listView.document.importAnnotation(fromXFDFPath: openPath!)
- if result {
- self.listView.setNeedsDisplayAnnotationViewForVisiblePages()
- self.reloadData()
- }
- }
- }
- }
-
- @IBAction func deleteItemAction(_ sender: NSMenuItem) {
- let selects = sender.representedObject as! NSIndexSet
- var indexs : [Int] = []
- for index in selects {
- indexs.append(index)
- }
- indexs.sort(){$0 > $1}
- self.tableView(tableView: self.tableView, deleteRowsWithIndexes: indexs)
- }
-
- @IBAction func deleteAllAnonationAction(_ sender: NSMenuItem) {
- for i in 0 ..< self.listView.document.pageCount {
- let page = self.listView.document.page(at: i)
- for annotation in page!.annotations {
- page?.removeAnnotation(annotation)
- self.listView.setNeedsDisplayForVisiblePages()
- }
- }
- }
-
- //MARK: NSNotification
-
- @IBAction func PDFViewActiveAnnotationDidChangeNotification(notification: NSNotification) {
- let pdfView : CPDFListView = notification.object as! CPDFListView
- if pdfView.isEqual(self.listView) {
- let annotion = pdfView.activeAnnotations.firstObject
- if (annotion != nil && KMOCToolClass.arrayContains(self.annotations, annotation: annotion)) {
- let index = KMOCToolClass.arrayIndex(of: self.annotations, annotation: annotion)
- let indexset = IndexSet.init(integer: index)
- self.tableView.selectRowIndexes(indexset, byExtendingSelection: false)
- self.tableView.scrollRowToVisible(index)
- }
- }
- }
-
-
- @IBAction func escButtonAction(_ sender: Any) {
- self.tableView.deselectAll(nil)
- }
-
- private func tableView(tableView:NSTableView,canDeleteRowsWithIndexes rowindexs : IndexSet) -> Bool {
- return true
- }
- private func tableView(tableView:NSTableView, deleteRowsWithIndexes rowindexs : [Int]) {
- var removeAnnotations: [Any] = []
- for index in rowindexs {
- if index < self.annotations.count {
- let annotation = self.annotations[index] as? CPDFAnnotation
- let page = annotation?.page
- if ((page?.annotations.contains(annotation!)) != nil) {
- page?.removeAnnotation(annotation!)
- } else {
- print("不存在")
- }
-
- if self.listView.activeAnnotations.contains(annotation) {
- removeAnnotations.append(annotation)
- }
- }
- }
-
- if removeAnnotations.count != 0 {
- self.listView.activeAnnotations.remove(removeAnnotations)
- self.listView.setNeedsDisplayForVisiblePages()
- }
- self.reloadData()
- self.selectedRowIndexs = []
- }
-
- }
- //MARK: NSTableViewDelegate,NSTableViewDataSource
- extension KMAnnotationViewController: NSTableViewDelegate,NSTableViewDataSource {
- func numberOfRows(in tableView: NSTableView) -> Int {
- if self.annotations.count > 0 {
- self.emptyView.isHidden = true
- } else {
- self.emptyView.isHidden = false
- }
- return self.annotations.count
- }
-
- func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
- if self.annotations[row] is [String:Any] {
- let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMAnnotationHeaderCellView"), owner: self) as! KMAnnotationHeaderCellView
- let dic : [String:Any] = self.annotations[row] as! [String : Any]
- let page : CPDFPage = dic[pages] as! CPDFPage
- cell.currentPage = Int(page.document.index(for: page))
- cell.notes = dic[label] as! [CPDFAnnotation]
- cell.updateCellInfo()
- return cell
- } else if self.annotations[row] is CPDFAnnotation {
- let cell : KMAnnotationTableCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMAnnotationTableCellView"), owner: self) as! KMAnnotationTableCellView
- cell.annotation = self.annotations[row] as? CPDFAnnotation
- cell.updateAnnotation(annotation: self.annotations[row] as! CPDFAnnotation)
- return cell
- }
- return nil
- }
-
- func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
- let tableRowView = KMAnnotationTableRowView()
- return tableRowView
- }
-
- func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
- if self.annotations[row] is [String:Any] {
- return 32
- } else if self.annotations[row] is CPDFAnnotation {
- var height: CGFloat = 40
-
- let paragraphStyle = NSMutableParagraphStyle()
- paragraphStyle.lineHeightMultiple = 1.32
- paragraphStyle.alignment = .left
- let attributes = [NSAttributedString.Key.paragraphStyle: paragraphStyle,
- NSAttributedString.Key.font : NSFont.SFProTextRegular(14.0)]
- let annotation = self.annotations[row] as! CPDFAnnotation
-
- if annotation.isKind(of: CPDFMarkupAnnotation.self) {
- var text : String = (annotation as! CPDFMarkupAnnotation).markupText()
- if text.count == 0 {
- let exproString = KMOCToolClass.exproString(self.annotations[row] as? CPDFAnnotation)!
- if exproString.count > 0 {
- text = exproString
- }
- }
-
- if text.hasPrefix("\n") {
- text = text.filter{ $0 != "\n" }
- }
-
- if text.count > 0 {
- let size = NSString(string: text).boundingRect(with: NSSize(width: tableView.frame.size.width-28, height: 300), options: NSString.DrawingOptions(rawValue: 3), attributes: attributes).size
- height += 4
- height = height + size.height
- }
- } else if (annotation.isKind(of: CPDFInkAnnotation.self)) ||
- (annotation.isKind(of: CPDFSquareAnnotation.self)) ||
- (annotation.isKind(of: CPDFLineAnnotation.self)) ||
- (annotation.isKind(of: CPDFCircleAnnotation.self)) {
- height += 4
- height += 58
- }
-
- var contentsString : String = annotation.contents ?? ""
- if contentsString.count > 0 {
- if contentsString.hasSuffix("\n") {
- contentsString = contentsString.filter{ $0 != "\n" }
- }
-
- let size = NSString(string: contentsString).boundingRect(with: NSSize(width: tableView.frame.size.width-28, height: 300), options: NSString.DrawingOptions(rawValue: 3), attributes: attributes).size
- height = height + 4
- height = height + 4
- height = height + size.height
- height = height + 4
- }
-
- return height
- }
- return 52
- }
- func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
- if self.annotations[row] is [String:Any] {
- return false
- }
-
- return true
- }
-
- func tableViewSelectionDidChange(_ notification: Notification) {
- for index in self.selectedRowIndexs {
- if self.tableView.selectedRowIndexes.contains(index) {
- let rowView: KMAnnotationTableRowView = self.tableView.rowView(atRow: index, makeIfNecessary: false) as! KMAnnotationTableRowView
- rowView.itemSelect = true
- } else {
- let rowView: KMAnnotationTableRowView = self.tableView.rowView(atRow: index, makeIfNecessary: false) as! KMAnnotationTableRowView
- rowView.itemSelect = false
- }
- }
-
- // if self.tableView.selectedRowIndexes.count > 1 {
- // var newAnnonations : [CPDFAnnotation] = []
- // for itemIndex in self.tableView.selectedRowIndexes {
- // let annotation = self.annotations[itemIndex]as! CPDFAnnotation
- // if annotation != nil {
- // newAnnonations.append(annotation)
- // }
- // }
- // self.listView.updateActiveAnnotations(newAnnonations)
- // self.listView.setNeedsDisplayAnnotationViewForVisiblePages()
- // } else {
- // let selectedRow = self.tableView.selectedRow
- // if selectedRow >= 0 && selectedRow < self.annotations.count {
- // let annotation = self.annotations[selectedRow]
- // if (annotation as AnyObject).isKind(of: CPDFAnnotation.self) {
- // self.listView.go(to: (annotation as! CPDFAnnotation).bounds, on: (annotation as! CPDFAnnotation).page, animated: true)
- //
- // self.listView.updateActiveAnnotations([annotation as! CPDFAnnotation])
- // self.listView.setNeedsDisplayAnnotationViewForVisiblePages()
- // }
- // }
- // }
- self.selectedRowIndexs = self.tableView.selectedRowIndexes
- }
-
- }
- extension KMAnnotationViewController : NSMenuDelegate {
- func menuNeedsUpdate(_ menu: NSMenu) {
- menu.removeAllItems()
- var selectedRowIndexes = self.tableView.selectedRowIndexes
- let clickRow = self.tableView.clickedRow
- if clickRow >= 0 {
- if !selectedRowIndexes.contains(clickRow) {
- selectedRowIndexes = [clickRow]
- }
- var item = NSMenuItem()
- if selectedRowIndexes.count == 1 {
- let annotation : CPDFAnnotation = annotations[selectedRowIndexes.first ?? 0] as! CPDFAnnotation
- if annotation.contents?.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) ?? 0 > 0 {
- item = menu.addItem(withTitle: NSLocalizedString("Copy Text", comment: ""), action: #selector(flattenButtonAction), target: self)
- item.representedObject = selectedRowIndexes
- menu.addItem(NSMenuItem.separator())
- }
- }
-
- item = menu.addItem(withTitle: NSLocalizedString("Export Annotation", comment: ""), action: #selector(exportItemAction), target: self)
- item = menu.addItem(withTitle: NSLocalizedString("Import Annotation", comment: ""), action: #selector(importItemAction), target: self)
- menu.addItem(NSMenuItem.separator())
- if self.tableView(tableView: self.tableView, canDeleteRowsWithIndexes: selectedRowIndexes) {
- item = menu.addItem(withTitle: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction), target: self)
- item.representedObject = selectedRowIndexes
- menu.addItem(NSMenuItem.separator())
- }
-
- }
- }
- }
|