// // KMNOutlineController.swift // PDF Reader Pro // // Created by User-Tangchao on 2024/11/12. // import Cocoa import KMComponentLibrary class KMNOutlineController: KMNBaseViewController { @IBOutlet weak var outlineView: NSView! @IBOutlet weak var outlineMoreButton: NSButton! @IBOutlet weak var outlineAddButton: NSButton! @IBOutlet weak var outlineSearchButton: NSButton! @IBOutlet weak var outlineLabel: NSTextField! @IBOutlet weak var outlineSearchField: KMLeftSideViewSearchField! @IBOutlet weak var outlineDoneButton: NSButton! @IBOutlet var tocOutlineView: KMTocOutlineView! var handdler = KMNOutlineHanddler() var isSearchOutlineMode = false convenience init() { self.init(nibName: "KMNOutlineController", bundle: nil) } override func viewDidLoad() { super.viewDidLoad() // Do view setup here. if let data = tocOutlineView.enclosingScrollView { view.addSubview(data) data.frame = view.bounds data.autoresizingMask = [.width, .height] } initSubViews() initDefalutValue() } override func updateUILanguage() { super.updateUILanguage() } override func updateUIThemeColor() { super.updateUIThemeColor() KMMainThreadExecute { self.view.wantsLayer = true let color = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle") self.view.layer?.backgroundColor = color.cgColor } } func initSubViews() { // self.outlineSearchField.delegate = self // self.outlineAddButton.target = self // self.outlineAddButton.action = #selector(outlineContextMenuItemClicked_AddEntry) // // self.outlineMoreButton.target = self // self.outlineMoreButton.tag = 302 // self.outlineMoreButton.action = #selector(leftSideViewMoreButtonAction) // // self.outlineDoneButton.target = self // self.outlineDoneButton.tag = 310 // self.outlineDoneButton.action = #selector(leftSideViewDoneButtonAction) // // let menuOutline = NSMenu() // _ = menuOutline.addItem(title: KMLocalizedString("Ignore Case"), action: #selector(toggleOutlineCaseInsensitiveSearch), target: self) // (self.outlineSearchField.cell as? NSSearchFieldCell)?.searchMenuTemplate = menuOutline // self.outlineSearchField.target = self // self.tocOutlineView.menu = NSMenu() // self.tocOutlineView.menu?.delegate = self self.tocOutlineView.delegate = self self.tocOutlineView.dataSource = self self.tocOutlineView.botaDelegate = self self.tocOutlineView.botaDataSource = self self.tocOutlineView.tocDelegate = self self.tocOutlineView.registerForDraggedTypes([.localDraggedTypes]) self.tocOutlineView.target = self self.tocOutlineView.doubleAction = #selector(outlineContextMenuItemClicked_Rename) } func initDefalutValue() { // self.outlineView.wantsLayer = true // self.outlineView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor // // self.outlineLabel.stringValue = KMLocalizedString("Outline"); // self.outlineLabel.textColor = KMAppearance.Layout.h0Color() // self.outlineAddButton.toolTip = KMLocalizedString("Add Item") // // self.outlineSearchButton.toolTip = KMLocalizedString("Search") // // self.outlineDoneButton.title = KMLocalizedString("Done"); // self.outlineDoneButton.toolTip = KMLocalizedString("Done"); // self.outlineDoneButton.setTitleColor(KMAppearance.Layout.w0Color()) // self.outlineDoneButton.wantsLayer = true // self.outlineDoneButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor // self.outlineDoneButton.layer?.cornerRadius = 4.0 // // self.outlineSearchField.wantsLayer = true // self.outlineSearchField.backgroundColor = KMAppearance.Layout.l_1Color() // self.outlineSearchField.layer?.backgroundColor = KMAppearance.Layout.l_1Color().cgColor // self.outlineSearchField.layer?.borderWidth = 1.0 // self.outlineSearchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor // // self.outlineDoneButton.isHidden = true // self.outlineSearchField.isHidden = true // // (self.outlineSearchField.cell as? NSSearchFieldCell)?.placeholderString = KMLocalizedString("Search Outline") // self.tocOutlineView.backgroundColor = KMAppearance.Layout.l0Color() tocOutlineView.backgroundColor = .orange tocOutlineView.enclosingScrollView?.drawsBackground = false tocOutlineView.enclosingScrollView?.backgroundColor = .blue tocOutlineView.enclosingScrollView?.wantsLayer = true tocOutlineView.enclosingScrollView?.layer?.backgroundColor = NSColor.red.cgColor self.tocOutlineView.autoresizesOutlineColumn = false self.tocOutlineView.allowsMultipleSelection = true self.tocOutlineView.allowsEmptySelection = true self.tocOutlineView.focusRingType = .none self.tocOutlineView.autoresizesSubviews = true } func updateOutlineSelection() { // if self.outlineRoot() == nil || self.updatingOutlineSelection { // return // } // self.updatingOutlineSelection = true // let numRows = self.tocOutlineView.numberOfRows // let currentPage = self.currentPage() // var flagIdx = NSNotFound // for i in 0 ..< numRows { // guard let ol = self.tocOutlineView.item(atRow: i) as? CPDFOutline else { // continue // } // guard let page = ol.km_page else { // continue // } // if page.isEqual(to: currentPage) { // flagIdx = i // break // } // } // // if flagIdx != NSNotFound { // self.tocOutlineView.km_safe_selectRowIndexes(.init(integer: flagIdx), byExtendingSelection: false) // self.tocOutlineView.scrollRowToVisible(flagIdx) // } else { // self.tocOutlineView.deselectRow(self.tocOutlineView.selectedRow) // } // self.updatingOutlineSelection = false } func newAddOutlineEntryEditingMode(_ index: Int) { // self.renamePDFOutline = self.tocOutlineView.item(atRow: self.tocOutlineView.selectedRow) as? CPDFOutline // let row = self.tocOutlineView.selectedRow // let viewS = self.tocOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) // let targrtTextField = viewS?.subviews.first as? NSTextField // self.renamePDFOutlineTextField = targrtTextField // targrtTextField?.delegate = self // targrtTextField?.isEditable = true // targrtTextField?.becomeFirstResponder() } func selectedRowIndexes() -> IndexSet { var selectedIndexes = self.tocOutlineView.selectedRowIndexes let clickedRow = self.tocOutlineView.clickedRow if clickedRow != -1 && selectedIndexes.contains(clickedRow) == false { let indexes = IndexSet(integer: clickedRow) selectedIndexes = indexes } return selectedIndexes } func editOutlineUI(_ editVC: KMOutlineEditViewController) { guard let ol = editVC.outline else { return } if editVC.pageButton.state == .on { let numberString = editVC.outlineTargetPageIndexTextField.stringValue let idx = Int(numberString) ?? 1 let newPage = editVC.pdfView?.document.page(at: UInt(idx-1)) if let data = newPage?.pageIndex(), data != editVC.originalPageIndex { //新page不存在 if (newPage == nil) { } else { let pageSize = newPage?.bounds(for: .cropBox).size ?? .zero if let dest = CPDFDestination(page: newPage, at: NSMakePoint(pageSize.width, pageSize.height)) { self.changePDFOutlineDestination(dest, PDFoutline: ol) } } } } else if editVC.urlButton.state == .on { var urlString = editVC.outlineURLTextField.stringValue let tLowerUrl = urlString.lowercased() if tLowerUrl.hasPrefix(kHTTPSPrefix) == false && tLowerUrl.hasPrefix(kFTPPrefix) == false && tLowerUrl.hasPrefix(kHTTPPrefix) == false && urlString.isEmpty == false { urlString = "\(kHTTPPrefix)\(urlString)" } if let act = CPDFURLAction(url: urlString) { if editVC.originalURLString != editVC.outlineURLTextField.stringValue { self.changePDFAction(act, PDFOutline: ol) } } } else if editVC.mailButton.state == .on { var mailString = editVC.mailAddressTextField.stringValue let tLowerStr = mailString.lowercased() if tLowerStr.hasPrefix(kEmailPrefix) == false { mailString = "\(kEmailPrefix)\(mailString)" } if var urlAction = CPDFURLAction(url: mailString) { if urlAction.url() == nil { urlAction = CPDFURLAction(url: kEmailPrefix) } if mailString != editVC.originalURLString { self.changePDFAction(urlAction, PDFOutline: ol) } } } if editVC.outlineNameTextView.string != editVC.originalLabel { self.renamePDFOutline(ol, label: editVC.outlineNameTextView.string) } } @objc func outlineContextMenuItemClicked_AddEntry(_ sender: AnyObject?) { // let ris = self.selectedRowIndexes() // if ris.isEmpty { // var rootOL = self.outlineRoot() // if rootOL == nil { // rootOL = self.setNewOutlineRoot() // } // // let ol = CPDFOutline() // ol.label = String(format: "%@ %ld", KMLocalizedString("Page"), self.currentPageIndex()+1) // ol.destination = self.currentDestination() // self.addoutline(parent: rootOL, addOutline: ol, index: Int(rootOL?.numberOfChildren ?? 0), needExpand: false) // return // } // // if let row = ris.last { // let item = self.tocOutlineView.item(atRow: row) as? CPDFOutline // let ol = CPDFOutline() // ol.label = String(format: "%@ %ld", KMLocalizedString("Page"), self.currentPageIndex()+1) // ol.destination = self.currentDestination() // let idx = Int(item?.index ?? 0) + 1 // self.addoutline(parent: item?.parent, addOutline: ol, index: idx, needExpand: false) // self.tocOutlineView.scrollRowToVisible(idx) // self.tocOutlineView.deselectRow(idx) // } } @IBAction func outlineNormalSearchButtonAction(_ sender: NSButton) { self.outlineSearchField.isHidden = false self.outlineDoneButton.isHidden = false self.outlineLabel.isHidden = true self.outlineSearchButton.isHidden = true self.outlineMoreButton.isHidden = true self.outlineAddButton.isHidden = true self.outlineSearchField.becomeFirstResponder() } @IBAction func toc_expandAllComments(_ sender: AnyObject?) { // if (self.tocType == .unfold) { // return // } // self.tocType = .unfold // self.tocOutlineView.expandItem(nil, expandChildren: true) } @IBAction func toc_foldAllComments(_ sender: AnyObject?) { // if (self.tocType == .fold) { // return // } // self.tocType = .fold // self.tocOutlineView.collapseItem(nil, collapseChildren: true) } @objc func leftSideEmptyAnnotationClick_DeleteOutline(_ sender: AnyObject?) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "" alert.informativeText = KMLocalizedString("This will permanently remove all outlines. Are you sure to continue?") alert.addButton(withTitle: KMLocalizedString("Yes")) alert.addButton(withTitle: KMLocalizedString("No")) let response = alert.runModal() if response == .alertFirstButtonReturn { // if let item = self.outlineRoot() { // self.removeAllOutline(item) // } } } func showSearchOutlineBlankState(_ toShowState: Bool) { // if (toShowState) { // let documentViewFrame = self.tocOutlineView.enclosingScrollView?.documentView?.frame ?? .zero // let width = self.leftSideEmptyVC.outlineSearchView.bounds.size.width // let height = self.leftSideEmptyVC.outlineSearchView.bounds.size.height // self.leftSideEmptyVC.outlineSearchView.frame = NSMakeRect((documentViewFrame.size.width - width)/2.0, (documentViewFrame.size.height - height)/2.0, width, height) // self.tocOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.outlineSearchView) // self.leftSideEmptyVC.outlineSearchView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] // } else { // self.leftSideEmptyVC.outlineSearchView.removeFromSuperview() // } } func removeAllOutline(_ outline: CPDFOutline) { // _ = self.setNewOutlineRoot() // // for i in 0 ..< self.pageCount() { // self.pdfDocument()?.removeBookmark(forPageIndex: UInt(i)) // } // self.layoutDocumentView() // DispatchQueue.main.async { // } } func updateSelectRowHeight() { guard let ol = self.tocOutlineView.selectedItem() as? CPDFOutline else { return } let attriString = NSMutableAttributedString() let attri = [NSAttributedString.Key.foregroundColor : KMAppearance.Layout.h0Color()] attriString.append(.init(string: ol.label ?? "", attributes: attri)) let row = self.tocOutlineView.selectedRow let column = self.tocOutlineView.tableColumn(withIdentifier: kLabelColumnId) let cell = column?.dataCell(forRow: row) (cell as? NSCell)?.objectValue = attriString let w = self.view.frame.size.width-86 let num = self.getNum(ol) let gap = self.tocOutlineView.indentationPerLevel var rowH = ((cell as? NSCell)?.cellSize(forBounds: NSMakeRect(0, 0, w - (num > 0 ? 16 : 0) - gap * num.cgFloat, CGFloat.greatestFiniteMagnitude)).height) ?? 0 rowH = fmax(rowH, self.tocOutlineView.rowHeight) + 25 let view = self.tocOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) let frame = view?.frame ?? .zero view?.frame = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, rowH) } func getNum(_ ol: CPDFOutline?) -> Int { var num = 0 var outline = ol?.parent repeat { outline = outline?.parent if outline != nil { num += 1 } } while outline != nil return num } func addOutlineAfter(_ ol: CPDFOutline) { // if self.leftView.segmentedControl.selectedSegment == 2 { // // } else { // self.leftView.segmentedControl.selectedSegment = 2 // } // // Task { @MainActor in // // let idx = self.tocOutlineView.row(forItem: ol) // self.updatingOutlineSelection = true // self.tocOutlineView.km_selectItem(ol, byExtendingSelection: false) // self.updatingOutlineSelection = false // self.newAddOutlineEntryEditingMode(idx) // } // // DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // self.tocOutlineView.scrollRowToVisible(self.tocOutlineView.row(forItem: ol)) // } } func removeOutlineAfter(_ ol: CPDFOutline) { Task { @MainActor in } } func demoteOutlineAfter(_ ol: CPDFOutline) { Task { @MainActor in self.tocOutlineView.expandItem(ol.parent) self.tocOutlineView.km_selectItem(ol, byExtendingSelection: false) } } func promoteOutlineAfter(_ ol: CPDFOutline) { Task { @MainActor in self.tocOutlineView.km_selectItem(ol, byExtendingSelection: false) } } func showOutlineEmptyView() { // let view = self.tocOutlineView.enclosingScrollView?.documentView // let viewFrame = view?.frame ?? .zero // let emptyVcSize = self.leftSideEmptyVC.emptyOutlineView.frame.size // self.leftSideEmptyVC.emptyOutlineView.frame = NSMakeRect((viewFrame.size.width-emptyVcSize.width)/2.0,(viewFrame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) // self.leftSideEmptyVC.emptyOutlineView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] // self.tocOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptyOutlineView) // self.leftSideEmptyVC.deleteOutlineBtn.isEnabled = false } func hideOutlineEmptyView() { // self.leftSideEmptyVC.emptyOutlineView.removeFromSuperview() // self.leftSideEmptyVC.deleteOutlineBtn.isEnabled = true } func fetchOutlines(for ol: CPDFOutline, searchString: String) -> [CPDFOutline] { var ols: [CPDFOutline] = [] for i in 0 ..< ol.numberOfChildren { guard let child = ol.child(at: i) else { continue } // if self.hasContainString(searchString, rootOutline: child) { // ols.append(child) // } } return ols } @objc func goToSelectedOutlineItem(_ sender: AnyObject?) { // let cnt = self.tocOutlineView.selectedRowIndexes.count // if cnt == 0 || cnt > 1 { // return // } // guard let item = self.tocOutlineView.selectedItem() else { // return // } // if let bk = item as? CPDFBookmark { // self.listView?.go(toPageIndex: bk.pageIndex, animated: true) // } else if let ol = item as? CPDFOutline { // if let dest = ol.destination { // self.listView?.go(to: dest) // } else if let act = ol.action { // if !self.isFirst { // self.listView?.perform(act) // } // } // } // self.isFirst = false } public func refreshUIOfOutlineIfNeed() { // if self.type.methodType != .Outline { // return // } // Task { @MainActor in // // } } } // MARK: - Undo & Redo extension KMNOutlineController { @objc dynamic func changePDFOutlineDestination(_ destination: CPDFDestination?, PDFoutline outline: CPDFOutline) { // if let des = destination { // (self.listView?.undoManager?.prepare(withInvocationTarget: self) as AnyObject).changePDFOutlineDestination(outline.destination, PDFoutline: outline) // let newDes = CPDFDestination(document: des.document, pageIndex: des.pageIndex, at: des.point, zoom: des.zoom) // outline.destination = newDes // // self.tocOutlineView.reloadItem(outline) // } } @objc dynamic func changePDFAction(_ action: CPDFAction?, PDFOutline outline: CPDFOutline) { // (self.listView?.undoManager?.prepare(withInvocationTarget: self) as AnyObject).changePDFAction(outline.action, PDFOutline: outline) // outline.action = action } @objc dynamic func renamePDFOutline(_ outline: CPDFOutline, label: String) { // if (self.isRenameNoteOutline) { // if outline is CPDFAnnotation { //// if([outline isKindOfClass:[PDFAnnotation class]]) { //// PDFAnnotation *annotation = (PDFAnnotation *)outline; //// annotation.string = label; //// [rightSideController.noteOutlineView reloadData]; //// [rightSideController.noteOutlineView selectRowIndexes:[[[NSIndexSet alloc] initWithIndex:[rightSideController.noteOutlineView rowForItem:outline]] autorelease] byExtendingSelection:NO]; // } // } else { // (self.listView?.undoManager?.prepare(withInvocationTarget: self) as AnyObject).renamePDFOutline(outline, label: outline.label) // outline.label = label // // self.tocOutlineView.km_selectItem(outline, byExtendingSelection: false) // } } @objc dynamic func addoutline(parent parentOutline: CPDFOutline?, addOutline: CPDFOutline, index: Int, needExpand: Bool) { // var tempO: CPDFOutline? = addOutline // if addOutline.label != nil { // parentOutline?.insertChild(addOutline, at: UInt(index)) // } else { // let outline = parentOutline?.km_insertChild(label: String(format: "%@ %ld", KMLocalizedString("Page"), self.currentPageIndex()+1), dest: self.currentDestination(), at: index) // tempO = outline // } // // guard let outline = tempO else { // return // } // // (self.listView?.undoManager?.prepare(withInvocationTarget: self) as AnyObject).removeOutline(parent: parentOutline, removeOutline: outline, index: index, needExpand: needExpand) // self.view.window?.makeFirstResponder(nil) // // Task { @MainActor in // // // if (needExpand) { // self.tocOutlineView.expandItem(parentOutline) // } // let idx = self.tocOutlineView.row(forItem: outline) // self.tocOutlineView.selectRowIndexes(IndexSet(integer: idx), byExtendingSelection: false) // // self.newAddOutlineEntryEditingMode(index) // } // // DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // self.tocOutlineView.scrollRowToVisible(self.tocOutlineView.row(forItem: outline)) // } } @objc dynamic func removeOutline(parent parentOutline: CPDFOutline?, removeOutline: CPDFOutline, index: Int, needExpand: Bool) { // (self.listView?.undoManager?.prepare(withInvocationTarget: self) as AnyObject).addoutline(parent: parentOutline, addOutline: removeOutline, index: index, needExpand: true) // removeOutline.removeFromParent() // if (needExpand) { // self.tocOutlineView.expandItem(parentOutline) // } // Task { @MainActor in // // } } @objc dynamic func dragPDFOutline(_ outline: CPDFOutline?, toIndex index: Int, newParentOutline: CPDFOutline?) { // guard let doc = self.view.window?.windowController?.document as? NSDocument else { // NSSound.beep() // return // } // if outline == nil { // NSSound.beep() // return // } // (doc.undoManager?.prepare(withInvocationTarget: self) as AnyObject).dragPDFOutline(outline, toIndex: Int(outline!.index), newParentOutline: outline?.parent) // outline?.removeFromParent() // newParentOutline?.insertChild(outline, at: UInt(index)) // // self.tocOutlineView.expandItem(newParentOutline) // self.tocOutlineView.selectRowIndexes(IndexSet(integer: self.tocOutlineView.row(forItem: outline)), byExtendingSelection: false) } } // MARK: - Menu Actions extension KMNOutlineController { func outlineListMenu(_ menu: NSMenu) { _ = menu.addItem(withTitle: NSLocalizedString("Add Item", comment: ""), action: #selector(outlineContextMenuItemClicked_AddEntry), target: self, tag: KMOutlineViewMenuItemTag.addEntry.rawValue) _ = menu.addItem(withTitle: NSLocalizedString("Add Sub-Item", comment: ""), action: #selector(outlineContextMenuItemClicked_AddChildEntry), target: self, tag: KMOutlineViewMenuItemTag.addChild.rawValue) _ = menu.addItem(withTitle: NSLocalizedString("Add To A Higher Level", comment: ""), action: #selector(outlineContextMenuItemClicked_AddAuntEntry), target: self, tag: KMOutlineViewMenuItemTag.addAunt.rawValue) menu.addItem(.separator()) _ = menu.addItem(withTitle: NSLocalizedString("Delete", comment: ""), action: #selector(outlineContextMenuItemClicked_RemoveEntry), target: self, tag: KMOutlineViewMenuItemTag.remove.rawValue) menu.addItem(.separator()) _ = menu.addItem(withTitle: NSLocalizedString("Edit", comment: ""), action: #selector(outlineContextMenuItemClicked_Edit), target: self, tag: KMOutlineViewMenuItemTag.edit.rawValue) _ = menu.addItem(withTitle: NSLocalizedString("Change Destination", comment: ""), action: #selector(outlineContextMenuItemClicked_SetDestination), target: self, tag: KMOutlineViewMenuItemTag.setDestination.rawValue) _ = menu.addItem(withTitle: NSLocalizedString("Rename", comment: ""), action: #selector(outlineContextMenuItemClicked_Rename), target: self, tag: KMOutlineViewMenuItemTag.rename.rawValue) menu.addItem(.separator()) _ = menu.addItem(withTitle: NSLocalizedString("Promote", comment: ""), action: #selector(outlineContextMenuItemClicked_Promote), target: self, tag: KMOutlineViewMenuItemTag.promote.rawValue) _ = menu.addItem(withTitle: NSLocalizedString("Demote", comment: ""), action: #selector(outlineContextMenuItemClicked_Demote), target: self, tag: KMOutlineViewMenuItemTag.demote.rawValue) } func outlineMoreMenu(_ view: NSButton) { // let menu = NSMenu() // let expandAllItem = menu.addItem(title: KMLocalizedString("Expand All"), action: #selector(toc_expandAllComments), target: self) // expandAllItem?.representedObject = self.tocOutlineView // // let foldAllItem = menu.addItem(title: KMLocalizedString("Collapse All"), action: #selector(toc_foldAllComments), target: self) // foldAllItem?.representedObject = self.tocOutlineView // let rootOL = self.outlineRoot() // let childNum = rootOL?.numberOfChildren ?? 0 // // var num = 0 // for i in 0 ..< childNum { // let ol = rootOL?.child(at: i) // if self.tocOutlineView.isItemExpanded(ol) { // num += 1 // } // } // // self.tocType = .none // if childNum > 0 { // if num == 0 { // self.tocType = .fold // } else if num == childNum { // self.tocType = .unfold // } // } // // expandAllItem?.state = self.tocType == .unfold ? .on : .off // foldAllItem?.state = self.tocType == .fold ? .on : .off // // let removeEntryItem = menu.addItem(title: KMLocalizedString("Remove All Outlines"), action: #selector(leftSideEmptyAnnotationClick_DeleteOutline), target: self) // removeEntryItem?.representedObject = self.tocOutlineView // if let data = NSApp.currentEvent { // NSMenu.popUpContextMenu(menu, with: data, for: view) // } } func outlineListValidateMenuItem(_ menuItem: NSMenuItem) -> Bool { // if self.isLocked() { // return false // } let action = menuItem.action // if (action == #selector(leftSideEmptyAnnotationClick_DeleteOutline)) { // guard let rootOL = self.outlineRoot() else { // return false // } // let childNum = rootOL.numberOfChildren // if (self.isSearchOutlineMode) { // let ols = self.fetchOutlines(for: rootOL, searchString: self.outlineSearchField.stringValue) // return ols.count > 0 // } else { // return childNum > 0 // } // } else if action == #selector(toggleOutlineCaseInsensitiveSearch) { // menuItem.state = self.outlineIgnoreCaseFlag ? .on : .off // return true // } // // if (self.isSearchOutlineMode) { // if (action == #selector(outlineContextMenuItemClicked_AddEntry) || // action == #selector(outlineContextMenuItemClicked_AddChildEntry) || // action == #selector(outlineContextMenuItemClicked_AddAuntEntry) || // action == #selector(outlineContextMenuItemClicked_Edit) || // action == #selector(outlineContextMenuItemClicked_Rename) // ) { // return false // } // } if (self.tocOutlineView.selectedRowIndexes.count > 1) { if (menuItem.tag == KMOutlineViewMenuItemTag.remove.rawValue) { return true } return false } else if (self.tocOutlineView.selectedRowIndexes.count > 0) { if (action == #selector(outlineContextMenuItemClicked_AddChildEntry) || action == #selector(outlineContextMenuItemClicked_SetDestination)) { return true } } if (self.tocOutlineView.clickedRow == -1) { if (action == #selector(outlineContextMenuItemClicked_AddEntry)) { return true } else { return false } } else { let clickedOutline = self.tocOutlineView.item(atRow: self.tocOutlineView.clickedRow) as? CPDFOutline if (action == #selector(outlineContextMenuItemClicked_Demote)) { if let data = clickedOutline { // return self.pdfDocument()?.canDemote(outline: data) ?? false } return false } if (action == #selector(outlineContextMenuItemClicked_Promote)) { if let data = clickedOutline { // return self.pdfDocument()?.canPromote(outline: data) ?? false } else { return false } } if (action == #selector(outlineContextMenuItemClicked_AddAuntEntry)) { let parentOutLine = clickedOutline?.parent let grandparentOutLine = parentOutLine?.parent if (grandparentOutLine != nil) { return true } else { return false } } } return true } //添加子节点 @objc func outlineContextMenuItemClicked_AddChildEntry(_ sender: AnyObject?) { guard let row = self.selectedRowIndexes().last else { return } let item = self.tocOutlineView.item(atRow: row) as? CPDFOutline let ol = CPDFOutline() // ol.label = String(format: "%@ %ld", KMLocalizedString("Page"), "\(self.currentPageIndex() + 1)") // ol.destination = self.currentDestination() self.addoutline(parent: item, addOutline: ol, index: Int(item?.numberOfChildren ?? 0), needExpand: true) } //添加上一级节点 @objc func outlineContextMenuItemClicked_AddAuntEntry(_ sender: AnyObject?) { guard let row = self.selectedRowIndexes().last else { return } let item = self.tocOutlineView.item(atRow: row) as? CPDFOutline let parentOL = item?.parent if parentOL?.parent != nil { let ol = CPDFOutline() // ol.label = String(format: "%@ %ld", KMLocalizedString("Page"), "\(self.currentPageIndex() + 1)") // ol.destination = self.currentDestination() self.addoutline(parent: parentOL?.parent, addOutline: ol, index: Int(parentOL?.index ?? 0) + 1, needExpand: false) } } //移除节点 @objc func outlineContextMenuItemClicked_RemoveEntry(_ sender: AnyObject?) { for idx in self.selectedRowIndexes() { let item = self.tocOutlineView.item(atRow: idx) if let ol = item as? CPDFOutline { self.removeOutline(parent: ol.parent , removeOutline: ol, index: Int(ol.index), needExpand: false) } } } //重置目的 @objc func outlineContextMenuItemClicked_SetDestination(_ sender: AnyObject?) { guard let ol = self.tocOutlineView.clickedItem() as? CPDFOutline else { return } Task { let res = await KMAlertTool.runModel(style: .informational, message: KMLocalizedString("Are you sure you want to set the destination as the current location?"), buttons: [KMLocalizedString("Yes"), KMLocalizedString("No")]) if (res == .alertFirstButtonReturn) { // self.changePDFOutlineDestination(self.currentDestination(), PDFoutline: ol) let idx = self.tocOutlineView.row(forItem: ol) self.tocOutlineView.km_safe_selectRowIndexes(.init(integer: idx), byExtendingSelection: false) } } } //弹出菜单 @objc func outlineContextMenuItemClicked_Edit(_ sender: AnyObject?) { let ol: CPDFOutline? = self.tocOutlineView.km.clickedItem() guard let _ = ol?.km_page else { return } let popover = NSPopover() // popover.delegate = self // let vc = KMOutlineEditViewController(outline: ol, document: self.listView) if let cell = self.tocOutlineView.rowView(atRow: self.tocOutlineView.clickedRow, makeIfNecessary: true) { // popover.contentViewController = vc popover.animates = true popover.behavior = .transient popover.show(relativeTo: cell.bounds, of: cell, preferredEdge: .minX) } } //重命名 @objc func outlineContextMenuItemClicked_Rename(_ sender: AnyObject?) { if (self.tocOutlineView.clickedRow < 0) { return } // self.renamePDFOutline = self.tocOutlineView.km.clickedItem() // let cell = self.tocOutlineView.view(atColumn: 0, row: self.tocOutlineView.clickedRow, makeIfNecessary: true) // let tf = cell?.subviews.first as? NSTextField // self.renamePDFOutlineTextField = tf // tf?.delegate = self // tf?.isEditable = true // tf?.becomeFirstResponder() } //降级节点 @objc func outlineContextMenuItemClicked_Demote(_ sender: AnyObject?) { // self.delegate?.controller?(controller: self, itemClick: sender, itemKey: .demote, params: nil) } //升级节点 @objc func outlineContextMenuItemClicked_Promote(_ sender: AnyObject?) { // self.delegate?.controller?(controller: self, itemClick: sender, itemKey: .promote, params: nil) } @objc func toggleOutlineCaseInsensitiveSearch(_ sender: NSMenuItem) { // if (sender.state == .on) { // self.outlineIgnoreCaseFlag = false // } else { // self.outlineIgnoreCaseFlag = true // } // if (self.outlineSearchField.stringValue.isEmpty == false) { // // self.tocOutlineView.expandItem(nil, expandChildren: true) // } } } // MARK: - KMTocOutlineViewDelegate extension KMNOutlineController: KMTocOutlineViewDelegate { func outlineView(_ anOutlineView: NSOutlineView, highlightLevelForRow row: Int) -> Int { // if self.tocOutlineView.isEqual(to: anOutlineView) { // let numRows = anOutlineView.numberOfRows // if let outline = anOutlineView.item(atRow: row) as? CPDFOutline { // let firstPage = outline.km_pageIndex // var lastPage = self.pageCount() // if row + 1 < numRows { // lastPage = Int((anOutlineView.item(atRow: row + 1) as? CPDFOutline)?.km_pageIndex ?? UInt(NSNotFound)) // } // // NSRange range = NSMakeRange(firstPage, MAX(1LU, lastPage - firstPage)); // // NSUInteger i, iMax = [lastViewedPages count]; // // for (i = 0; i < iMax; i++) { // // if (NSLocationInRange((NSUInteger)[lastViewedPages pointerAtIndex:i], range)) // // return i; // // } // } // } return NSNotFound } func outlineView(_ anOutlineView: NSOutlineView, imageContextForItem item: Any?) -> AnyObject? { if anOutlineView.isEqual(to: self.tocOutlineView) { if item == nil { return true as AnyObject } if item is CPDFOutline { return (item as! CPDFOutline).destination } } return nil } } extension KMNOutlineController: KMNoteOutlineViewDelegate { func outlineView(_ anOutlineView: NSOutlineView, canResizeRowByItem item: AnyObject?) -> Bool? { // if anOutlineView.isEqual(to: self.noteOutlineView) { // return true // } return false } func outlineView(_ anOutlineView: NSOutlineView, setHeight newHeight: CGFloat, ofRowByItem item: AnyObject?) { // [rowHeights setFloat:newHeight forKey:item]; } func outlineView(_ anOutlineView: NSOutlineView, didChangeHiddenOfTableColumn aTableColumn: NSTableColumn) { // if (mwcFlags.autoResizeNoteRows && // [ov isEqual:rightSideController.noteOutlineView] && // [[tableColumn identifier] isEqualToString:NOTE_COLUMNID]) { // [rowHeights removeAllFloats]; // [rightSideController.noteOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [rightSideController.noteOutlineView numberOfRows])]]; // } } func outlineViewCommandKeyPressedDuringNavigation(_ anOutlineView: NSOutlineView) { // PDFAnnotation *annotation = [[self selectedNotes] lastObject]; // if (annotation) { // [pdfView scrollAnnotationToVisible:annotation]; // [pdfView setActiveAnnotation:annotation]; // } } } // MARK: - NSOutlineViewDelegate, NSOutlineViewDataSource extension KMNOutlineController: NSOutlineViewDelegate, NSOutlineViewDataSource { func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { if outlineView.isEqual(to: self.tocOutlineView) { if self.handdler.isLocked() { return 0 } if item == nil { // 第一层 guard let rootOL = self.handdler.outlineRoot() else { self.showOutlineEmptyView() return 0 } // 搜索按钮 self.outlineSearchButton.isEnabled = rootOL.numberOfChildren > 0 if self.isSearchOutlineMode { // 是否为搜索模块 // if self.hasContainString(self.outlineSearchField.stringValue, rootOutline: rootOL) == false { // self.showSearchOutlineBlankState(true) // return 0 // } self.showSearchOutlineBlankState(false) let ols = self.fetchOutlines(for: rootOL, searchString: self.outlineSearchField.stringValue) return ols.count } if rootOL.numberOfChildren == 0 { // 没有数据 self.showOutlineEmptyView() return 0 } // 有数据 self.hideOutlineEmptyView() return Int(rootOL.numberOfChildren) } else { // 第二层 + guard let ol = item as? CPDFOutline, ol.numberOfChildren > 0 else { return 0 } if self.isSearchOutlineMode == false { return Int(ol.numberOfChildren) } // 搜索模式 let ols = self.fetchOutlines(for: ol, searchString: self.outlineSearchField.stringValue) return ols.count } } return 0 } func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { if outlineView.isEqual(to: self.tocOutlineView) { if self.handdler.isLocked() { return "" } if item == nil { if self.isSearchOutlineMode { guard let rootOL = self.handdler.outlineRoot(), rootOL.numberOfChildren > 0 else { return "" } let ols = self.fetchOutlines(for: rootOL, searchString: self.outlineSearchField.stringValue) return ols.safe_element(for: index) as Any } let rootOL = self.handdler.outlineRoot() return rootOL?.child(at: UInt(index)) as Any } else { if let ol = item as? CPDFOutline { if self.isSearchOutlineMode == false { return ol.child(at: UInt(index)) as Any } let ols = self.fetchOutlines(for: ol, searchString: self.outlineSearchField.stringValue) return ols.safe_element(for: index) as Any } } } return item as Any } func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { if outlineView.isEqual(to: self.tocOutlineView) { let cell = outlineView.makeView(withIdentifier: KMTocTableCellView.km_identifier, owner: self) as! KMTocTableCellView let columnId = tableColumn?.identifier.rawValue var title = "" var pageLabel = "" if let ol = item as? CPDFOutline { title = ol.label ?? "" if ol.actionType == .page { pageLabel = "\((ol.destination?.pageIndex ?? 0) + 1)" } } cell.pageLabel.stringValue = pageLabel if columnId == kLabelColumnId.rawValue { if self.isSearchOutlineMode { let attriString = NSMutableAttributedString(string: title) var searchString = self.outlineSearchField.stringValue var roughString = title // if self.outlineIgnoreCaseFlag { // roughString = roughString.lowercased() // searchString = searchString.lowercased() // } // let ranges = roughString.ranges(of: searchString) for range in ranges.nsRnage { attriString.addAttribute(.font, value: NSFont.boldSystemFont(ofSize: 13), range: range) } cell.tocLabel.attributedStringValue = attriString } else { cell.tocLabel.stringValue = title } } cell.tocLabel.textColor = KMAppearance.titleColor() return cell } return nil } func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { if outlineView.isEqual(self.tocOutlineView) { if let ol = item as? CPDFOutline { let string: NSString = ol.label as NSString let ps = NSMutableParagraphStyle() ps.lineHeightMultiple = 1.32 ps.alignment = .left let attris = [NSAttributedString.Key.paragraphStyle : ps, NSAttributedString.Key.font : NSFont.SFProTextRegularFont(14.0)] let size = string.boundingRect(with: NSMakeSize(outlineView.frame.size.width - 30, 200), options: NSString.DrawingOptions(rawValue: 3), attributes: attris) return max(40, size.height + 16) } return 40 } return outlineView.rowHeight } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { if outlineView.isEqual(self.tocOutlineView) { guard let ol = item as? CPDFOutline else { return false } if self.isSearchOutlineMode == false { return ol.numberOfChildren > 0 } let ols = self.fetchOutlines(for: ol, searchString: self.outlineSearchField.stringValue) return ols.count > 0 } return false } func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { if outlineView.isEqual(self.tocOutlineView) { let itemView = KMBotaTableRowView() itemView.backgroundColor = .purple return itemView } return nil } func outlineViewSelectionDidChange(_ notification: Notification) { if self.tocOutlineView.isEqual(to: notification.object) { // if self.dragIn { // return // } // if self.updatingOutlineSelection == false { // self.updatingOutlineSelection = true // self.goToSelectedOutlineItem(nil) // self.updatingOutlineSelection = false // } } } func outlineViewItemDidExpand(_ notification: Notification) { // if self.tocOutlineView.isEqual(to: notification.object) { // self.updateOutlineSelection() // } } func outlineViewItemDidCollapse(_ notification: Notification) { // if self.tocOutlineView.isEqual(to: notification.object) { // self.updateOutlineSelection() // } } func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { var dragOp = NSDragOperation(rawValue: 0) if outlineView.isEqual(to: self.tocOutlineView) { if (index == -1) { dragOp = NSDragOperation(rawValue: 0) } else { dragOp = .move } } return dragOp } func outlineView(_ outlineView: NSOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool { if outlineView.isEqual(to: self.tocOutlineView) { if (self.tocOutlineView.selectedRowIndexes.count > 1) { return false } let tIndex = IndexSet(integer: self.tocOutlineView.clickedRow) self.tocOutlineView.deselectRow(tIndex.first ?? 0) // self._dragPDFOutline = items.first as? CPDFOutline let set = IndexSet(integer: 0) let zNSIndexSetData = NSKeyedArchiver.archivedData(withRootObject: set) // [pasteboard declareTypes:[NSArray arrayWithObject:kKMPDFViewOutlineDragDataType] owner:self]; pasteboard.declareTypes([.localDraggedTypes], owner: self) pasteboard.setData(zNSIndexSetData, forType: .localDraggedTypes) return true } return false } func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { if outlineView.isEqual(to: self.tocOutlineView) { if (index < 0) { return false } let outline = item as? CPDFOutline // guard let dragPDFOL = self._dragPDFOutline else { // return false // } //root,drag item to root // if ((outline?.parent == nil)) { // //fetch root // var root = dragPDFOL // while (root.parent != nil) { // root = root.parent // } // if dragPDFOL.parent.isEqual(to: root) { // if dragPDFOL.index > index { // self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: root) // } else { //// if let bks = self.bookmarks(), bks.count > 0 { //// if let data = item as? String, data == Self.kOutlineRootBookmarkItem { //// //// } else { //// if index > 1 { //// self.dragPDFOutline(self._dragPDFOutline, toIndex: index-2, newParentOutline: root) //// } //// } //// } else { // self.dragPDFOutline(self._dragPDFOutline, toIndex: index-1, newParentOutline: root) //// } // } // } else { // self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: root) // } // } else { // //在同一个层级内移动 // if dragPDFOL.parent.isEqual(to: item) { // if (dragPDFOL.index > 0) { // if dragPDFOL.index > index { // self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: outline) // }else{ // self.dragPDFOutline(self._dragPDFOutline, toIndex: index-1, newParentOutline: outline) // } // } else { // return false // } // } else { // var tOutlline: CPDFOutline? = outline // var isContains = false // while (tOutlline != nil) { // if tOutlline!.isEqual(to: self._dragPDFOutline) { // isContains = true // break // } // tOutlline = tOutlline?.parent // } // if (!isContains) { // self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: outline) // } // } // } return true } return false } func outlineView(_ outlineView: NSOutlineView, willDisplayOutlineCell cell: Any, for tableColumn: NSTableColumn?, item: Any) { if outlineView.isEqual(to: self.tocOutlineView) { if outlineView.selectionHighlightStyle == .regular && outlineView.isRowSelected(outlineView.row(forItem: item)) { (cell as? NSCell)?.backgroundStyle = .lowered } } } func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) { // self.dragIn = true } func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) { // self.dragIn = false } /* #pragma mark NSOutlineView delegate protocol - (void)outlineView:(NSOutlineView *)ov didClickTableColumn:(NSTableColumn *)tableColumn { if ([ov isEqual:rightSideController.noteOutlineView]) { NSTableColumn *oldTableColumn = [ov highlightedTableColumn]; NSTableColumn *newTableColumn = ([NSEvent modifierFlags] & NSEventModifierFlagCommand) ? nil : tableColumn; NSMutableArray *sortDescriptors = nil; BOOL ascending = YES; if ([oldTableColumn isEqual:newTableColumn]) { sortDescriptors = [[[rightSideController.noteArrayController sortDescriptors] mutableCopy] autorelease]; [sortDescriptors replaceObjectAtIndex:0 withObject:[[sortDescriptors firstObject] reversedSortDescriptor]]; ascending = [[sortDescriptors firstObject] ascending]; } else { NSString *tcID = [newTableColumn identifier]; NSSortDescriptor *pageIndexSortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationPageIndexKey ascending:ascending] autorelease]; NSSortDescriptor *boundsSortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SKPDFAnnotationBoundsOrderKey ascending:ascending selector:@selector(compare:)] autorelease]; sortDescriptors = [NSMutableArray arrayWithObjects:pageIndexSortDescriptor, boundsSortDescriptor, nil]; if ([tcID isEqualToString:TYPE_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationTypeKey ascending:YES selector:@selector(noteTypeCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:COLOR_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationColorKey ascending:YES selector:@selector(colorCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:NOTE_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationStringKey ascending:YES selector:@selector(localizedCaseInsensitiveNumericCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:AUTHOR_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationUserNameKey ascending:YES selector:@selector(localizedCaseInsensitiveNumericCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:DATE_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationModificationDateKey ascending:YES] autorelease] atIndex:0]; } if (oldTableColumn) [ov setIndicatorImage:nil inTableColumn:oldTableColumn]; [ov setHighlightedTableColumn:newTableColumn]; } [rightSideController.noteArrayController setSortDescriptors:sortDescriptors]; if (newTableColumn) [ov setIndicatorImage:[NSImage imageNamed:ascending ? @"NSAscendingSortIndicator" : @"NSDescendingSortIndicator"] inTableColumn:newTableColumn]; [ov reloadData]; } } - (void)outlineViewColumnDidResize:(NSNotification *)notification{ if (mwcFlags.autoResizeNoteRows && [[notification object] isEqual:rightSideController.noteOutlineView] && [[[[notification userInfo] objectForKey:@"NSTableColumn"] identifier] isEqualToString:NOTE_COLUMNID] && [(SKScrollView *)[[notification object] enclosingScrollView] isResizingSubviews] == NO) { [rowHeights removeAllFloats]; [rightSideController.noteOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [rightSideController.noteOutlineView numberOfRows])]]; } } - (void)sizeOutlineViewToContents:(NSOutlineView*) outlineView; { NSInteger rowCount = [outlineView numberOfRows]; for (NSInteger i = 0; i < rowCount; i++){ CGFloat rowHeight = 0; PDFOutline *outline = [leftSideController.tocOutlineView itemAtRow:i]; if (!outline){ continue; } NSMutableAttributedString *attributedString = [[[NSMutableAttributedString alloc]init] autorelease]; NSDictionary *dictAttr1 = @{NSForegroundColorAttributeName:[KMAppearance KMColor_Layout_H0]}; NSAttributedString *attr1 = [[NSAttributedString alloc]initWithString:outline.label attributes:dictAttr1]; [attributedString appendAttributedString:attr1]; // NSTableCellView *viewS = [leftSideController.tocOutlineView viewAtColumn:0 row:i makeIfNecessary:YES]; NSTableColumn *tableColumn = [leftSideController.tocOutlineView tableColumnWithIdentifier:LABEL_COLUMNID]; // id cell = [tableColumn dataCell]; id cell = [tableColumn dataCellForRow:i]; [cell setObjectValue:attributedString]; CGFloat w = leftSideController.view.frame.size.width - 86;//[tableColumn width] > 260 ? [tableColumn width] : 260; NSInteger num = [self getNum:outline]; CGFloat gap = [leftSideController.tocOutlineView indentationPerLevel]; rowHeight = [cell cellSizeForBounds:NSMakeRect(0.0, 0.0, w - (num > 0?16:0) - gap*num, CGFLOAT_MAX)].height; rowHeight = fmax(rowHeight, [leftSideController.tocOutlineView rowHeight]) + 25; [rowHeights setFloat:rowHeight forKey:outline]; if (@available(macOS 10.13, *)) { } else { rowHeight = 40.0; } // CGRect fram = viewS.frame; // viewS.frame = CGRectMake(fram.origin.x, fram.origin.y, fram.size.width, rowHeight); } [leftSideController.tocOutlineView reloadData]; } */ func noteItems(_ items: NSArray) -> NSArray { let noteItems = NSMutableArray() for item in items { guard let anno = (item as? KMBotaAnnotationModel)?.anno else { continue } if anno.type == nil { // item = [(SKNoteText *)item note]; } if noteItems.contains(anno) == false { noteItems.add(anno) } } return noteItems } } // MARK: - KMCustomOutlineViewDelegate, KMCustomOutlineViewDataSource extension KMNOutlineController: KMCustomOutlineViewDelegate, KMCustomOutlineViewDataSource { func outlineView(_ anOutlineView: NSOutlineView, canDeleteItems items: [Any]) -> Bool { if anOutlineView.isEqual(to: self.tocOutlineView) { return items.count > 0 } return false } func outlineView(_ anOutlineView: NSOutlineView, deleteItems items: [Any]) { if anOutlineView.isEqual(to: self.tocOutlineView) { self.outlineContextMenuItemClicked_RemoveEntry(nil) } } func outlineView(_ anOutlineView: NSOutlineView, canCopyItems items: [Any]) -> Bool { return false } func outlineView(_ anOutlineView: NSOutlineView, typeSelectHelperSelectionStrings aTypeSelectHelper: SKTypeSelectHelper) -> NSArray { if self.tocOutlineView.isEqual(to: anOutlineView) { let count = self.tocOutlineView.numberOfRows let array = NSMutableArray(capacity: count) for i in 0 ..< count { // [array addObject:[[(PDFOutline *)[leftSideController.tocOutlineView itemAtRow:i] label] lossyStringUsingEncoding:NSASCIIStringEncoding]]; let item = self.tocOutlineView.item(atRow: i) as? CPDFOutline array.add(item?.label ?? "") } return array } return NSArray() } }