// // KMLeftSideViewController+Note.swift // PDF Reader Pro // // Created by tangchao on 2023/12/23. // import Foundation extension KMLeftSideViewController.Key { static let noteAscendSortKey = "KMLeftSideViewAscendSortBoolKey" static let noteSortTypeKey = "KMLeftSideViewNoteSortTypeKey" static let noteTableColumn = "KMNoteOutlineViewTableColumnKey" static let noteFilterPage = "kKMNoteFilterAnnotationPageKey" static let noteFilterTime = "kKMNoteFilterAnnotationTimeKey" static let noteFilterAuther = "kKMNoteFilterAnnotationAutherKey" } // MARK: - Action extension KMLeftSideViewController { func note_initSubViews() { self.noteSearchField.backgroundColor = KMAppearance.Layout.l_1Color() self.noteSearchField.wantsLayer = true self.noteSearchField.layer?.backgroundColor = KMAppearance.Layout.l_1Color().cgColor self.noteSearchField.layer?.borderWidth = 1.0 self.noteMoreButton.target = self self.noteMoreButton.tag = 304 self.noteMoreButton.action = #selector(leftSideViewMoreButtonAction) self.moreButtonLayer = KMButtonLayer() self.noteMoreButton.layer?.addSublayer(self.moreButtonLayer!) self.moreButtonLayer?.frame = NSMakeRect(0, 0, NSWidth(self.noteMoreButton.bounds), NSHeight(self.noteMoreButton.bounds)) self.noteFilterButton.target = self self.noteFilterButton.action = #selector(noteFilterAction) self.filterButtonLayer = NSView() self.noteFilterButton.addSubview(self.filterButtonLayer!) self.filterButtonLayer?.frame = NSMakeRect(14, 2, 8, 8) self.noteFilterButton.superview?.addSubview(self.noteFilterButtonHoverView, positioned: .below, relativeTo: self.noteFilterButton) self.noteFilterButtonHoverView.frame = self.noteFilterButton.frame self.noteFilterButtonHoverView.autoresizingMask = [.minXMargin] self.noteFilterButtonHoverView.km_add_size_constraint(equalTo: self.noteFilterButton, size: .zero) self.noteFilterButtonHoverView.km_add_centerX_constraint(equalTo: self.noteFilterButton) self.noteFilterButtonHoverView.km_add_centerY_constraint(equalTo: self.noteFilterButton) self.noteFilterButtonHoverView.wantsLayer = true self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor.clear.cgColor self.noteFilterButtonHoverView.layer?.cornerRadius = 6 self.noteFilterButtonHoverView.hoverAction = { [weak self] sender, act in let selected = self?.noteFilterSelected ?? false if selected { return } if act == .enter { if KMAppearance.isDarkMode() { sender.layer?.backgroundColor = NSColor(white: 1, alpha: 0.05).cgColor } else { sender.layer?.backgroundColor = NSColor(white: 0, alpha: 0.05).cgColor } } else if act == .exit { sender.layer?.backgroundColor = .clear } } self.noteDoneButton.action = #selector(leftSideViewDoneButtonAction) self.noteDoneButton.target = self self.noteDoneButton.tag = 311 self.noteDoneButton.isHidden = true self.noteSearchField.delegate = self self.noteSearchField.isHidden = true self.noteSearchField.endEditCallBack = { [weak self] isEndEdit in // if (isEndEdit) { // self.noteSearchField.isHidden = true // self.noteSearchButton.isHidden = false // self.noteTitleLabel.isHidden = false // } } self.sortTypeBox.downCallback = { [unowned self] downEntered, mouseBox, _ in if (downEntered) { let menu = NSMenu() let timeItem = menu.addItem(title: KMLocalizedString("Time", nil), action: #selector(sortTypeAction), target: self) timeItem?.representedObject = self timeItem?.tag = 0 let pageItem = menu.addItem(title: KMLocalizedString("Page", nil), action: #selector(sortTypeAction), target: self) pageItem?.representedObject = self timeItem?.tag = 1 if (self.noteSortType == .time) { timeItem?.state = .on pageItem?.state = .off } else if (self.noteSortType == .page) { timeItem?.state = .off pageItem?.state = .on } menu.popUp(positioning: nil, at: NSMakePoint(-10, 0), in: self.sortTypeBox) } } self.noteOutlineView.delegate = self self.noteOutlineView.dataSource = self self.noteOutlineView.botaDelegate = self self.noteOutlineView.botaDataSource = self self.noteOutlineView.noteDelegate = self self.noteOutlineView.menu = NSMenu() self.noteOutlineView.menu?.delegate = self self.noteOutlineView.typeSelectHelper = SKTypeSelectHelper(matchOption: .SKSubstringMatch) self.noteOutlineView.enclosingScrollView?.scrollerStyle = .legacy self.noteOutlineView.enclosingScrollView?.autohidesScrollers = true self.noteOutlineView.registerForDraggedTypes(NSColor.readableTypes(for: NSPasteboard(name: .drag))) self.noteOutlineView.target = self self.noteOutlineView.doubleAction = #selector(selectSelectedNote) } func note_initDefalutValue() { self.noteView.wantsLayer = true self.noteView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor let sud = UserDefaults.standard if let dict = sud.dictionary(forKey: Self.Key.noteTableColumn) { self.noteTypeDict = dict } else { self.noteTypeDict = [Self.Key.noteFilterPage : false, Self.Key.noteFilterTime : false, Self.Key.noteFilterAuther : false] sud.sync_setValue(self.noteTypeDict, forKey: Self.Key.noteTableColumn) } self.caseInsensitiveNoteSearch = sud.bool(forKey: SKCaseInsensitiveNoteSearchKey) self.isAscendSort = KMDataManager.ud_bool(forKey: Self.Key.noteAscendSortKey) self.noteTitleLabel.stringValue = KMLocalizedString("Notes", nil); self.noteTitleLabel.textColor = KMAppearance.Layout.h0Color() self.noteSearchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor self.noteMoreButton.wantsLayer = true self.moreButtonLayer?.layerType = .none self.moreButtonLayer?.isHidden = true self.noteFilterButton.toolTip = KMLocalizedString("Sort", nil) self.noteFilterButton.wantsLayer = true self.filterButtonLayer?.isHidden = true self.filterButtonLayer?.wantsLayer = true self.filterButtonLayer?.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.filterButtonLayer?.layer?.cornerRadius = 4.0 self.noteFilterSelected = false if (self.isAscendSort) { self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankReverse) self.noteSortButton.toolTip = KMLocalizedString("ascending sort", nil) } else { self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankPositive) self.noteSortButton.toolTip = KMLocalizedString("descending sort", nil) } self.noteSearchButton.toolTip = KMLocalizedString("Search", nil) self.noteDoneButton.title = KMLocalizedString("Done", nil) self.noteDoneButton.toolTip = KMLocalizedString("Done", nil) self.noteDoneButton.setTitleColor(KMAppearance.Layout.w0Color()) self.noteDoneButton.wantsLayer = true self.noteDoneButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.noteDoneButton.layer?.cornerRadius = 4.0 // self.noteHeaderView.wantsLayer = true // self.noteHeaderView.layer?.backgroundColor = KMAppearance.Else.textTagColor().cgColor // self.noteHeaderView.layer?.cornerRadius = 1.0 // let sortType = KMDataManager.ud_integer(forKey: Self.Key.noteSortTypeKey) var sortType = 0 if let data = self.listView?.document?.documentURL.path { sortType = KMDataManager.udExtension_object(forKey: Self.Key.noteSortTypeKey + data) as? Int ?? 0 } if (sortType == 1) { self.noteSortType = KMNoteSortType(rawValue: sortType) ?? .none if (self.noteSortType == .time) { self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil) self.sortTypeBox.toolTip = KMLocalizedString("Time", nil) } else if (self.noteSortType == .page) { self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil) self.sortTypeBox.toolTip = KMLocalizedString("Page", nil) } } else { self.noteSortType = .page self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil) } self.sortTypeLabel.textColor = KMAppearance.Layout.h1Color() self.noteOutlineView.backgroundColor = KMAppearance.Layout.l0Color() self.noteOutlineView.autoresizesOutlineColumn = false self.noteOutlineView.indentationPerLevel = 0 } func annoListIsShowPage() -> Bool { return !(self.noteTypeDict[Self.Key.noteFilterPage] as? Bool ?? false) } func annoListIsShowTime() -> Bool { return !(self.noteTypeDict[Self.Key.noteFilterTime] as? Bool ?? false) } func annoListIsShowAnther() -> Bool { return !(self.noteTypeDict[Self.Key.noteFilterAuther] as? Bool ?? false) } } // MARK: - Menu extension KMLeftSideViewController { func annoListMenu(_ menu: NSMenu) { var item: NSMenuItem? var items: NSArray? var rowIndexes = self.noteOutlineView.selectedRowIndexes let row = self.noteOutlineView.clickedRow if row == -1 { // _ = self._addExportPDFMenu(menu) _ = self._addDeleteAllAnnoItem(menu) return } if rowIndexes.contains(row) == false { rowIndexes = IndexSet(integer: row) } items = self.noteOutlineView.itemsAtRowIndexes(rowIndexes) as NSArray guard let model = self.fetchAnnoModel(for: row) else { return } let isFold = model.isFold() item = menu.addItem(title: KMLocalizedString("Expand", nil), action: #selector(unfoldNoteAction), target: self) item?.state = isFold ? .off : .on item?.representedObject = items item = menu.addItem(title: KMLocalizedString("Collapse", nil), action: #selector(foldNoteAction), target: self) item?.state = isFold ? .on : .off item?.representedObject = items menu.addItem(.separator()) let hideNotes = self.hideNotes() // var theItems: [KMBotaAnnotationModel] = [] // for data in items ?? [] { // if let annoModel = data as? KMBotaAnnotationModel { // theItems.append(annoModel) // } // } if hideNotes == false && (items?.count ?? 0) == 1 { // let annotation = self.noteItems(items!).lastObject as? CPDFAnnotation // if let data = annotation?.isEditable(), data { // if annotation?.type == nil { // let isNote = annotation?.isNote() ?? false // if isNote { // // [NSLocalizedString(@"Edit", @"Menu item title") stringByAppendingEllipsis] // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editNoteTextFromTable), target: self) // item?.representedObject = annotation // } // } else if let data = self.noteOutlineView.tableColumn(withIdentifier: NSUserInterfaceItemIdentifier("note"))?.isHidden, data { // // [NSLocalizedString(@"Edit", @"Menu item title") stringByAppendingEllipsis] // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editThisAnnotation), target: self) // item?.representedObject = annotation // } else { // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editNoteFromTable), target: self) // item?.representedObject = annotation // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editThisAnnotation), target: self) // item?.representedObject = annotation // item?.keyEquivalentModifierMask = [.option] // item?.isAlternate = true // } // } } if menu.numberOfItems > 0 { // _ = self._addExportPDFMenu(menu) // menu.addItem(.separator()) if self.outlineView(self.noteOutlineView, canDeleteItems: items as? [Any] ?? []) { item = menu.addItem(title: KMLocalizedString("Delete", "Menu item title"), action: #selector(deleteNotes), target: self) item?.representedObject = items } _ = self._addDeleteAllAnnoItem(menu) } } private func _addExportPDFMenu(_ menu: NSMenu) -> NSMenu { var item = menu.addItem(title: NSLocalizedString("Export Annotations…", tableName: "", comment: ""), action: nil, target: self) let subMenu = NSMenu() item?.submenu = subMenu item = subMenu.addItem(title: NSLocalizedString("PDF", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 0 item = subMenu.addItem(title: NSLocalizedString("PDF Bundle", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 1 item = subMenu.addItem(title: NSLocalizedString("PDF Reader Pro Edition Notes", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 2 item = subMenu.addItem(title: NSLocalizedString("Notes as Text", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 3 item = subMenu.addItem(title: NSLocalizedString("Notes as RTF", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 4 item = subMenu.addItem(title: NSLocalizedString("Notes as RTFD", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 5 item = subMenu.addItem(title: NSLocalizedString("Notes as FDF", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self) item?.tag = 6 return menu } private func _addDeleteAllAnnoItem(_ menu: NSMenu) -> NSMenuItem? { return menu.addItem(title: NSLocalizedString("Remove All Annotations", tableName: "", comment: ""), action: #selector(removeAllAnnotations), target: self) } private func _addDeleteAllReplyAnnoItem(_ menu: NSMenu) -> NSMenuItem? { return menu.addItem(title: NSLocalizedString("Delete All Reply", tableName: "", comment: ""), action: #selector(removeAllReplyAnnotations), target: self) } func annoListMoreMenu(_ view: NSButton) { let menu = NSMenu() let object = KMPopupMenuObject() object.menuTag = 1001 menu.delegate = object object.enterControllerCallback = { [weak self] isEnter in if (isEnter) { self?.moreButtonLayer?.isHidden = false } else { self?.moreButtonLayer?.isHidden = true } } let expandAllItem = menu.addItem(title: KMLocalizedString("Expand All", nil), action: #selector(note_expandAllComments), target: self) expandAllItem?.representedObject = self.noteOutlineView let foldAllItem = menu.addItem(title: KMLocalizedString("Collapse All", nil), action: #selector(note_foldAllComments), target: self) foldAllItem?.representedObject = self.noteOutlineView let type = self.annoListModel?.foldType ?? .none // expandAllItem?.state = type == .unfold ? .on : .off expandAllItem?.state = .on for secM in self.annoListModel?.datas ?? [] { if secM.isExpand == false { expandAllItem?.state = .off break } } // foldAllItem?.state = type == .fold ? .on : .off foldAllItem?.state = .on for secM in self.annoListModel?.datas ?? [] { if secM.isExpand { foldAllItem?.state = .off break } } let showItem = menu.addItem(title: KMLocalizedString("Show Note", nil), action: nil, target: self) let subMenu = NSMenu() let pageItem = subMenu.addItem(title: KMLocalizedString("Page", nil), action: #selector(noteShowNoteAction), target: self) pageItem?.state = self.annoListIsShowPage() ? .on : .off pageItem?.representedObject = self.noteOutlineView pageItem?.tag = 101 let timeItem = subMenu.addItem(title: KMLocalizedString("Time", nil) , action: #selector(noteShowNoteAction), target: self) timeItem?.state = self.annoListIsShowTime() ? .on : .off timeItem?.representedObject = self.noteOutlineView timeItem?.tag = 102 let authorItem = subMenu.addItem(title: KMLocalizedString("Author", nil) , action: #selector(noteShowNoteAction), target: self) authorItem?.state = self.annoListIsShowAnther() ? .on : .off authorItem?.representedObject = self.noteOutlineView authorItem?.tag = 103 showItem?.submenu = subMenu menu.addItem(.separator()) // _ = self._addExportPDFMenu(menu) // menu.addItem(.separator()) _ = self._addDeleteAllAnnoItem(menu) _ = self._addDeleteAllReplyAnnoItem(menu) menu.addItem(.separator()) let importItem = NSMenuItem(title: NSLocalizedString("Import Annotations", comment: ""), action: #selector(importNotes), keyEquivalent: "") importItem.target = self menu.addItem(importItem) let exportItem = NSMenuItem(title: NSLocalizedString("Export Annotations", comment: ""), action: #selector(exportNotes), keyEquivalent: "") exportItem.target = self menu.addItem(exportItem) if let data = NSApp.currentEvent { NSMenu.popUpContextMenu(menu, with: data, for: view, with: nil) } } func annoListValidateMenuItem(_ menuItem: NSMenuItem) -> Bool { let action = menuItem.action if (action == #selector(note_expandAllComments) || action == #selector(note_foldAllComments) || action == #selector(exportAnnotationNotes) || action == #selector(removeAllAnnotations)) { let cnt = self.annoListModel?.datas.count ?? 0 return cnt > 0 } else if (action == #selector(unfoldNoteAction) || action == #selector(foldNoteAction)) { let row = self.noteOutlineView.clickedRow let foldNote = self.fetchNote(for: row) // SKNPDFAnnotationNote // if foldNote is CPDFMarkupAnnotation || foldNote is CPDFTextAnnotation { return true // } else { // return false // } } else if (action == #selector(editNoteFromTable)) { let row = self.noteOutlineView.clickedRow let foldNote = self.fetchNote(for: row) // if (@available(macOS 10.13, *)) { // if ([foldNote.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeSignature]) { // return NO; // } // } if foldNote is CPDFStampAnnotation || foldNote is KMAnnotationStamp || foldNote is CPDFListStampAnnotation { return false } else { return true } } return true } @IBAction func note_expandAllComments(_ sender: AnyObject?) { guard let model = self.annoListModel else { return } // if (model.foldType == .unfold) { // 已全部展开 // return // } // model.foldType = .unfold for secM in self.annoListModel?.datas ?? [] { // for model in secM.items { // if let data = model as? KMBotaAnnotationFooterModel { // data.isExpand = true // } // } secM.isExpand = true } self.noteOutlineView.reloadData() } @IBAction func note_foldAllComments(_ sender: AnyObject?) { guard let model = self.annoListModel else { return } // if (model.foldType == .fold) { // return // } // model.foldType = .fold for secM in self.annoListModel?.datas ?? [] { // for model in secM.items { // if let data = model as? KMBotaAnnotationFooterModel { // data.isExpand = false // } // } secM.isExpand = false } self.noteOutlineView.reloadData() } @IBAction func noteShowNoteAction(_ sender: AnyObject?) { let item = sender as? NSMenuItem let tag = item?.tag ?? 0 if (tag == 100) { } else if (tag == 101) { let isPage = !self.annoListIsShowPage() self.noteTypeDict[Self.Key.noteFilterPage] = !isPage } else if (tag == 102) { let isTime = !self.annoListIsShowTime() self.noteTypeDict[Self.Key.noteFilterTime] = !isTime } else if (tag == 103) { let isAuther = !self.annoListIsShowAnther() self.noteTypeDict[Self.Key.noteFilterAuther] = !isAuther } UserDefaults.standard.sync_setValue(self.noteTypeDict, forKey: Self.Key.noteTableColumn) // 更新数据 var models: [KMBotaAnnotationModel] = [] if self.noteSearchMode { // models = self.noteSearchArray for model in self.noteSearchArray { guard let data = model as? KMBotaAnnotationModel else { continue } models.append(data) } } else { let selModels = self.annoListModel?.datas ?? [] for selModel in selModels { for item in selModel.items { if let data = item as? KMBotaAnnotationModel { models.append(data) } } } } for model in models { model.showPage = self.annoListIsShowPage() model.showTime = self.annoListIsShowTime() model.showAuthor = self.annoListIsShowAnther() } let selectRow = self.noteOutlineView.selectedRow self.noteOutlineView.reloadData() self.noteOutlineView.selectRowIndexes(IndexSet(integer: selectRow), byExtendingSelection: false) } @objc func importNotes(_ sender: NSMenuItem) { let panel = NSOpenPanel() panel.allowedFileTypes = ["xfdf"] panel.allowsMultipleSelection = false panel.beginSheetModal(for: self.view.window!) { resp in if resp != .OK { return } if let result = self.pdfDocument()?.importAnnotation(fromXFDFPath: panel.url?.path), result { self.mainViewController?.convertNotesUsingPDFDocument(self.pdfDocument()!, callback: { [weak self] in self?.reloadAnnotation() self?.listView?.setNeedsDisplayForVisiblePages() }) } } } @objc func exportNotes(_ sender: NSMenuItem?) { guard let cnt = self.listView?.notes?.count, cnt > 0 else { NSSound.beep() return } let fileName = "\(self.pdfDocument()?.documentURL.deletingPathExtension().lastPathComponent ?? "")" + "_xfdf" let panel = NSSavePanel() panel.directoryURL = self.pdfDocument()?.documentURL.deletingLastPathComponent() panel.allowedFileTypes = ["xfdf"] panel.nameFieldStringValue = fileName panel.beginSheetModal(for: self.view.window!) { resp in if resp != .OK { return } let filePath = panel.url?.path if let success = self.pdfDocument()?.exportAnnotation(toXFDFPath: filePath), success { NSWorkspace.shared.selectFile(filePath, inFileViewerRootedAtPath: "") } else { Task { _ = await KMAlertTool.runModel(message: NSLocalizedString("Export Failure!", comment: ""), buttons: ["OK"]) } } } } @objc func exportAnnotationNotes(_ sender: AnyObject?) { let doc = self.view.window?.windowController?.document as? NSDocument doc?.saveTo(sender) } // 展开 @objc func unfoldNoteAction(_ sender: NSMenuItem) { if sender.state == .on { return } let row = self.noteOutlineView.clickedRow guard let model = self.fetchAnnoModel(for: row) else { return } model.foldType = .unfold model.isExpand = true let viewS = self.noteOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) (viewS as? KMNoteTableViewCell)?.isFold = false model.footerModel?.isExpand = true // self.noteOutlineView.reloadItem(model.footerModel) self.noteOutlineView.reloadData() } @objc func foldNoteAction(_ sender: NSMenuItem) { if sender.state == .on { return } let row = self.noteOutlineView.clickedRow guard let model = self.fetchAnnoModel(for: row) else { return } model.foldType = .fold model.isExpand = false let viewS = self.noteOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) (viewS as? KMNoteTableViewCell)?.isFold = true model.footerModel?.isExpand = false // self.noteOutlineView.reloadItem(model.footerModel) self.noteOutlineView.reloadData() } @objc func deleteNotes(_ sender: NSMenuItem) { self.outlineView(self.noteOutlineView, deleteItems: sender.representedObject as? [Any] ?? []) } @objc func removeAllAnnotations(_ sender: AnyObject?) { guard let doc = self.pdfDocument() else { return } Task { let response = await KMAlertTool.runModel(message: KMLocalizedString("This will permanently remove all annotations. Are you sure to continue?", nil), buttons: [KMLocalizedString("Yes", nil), KMLocalizedString("No", nil)]) if response == .alertFirstButtonReturn { // var annos: [CPDFAnnotation] = [] // for i in 0 ..< doc.pageCount { // let page = self.pdfDocument()?.page(at: i) // for anno in page?.annotations ?? [] { // if anno is CPDFTextWidgetAnnotation || anno is CPDFButtonWidgetAnnotation || anno is CPDFChoiceWidgetAnnotation { // continue // } // // if ([annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeSignature]) { // // continue; // // } // if anno is CPDFLinkAnnotation { // continue // } // annos.append(anno) // } // } self.dataUpdating = true // for anno in annos { // self.listView?.remove(anno) // } for model in self.annoListModel?.datas ?? [] { for item in model.items { if let anno = item.anno { if let data = anno as? CPDFFreeTextAnnotation { self.listView?.commitEditAnnotationFreeText(data) } self.listView?.remove(anno) } } } self.annoListModel?.datas.removeAll() self.dataUpdating = false self.note_refrshUIIfNeed() } } } @objc func removeAllReplyAnnotations(_ sender: NSMenuItem?) { Task { let response = await KMAlertTool.runModel(message: KMLocalizedString("Are you sure to delete all comment replies?", nil), buttons: [KMLocalizedString("Yes", nil), KMLocalizedString("No", nil)]) if response == .alertFirstButtonReturn { self.dataUpdating = true var hasAnno = false for model in self.annoListModel?.datas ?? [] { for item in model.items { // if let anno = item.anno { // self.listView?.remove(anno) // } guard let annoM = item as? KMBotaAnnotationModel else { continue } for replyM in annoM.replyAnnos { // self.listView?.remove(replyM.replyAnno) replyM.replyAnno?.page.removeAnnotation(replyM.replyAnno) hasAnno = true } annoM.replyAnnos.removeAll() } } self.dataUpdating = false self.note_refrshUIIfNeed() if let data = self.view.window, hasAnno { KMTools.setDocumentEditedState(window: data) } } } } @objc func editNoteTextFromTable(_ sender: NSMenuItem) { // PDFAnnotation *annotation = [sender representedObject]; guard let annotation = sender.representedObject as? CPDFAnnotation else { return } self.listView?.scrollAnnotationToVisible(annotation) // self.listView.activeAnnotation = annotation // [self showNote:annotation]; // SKNoteWindowController *noteController = (SKNoteWindowController *)[self windowControllerForNote:annotation]; // [[noteController window] makeFirstResponder:[noteController textView]]; // [[noteController textView] selectAll:nil]; } @objc func editThisAnnotation(_ sender: AnyObject?) { guard let annotation = (sender as? NSMenuItem)?.representedObject as? CPDFAnnotation else { NSSound.beep() return } self.listView?.edit(annotation) } @objc func editNoteFromTable(_ sender: AnyObject?) { guard let annotation = (sender as? NSMenuItem)?.representedObject as? CPDFAnnotation else { NSSound.beep() return } let model = fetchAnnoModel(for: annotation) let row = self.noteOutlineView.row(forItem: model) self.noteOutlineView.km_safe_selectRowIndexes(.init(integer: row), byExtendingSelection: false) let noteIndex = self.noteOutlineView.column(withIdentifier: .init("note")) if (noteIndex >= 0) { self.noteOutlineView.scrollColumnToVisible(noteIndex) // self.isRenameNoteOutline = true // self.renamePDFOutline = [rightSideController.noteOutlineView itemAtRow:rightSideController.noteOutlineView.clickedRow]; let viewS = self.noteOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as? KMNoteTableViewCell viewS!.isFold = false let targrtTextField = viewS?.noteContentLabel self.editNoteTextField = targrtTextField self.editNote = annotation targrtTextField?.delegate = self targrtTextField?.isEditable = true targrtTextField?.becomeFirstResponder() } } @IBAction func noteSortAction(_ sender: AnyObject?) { if (self.isAscendSort) { self.isAscendSort = false self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankPositive) self.noteSortButton.toolTip = KMLocalizedString("descending sort", nil) } else { self.isAscendSort = true self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankReverse) self.noteSortButton.toolTip = KMLocalizedString("ascending sort", nil) } KMDataManager.ud_set(self.isAscendSort, forKey: Self.Key.noteAscendSortKey) if self.noteSearchMode { self.reloadNoteForSearchMode() } else { self.reloadNoteForSortMode() } } @IBAction func noteSearchAction(_ sender: NSButton) { self.noteSearchField.isHidden = false self.noteTitleLabel.isHidden = true self.noteSearchButton.isHidden = true self.noteDoneButton.isHidden = false self.noteFilterButton.isHidden = true self.noteMoreButton.isHidden = true self.noteFilterButtonHoverView.isHidden = true self.noteSearchField.becomeFirstResponder() } @IBAction func noteFilterAction(_ sender: AnyObject?) { let button = sender as? NSButton if self.noteFilterSelected == false { if KMAppearance.isDarkMode() { self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(white: 1, alpha: 0.05).cgColor } else { self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(white: 0, alpha: 0.05).cgColor } } let menu = NSMenu() let filterViewController = KMNoteOutlineFilterViewController() filterViewController.listView = self.listView filterViewController.view.layer?.backgroundColor = .clear filterViewController.hoverView = self.noteFilterButtonHoverView var states: [CPDFAnnotationState] = [] for anno in self.allAnnotations { if let reviewS = self.noteReplyHanddler.fetchReviewState(anno) { if states.contains(reviewS) == false { states.append(reviewS) } } else { if states.contains(.none) == false { states.append(.none) } } if let markS = self.noteReplyHanddler.fetchAnnoState(anno) { if states.contains(markS) == false { states.append(markS) } } else { if states.contains(.unMarked) == false { states.append(.unMarked) } } } filterViewController.updateStates(states: states) filterViewController.setNotesArray(self.allAnnotations as NSArray) filterViewController.applyFilterCallback = { [weak self] typeArr, colorArr, authorArr, isEmpty in menu.cancelTracking() if (isEmpty) { self?.filterButtonLayer?.isHidden = true self?.noteFilterButtonHoverView.layer?.backgroundColor = .clear self?.noteFilterSelected = false } else { self?.filterButtonLayer?.isHidden = false self?.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(hex: "#227AFF").withAlphaComponent(0.3).cgColor self?.noteFilterSelected = true } self?.reloadAnnotation() } filterViewController.cancelCallback = { isCancel in if (isCancel) { menu.cancelTracking() } } let item = menu.addItem(withTitle: "", action: nil, keyEquivalent: "") item.target = self item.representedObject = filterViewController item.view = filterViewController.view menu.popUp(positioning: nil, at: NSMakePoint(-130, 30), in: button) // let win = KMNoteOutlineFilterViewController_window(contentRect: .zero, styleMask: .borderless, backing: .buffered, defer: false) // win.center() // win.contentViewController = filterViewController // win.orderFront(nil) } func fetchNote(for index: Int) -> CPDFAnnotation? { return self.fetchAnnoModel(for: index)?.anno } func fetchAnnoModel(for index: Int) -> KMBotaAnnotationModel? { if self.noteSearchMode { // 搜索模式 return self.noteSearchArray.safe_element(for: index) as? KMBotaAnnotationModel } else { // 常规模式(非搜索) // let model = self.annoListModel?.datas.safe_element(for: index) let model = self.noteOutlineView.item(atRow: index) if let data = model as? KMBotaAnnotationSectionModel { return nil } if let data = model as? KMBotaAnnotationModel { return data } if let data = model as? KMBotaAnnotationFooterModel { // return data.annoModel } if let data = model as? KMBotaAnnotationReplyModel { // return data.annoModel } // return self.annoListModel?.datas.safe_element(for: index) as? KMBotaAnnotationModel return nil } } func fetchAnnoModel(for anno: CPDFAnnotation) -> KMBotaAnnotationModel? { if self.noteSearchMode { // 搜索模式 for model in self.noteSearchArray { guard let data = model as? KMBotaAnnotationModel else { continue } if anno.isEqual(to: data.anno) { return data } } } else { // 常规模式(非搜索) for model in self.annoListModel?.datas ?? [] { for item in model.items { if let data = item as? KMBotaAnnotationModel { if anno.isEqual(to: data.anno) { return data } } } } } return nil } @IBAction @objc func sortTypeAction(_ sender: NSMenuItem) { let item = sender let tag = item.tag if (item.state == .on) { item.state = .off } else { item.state = .on } if (tag == 0) { self.noteSortType = .page self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil) self.sortTypeBox.toolTip = KMLocalizedString("Page", nil) } else if (tag == 1) { self.noteSortType = .time self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil) self.sortTypeBox.toolTip = KMLocalizedString("Time", nil) } // KMDataManager.ud_set(self.noteSortType.rawValue, forKey: Self.Key.noteSortTypeKey) if let data = self.listView?.document?.documentURL.path { KMDataManager.udExtension_set(self.noteSortType.rawValue, forKey: Self.Key.noteSortTypeKey + data) } if self.noteSearchMode { self.reloadNoteForSearchMode() } else { self.reloadAnnotation() } } func showNoteEmptyView() { let view = self.noteOutlineView.enclosingScrollView?.documentView let viewFrame = view?.frame ?? .zero let emptyVcSize = self.leftSideEmptyVC.emptyAnnotationView.frame.size self.leftSideEmptyVC.emptyAnnotationView.frame = NSMakeRect((viewFrame.size.width-emptyVcSize.width)/2.0,(viewFrame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) self.leftSideEmptyVC.emptyAnnotationView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self.noteOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptyAnnotationView) self.leftSideEmptyVC.exportAnnotationBtn.isEnabled = false self.leftSideEmptyVC.deleteAnnotationBtn.isEnabled = false if (self.leftView.segmentedControl.selectedSegment == KMSelectedSegmentType.annotation.rawValue) { self.noteHeaderView.isHidden = true self.toolButtonBoxLayoutConstraint.constant = 40.0 } } func hideNoteEmptyView() { self.leftSideEmptyVC.emptyAnnotationView.removeFromSuperview() self.leftSideEmptyVC.exportAnnotationBtn.isEnabled = true self.leftSideEmptyVC.deleteAnnotationBtn.isEnabled = true if (self.leftView.segmentedControl.selectedSegment == 3) { self.noteHeaderView.isHidden = false self.toolButtonBoxLayoutConstraint.constant = 64.0 } } } // MARK: - Note extension KMLeftSideViewController { public func refreshUIForAddAnnotation(annos: [CPDFAnnotation]?, page: CPDFPage?) { let need = self._annoList_needRefreshUI(annos: annos) if need == false { return } self.updateThumbnail(at: Int(page?.pageIndex() ?? 0)) self.note_reloadDataIfNeed() } public func refreshUIForAnnoAttributeDidChange(_ anno: CPDFAnnotation?, attributes: [String : Any]?) { self.updateThumbnail(at: Int(anno?.page?.pageIndex() ?? 0)) let need = self._annoList_needRefreshUI(annos: anno != nil ? [anno!] : []) if need == false { return } if let data = anno { self.note_reloadDataForAnnoIfNeed(anno: data) } } public func annoList_refreshUIForDeleteAnnotations(annos: [CPDFAnnotation]?, page: CPDFPage?) { self.updateThumbnail(at: Int(page?.pageIndex() ?? 0)) if self.type.methodType != .Annotation { return } let need = self._annoList_needRefreshUI(annos: annos) if need == false { return } for anno in annos ?? [] { if let model = self.fetchAnnoModel(for: anno) { self.noteSearchArray.removeObject(model) // self.annoListModel?.datas.removeObject(model) model.sectionModel?.items.removeObject(model) if let footer = model.footerModel { model.sectionModel?.items.removeObject(footer) } if let cnt = model.sectionModel?.items.count, cnt == 0 { self.annoListModel?.datas.removeObject(model.sectionModel!) } if self.allAnnotations.contains(anno) { self.allAnnotations.removeObject(anno) } } } if self.dataUpdating == false { self.note_refrshUIIfNeed() } } func note_refrshUIIfNeed() { if self.type.methodType != .Annotation { return } Task { @MainActor in self.noteOutlineView.reloadData() } } func note_reloadDataIfNeed() { if self.type.methodType != .Annotation { return } self.reloadAnnotation() } func note_reloadDataForAnnoIfNeed(anno: CPDFAnnotation) { if self.type.methodType != .Annotation { return } if anno is CPDFLineAnnotation || anno is CPDFSquareAnnotation || anno is CPDFCircleAnnotation || anno is CPDFInkAnnotation { // 形状注释 + Ink 需要显示框住的内容【刷新】 for item in self.annoListModel?.datas ?? [] { for itemM in item.items { if anno.isEqual(to: itemM.anno) { self.noteOutlineView.reloadItem(itemM) break } } } } else { for item in self.annoListModel?.datas ?? [] { for itemM in item.items { if anno.isEqual(to: itemM.anno) { self.noteOutlineView.reloadItem(itemM) break } } } } } func reloadAnnotation() { if self.listView != nil { let filterKey = self.pdfDocument()?.documentURL.path ?? "" let typeArr: [String] = KMBotaTools.noteFilterAnnoTypes(key: filterKey) let colorArr: [Any] = KMBotaTools.noteFilterColors(key: filterKey) let authorArr: [Any] = KMBotaTools.noteFilterAuthors(key: filterKey) var stateArr: [NSNumber] = KMBotaTools.noteFilterStates(key: filterKey) if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 && stateArr.count == 0 { // self.filtrateButton.image = NSImage(named: "KMImageNameAnnotationsFiltrate") self.filterButtonLayer?.isHidden = true self.noteFilterButtonHoverView.layer?.backgroundColor = .clear self.noteFilterSelected = false } else { // self.filtrateButton.image = NSImage(named: "icon_annotation_screening_select")self.filterButtonLayer?.isHidden = true self.filterButtonLayer?.isHidden = false self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(hex: "#227AFF").withAlphaComponent(0.3).cgColor self.noteFilterSelected = true } var hasMark = false var hasReview = false for data in stateArr { let state = data.intValue if state == CPDFAnnotationState.marked.rawValue || state == CPDFAnnotationState.unMarked.rawValue { hasMark = true } if state == CPDFAnnotationState.none.rawValue || state == CPDFAnnotationState.accepted.rawValue || state == CPDFAnnotationState.rejected.rawValue || state == CPDFAnnotationState.canceled.rawValue || state == CPDFAnnotationState.completed.rawValue { hasReview = true } } if hasMark == false { stateArr.append(NSNumber(value: CPDFAnnotationState.marked.rawValue)) stateArr.append(NSNumber(value: CPDFAnnotationState.unMarked.rawValue)) } if hasReview == false { stateArr.append(NSNumber(value: CPDFAnnotationState.none.rawValue)) stateArr.append(NSNumber(value: CPDFAnnotationState.accepted.rawValue)) stateArr.append(NSNumber(value: CPDFAnnotationState.rejected.rawValue)) stateArr.append(NSNumber(value: CPDFAnnotationState.canceled.rawValue)) stateArr.append(NSNumber(value: CPDFAnnotationState.completed.rawValue)) } var annotationArray: [CPDFAnnotation] = [] var allAnnotation: [CPDFAnnotation] = [] for i in 0 ..< self.pageCount() { let page = self.pdfDocument()?.page(at: UInt(i)) var annos: [CPDFAnnotation] = [] // 处理过滤 let types = ["Highlight","Underline","Strikeout","Squiggly","Freehand","FreeText","Note","Square","Circle","Line","Stamp","Arrow","Image","Redact","Sign"/*, "table"*/,"Polyline","Polygon"] if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 && stateArr.count == 0 { annos = KMOCToolClass.filterAnnotation(annotations: page?.annotations ?? [],types: types) as? [CPDFAnnotation] ?? [] annotationArray += annos } else { var filterAnnos: [CPDFAnnotation] = page?.annotations ?? [] let allAnnos = KMOCToolClass.filterAnnotation(annotations: filterAnnos,types: types) as? [CPDFAnnotation] ?? [] annotationArray += allAnnos if typeArr.count > 0 { var theTypes = typeArr if typeArr.contains(CPDFAnnotation.kType.measureArrow) && typeArr.contains(CPDFAnnotation.kType.arrow) == false { theTypes.append(CPDFAnnotation.kType.arrow) } filterAnnos = (KMOCToolClass.filterAnnotation(annotations: filterAnnos, types: theTypes) as? [CPDFAnnotation]) ?? [] } if (colorArr.count > 0) { filterAnnos = (KMOCToolClass.filterAnnotation(annotations: filterAnnos,colors: colorArr) as? [CPDFAnnotation]) ?? [] } if (authorArr.count > 0) { filterAnnos = (KMOCToolClass.filterAnnotation(annotations: filterAnnos,authors: authorArr) as? [CPDFAnnotation]) ?? [] } if typeArr.isEmpty == false { if typeArr.contains(CPDFAnnotation.kType.measureArrow) { if typeArr.contains(CPDFAnnotation.kType.arrow) == false { for anno in filterAnnos { if let data = anno as? CPDFLineAnnotation, data.type == CPDFAnnotation.kType.arrow && data.isMeasure == false { filterAnnos.removeObject(anno) } } } } else { for anno in filterAnnos { if let data = anno as? CPDFLineAnnotation, data.isMeasure { filterAnnos.removeObject(anno) } } } } if stateArr.count > 0 { for anno in filterAnnos { let markState = self.noteReplyHanddler.fetchAnnoState(anno) ?? .unMarked let reviewState = self.noteReplyHanddler.fetchReviewState(anno) ?? .none if stateArr.contains(NSNumber(value: markState.rawValue)) == false || stateArr.contains(NSNumber(value: reviewState.rawValue)) == false { filterAnnos.removeObject(anno) } } } annos = filterAnnos } //添加签名注释 for annotation in page?.annotations ?? [] { if annotation.isKind(of: CPDFSignatureAnnotation.self) { annos.append(annotation) annotationArray.append(annotation) } } for annotation in annos { if annotation.isKind(of: KMTableAnnotation.self) { annos.removeObject(annotation) if annotationArray.contains(annotation) { annotationArray.removeObject(annotation) } } else if annotation.annotationShouldDisplay() == false { annos.removeObject(annotation) if annotationArray.contains(annotation) { annotationArray.removeObject(annotation) } } else if annotation.isKind(of: CPDFLinkAnnotation.self) { annos.removeObject(annotation) if annotationArray.contains(annotation) { annotationArray.removeObject(annotation) } } else if annotation.isForm() { annos.removeObject(annotation) if annotationArray.contains(annotation) { annotationArray.removeObject(annotation) } } } // 添加刷选后的注释 allAnnotation += annos //添加所有annotation 用于筛选 // annotationArray += (page?.annotations ?? []) // annotationArray += annos } // 处理排序 if self.noteSortType == .page { /// 排序(升序) if self.isAscendSort { allAnnotation.sort { let idx0 = $0.page?.pageIndex() ?? 0 let idx1 = $1.page?.pageIndex() ?? 0 return idx0 <= idx1 } } else { allAnnotation.sort { let idx0 = $0.page?.pageIndex() ?? 0 let idx1 = $1.page?.pageIndex() ?? 0 return idx0 > idx1 } } } else if self.noteSortType == .time { /// 排序(升序) if self.isAscendSort { allAnnotation.sort { if $0.modificationDate() == nil { return false } if $1.modificationDate() == nil { return false } return $0.modificationDate() <= $1.modificationDate() } } else { allAnnotation.sort { if $0.modificationDate() == nil { return false } if $1.modificationDate() == nil { return false } return $0.modificationDate() > $1.modificationDate() } } } // 数据模型化 let model = KMAnnotationListModel() var datas: [KMBotaAnnotationModel] = [] var prePageIdx: Int = NSNotFound var preDate: Date? var secM: KMBotaAnnotationSectionModel? for anno in allAnnotation { let item = KMBotaAnnotationModel() item.anno = anno item.showPage = self.annoListIsShowPage() item.showTime = self.annoListIsShowTime() item.showAuthor = self.annoListIsShowAnther() // datas.append(item) if self.noteSortType == .page { let pageIdx = Int(anno.pageIndex()) if pageIdx != prePageIdx { // 不是同一个页面 secM = KMBotaAnnotationSectionModel() model.datas.append(secM!) } secM?.items.append(item) prePageIdx = Int(anno.pageIndex()) } else { // time let date = anno.modificationDate() if let same = date?.isSameDay(other: preDate), same == false { // 不是同一天 secM = KMBotaAnnotationSectionModel() model.datas.append(secM!) } secM?.items.append(item) preDate = date } item.sectionModel = secM let replyAnnos = self.noteReplyHanddler.fetchReplyAnnotations(anno) ?? [] for replyAnno in replyAnnos { let replyM = KMBotaAnnotationReplyModel() replyM.anno = anno replyM.replyAnno = replyAnno // secM?.items.append(replyM) replyM.annoModel = item item.replyAnnos.append(replyM) } let footerI = KMBotaAnnotationFooterModel() footerI.anno = anno secM?.items.append(footerI) item.footerModel = footerI footerI.annoModel = item } // model.datas = datas self.annoListModel = model // 转换对象,用于数据显示 self.allAnnotations = annotationArray self.noteFilterButton.isEnabled = self.allAnnotations.count >= 1 } self.note_refrshUIIfNeed() } func reloadNoteForSearchMode() { if self.noteSearchMode == false { return } // 处理排序 if self.noteSortType == .page { if self.isAscendSort { /// 排序(升序) self.noteSearchArray.sort { let idx0 = $0.anno?.page?.pageIndex() ?? 0 let idx1 = $1.anno?.page?.pageIndex() ?? 0 return idx0 <= idx1 } } else { self.noteSearchArray.sort { let idx0 = $0.anno?.page?.pageIndex() ?? 0 let idx1 = $1.anno?.page?.pageIndex() ?? 0 return idx0 > idx1 } } } else if self.noteSortType == .time { if self.isAscendSort { /// 排序(升序) self.noteSearchArray.sort { if $0.anno?.modificationDate() == nil { return false } if $1.anno?.modificationDate() == nil { return false } return $0.anno!.modificationDate() <= $1.anno!.modificationDate() } } else { self.noteSearchArray.sort { if $0.anno?.modificationDate() == nil { return false } if $1.anno?.modificationDate() == nil { return false } return $0.anno!.modificationDate() > $1.anno!.modificationDate() } } } self.note_refrshUIIfNeed() } func reloadNoteForSortMode() { // 处理排序 var models: [KMBotaAnnotationModel] = [] for selM in self.annoListModel?.datas ?? [] { for model in selM.items { guard let annoModel = model as? KMBotaAnnotationModel else { continue } models.append(annoModel) } } if self.noteSortType == .page { if self.isAscendSort { /// 排序(升序) models.sort { let idx0 = $0.anno?.page?.pageIndex() ?? 0 let idx1 = $1.anno?.page?.pageIndex() ?? 0 return idx0 <= idx1 } } else { models.sort { let idx0 = $0.anno?.page?.pageIndex() ?? 0 let idx1 = $1.anno?.page?.pageIndex() ?? 0 return idx0 > idx1 } } } else if self.noteSortType == .time { if self.isAscendSort { /// 排序(升序) models.sort { if $0.anno?.modificationDate() == nil { return false } if $1.anno?.modificationDate() == nil { return false } return $0.anno!.modificationDate() <= $1.anno!.modificationDate() } } else { models.sort { if $0.anno?.modificationDate() == nil { return false } if $1.anno?.modificationDate() == nil { return false } return $0.anno!.modificationDate() > $1.anno!.modificationDate() } } } // 数据模型\化 let listModel = KMAnnotationListModel() var prePageIdx: Int = NSNotFound var preDate: Date? var secM: KMBotaAnnotationSectionModel? for itemM in models { guard let anno = itemM.anno else { continue } if self.noteSortType == .page { let pageIdx = Int(anno.pageIndex()) if pageIdx != prePageIdx { // 不是同一个页面 secM = KMBotaAnnotationSectionModel() listModel.datas.append(secM!) secM?.isExpand = itemM.sectionModel?.isExpand ?? true } secM?.items.append(itemM) prePageIdx = Int(anno.pageIndex()) } else { // time let date = anno.modificationDate() if let same = date?.isSameDay(other: preDate), same == false { // 不是同一天 secM = KMBotaAnnotationSectionModel() listModel.datas.append(secM!) secM?.isExpand = itemM.sectionModel?.isExpand ?? true } secM?.items.append(itemM) preDate = date } var tmpSelM = itemM.sectionModel itemM.sectionModel = secM let footerI = KMBotaAnnotationFooterModel() footerI.anno = anno secM?.items.append(footerI) footerI.isExpand = itemM.footerModel?.isExpand ?? false itemM.footerModel = footerI footerI.annoModel = itemM } self.annoListModel = listModel self.note_refrshUIIfNeed() } // 搜索 Action func updateNoteFilterPredicate() { var stringValue = self.noteSearchField.stringValue // 清空数据 self.noteSearchArray.removeAll() if stringValue.isEmpty { for model in self.annoListModel?.datas ?? [] { for item in model.items { guard let _ = item.anno else { continue } guard let data = item as? KMBotaAnnotationModel else { continue } self.noteSearchArray.append(item) } } } else { // 忽略大小写 let caseInsensite = self.caseInsensitiveNoteSearch if caseInsensite { stringValue = stringValue.lowercased() } for model in self.annoListModel?.datas ?? [] { for item in model.items { guard let note = item.anno else { continue } var noteString = "" if let anno = note as? CPDFMarkupAnnotation { noteString = anno.markupContent() } else { noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: note) } if caseInsensite { noteString = noteString.lowercased() } guard let data = item as? KMBotaAnnotationModel else { continue } if noteString.contains(stringValue) { self.noteSearchArray.append(item) } } } } self.note_refrshUIIfNeed() } @objc func selectSelectedNote(_ sender: AnyObject?) { if self.hideNotes() == false { let selectedNotes = self.selectedNotes() if selectedNotes.count == 1 { let annotation = selectedNotes.last! self.listView?.go(to: annotation.bounds, on: annotation.page, animated: true) // [pdfView scrollAnnotationToVisible:annotation]; // [pdfView setActiveAnnotation:annotation]; self.listView?.updateActiveAnnotations([annotation]) self.listView?.setNeedsDisplayAnnotationViewForVisiblePages() if annotation is CPDFPolygonAnnotation || annotation is CPDFPolylineAnnotation { self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: annotation) } else if let anno = annotation as? CPDFLineAnnotation { if anno.isMeasure { self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: annotation) } } // } } // NSInteger column = [sender clickedColumn]; // if (column != -1) { // NSString *colID = [[[sender tableColumns] objectAtIndex:column] identifier]; // // if ([colID isEqualToString:@"color"]){ // for (PDFAnnotation *annotation in self.pdfView.activeAnnotations) { // if (![annotation isKindOfClass:[PDFAnnotationChoiceWidget class]] && // ![annotation isKindOfClass:[PDFAnnotationButtonWidget class]] && // ![annotation isKindOfClass:[PDFAnnotationTextWidget class]]) { // [[NSColorPanel sharedColorPanel] orderFront:nil]; // break; // } // // } // } // } } } func selectedNotes() -> [CPDFAnnotation] { var selectedNotes: [CPDFAnnotation] = [] let rowIndexes = self.noteOutlineView.selectedRowIndexes for row in rowIndexes { let item = self.noteOutlineView.item(atRow: row) if item is KMBotaAnnotationModel { if let anno = (item as! KMBotaAnnotationModel).anno { // if anno.type == nil { // item = [(SKNoteText *)item note]; // } if selectedNotes.contains(anno) == false { selectedNotes.append(anno) } } } } return selectedNotes } func clearAnnotationFilterData() { if let _key = self.pdfDocument()?.documentURL?.path { let userDefaults = UserDefaults.standard let typeData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false) userDefaults.set(typeData, forKey: NoteFilterVC.filterSelectTypeKey + _key) let colorData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false) userDefaults.set(colorData, forKey: NoteFilterVC.filterSelectColorKey + _key) let authorData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false) userDefaults.set(authorData, forKey: NoteFilterVC.filterSelectAuthorKey + _key) userDefaults.synchronize() } } private func _annoList_needRefreshUI(annos: [CPDFAnnotation]?) -> Bool { guard let data = annos else { return false } var need = false for anno in data { // if anno.isKind(of: KMTableAnnotation.self) == false { // return true // } if anno.isForm() { continue } if anno is CPDFLinkAnnotation { continue } need = true } return need } }