// // KMOutlineViewController.swift // PDF Reader Pro // // Created by lxy on 2022/10/10. // import Cocoa class KMOutlineViewController: NSViewController { @IBOutlet var contendView: NSView! @IBOutlet weak var topView: NSView! @IBOutlet weak var titleLabel: NSTextField! @IBOutlet weak var lineView: NSView! @IBOutlet weak var addButton: NSButton! @IBOutlet weak var moreButton: NSButton! @IBOutlet var topSepline: NSView! @IBOutlet weak var emptyView: NSView! @IBOutlet weak var bigTipLabel: NSTextField! @IBOutlet weak var tipLabel: NSTextField! @IBOutlet weak var BOTAOutlineView: KMBOTAOutlineView! var dragPDFOutline : KMBOTAOutlineItem! var renameTextField : NSTextField! var listView : CPDFListView! var renamePDFOutline : KMBOTAOutlineItem! let moreMenu = NSMenu() var isLocalEvent = false func dealloc() { NotificationCenter.default.removeObserver(self) } deinit { NotificationCenter.default.removeObserver(self) self.BOTAOutlineView.delegate = nil } override func viewWillDisappear() { super.viewWillDisappear() self.cancelSelect() } override func viewDidLoad() { super.viewDidLoad() self.emptyView.wantsLayer = true // self.emptyView.layer?.backgroundColor = NSColor.red.cgColor self.addOutlineMenu() self.titleLabel.stringValue = NSLocalizedString("Outline", comment: "") self.topView.wantsLayer = true self.BOTAOutlineView.delegate = self self.BOTAOutlineView.inputData = self.listView.document.outlineRoot() self.BOTAOutlineView.outlineView.doubleAction = #selector(outlineViewDoubleAction) self.refreshUI() self.initNotification() } func refreshUI() { self.contendView.wantsLayer = true self.contendView.layer?.backgroundColor = NSColor.km_init(hex: "#F7F8FA").cgColor self.topSepline.wantsLayer = true self.topSepline.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor self.titleLabel.textColor = NSColor.km_init(hex: "#252629") self.titleLabel.font = NSFont.SFProTextSemiboldFont(14.0) self.bigTipLabel.font = NSFont.SFProTextRegularFont(14.0) self.bigTipLabel.textColor = NSColor.km_init(hex: "#616469") self.bigTipLabel.stringValue = NSLocalizedString("No Outlines", comment: "") self.lineView.backgroundColor(NSColor.km_init(hex: "#EDEEF0")) let title = NSLocalizedString("To create an outline, please right-click on the selected page and choose \"Add Outline\", or click \"Add\" in the upper right corner.", comment: "") let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 1.32 paragraphStyle.alignment = .center self.tipLabel.attributedStringValue = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor.km_init(hex: "#94989C")]) self.addButton.toolTip = NSLocalizedString("Add Outline", comment: "") self.moreButton.toolTip = NSLocalizedString("More", comment: "") } private func addOutlineMenu(){ moreMenu.addItem(withTitle: NSLocalizedString("Expand All", comment: ""), action: #selector(expandAllComments), target: self, tag: 0) moreMenu.addItem(withTitle: NSLocalizedString("Collapse All", comment: ""), action: #selector(collapseAllComments), target: self, tag: 1) moreMenu.addItem(withTitle: NSLocalizedString("Remove All Outlines", comment: ""), action: #selector(removeAllOutlineItem), target: self, tag: 2) } func addRightMenu(view: NSView, event: NSEvent) { let menus : NSMenu = NSMenu(title: "") let addItem = self.menuItemWithTitle(title: NSLocalizedString("Add Item", comment: ""), action: #selector(addItemAction)) let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Add Sub-Item", comment: ""), action: #selector(addChildItemAction)) let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Add A Higher Level", comment: ""), action: #selector(addHigherItemAction)) let deleteItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction)) // let editItem = self.menuItemWithTitle(title: NSLocalizedString("Edit", comment: ""), action: #selector(editItemAction)) let renameItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameItemAction)) let changeItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeItemAction)) let promoteItem = self.menuItemWithTitle(title: NSLocalizedString("Promote", comment: ""), action: #selector(promoteItemAction)) let demoteItem = self.menuItemWithTitle(title: NSLocalizedString("Demote", comment: ""), action: #selector(demoteItemAction)) menus.addItem(addItem) menus.addItem(addChildItem) menus.addItem(addHigherItem) menus.addItem(NSMenuItem.separator()) menus.addItem(deleteItem) menus.addItem(NSMenuItem.separator()) // menus.addItem(editItem) menus.addItem(renameItem) menus.addItem(changeItem) menus.addItem(NSMenuItem.separator()) menus.addItem(promoteItem) menus.addItem(demoteItem) let point = view.convert(event.locationInWindow, from: nil) menus.popUp(positioning: nil, at: point, in: view) } func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem { let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "") menuItem.target = self return menuItem } func initNotification() { NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil) } func removeNotification() { NotificationCenter.default.removeObserver(self) } func reloadData() { self.BOTAOutlineView.reloadData(expandItemType: .none) } func editOutlineUI(editVC : KMOutlineEditViewController!) { if editVC.pageButton.state == NSControl.StateValue.on { let index = Int(editVC.outlineTargetPageIndexTextField.stringValue)! if editVC.originalDestination?.pageIndex != index { let page = editVC.pdfView?.document.page(at: UInt(index)) if page != nil { let destination = CPDFDestination.init(document: editVC.pdfView!.document, pageIndex: index) editVC.outline?.destination = destination } else { __NSBeep() } } } else if editVC.urlButton.state == NSControl.StateValue.on { if editVC.originalURLString != editVC.outlineURLTextField.stringValue { var urlString = editVC.outlineURLTextField.stringValue let tLowerUrl = urlString.lowercased() if !tLowerUrl.hasPrefix("https://") && !tLowerUrl.hasPrefix("pf]://") && !urlString.hasPrefix("https://") && urlString.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) > 0 { urlString = "http://\(urlString)" } let action = CPDFURLAction.init(url: urlString) editVC.outline?.action = action } } else if editVC.mailButton.state == NSControl.StateValue.on { var mailString = editVC.mailAddressTextField.stringValue let tLowerStr = mailString.lowercased() if !tLowerStr.hasPrefix("mailto:") { mailString = "mailto:\(mailString)" } if mailString != editVC.originalURLString { var action = CPDFURLAction.init(url: mailString) if action?.url == nil { action = CPDFURLAction.init(url: "mailto:") } editVC.outline?.action = action } } // self.mainWindowController.document?.undo() // if editVC.outlineNameTextView.string != editVC.originalLabel { // self.renamePDFOutline(outline: editVC.outline, label: editVC.outlineNameTextView.string) // } } } //Data extension KMOutlineViewController { func updateExtempViewState() { if(self.listView.document.outlineRoot() == nil || self.listView.document.outlineRoot().numberOfChildren == 0) { //无数据时的图 self.emptyView.isHidden = false } else { self.emptyView.isHidden = true } } } //MARK: - Notification extension KMOutlineViewController { @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) { if notification.object is CPDFDocument { let pdfdocument : CPDFDocument = notification.object as! CPDFDocument if pdfdocument.isEqual(self.listView.document) { if !isLocalEvent { self.updateOutlineSelection() } self.isLocalEvent = false } } } } //MARK: - Menu 右键菜单 extension KMOutlineViewController { @objc func outlineViewDoubleAction() { if(self.BOTAOutlineView.outlineView.clickedRow >= 0) { self.renameItemAction() } } @objc func addItemAction() { let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView let selectRowIndexs = outlineView.selectedRowIndexes let dataCount = self.BOTAOutlineView.data?.children.count var index: Int var parent: KMBOTAOutlineItem = KMBOTAOutlineItem() var outlineItem: KMBOTAOutlineItem = KMBOTAOutlineItem() if selectRowIndexs.count == 0 { var lastOulineItem = KMBOTAOutlineItem() if dataCount == nil || dataCount == 0 { let item = KMBOTAOutlineItem() item.outline = self.listView.document.setNewOutlineRoot() item.parent = nil parent = item lastOulineItem = item } else { outlineItem = outlineView.item(atRow: outlineView.numberOfRows - 1) as! KMBOTAOutlineItem lastOulineItem = outlineItem while lastOulineItem.parent != nil { lastOulineItem = lastOulineItem.parent! } parent = lastOulineItem } index = Int(lastOulineItem.outline.numberOfChildren) } else { outlineItem = outlineView.item(atRow: selectRowIndexs.last ?? 0) as! KMBOTAOutlineItem parent = outlineItem.parent ?? KMBOTAOutlineItem() index = Int(outlineItem.outline.index + 1) } self.addOutlineToIndex(index: index, parent: parent) } @objc func addChildItemAction() { let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView let selectRowIndexs = outlineView.selectedRowIndexes if selectRowIndexs.count != 0 { let outlineItem: KMBOTAOutlineItem = outlineView.item(atRow: selectRowIndexs.last!) as! KMBOTAOutlineItem let index = outlineItem.outline.numberOfChildren self.addOutlineToIndex(index: NSInteger(index), parent: outlineItem) } } @objc func addHigherItemAction() { let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView let selectRowIndexs = outlineView.selectedRowIndexes if selectRowIndexs.count != 0 { let outlineItem: KMBOTAOutlineItem = outlineView.item(atRow: selectRowIndexs.last!) as! KMBOTAOutlineItem var parent = outlineItem.parent let index = NSInteger(parent!.outline.index) + 1 parent = parent?.parent if parent != nil { self.addOutlineToIndex(index: index, parent: parent!) } } } @objc func deleteItemAction() { let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView let selectRowIndexs = outlineView.selectedRowIndexes if selectRowIndexs.count != 0 { var outlineItems: [KMBOTAOutlineItem] = [] for index in selectRowIndexs { let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: index) as! KMBOTAOutlineItem outlineItem.toIndex = index outlineItem.parent = outlineItem.parent ?? KMBOTAOutlineItem() outlineItems.append(outlineItem) } self.deleteOutline(outlineItems: outlineItems) } } @objc func editItemAction() { if self.BOTAOutlineView.outlineView.clickedRow >= 0 { if self.BOTAOutlineView.outlineView.rowView(atRow: self.BOTAOutlineView.outlineView.clickedRow, makeIfNecessary: true) != nil { let cell = self.BOTAOutlineView.outlineView.rowView(atRow: self.BOTAOutlineView.outlineView.clickedRow, makeIfNecessary: true) let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem let outlineEditViewController = KMOutlineEditViewController.init(outline: outlineItem.outline, document: self.listView) let popover = NSPopover() popover.delegate = self popover.contentViewController = outlineEditViewController popover.animates = true popover.behavior = .transient popover.show(relativeTo: cell!.bounds, of: cell!, preferredEdge: .minX) } } else { __NSBeep() } } @objc func renameItemAction() { if self.BOTAOutlineView.outlineView.clickedRow >= 0 { self.renameOutlineWithRow(row: self.BOTAOutlineView.outlineView.clickedRow) } else { __NSBeep() } } @objc func changeItemAction() { if self.BOTAOutlineView.outlineView.clickedRow >= 0 { let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem let alter = NSAlert() alter.alertStyle = NSAlert.Style.informational alter.messageText = NSLocalizedString("Are you sure you want to set the target location of the selected outline to the current page?", comment: "") alter.addButton(withTitle: NSLocalizedString("Yes", comment:"")) alter.addButton(withTitle: NSLocalizedString("No", comment:"")) let modlres = alter.runModal() if modlres == NSApplication.ModalResponse.alertFirstButtonReturn { self.changeLocation(outlineItem: outlineItem, destination: self.fetchCurrentDestination()) } } else { __NSBeep() } } @objc func promoteItemAction() { if self.BOTAOutlineView.outlineView.clickedRow >= 0 { let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem var parent = outlineItem.parent let index = NSInteger(parent!.outline.index) + 1 parent = parent?.parent if parent != nil { self.moveOutline(outlineItem: outlineItem, index: index, parent: parent) } } } @objc func demoteItemAction() { if self.BOTAOutlineView.outlineView.clickedRow >= 0 { let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem let parent = outlineItem.parent let newParent = parent?.children[Int(outlineItem.outline.index) - 1] let index = newParent?.children.count if (index != nil) { self.moveOutline(outlineItem: outlineItem, index: NSInteger(index ?? 0), parent: newParent) } } } @objc private func expandAllComments(item: NSMenuItem) { self.BOTAOutlineView.expandAllComments(item: item) } @objc private func collapseAllComments(item: NSMenuItem) { self.BOTAOutlineView.collapseAllComments(item: item) } @objc private func removeAllOutlineItem(item: NSMenuItem) { let alter = NSAlert() alter.alertStyle = NSAlert.Style.informational alter.messageText = NSLocalizedString("This will permanently remove all outlines. Are you sure to continue?", comment: "") alter.addButton(withTitle: NSLocalizedString("Yes", comment:"")) alter.addButton(withTitle: NSLocalizedString("No", comment:"")) let modlres = alter.runModal() if modlres == NSApplication.ModalResponse.alertFirstButtonReturn { self.removeAllOutline() } } @objc private func removeAllOutline() { guard let data = self.BOTAOutlineView.data else { return } for item in data.children { item.toIndex = Int(item.outline.index) } self.deleteOutline(outlineItems: data.children) self.BOTAOutlineView.reloadData(expandItemType: .none) } } //MARK: - Action extension KMOutlineViewController { @IBAction func addNewOutline(_ sender: Any) { self.addItemAction() } @IBAction func moreButton_click(_ sender: NSButton) { let rect = sender.convert(sender.bounds, to: self.view) moreMenu.popUp(positioning: nil, at: NSPoint(x: rect.origin.x, y: rect.origin.y-10), in: self.view) } @IBAction func escButtonAction(_ sender: Any) { self.cancelSelect() } func cancelSelect() { self.BOTAOutlineView.cancelSelect() } func renameOutlineWithRow(row: NSInteger) { DispatchQueue.main.async { self.renamePDFOutline = self.BOTAOutlineView.outlineView.item(atRow: row) as? KMBOTAOutlineItem let cell : KMBOTAOutlineCellView = self.BOTAOutlineView.outlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as! KMBOTAOutlineCellView self.renameTextField = cell.titleLabel self.renameTextField.delegate = self self.renameTextField.isEditable = true self.renameTextField.becomeFirstResponder() } } func addOutlineToIndex(index: Int, parent: KMBOTAOutlineItem) { let pageIndex: Int = self.listView.currentPageIndex let label: String = self.fetchCurrentLabel(pageIndex: pageIndex) let destination: CPDFDestination = self.fetchCurrentDestination() self.addOutlineToIndex(index: index, pageIndex: pageIndex, destination: destination, lable: label, parent: parent) } func addOutlineToIndex(index: Int, pageIndex: Int, destination: CPDFDestination, lable: String, parent: KMBOTAOutlineItem) { let outlineItem = KMBOTAOutlineItem() outlineItem.destination = destination outlineItem.label = lable outlineItem.parent = parent outlineItem.toIndex = index self.addOutline(outlineItems: [outlineItem]) let tempOutlineView = self.BOTAOutlineView! var index = -1 if tempOutlineView.outlineView.numberOfRows == 1 || tempOutlineView.data == nil { index = 0 } else { index = tempOutlineView.outlineView.row(forItem: outlineItem) } tempOutlineView.selectIndex(index: index) //滑动到指定位置 if(tempOutlineView.outlineView.selectedRow >= 0) { self.renameOutlineWithRow(row: tempOutlineView.outlineView.selectedRow) } let row = tempOutlineView.outlineView.row(forItem: outlineItem) if Thread.current.isMainThread { tempOutlineView.outlineView.scrollToVisible(tempOutlineView.outlineView.rect(ofRow: row)) } else { DispatchQueue.main.async { tempOutlineView.outlineView.scrollToVisible(tempOutlineView.outlineView.rect(ofRow: row)) } } } func updateOutlineSelection() { KMPrint("updateOutlineSelection") let currentPageIndex = self.listView.currentPageIndex let numRows = self.BOTAOutlineView.outlineView.numberOfRows if numRows > 0 { for i in 0...numRows - 1 { let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: i) as! KMBOTAOutlineItem if (outlineItem.outline.destination == nil) { continue } if outlineItem.outline.destination.pageIndex == currentPageIndex { self.BOTAOutlineView.selectIndex(index: currentPageIndex) break } } } } func fetchCurrentDestination() -> CPDFDestination { //destination var destination: CPDFDestination let pageIndex: Int if self.listView.currentSelection != nil { let des :CPDFDestination = CPDFDestination.init(document: self.listView.document, pageIndex: Int(self.listView.currentSelection.page.pageIndex()), at: CGPoint(x: self.listView.currentSelection.bounds.origin.x, y: self.listView.currentSelection.bounds.origin.y + self.listView.currentSelection.bounds.size.height), zoom: self.listView.scaleFactor) destination = des pageIndex = Int(self.listView.currentSelection.page.pageIndex()) } else { let des :CPDFDestination = CPDFDestination.init(document: self.listView.document, pageIndex: Int(self.listView.currentPageIndex), at: CGPoint(x: 0, y: self.listView.currentPage().size.height), zoom: self.listView.scaleFactor) destination = des pageIndex = Int(self.listView.currentPageIndex) } if "\(destination.point.x )" == "nan" { destination = CPDFDestination(document: self.listView.document, pageIndex: pageIndex, at: CGPoint(x: 0, y: 0), zoom: self.listView.scaleFactor) } return destination } func fetchCurrentLabel(pageIndex: Int) -> String{ //label var label = "\(NSLocalizedString("Page", comment: ""))\(pageIndex + 1)" if self.listView.currentSelection != nil && self.listView.currentSelection.selectionsByLine != nil { for currentSelection in self.listView.currentSelection.selectionsByLine { label = currentSelection.string() } } return label } } //MARK: - KMBOTAOutlineViewDelegate extension KMOutlineViewController: KMBOTAOutlineViewDelegate { func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, rightDidMoseDown: KMBOTAOutlineItem, event: NSEvent) { let row = outlineView.outlineView.row(forItem: rightDidMoseDown) if outlineView.outlineView.rowView(atRow: row, makeIfNecessary: false) != nil { let rowView = outlineView.outlineView.rowView(atRow: row, makeIfNecessary: false) self.addRightMenu(view: rowView!, event: event) } } func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, didReloadData: KMBOTAOutlineItem) { self.updateExtempViewState() } func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, didSelectItem: [KMBOTAOutlineItem]) { if self.BOTAOutlineView.outlineView.selectedRowIndexes.count == 1 { self.isLocalEvent = true let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow:self.BOTAOutlineView.outlineView.selectedRow) as! KMBOTAOutlineItem if outlineItem.outline.destination != nil { if outlineItem.outline.destination.page() != nil { self.listView.go(toTargetPoint: outlineItem.outline.destination.point, on: outlineItem.outline.destination.page() , at: .top) } else { let alter = NSAlert() alter.alertStyle = NSAlert.Style.informational alter.messageText = NSLocalizedString("The target page is invalid, please relocate it.", comment: "") alter.addButton(withTitle: NSLocalizedString("OK", comment:"")) alter.beginSheetModal(for: self.view.window ?? NSWindow()) } } else if outlineItem.outline.action != nil { let alter = NSAlert() alter.alertStyle = NSAlert.Style.informational alter.messageText = NSLocalizedString("The target page is invalid, please relocate it.", comment: "") alter.addButton(withTitle: NSLocalizedString("OK", comment:"")) alter.beginSheetModal(for: self.view.window ?? NSWindow()) // self.listView.perform(outlineItem.outline.action) } } } func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool { if outlineView.outlineView.selectedRowIndexes.count > 1 || outlineView.outlineView.selectedRow == -1 { return false } self.dragPDFOutline = items.first as? KMBOTAOutlineItem let indexSet = [outlineView.outlineView.clickedRow] let indexSetData: Data = try!NSKeyedArchiver.archivedData(withRootObject: indexSet, requiringSecureCoding: true) pasteboard.declareTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")], owner: self) pasteboard.setData(indexSetData, forType: NSPasteboard.PasteboardType(rawValue: NSPasteboard.PasteboardType.RawValue("kKMPDFViewOutlineDragDataType"))) return true } func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { var dragOperation = NSDragOperation.init(rawValue: 0) if index >= 0 { dragOperation = NSDragOperation.move } return dragOperation } func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { guard let dragOutlineItem = self.dragPDFOutline else { return false } let outlineItem: KMBOTAOutlineItem = (item ?? KMBOTAOutlineItem()) as! KMBOTAOutlineItem if index < 0 { return false } if outlineItem.parent == nil { var root = dragOutlineItem.parent while root?.parent?.children != nil { root = root?.parent! } if dragOutlineItem.parent!.isEqual(root) { if dragOutlineItem.outline.index > index { self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: root) } else { self.moveOutline(outlineItem: dragOutlineItem, index: index - 1, parent: root) } } else { self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: root) } } else { if dragOutlineItem.parent!.isEqual(item) { // if dragOutlineItem.outline.index != 0 { if dragOutlineItem.outline.index > index { self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: item as? KMBOTAOutlineItem) } else { self.moveOutline(outlineItem: dragOutlineItem, index: index - 1, parent: item as? KMBOTAOutlineItem) } // } else { // return false // } } else { var tOutline = outlineItem var isContains = false while (tOutline.parent != nil) { if tOutline.outline.isEqual(dragOutlineItem.outline) { isContains = true break } tOutline = tOutline.parent! } if isContains == false { self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: item as? KMBOTAOutlineItem) } } } self.BOTAOutlineView.selectItem(outlineItem: dragOutlineItem) return true } } //MARK: - NSTextFieldDelegate extension KMOutlineViewController: NSTextFieldDelegate { func controlTextDidEndEditing(_ obj: Notification) { if (self.renameTextField.isEqual(obj.object)) { let textField : NSTextField = obj.object as! NSTextField self.renamePDFOutline(outlineItem: self.renamePDFOutline, label: textField.stringValue) } } } //MARK: - NSPopoverDelegate extension KMOutlineViewController: NSPopoverDelegate { func popoverWillClose(_ notification: Notification) { let popover : NSPopover = notification.object as! NSPopover if popover.contentViewController!.isKind(of: KMOutlineEditViewController.self) { } } } //MARK: - NSMenuItemValidation extension KMOutlineViewController: NSMenuDelegate, NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { let action = menuItem.action if action == #selector(addItemAction) || action == #selector(addChildItemAction) || action == #selector(addHigherItemAction) || action == #selector(deleteItemAction) || action == #selector(editItemAction) || action == #selector(changeItemAction) || action == #selector(renameItemAction) || action == #selector(promoteItemAction) || action == #selector(demoteItemAction) { if self.BOTAOutlineView.outlineView.selectedRowIndexes.count > 1 { if action == #selector(deleteItemAction) { return true } return false } else if self.BOTAOutlineView.outlineView.selectedRowIndexes.count > 0 { if action == #selector(addChildItemAction) || action == #selector(changeItemAction) { return true } } if self.BOTAOutlineView.outlineView.clickedRow == -1 { if action == #selector(addItemAction) { return true } else { return false } } else { let outlineItem : KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem if outlineItem.outline.index > 0 { if action == #selector(demoteItemAction) { return true } } else { if action == #selector(demoteItemAction) { return false } } let parentOutline = outlineItem.outline.parent let grandparentOutline = parentOutline?.parent if grandparentOutline != nil { if action == #selector(addHigherItemAction) { return true } else if action == #selector(promoteItemAction) { return true } } else { if action == #selector(addHigherItemAction) { return false } else if action == #selector(promoteItemAction) { return false } } } return true } if (action == #selector(undo)) { return self.listView.undoManager?.canUndo ?? false } if (action == #selector(redo)) { return self.listView.undoManager?.canRedo ?? false } if (action == #selector(expandAllComments)) { var canExpand = false for row in 0.. $1.toIndex} for outlineItem in tempOutlineItems { outlineItem.outline.removeFromParent() let index = outlineItem.parent?.children.firstIndex(of: outlineItem) outlineItem.toIndex = index! outlineItem.parent?.children.removeObject(outlineItem) } //展开 for outlineItem in tempOutlineItems { outlineItem.parent?.isItemExpanded = true tempOutlineView.outlineView.expandItem(outlineItem.parent) } tempOutlineView.outlineView.reloadData() //删除需要取消选中 tempOutlineView.cancelSelect() //刷新nil数据 self.updateExtempViewState() self.listView.undoManager?.registerUndo(withTarget: self) { [weak self] targetType in self?.addOutline(outlineItems: tempOutlineItems) } } func addOutline(outlineItems: [KMBOTAOutlineItem]) { NSApp.mainWindow?.makeFirstResponder(self.BOTAOutlineView) let tempOutlineView = self.BOTAOutlineView! //先取消选中 tempOutlineView.cancelSelect() var tempOutlineItems: [KMBOTAOutlineItem] = outlineItems tempOutlineItems.sort(){$0.toIndex < $1.toIndex} for outlineItem in tempOutlineItems { if outlineItem.outline.label != nil { outlineItem.parent?.outline.insertChild(outlineItem.outline, at: UInt(outlineItem.toIndex)) } else { let outline = outlineItem.parent?.outline.insertChild(at: UInt(outlineItem.toIndex)) outline?.label = outlineItem.label outline?.destination = outlineItem.destination outlineItem.outline = outline! } outlineItem.parent?.children.insert(outlineItem, at: outlineItem.toIndex) } if tempOutlineView.data?.children.count == 0 || tempOutlineView.data == nil { tempOutlineView.inputData = self.listView.document.outlineRoot() } else { tempOutlineView.outlineView.reloadData() } //展开 // DispatchQueue.main.async { for outlineItem in tempOutlineItems { var tempParent = outlineItem while tempParent.parent != nil { tempParent.isItemExpanded = true tempParent = tempParent.parent! tempOutlineView.outlineView.expandItem(tempParent) } tempOutlineView.outlineView.expandItem(tempParent.parent) } // } self.updateExtempViewState() self.listView.undoManager?.registerUndo(withTarget: self) { [weak self] targetType in self?.deleteOutline(outlineItems: tempOutlineItems) } } @IBAction func undo(_ sender: Any) { if (self.listView.undoManager?.canUndo ?? false) { self.listView.undoManager?.undo() } } @IBAction func redo(_ sender: Any) { if (self.listView.undoManager?.canRedo ?? false) { self.listView.undoManager?.redo() } } }