// // KMBatchProcessingTableView.swift // PDF Master // // Created by lizhe on 2022/11/18. // import Cocoa class KMBatchTableView: NSTableView { var mouseDownAction: (() -> Void)? // override func mouseDown(with event: NSEvent) { // super.mouseDown(with: event) // mouseDownAction?() // } } class KMBatchProcessingTableView: NSView { @IBOutlet var contentView: NSView! @IBOutlet weak var tableView: KMBatchTableView! var orderClickRow = -1 let tableRowPasteboardType: NSPasteboard.PasteboardType = NSPasteboard.PasteboardType(rawValue: "private.table-row") lazy var presenter: KMBatchProcessingTableViewPresenter! = KMBatchProcessingTableViewPresenter() weak var delegate: KMBatchProcessingTableViewDelegate? var selectModels: [KMBatchProcessingTableViewModel] = [] var inputType: KMBatchCollectionViewType = .convertPDF { didSet { self.reloadData() } } var isDrag = false /** @abstract 外部传入数据 @param inputData 文件路劲 */ var inputData: [URL]! { didSet { self.presenter.addData(data: inputData) } } //内部使用数据 var data: [KMBatchProcessingTableViewModel]? fileprivate var options: KMBatchProcessingTableViewOptions? override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Drawing code here. } // MARK: 初始化 public required init?(coder decoder: NSCoder) { super.init(coder: decoder) initContentView() setup() } override init(frame frameRect: NSRect) { super.init(frame: frameRect) initContentView() setup() } private func initContentView() { //绑定xib let resource = NSNib(nibNamed: String(describing: self.classForCoder.self), bundle: Bundle(for: self.classForCoder.self))! resource.instantiate(withOwner: self, topLevelObjects: nil) addSubview(contentView) contentView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ contentView.topAnchor.constraint(equalTo: topAnchor), contentView.leftAnchor.constraint(equalTo: leftAnchor), contentView.rightAnchor.constraint(equalTo: rightAnchor), contentView.bottomAnchor.constraint(equalTo: bottomAnchor)]) contentView.updateConstraintsForSubtreeIfNeeded() } func setup() { self.tableView.dataSource = self self.tableView.delegate = self // self.tableView.allowsMultipleSelection = true self.tableView.registerForDraggedTypes([NSPasteboard.PasteboardType.string, NSPasteboard.PasteboardType.fileURL, self.tableRowPasteboardType]) //支持拖拽 self.tableView.setDraggingSourceOperationMask([.copy, .delete], forLocal: false) self.tableView.mouseDownAction = { [unowned self] in self.cancelAllSelect() } self.options = .all self.presenter.initPresenter(view: self, data: []) } func reloadData() { for _ in self.tableView.tableColumns { self.tableView.removeTableColumn(self.tableView.tableColumns[0]) } if inputType == .imageToPDF { self.options = [.number, .name, .dimension, .size, .state] } else { self.options = [.number, .name, .order, .size, .state] } if (options!.contains(KMBatchProcessingTableViewOptions.number)) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString(" ", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.number.rawValue)) // column.resizingMask = .userResizingMask column.width = 40 self.tableView.addTableColumn(column) } if (options!.contains(KMBatchProcessingTableViewOptions.name)) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString("File Name", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.name.rawValue)) // column.resizingMask = .userResizingMask if self.inputType == .imageToPDF { column.width = 220 } else { column.width = self.canShowOrder() ? 180 : 344 } self.tableView.addTableColumn(column) } if (options!.contains(KMBatchProcessingTableViewOptions.order) && self.canShowOrder()) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString("Page Range", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.order.rawValue)) column.resizingMask = .userResizingMask column.width = 164 self.tableView.addTableColumn(column) } if (options!.contains(KMBatchProcessingTableViewOptions.dimension)) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString("Dimension", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.dimension.rawValue)) column.resizingMask = .userResizingMask column.width = 164 self.tableView.addTableColumn(column) } if (options!.contains(KMBatchProcessingTableViewOptions.size)) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString("Size", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.size.rawValue)) column.resizingMask = .userResizingMask column.width = 88 self.tableView.addTableColumn(column) } if (options!.contains(KMBatchProcessingTableViewOptions.state)) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString("Status", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.state.rawValue)) column.headerCell.textColor = NSColor.red column.resizingMask = .userResizingMask column.width = 56 self.tableView.addTableColumn(column) } if (options!.contains(KMBatchProcessingTableViewOptions.delete)) { let column = NSTableColumn() column.headerCell = KMBatchProcessingColumnHeaderCell.init() column.title = NSLocalizedString("", comment: "") column.identifier = NSUserInterfaceItemIdentifier(String(KMBatchProcessingTableViewOptions.delete.rawValue)) column.resizingMask = .userResizingMask column.width = 30 self.tableView.addTableColumn(column) } self.tableView.reloadData() } func canShowOrder() -> Bool { if (self.inputType == .imageToPDF || self.inputType == .security || self.inputType == .compress) { return false } else { return true } } } extension KMBatchProcessingTableView: NSTableViewDelegate { func numberOfRows(in tableView: NSTableView) -> Int { return self.data?.count ?? 0 } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { var cell: KMBatchProcessingTableCell? if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.number.rawValue)) { cell = KMBatchProcessingNumTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) } else if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.name.rawValue)) { cell = KMBatchProcessingNameTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) } else if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.order.rawValue)) { cell = KMBatchProcessingOrderTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) if cell != nil { (cell as? KMBatchProcessingOrderTableCell)!.cellAction = { [weak self] pageRange in self?.orderClickRow = row } } } else if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.dimension.rawValue)) { cell = KMBatchProcessingWidthTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) } else if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.size.rawValue)) { cell = KMBatchProcessingSizeTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) } else if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.state.rawValue)) { cell = KMBatchProcessingStateTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) } else if (tableColumn?.identifier.rawValue == String(KMBatchProcessingTableViewOptions.delete.rawValue)) { cell = KMBatchProcessingDeleteTableCell.init(frame: CGRect(x: 0, y: 0, width: tableColumn!.width, height:tableView.rowHeight)) cell?.action = { [unowned self] (view) in self.presenter.deleteData(model: view.model) } } if(cell != nil) { if (self.data!.count > row) { let model = self.data![row] model.type = self.inputType cell!.model = model } } return cell } func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { //悬浮删除 let tableRowView = KMBatchProcessingTableRowView() tableRowView.selectionHighlightStyle = .none tableRowView.model = self.data?[row] tableRowView.hoverCallback = { [unowned self] (mouseEntered, mouseBox) in if data != nil { for i in 0...self.data!.count - 1 { let model = self.data![i] if model.hover == true { model.hover = false let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) self.tableView.reloadData(forRowIndexes: IndexSet(integer: i), columnIndexes: IndexSet(integer: columnIndex)) if self.tableView.rowView(atRow: i, makeIfNecessary: false) is KMBatchProcessingTableRowView { let rowView: KMBatchProcessingTableRowView = self.tableView.rowView(atRow: i, makeIfNecessary: false) as! KMBatchProcessingTableRowView rowView.reloadData() } } if i == row { if mouseEntered { model.hover = true } else { model.hover = false } let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) self.tableView.reloadData(forRowIndexes: IndexSet(integer: i), columnIndexes: IndexSet(integer: columnIndex)) if self.tableView.rowView(atRow: i, makeIfNecessary: false) is KMBatchProcessingTableRowView { let rowView: KMBatchProcessingTableRowView = self.tableView.rowView(atRow: i, makeIfNecessary: false) as! KMBatchProcessingTableRowView rowView.reloadData() } } } } } tableRowView.rightMouseCallback = { [unowned self] (view, event) in // self.didSelectItem(shouldSelectRow: row) DispatchQueue.main.async { if self.tableView.rowView(atRow: row, makeIfNecessary: false) != nil { let rowView = self.tableView.rowView(atRow: row, makeIfNecessary: false) self.addRightMenu(view: rowView!, event: event) } } } // tableRowView.mouseDownCallback = { [unowned self] (mouseEntered, mouseBox) in // if self.canSelect(row: row) { // for i in 0...self.data!.count - 1 { // let model = self.data![i] // if model.select == true { // model.select = false // let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) // self.tableView.reloadData(forRowIndexes: IndexSet(integer: i), columnIndexes: IndexSet(integer: columnIndex)) // if self.tableView.rowView(atRow: i, makeIfNecessary: false) is KMBatchProcessingTableRowView { // let rowView: KMBatchProcessingTableRowView = self.tableView.rowView(atRow: i, makeIfNecessary: false) as! KMBatchProcessingTableRowView // rowView.reloadData() // } // } // // if i == row { // if mouseEntered { // model.select = true // } else { // model.select = false // } // let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) // self.tableView.reloadData(forRowIndexes: IndexSet(integer: i), columnIndexes: IndexSet(integer: columnIndex)) // if self.tableView.rowView(atRow: i, makeIfNecessary: false) is KMBatchProcessingTableRowView { // let rowView: KMBatchProcessingTableRowView = self.tableView.rowView(atRow: i, makeIfNecessary: false) as! KMBatchProcessingTableRowView // rowView.reloadData() // } // self.didSelectItem(shouldSelectRow: i) // } // } // // } else { // self.orderClickRow = -1 // } // } return tableRowView } func tableView(_ tableView: NSTableView, shouldSelect tableColumn: NSTableColumn?) -> Bool { return true } func tableView(_ tableView: NSTableView, didClick tableColumn: NSTableColumn) { } func tableViewSelectionDidChange(_ notification: Notification) { let tableView: NSTableView = notification.object as? NSTableView ?? NSTableView() if tableView == self.tableView { // 获取所有选中的行索引 let selectedIndexes = tableView.selectedRowIndexes if selectedIndexes.isEmpty { print("没有选中任何行") self.didSelectItems(indexs: IndexSet()) } else { // 打印所有选中的行索引 print("选中的行索引: \(selectedIndexes)") for index in selectedIndexes { print(index) } self.didSelectItems(indexs: selectedIndexes) } } } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { return true } func didSelectItems(indexs: IndexSet) { if self.data != nil && !self.isDrag { for i in 0..<(self.data?.count ?? 0) { let item = self.data?[i] if item != nil { if item!.select { item!.select = false self.tableView.reloadData(forRowIndexes: IndexSet(integer: i), columnIndexes: IndexSet()) self.reloadRowData(index: i) } } } self.selectModels.removeAll() for index in indexs { let model = self.data?[index] if model != nil { model?.select = true self.selectModels.append(model!) self.tableView.reloadData(forRowIndexes: IndexSet(integer: index), columnIndexes: IndexSet()) self.reloadRowData(index: index) } } // self.tableView.reloadData() } } func reloadRowData(index: Int) { let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) self.tableView.reloadData(forRowIndexes: IndexSet(integer: index), columnIndexes: IndexSet(integer: columnIndex)) if self.tableView.rowView(atRow: index, makeIfNecessary: false) is KMBatchProcessingTableRowView { let rowView: KMBatchProcessingTableRowView = self.tableView.rowView(atRow: index, makeIfNecessary: false) as! KMBatchProcessingTableRowView rowView.reloadData() } } func didSelectItem(shouldSelectRow row: Int) { self.didSelectItems(indexs: IndexSet(integer: row)) } func cancelAllSelect() { for i in 0...self.data!.count - 1 { let model = self.data![i] model.select = false model.hover = false let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) self.tableView.reloadData(forRowIndexes: IndexSet(integer: i), columnIndexes: IndexSet(integer: columnIndex)) if self.tableView.rowView(atRow: i, makeIfNecessary: false) is KMBatchProcessingTableRowView { let rowView: KMBatchProcessingTableRowView = self.tableView.rowView(atRow: i, makeIfNecessary: false) as! KMBatchProcessingTableRowView rowView.reloadData() } } } } extension KMBatchProcessingTableView: NSTableViewDataSource { func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { if self.inputType == .imageToPDF { return 80 } else { return 40 } } // func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { // let item: NSPasteboardItem = NSPasteboardItem() // // item.setString(String(row), forType: self.tableRowPasteboardType) // // return item // } func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { //自定义内部拖拽数据 let data: Data = try! NSKeyedArchiver.archivedData(withRootObject: rowIndexes, requiringSecureCoding: true) pboard.declareTypes([self.tableRowPasteboardType], owner: self) pboard.setData(data, forType: self.tableRowPasteboardType) return true } func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { guard dropOperation == .above else { return [] } self.isDrag = true self.cancelAllSelect() if let source = info.draggingSource as? NSTableView, source === tableView { tableView.draggingDestinationFeedbackStyle = .gap } else { tableView.draggingDestinationFeedbackStyle = .regular } return .move } func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool { var result: Bool = false; let pboard: NSPasteboard = info.draggingPasteboard if ((pboard.availableType(from: [NSPasteboard.PasteboardType.fileURL])) != nil) { //获取url var array: [URL] = [] for item: NSPasteboardItem in pboard.pasteboardItems! { let string: String = item.string(forType: NSPasteboard.PasteboardType.fileURL)! let url = NSURL(string: string) array.append(url! as URL) } //添加url self.presenter.insetData(data: array, index: row) result = true } else if ((pboard.availableType(from: [self.tableRowPasteboardType])) != nil) { //获取初始数据 let rowData: Data = pboard.data(forType: self.tableRowPasteboardType)! let rowIndexes: NSIndexSet = try!NSKeyedUnarchiver.unarchivedObject(ofClass: NSIndexSet.self, from: rowData)! if info.draggingSource as? NSTableView == tableView { //移除物件悬浮状态 if data != nil { let model = self.data![rowIndexes.firstIndex] model.hover = false let columnIndex = self.tableView.column(withIdentifier: NSUserInterfaceItemIdentifier(KMBatchProcessingTableViewOptions.delete.rawValue.description)) self.tableView.reloadData(forRowIndexes: rowIndexes as IndexSet, columnIndexes: IndexSet(integer: columnIndex)) } //数据处理 self.data!.move(with: IndexSet(rowIndexes), to: row) /** 数据刷新 */ tableView.beginUpdates() tableView.reloadData() tableView.endUpdates() // tableView.beginUpdates() // var oldIndexOffset = 0 // var newIndexOffset = 0 // // for oldIndex in rowIndexes { // if oldIndex < row { // tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1) // oldIndexOffset -= 1 // } else { // tableView.moveRow(at: oldIndex, to: row + newIndexOffset) // newIndexOffset += 1 // } // } // tableView.endUpdates() } result = true } self.isDrag = false return result } func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) { // Handle items dragged to Trash tableView.draggingDestinationFeedbackStyle = .none // if operation == .delete, let items = session.draggingPasteboard.pasteboardItems { // let indexes = items.compactMap{ $0.integer(forType: .tableViewIndex) } // // for index in indexes.reversed() { // self.data?.remove(at: index) // } // tableView.removeRows(at: IndexSet(indexes), withAnimation: .slideUp) // } DispatchQueue.main.async { self.isDrag = false } } } extension KMBatchProcessingTableView: KMBatchProcessingTableViewPresenterDelegate { func showData(presenter: KMBatchProcessingTableViewPresenter, data: [KMBatchProcessingTableViewModel]) { self.data = data self.tableView.reloadData() self.delegate?.reloadData(data: data) } } extension Array { mutating func move(from start: Index, to end: Index) { guard (0.. Bool { guard let data = data else { return false } if !self.isDrag && (self.orderClickRow != row && data[row].pageRange == .custom) { return true } else { return false } } }