// // KMLeftSideViewController+Outline.swift // PDF Reader Pro // // Created by tangchao on 2023/12/22. // import Foundation enum KMOutlineViewMenuItemTag: Int { case addEntry = 0 case addChild case addAunt case remove case edit case setDestination case rename case promote case demote } extension KMLeftSideViewController { func outline_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", "Menu item title"), 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 // kKMPDFViewOutlineDragDataType self.tocOutlineView.registerForDraggedTypes([.localDraggedTypes]) self.tocOutlineView.target = self // self.tocOutlineView.doubleAction = #selector(goToSelectedOutlineItem) self.tocOutlineView.doubleAction = #selector(outlineContextMenuItemClicked_Rename) } func outline_initDefalutValue() { self.outlineView.wantsLayer = true self.outlineView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.outlineLabel.stringValue = KMLocalizedString("Outline", nil); self.outlineLabel.textColor = KMAppearance.Layout.h0Color() self.outlineAddButton.toolTip = KMLocalizedString("Add Item", nil) self.outlineSearchButton.toolTip = KMLocalizedString("Search", nil) self.outlineDoneButton.title = KMLocalizedString("Done", nil); self.outlineDoneButton.toolTip = KMLocalizedString("Done", nil); 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", nil) self.tocOutlineView.backgroundColor = KMAppearance.Layout.l0Color() self.tocOutlineView.autoresizesOutlineColumn = false self.tocOutlineView.allowsMultipleSelection = true self.tocOutlineView.allowsEmptySelection = true self.tocOutlineView.focusRingType = .none self.tocOutlineView.autoresizesSubviews = true } func updateOutlineSelection() { if self.listView.document.outlineRoot() == nil || self.updatingOutlineSelection { return } self.updatingOutlineSelection = true let numRows = self.tocOutlineView.numberOfRows var arr = NSMutableArray() for i in 0 ..< numRows { guard let tPDFOutline = self.tocOutlineView.item(atRow: i) as? CPDFOutline else { continue } let tPage = tPDFOutline.destination?.page() if (tPage == nil) { continue } let tDict = NSDictionary(object: tPage as Any, forKey: "\(i)" as NSCopying) arr.add(tDict) } let currentPage = self.listView.currentPage() var hasExistInOutlineView = false for dict in arr { guard let _dict = dict as? NSDictionary else { continue } let page = _dict.allValues.last as? CPDFPage // NSInteger index = [dict.allKeys.lastObject integerValue]; let index = Int(_dict.allKeys.last as? String ?? "0") ?? 0 if let data = page?.isEqual(to: currentPage), data { self.tocOutlineView.selectRowIndexes(IndexSet(integer: index), byExtendingSelection: false) self.tocOutlineView.scrollRowToVisible(index) hasExistInOutlineView = true break } } if (!hasExistInOutlineView) { 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 loadUnfoldDate(_ foldType: KMFoldType) { // 清空所有折叠数组 self.allFoldNotes.removeAll() var mutableArray: [CPDFAnnotation] = [] if self.noteSearchMode { for note in self.noteSearchArray { if note is CPDFMarkupAnnotation { mutableArray.append(note) } } } else { for section in self.annotations { for item in section.annotations ?? [] { if let note = item.annotation, note is CPDFMarkupAnnotation { mutableArray.append(note) } } } } // for note in self.notes { // if note is CPDFMarkupAnnotation { // mutableArray.append(note) // } // } self.canFoldNotes = mutableArray self.allFoldNotes = [] } func selectedRowIndexes() -> IndexSet { var selectedIndexes = self.tocOutlineView.selectedRowIndexes let clickedRow = self.tocOutlineView.clickedRow if clickedRow != -1 && selectedIndexes.contains(clickedRow) == false { var indexes = IndexSet(integer: clickedRow) selectedIndexes = indexes } return selectedIndexes } func editOutlineUI(_ editVC: KMOutlineEditViewController) { 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)) let originalPage = editVC.originalDestination?.page() if let data = newPage?.pageIndex(), data != editVC.originalPageIndex { // if let data = newPage?.isEqual(to: originalPage), data { //新page不存在 if (newPage == nil) { } else { if let outL = editVC.outline { let pageSize = newPage?.bounds(for: .cropBox).size ?? .zero if let destination = CPDFDestination(page: newPage, at: CGPointMake(pageSize.width, pageSize.height)) { self.changePDFOutlineDestination(destination, PDFoutline: outL) } } } } } else if editVC.urlButton.state == .on { var urlString = editVC.outlineURLTextField.stringValue let tLowerUrl = urlString.lowercased() if tLowerUrl.hasPrefix("https://") == false && tLowerUrl.hasPrefix("ftp://") == false && tLowerUrl.hasPrefix("http://") == false && urlString.isEmpty == false { urlString = "http://\(urlString)" } if let urlAction = CPDFURLAction(url: urlString), editVC.outline != nil { if editVC.originalURLString != editVC.outlineURLTextField.stringValue { self.changePDFAction(urlAction, PDFOutline: editVC.outline!) } } } else if editVC.mailButton.state == .on { var mailString = editVC.mailAddressTextField.stringValue let tLowerStr = mailString.lowercased() if tLowerStr.hasPrefix("mailto:") == false { mailString = "mailto:\(mailString)" } if var urlAction = CPDFURLAction(url: mailString) { if urlAction.url() == nil { urlAction = CPDFURLAction(url: "mailto:") } if mailString != editVC.originalURLString && editVC.outline != nil { self.changePDFAction(urlAction, PDFOutline: editVC.outline!) } } } if editVC.outlineNameTextView.string == editVC.originalLabel { return } if let outL = editVC.outline { self.renamePDFOutline(outL, label: editVC.outlineNameTextView.string) } } @objc func outlineContextMenuItemClicked_AddEntry(_ sender: AnyObject?) { let PDFOutlineArray = NSMutableArray() let rowSet = self.selectedRowIndexes() for idx in rowSet { PDFOutlineArray.add(self.tocOutlineView.item(atRow: idx)) } if (PDFOutlineArray.count == 0) { var lastPDFLine = self.tocOutlineView.item(atRow: self.tocOutlineView.numberOfRows-1) as? CPDFOutline var rootPDFOutline: CPDFOutline? if (lastPDFLine != nil) { while (lastPDFLine!.parent != nil) { lastPDFLine = lastPDFLine?.parent } rootPDFOutline = lastPDFLine } else { rootPDFOutline = self.listView.document.outlineRoot() if ((rootPDFOutline == nil)) { // rootPDFOutline = CPDFOutline() // self.listView.document.setOutlineRoot(rootPDFOutline) rootPDFOutline = self.listView.document.setNewOutlineRoot() } } let addOutLine = CPDFOutline() addOutLine.label = String(format: "%@ %ld", KMLocalizedString("Page", nil), self.listView.currentPageIndex+1) addOutLine.destination = self.listView.currentDestination self.addoutline(parent: rootPDFOutline, addOutline: addOutLine, index: Int(rootPDFOutline?.numberOfChildren ?? 0), needExpand: false) } else { let currentPDFline = PDFOutlineArray.lastObject as? CPDFOutline let currentIndex = currentPDFline?.index ?? 0 var parent: CPDFOutline? parent = currentPDFline?.parent let addOutLine = CPDFOutline() addOutLine.label = String(format: "%@ %ld", KMLocalizedString("Page", nil), self.listView.currentPageIndex+1) addOutLine.destination = self.listView.currentDestination self.addoutline(parent: parent, addOutline: addOutLine, index: Int(currentIndex) + 1, needExpand: false) self.tocOutlineView.scrollRowToVisible(Int(currentIndex) + 1) self.tocOutlineView.deselectRow(Int(currentIndex)+1) } } @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.reloadData() self.tocOutlineView.expandItem(nil, expandChildren: true) } @IBAction func toc_foldAllComments(_ sender: AnyObject?) { if (self.tocType == .fold) { return } self.tocType = .fold self.tocOutlineView.reloadData() 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?", nil) alert.addButton(withTitle: KMLocalizedString("Yes", nil)) alert.addButton(withTitle: KMLocalizedString("No", nil)) let response = alert.runModal() if response == .alertFirstButtonReturn { if let item = self.listView.document.outlineRoot() { self.removeAllOutline(item) } } } func showSearchOutlineBlankState(_ toShowState: Bool) { if (toShowState) { self.leftSideEmptyVC.outlineSearchView.frame = CGRectMake((self.tocOutlineView.enclosingScrollView!.documentView!.frame.size.width - self.leftSideEmptyVC.outlineSearchView.bounds.size.width)/2.0, (self.tocOutlineView.enclosingScrollView!.documentView!.frame.size.height - self.leftSideEmptyVC.outlineSearchView.bounds.size.height)/2.0, self.leftSideEmptyVC.outlineSearchView.bounds.size.width, self.leftSideEmptyVC.outlineSearchView.bounds.size.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.listView.document.setNewOutlineRoot() for i in 0 ..< self.listView.document.pageCount { self.listView.document.removeBookmark(forPageIndex: i) } self.listView.layoutDocumentView() DispatchQueue.main.async { self.tocOutlineView.reloadData() } } func updateSelectRowHeight() { var rowHeight: CGFloat = 0 let outline: CPDFOutline? = self.tocOutlineView.km.selectedItem() if outline == nil { return } var attributedString = NSMutableAttributedString() var dictAttr1 = [NSAttributedString.Key.foregroundColor : KMAppearance.Layout.h0Color()] var attr1 = NSAttributedString(string: outline!.label, attributes: dictAttr1) attributedString.append(attr1) let row = self.tocOutlineView.selectedRow let viewS = self.tocOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) let tableColumn = self.tocOutlineView.tableColumn(withIdentifier: kLabelColumnId) // // id cell = [tableColumn dataCell]; let cell = tableColumn?.dataCell(forRow: row) (cell as? NSCell)?.objectValue = attributedString // CGFloat w = leftSideController.view.frame.size.width - 86;//[tableColumn width] > 260 ? [tableColumn width] : 260; let w = self.view.frame.size.width-86 let num = self.getNum(outline) let gap = self.tocOutlineView.indentationPerLevel rowHeight = ((cell as? NSCell)?.cellSize(forBounds: NSMakeRect(0, 0, w - (num > 0 ? 16 : 0) - gap * num.cgFloat, CGFLOAT_MAX)).height) ?? 0 rowHeight = fmax(rowHeight, self.tocOutlineView.rowHeight) + 25 // [rowHeights setFloat:rowHeight forKey:outline]; var fram = viewS?.frame ?? .zero viewS?.frame = NSMakeRect(fram.origin.x, fram.origin.y, fram.size.width, rowHeight) self.tocOutlineView.reloadData() } 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 } } // MARK: - Undo & Redo extension KMLeftSideViewController { @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.reloadData() self.tocOutlineView.km_selectItem(outline, byExtendingSelection: false) } } @objc dynamic func demoteOutlineWithGrandParent(_ grandParentOutline: CPDFOutline, demoteOutline: CPDFOutline, index: Int) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).promoteOutlineWithGrandParent(grandParentOutline, promoteOutline: demoteOutline, rowIndex: index) if grandParentOutline.isEqual(to: demoteOutline.parent) { let demoteIndex = demoteOutline.index let previousOutline = grandParentOutline.child(at: demoteIndex-1) demoteOutline.removeFromParent() previousOutline?.insertChild(demoteOutline, at: UInt(index)) self.tocOutlineView.reloadData() self.tocOutlineView.expandItem(previousOutline) } else { demoteOutline.removeFromParent() grandParentOutline.insertChild(demoteOutline, at: grandParentOutline.numberOfChildren) self.tocOutlineView.reloadData() self.tocOutlineView.expandItem(grandParentOutline) } self.tocOutlineView.km_selectItem(demoteOutline, byExtendingSelection: false) } @objc dynamic func promoteOutlineWithGrandParent(_ grandParentOutline: CPDFOutline, promoteOutline: CPDFOutline, rowIndex: Int) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).demoteOutlineWithGrandParent(grandParentOutline, demoteOutline: promoteOutline, index: rowIndex) let index = promoteOutline.parent?.index ?? 0 if grandParentOutline.isEqual(to: promoteOutline.parent) { promoteOutline.removeFromParent() grandParentOutline.parent.insertChild(promoteOutline, at: index+1) } else { promoteOutline.removeFromParent() grandParentOutline.insertChild(promoteOutline, at: index+1) } self.tocOutlineView.reloadData() self.tocOutlineView.km_selectItem(promoteOutline, 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?.insertChild(at: UInt(index)) outline?.label = String(format: "%@ %ld", KMLocalizedString("Page", nil), self.listView.currentPageIndex+1) outline?.destination = self.listView.currentDestination 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 self.tocOutlineView.reloadData() 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 self.tocOutlineView.reloadData() } } @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.reloadData() self.tocOutlineView.expandItem(newParentOutline) self.tocOutlineView.selectRowIndexes(IndexSet(integer: self.tocOutlineView.row(forItem: outline)), byExtendingSelection: false) } } // MARK: - Menu Actions extension KMLeftSideViewController { //添加子节点 @objc func outlineContextMenuItemClicked_AddChildEntry(_ sender: AnyObject?) { let PDFOutlineArray = NSMutableArray() let rowSet = self.selectedRowIndexes() for idx in rowSet { PDFOutlineArray.add(self.tocOutlineView.item(atRow: idx)) } let currentPDFline = PDFOutlineArray.lastObject as? CPDFOutline let addOutLine = CPDFOutline() addOutLine.label = String(format: "%@ %ld", KMLocalizedString("Page", nil), "\(self.listView.currentPageIndex + 1)") addOutLine.destination = self.listView.currentDestination self.addoutline(parent: currentPDFline, addOutline: addOutLine, index: Int(currentPDFline?.numberOfChildren ?? 0), needExpand: true) } //添加上一级节点 @objc func outlineContextMenuItemClicked_AddAuntEntry(_ sender: AnyObject?) { let PDFOutlineArray = NSMutableArray() let rowSet = self.selectedRowIndexes() for idx in rowSet { PDFOutlineArray.add(self.tocOutlineView.item(atRow: idx)) } let clickedOutline = PDFOutlineArray.lastObject as? CPDFOutline let fatherOutLine = clickedOutline?.parent let grandfatherOutLine = fatherOutLine?.parent if (grandfatherOutLine != nil) { let addOutLine = CPDFOutline() addOutLine.label = String(format: "%@ %ld", KMLocalizedString("Page", nil), "\(self.listView.currentPageIndex + 1)") addOutLine.destination = self.listView.currentDestination self.addoutline(parent: grandfatherOutLine, addOutline: addOutLine, index: Int(fatherOutLine?.index ?? 0) + 1, needExpand: false) } } //移除节点 @objc func outlineContextMenuItemClicked_RemoveEntry(_ sender: AnyObject?) { let set = self.selectedRowIndexes() var selectedPDFOutlineArr = NSMutableArray() for idx in set { selectedPDFOutlineArr.add(self.tocOutlineView.item(atRow: idx)) } //整体移除多选 for tOutline in selectedPDFOutlineArr { guard let outL = tOutline as? CPDFOutline else { continue } self.removeOutline(parent: outL.parent , removeOutline: outL, index: Int(outL.index), needExpand: false) } } //重置目的 @objc func outlineContextMenuItemClicked_SetDestination(_ sender: AnyObject?) { guard let setPDFOutline = self.tocOutlineView.item(atRow: self.tocOutlineView.clickedRow) as? CPDFOutline else { return } Task { let modalRes = await KMAlertTool.runModel(style: .informational, message: KMLocalizedString("Are you sure you want to set the destination as the current location?", nil), buttons: [KMLocalizedString("Yes", nil), KMLocalizedString("No", nil)]) if (modalRes == .alertFirstButtonReturn) { self.changePDFOutlineDestination(self.listView.currentDestination, PDFoutline: setPDFOutline) self.tocOutlineView.reloadData() let idx = self.tocOutlineView.row(forItem: setPDFOutline) self.tocOutlineView.selectRowIndexes(IndexSet(integer: idx), byExtendingSelection: false) } } } //弹出菜单 @objc func outlineContextMenuItemClicked_Edit(_ sender: AnyObject?) { let popover = NSPopover() popover.delegate = self let targetOutline: CPDFOutline? = self.tocOutlineView.km.clickedItem() let outlineEditViewController = KMOutlineEditViewController(outline: targetOutline, document: self.listView) let cell = self.tocOutlineView.rowView(atRow: self.tocOutlineView.clickedRow, makeIfNecessary: true) popover.contentViewController = outlineEditViewController popover.animates = true popover.behavior = .transient popover.show(relativeTo: cell?.bounds ?? .zero, of: cell!, preferredEdge: .minX) } //重命名 @objc func outlineContextMenuItemClicked_Rename(_ sender: AnyObject?) { if (self.tocOutlineView.clickedRow < 0) { return } self.renamePDFOutline = self.tocOutlineView.km.clickedItem() let viewS = self.tocOutlineView.view(atColumn: 0, row: self.tocOutlineView.clickedRow, makeIfNecessary: true) let targrtTextField = viewS?.subviews.first as? NSTextField self.renamePDFOutlineTextField = targrtTextField targrtTextField?.delegate = self targrtTextField?.isEditable = true targrtTextField?.becomeFirstResponder() } //降级节点 @objc func outlineContextMenuItemClicked_Demote(_ sender: AnyObject?) { guard let currentOutline: CPDFOutline = self.tocOutlineView.km.clickedItem() else { return } let parentOutLine = currentOutline.parent let newParentOutLine = parentOutLine?.child(at: currentOutline.index-1) var newIndex = 0 let newParentOutLineExpandState = self.tocOutlineView.isItemExpanded(newParentOutLine) if (newParentOutLineExpandState) { newIndex = self.tocOutlineView.clickedRow } else { newIndex = self.tocOutlineView.clickedRow + Int(newParentOutLine?.numberOfChildren ?? 0) } let currentIndex = currentOutline.index currentOutline.removeFromParent() self.demoteOutlineWithGrandParent(newParentOutLine!, demoteOutline: currentOutline, index: Int(currentIndex)) } //升级节点 @objc func outlineContextMenuItemClicked_Promote(_ sender: AnyObject?) { guard let currentOutline: CPDFOutline = self.tocOutlineView.km.clickedItem() else { return } let parentOutLine = currentOutline.parent if let grandParentOutLine = parentOutLine?.parent { self.promoteOutlineWithGrandParent(grandParentOutLine, promoteOutline: currentOutline, rowIndex:Int(currentOutline.index)) } } @objc func toggleOutlineCaseInsensitiveSearch(_ sender: NSMenuItem) { if (sender.state == .on) { self.outlineIgnoreCaseFlag = false } else { self.outlineIgnoreCaseFlag = true } if (self.outlineSearchField.stringValue.isEmpty == false) { self.tocOutlineView.reloadData() self.tocOutlineView.expandItem(nil, expandChildren: true) } } } // MARK: - KMTocOutlineViewDelegate extension KMLeftSideViewController: 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.listView.document.pageCount if row + 1 < numRows { lastPage = (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 KMLeftSideViewController: 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]; // } } }