// // KMLeftSideViewController+Outline.swift // PDF Master // // 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 updateOutlineSelection() { // if ([[pdfView document] outlineRoot] == nil || mwcFlags.updatingOutlineSelection) if self.listView.document.outlineRoot() == nil { return } // mwcFlags.updatingOutlineSelection = YES; 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) } // mwcFlags.updatingOutlineSelection = NO; } 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] = [] 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 addoutline(parent parentOutline: CPDFOutline?, addOutline: CPDFOutline, index: Int, needExpand: Bool) { var tempO = 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! } // [[[self.document undoManager] prepareWithInvocationTarget:self] removeOutlineWithParent:parentOutline removeOutline:addOutline 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: tempO) self.tocOutlineView.selectRowIndexes(IndexSet(integer: idx), byExtendingSelection: false) self.newAddOutlineEntryEditingMode(index) } // __block PDFOutline *taddOutline = [addOutline retain]; // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // [self.leftSideController.tocOutlineView scrollRowToVisible:[leftSideController.tocOutlineView rowForItem:taddOutline]]; // [taddOutline release]; // }); } func removeOutline(parent parentOutline: CPDFOutline?, removeOutline: CPDFOutline, index: Int, needExpand: Bool) { // [[[self.document undoManager] prepareWithInvocationTarget:self] addoutlineWithParent:parentOutline addOutline:removeOutline index:index needExpand:YES]; removeOutline.removeFromParent() if (needExpand) { self.tocOutlineView.expandItem(parentOutline) } Task { @MainActor in self.tocOutlineView.reloadData() } } 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?.isEqual(to: originalPage), data { //新page不存在 if (newPage == nil) { } else { let pageSize = newPage?.bounds(for: .cropBox).size ?? .zero if let destination = CPDFDestination(page: newPage, at: CGPointMake(pageSize.width, pageSize.height)) { self.changePDFOutlineDestination(destination, PDFoutline: editVC.outline) } } } } 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) { 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 { self.changePDFAction(urlAction, PDFOutline: editVC.outline) } } } if editVC.outlineNameTextView.string == editVC.originalLabel { return } self.renamePDFOutline(editVC.outline, label: editVC.outlineNameTextView.string) } } // MARK: - Undo & Redo extension KMLeftSideViewController { @objc dynamic func changePDFOutlineDestination(_ destination: CPDFDestination, PDFoutline outline: CPDFOutline) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).changePDFOutlineDestination(outline.destination, PDFoutline: outline) outline.destination = destination } @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) } } } // 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?.numberOfChildren ?? 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) } }