// // KMBatchOperateAddHeaderFooterViewController.swift // PDF Reader Pro // // Created by liujiajie on 2023/11/7. // import Cocoa class KMBatchOperateAddHeaderFooterViewController: KMBatchOperateBaseViewController, NSTableViewDelegate,NSTableViewDataSource{ var isBates = false var onlyManagerTemplate = false var isBatchOperation = false var pdfView: CPDFView? @IBOutlet var topBaseView: NSView! @IBOutlet var bottomBaseView: NSView! @IBOutlet var tableView: NSTableView! @IBOutlet var titleLabel: NSTextField! @IBOutlet var actionButton: NSButton! @IBOutlet var managerTemplateTitleLabel: NSTextField! @IBOutlet var addButton: NSButton! @IBOutlet var blankView: KMBlankView! @IBOutlet var managerTemplateTopConstraint: NSLayoutConstraint! @IBOutlet var managerTemplateButtonHeightConstraint: NSButton! @IBOutlet var topHeightConstraint: NSLayoutConstraint! @IBOutlet var lineView: NSView! var haveFiles = false var currentObject: KMHeaderFooterObject? override var interfaceStatus: KMBatchOperateInterfaceStatus?{ set{ super.interfaceStatus = newValue if newValue == .PrepareProcess { DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { let files = NSMutableArray() for url in self.successFilePathURLArray! { if FileManager.default.fileExists(atPath: url.path) { files.add(url) } } if files.count > 0 { let workspace = NSWorkspace.shared workspace.activateFileViewerSelecting(files as! [URL]) } } self.actionButton.tag = 1 self.actionButton.title = KMLocalizedString("Apply", nil) self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) } else { self.actionButton.tag = 0 self.actionButton.title = KMLocalizedString("Cancel", nil) self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) } } get{ return super.interfaceStatus } } deinit { NotificationCenter.default.removeObserver(self) } override func viewDidLoad() { super.viewDidLoad() localizedLanguage() configuInterface() NotificationCenter.default.addObserver(self, selector: #selector(headerFootersNotification(notification:)), name: Notification.Name(rawValue: "KMBatchOperateHeaderFootersNotification"), object: nil) // NotificationCenter.default.addObserver(self, selector: #selector(themeChanged(notification:)), name: Notification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(batchFilesCountNotification(notification:)), name: NSNotification.Name("KMBatchFilesCountNotification"), object: nil) } func localizedLanguage() { self.addButton.title = " " + KMLocalizedString("Add Template", nil) self.actionButton.title = KMLocalizedString("Apply", nil) self.titleLabel.stringValue = KMLocalizedString("Apply", nil) } func configuInterface() { self.titleLabel.font = NSFont.systemFont(ofSize: 14) self.titleLabel.textColor = KMAppearance.Layout.h0Color() self.tableView.enclosingScrollView?.borderType = .noBorder self.actionButton.wantsLayer = true self.addButton.wantsLayer = true self.topHeightConstraint.constant = 0 var color = NSColor(red: 227.0/255.0, green: 228.0/255.0, blue: 230.0/255.0, alpha: 1) if KMAppearance.isSupportNewColor() { if KMAppearance.isDarkMode() { color = NSColor(red: 59.0/255.0, green: 61/255.0, blue: 64.0/255.0, alpha: 1) } } self.lineView.wantsLayer = true self.lineView.layer?.backgroundColor = color.cgColor self.addButton.imagePosition = .imageLeft self.addButton.image = NSImage(named: "KMImageNameHeaderFooterAddBtn") self.addButton.layer?.backgroundColor = KMAppearance.Interactive.s0Color().cgColor self.addButton.setTitleColor(KMAppearance.Layout.h0Color()) if (onlyManagerTemplate){ managerTemplateTitleLabel.isHidden = false titleLabel.isHidden = true self.topBaseView.isHidden = false topHeightConstraint.constant = 40 }else{ managerTemplateTitleLabel.isHidden = true titleLabel.isHidden = true self.topBaseView.isHidden = true topHeightConstraint.constant = 0 } self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) self.actionButton.imagePosition = .noImage if !self.onlyManagerTemplate { if self.files?.count ?? 0 > 0 { self.haveFiles = true } else { self.haveFiles = false } } else { self.haveFiles = true } updateViewColor() updateActionButtonbackgroundColor() self.addButton.layer?.cornerRadius = 1.0 self.actionButton.layer?.cornerRadius = 1.0 self.topBaseView.wantsLayer = true self.topBaseView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.bottomBaseView.wantsLayer = true self.bottomBaseView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.managerTemplateTitleLabel.font = NSFont.systemFont(ofSize: 14) self.managerTemplateTitleLabel.textColor = KMAppearance.Layout.h0Color() self.addButton.isHidden = false self.managerTemplateTitleLabel.stringValue = NSLocalizedString("Manage Templates", comment: "") self.view.addSubview(self.blankView) self.blankView.mas_makeConstraints { make in make?.top.equalTo()(self.topBaseView.mas_bottom) make?.left.equalTo()(self.view) make?.right.equalTo()(self.view) make?.bottom.equalTo()(self.bottomBaseView.mas_top) make?.height.greaterThanOrEqualTo()(200) } self.blankView.titleLabel.stringValue = KMLocalizedString("No Templates", nil) self.tableView.backgroundColor = KMAppearance.Layout.l0Color() let menu = NSMenu(title: "") if !self.isBatchOperation && !self.isBates { menu.addItem(withTitle: NSLocalizedString("Batch Add Header & Footer", comment: ""), action: #selector(buttonItemClick_addBatch(_:)), keyEquivalent: "") } else if !self.isBatchOperation && self.isBates { menu.addItem(withTitle: NSLocalizedString("Batch Add Bates Numbers", comment: ""), action: #selector(buttonItemClick_addBatch(_:)), keyEquivalent: "") } if !self.isBates { menu.addItem(withTitle: NSLocalizedString("Remove All Header & Footer Templates", comment: ""), action: #selector(buttonItemClick_CleanAll(_:)), keyEquivalent: "") } else { menu.addItem(withTitle: NSLocalizedString("Remove All Bates Numbers Templates", comment: ""), action: #selector(buttonItemClick_CleanAll(_:)), keyEquivalent: "") } self.view.menu = menu } func updateActionButtonbackgroundColor() { let row = self.tableView.selectedRow if (self.files?.count ?? 0 > 0 || self.pdfView != nil) && row > -1 { self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) } else { self.actionButton.setTitleColor(KMAppearance.Layout.w0Color().withAlphaComponent(0.6)) self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().withAlphaComponent(0.6).cgColor } } @objc func buttonItemClick_addBatch(_ sender: Any) { let baseWindowController = KMBatchOperateBaseWindowController(windowNibName: "KMBatchOperateBaseWindowController") if #available(macOS 10.13, *) { baseWindowController.window?.makeKeyAndOrderFront(nil) } else { baseWindowController.showWindow(nil) } let arr = NSMutableArray() let file = KMBatchOperateFile(filePath: self.pdfView!.document!.documentURL!.path, type: self.isBates ? .AddBates : .AddHeaderFooter) arr.add(file) baseWindowController.checkNeedPasswordSwitchToOperateType(operateType: self.isBates ? .AddBates : .AddHeaderFooter, files: arr as! [KMBatchOperateFile]) } @objc func buttonItemClick_CleanAll(_ sender: Any) { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = "" alert.informativeText = NSLocalizedString("Are you sure to delete all templates?", comment: "") alert.addButton(withTitle: NSLocalizedString("Delete", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in if response == .alertFirstButtonReturn { self.deleteAll() } } } @objc func headerFootersNotification(notification: Notification) { if let addHeaderFooter = notification.object as? KMBatchOperateAddHeaderFooterViewController { if self != addHeaderFooter { loadData() } } } @objc func themeChanged(notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.updateViewColor() } } @objc func batchFilesCountNotification(notification: Notification) { let files: Array? = notification.object as? [KMBatchOperateFile] self.files? = files ?? [] if files?.count ?? 0 > 0 { haveFiles = true } else { haveFiles = false } updateActionButtonbackgroundColor() } func loadData() { self.tableView.reloadData() } func updateViewColor() { if KMAppearance.isDarkMode() { self.bottomBaseView.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.addButton.layer?.backgroundColor = NSColor(red: 0.337, green: 0.345, blue: 0.353, alpha: 1).cgColor self.addButton.setTitleColor(NSColor.white) self.actionButton.layer?.backgroundColor = NSColor(red: 0.306, green: 0.498, blue: 0.859, alpha: 1).cgColor self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) } else { self.bottomBaseView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1).cgColor self.addButton.layer?.backgroundColor = NSColor(red: 0.855, green: 0.859, blue: 0.871, alpha: 1).cgColor self.addButton.setTitleColor(NSColor(red: 0.055, green: 0.067, blue: 0.078, alpha: 1)) self.actionButton.layer?.backgroundColor = NSColor(red: 0.153, green: 0.235, blue: 0.384, alpha: 1).cgColor self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) } } func deleteAll() { if self.isBates { for waterMark in KMHeaderFooterManager.defaultManager.onlyBatesObjects { KMHeaderFooterManager.defaultManager.removeHeaderFooter(waterMark) } } else { for waterMark in KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects { KMHeaderFooterManager.defaultManager.removeHeaderFooter(waterMark) } } reloadTable() loadData() postNotification() } func reloadTable() { self.tableView.noteNumberOfRowsChanged() var count = 0 var array: [KMHeaderFooterObject] = [] if self.isBates { count = KMHeaderFooterManager.defaultManager.onlyBatesObjects.count array = KMHeaderFooterManager.defaultManager.onlyBatesObjects } else { count = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects.count array = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects } if array.contains(self.currentObject ?? KMHeaderFooterObject()) { let row = array.firstIndex(of: self.currentObject!)! let indexSet = IndexSet(integer: row) self.tableView.selectRowIndexes(indexSet, byExtendingSelection: false) } updateActionButtonbackgroundColor() } func postNotification() { NotificationCenter.default.post(name: NSNotification.Name("KMBatchOperateHeaderFootersNotification"), object: self) } @IBAction func buttonClicked_AddHeaderFooter(_ sender: Any) { // guard let windowController = self.view.window?.windowController else { return } // var filePath: String? // var password: String? // if let mainWC = windowController as? MainWindowController { // // filePath = mainWC.pdfView?.document?.documentURL?.path // // password = (mainWC.pdfView?.document as? CPDFDocument)?.password // } // let wc = KMHeaderFooterManagerWindowController(baseFile: filePath, headerFooter: nil, password: password, type: .KMBatchModifyTemplateType_Add) // wc.isBates = self.isBates // wc.operateCallBack = { [weak self] obj in // self?.currentObject = obj // self?.loadData() // self?.postNotification() // DispatchQueue.main.async { // guard let strongSelf = self else { // return // } // strongSelf.tableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) // } // } // wc.beginSheetModal(for: NSApp.keyWindow!, completionHandler: nil) let filePath: String = Bundle.main.path(forResource: "Quick Start Guide", ofType: "pdf") ?? "" var cdocument = self.pdfView?.document if isBatchOperation { cdocument = CPDFDocument(url: URL(fileURLWithPath: filePath)) } let windowController = KMHeaderFooterWindowController(windowNibName: "KMHeaderFooterWindowController") windowController.isBatch = isBatchOperation windowController.type = .Add windowController.isBates = isBates windowController.pdfDocument = cdocument windowController.cancelAction = { [unowned self] controller in self.km_endSheet() } windowController.operateCallBack = { [unowned self] wController, model in self.currentObject = model self.loadData() self.postNotification() let set = NSIndexSet(index: 0) self.tableView.selectRowIndexes(set as IndexSet, byExtendingSelection: false) } self.km_beginSheet(windowC: windowController) } @IBAction func buttonClicked_Action(_ sender: NSButton) { if (!self.onlyManagerTemplate) { if (!self.haveFiles) { return } } if (self.onlyManagerTemplate) { if (sender.tag == 1) { var headFooter: KMHeaderFooterObject? let row = self.tableView.selectedRow if (row == -1) { NSSound.beep() return } else { if (self.isBates) { headFooter = KMHeaderFooterManager.defaultManager.onlyBatesObjects[row] } else { headFooter = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects[row] } } let openPanel = NSOpenPanel() openPanel.canChooseFiles = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.beginSheetModal(for: self.view.window!) { result in if (result == .OK) { for fileURL in openPanel.urls { self.hiddenWindowCloseButtonIfNeeded() self.successFilePathURLArray?.removeAll() if (self.isBates) { let file = KMBatchOperateFile(filePath: self.pdfView?.document.documentURL.path ?? "", type: .AddBates) if file.fileType == .PDF { file.addBatesInfo.pageChoice = KMBatchOperatePageChoice(rawValue: headFooter?.pageRangeType.rawValue ?? 0) ?? .All file.addBatesInfo.pageRangeString = headFooter?.pageRangeString file.addBatesInfo.savePath = fileURL.path if file.status == .Waiting { let op = KMBatchAddHeaderFooterOperation(file: file, headerFooter: headFooter!) op.delegate = self self.queue?.addOperation(op) } } } else { self.successFilePathURLArray?.removeAll() let file = KMBatchOperateFile(filePath: self.pdfView?.document.documentURL.path ?? "", type: .AddHeaderFooter) if file.fileType == .PDF { file.addHeaderFooterInfo.pageChoice = KMBatchOperatePageChoice(rawValue: headFooter?.pageRangeType.rawValue ?? 0) ?? .All file.addHeaderFooterInfo.pageRangeString = headFooter?.pageRangeString file.addHeaderFooterInfo.savePath = fileURL.path if file.status == .Waiting { let op = KMBatchAddHeaderFooterOperation(file: file, headerFooter: headFooter!) op.delegate = self self.queue?.addOperation(op) } } } if self.queue?.operations.count ?? 0 > 0 { self.interfaceStatus = .Processing } } } } } else if (sender.tag == 0) { if let cnt = self.queue?.operations.count, cnt > 0 { self.queue?.cancelAllOperations() } self.interfaceStatus = .PrepareProcess } } else { //点击开始 if (sender.tag == 1) { if (!self.checkAndResetTask()) { NSSound.beep() return; } var headFooter: KMHeaderFooterObject? let row = self.tableView.selectedRow if (row == -1 || self.files?.count ?? 0 < 1) { NSSound.beep() return; } else { if (self.isBates) { headFooter = KMHeaderFooterManager.defaultManager.onlyBatesObjects[row] } else { headFooter = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects[row] } } self.choosePathAndBeginOperation(headFooter!) } else if (sender.tag == 0) { if let cnt = self.queue?.operations.count, cnt > 0 { self.queue?.cancelAllOperations() } self.interfaceStatus = .PrepareProcess } } } func checkAndResetTask() -> Bool { if files?.count ?? 0 < 1 { return false } for i in 0..<(files?.count ?? 0) { if let file = files?[i] as? KMBatchOperateFile { if isBates { file.addBatesInfo.resetState() } else { file.addHeaderFooterInfo.resetState() } } } return true } func choosePathAndBeginOperation(_ obj: Any) { let openPanel = NSOpenPanel() openPanel.canChooseFiles = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.beginSheetModal(for: self.view.window!) { (result) in if result == NSApplication.ModalResponse.OK { for fileURL in openPanel.urls { self.choosePath = fileURL.path if self.isBates { self.beginAddBates(obj as! KMHeaderFooterObject) } else { self.beginAddHeaderFooter(obj as! KMHeaderFooterObject) } } } } } func beginAddBates(_ bates: KMHeaderFooterObject) { hiddenWindowCloseButtonIfNeeded() successFilePathURLArray?.removeAll() for i in 0..<(files?.count ?? 0) { if let file = files?[i] as? KMBatchOperateFile { if file.fileType == .PDF { file.addBatesInfo.savePath = choosePath if file.status == .Waiting { let operation = KMBatchAddHeaderFooterOperation(file: file, headerFooter: bates) operation.delegate = self queue?.addOperation(operation) } } } } if queue?.operationCount ?? 0 > 0 { interfaceStatus = .Processing } } func beginAddHeaderFooter(_ headerFooter: KMHeaderFooterObject) { hiddenWindowCloseButtonIfNeeded() successFilePathURLArray?.removeAll() for i in 0..<(files?.count ?? 0) { if let file = files?[i] as? KMBatchOperateFile { if file.fileType == .PDF { file.addHeaderFooterInfo.savePath = choosePath if file.status == .Waiting { let operation = KMBatchAddHeaderFooterOperation(file: file, headerFooter: headerFooter) operation.delegate = self queue?.addOperation(operation) } } } } if queue?.operationCount ?? 0 > 0 { interfaceStatus = .Processing } } func modify(obj: KMHeaderFooterObject) { // let windowController = self.view.window?.windowController // var filePath: String? = nil // var password: String? = nil // if let mainWindowController = windowController as? MainWindowController { // filePath = mainWindowController.mainViewController.listView?.document?.documentURL?.path // let document = mainWindowController.mainViewController.listView?.document as? CPDFDocument // password = document?.password // } // let wc = KMHeaderFooterManagerWindowController(baseFile: filePath, headerFooter: obj, password: password, type: .KMBatchModifyTemplateType_Edit) // wc?.isBates = self.isBates // let blockSelf = self // wc?.operateCallBack = { obj in // blockSelf.currentObject = obj // blockSelf.loadData() // blockSelf.reloadTable() // blockSelf.postNotification() // } // wc?.beginSheetModalForWindow(NSApp.keyWindow, completionHandler: nil) var filePath: String = Bundle.main.path(forResource: "Quick Start Guide", ofType: "pdf") ?? "" if self.pdfView?.document.documentURL.path.count ?? 0 > 0 { filePath = self.pdfView?.document.documentURL.path ?? "" } let cdocument = CPDFDocument(url: URL(fileURLWithPath: filePath)) let windowController = KMHeaderFooterWindowController(windowNibName: "KMHeaderFooterWindowController") windowController.headerFooterObj = obj windowController.isBatch = isBatchOperation windowController.type = .Edit windowController.isBates = isBates windowController.pdfDocument = cdocument windowController.cancelAction = { [unowned self] controller in self.km_endSheet() } windowController.operateCallBack = { wController, model in self.currentObject = model self.loadData() self.postNotification() let set = NSIndexSet(index: 0) self.tableView.selectRowIndexes(set as IndexSet, byExtendingSelection: false) } self.km_beginSheet(windowC: windowController) } func delete(obj: KMHeaderFooterObject) { KMHeaderFooterManager.defaultManager.removeHeaderFooter(obj) loadData() reloadTable() postNotification() } func headerFooterInterfaceSelectHeaderFooter(headerFooter: KMHeaderFooterObject) { self.tableView.reloadData() if let index = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects.firstIndex(of: headerFooter) { let indexSet = IndexSet(integer: index) self.tableView.selectRowIndexes(indexSet, byExtendingSelection: false) } } func batesInterfaceSelectBates(headerFooter: KMHeaderFooterObject) { self.tableView.reloadData() if let index = KMHeaderFooterManager.defaultManager.onlyBatesObjects.firstIndex(of: headerFooter) { let indexSet = IndexSet(integer: index) self.tableView.selectRowIndexes(indexSet, byExtendingSelection: false) } } func numberOfRows(in tableView: NSTableView) -> Int { var count = 0 if self.isBates { count = KMHeaderFooterManager.defaultManager.onlyBatesObjects.count } else { count = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects.count } self.blankView.isHidden = count != 0 return count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { var obj: KMHeaderFooterObject? = nil if self.isBates { obj = KMHeaderFooterManager.defaultManager.onlyBatesObjects[row] } else { obj = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects[row] } guard let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "mainCell"), owner: self) as? KMHeaderFooterTableCellView else { return nil } cellView.updateInterface(obj!) cellView.headerFooterTableCellViewCallback = { type in if type == .Edit { let indexSet = IndexSet(integer: row) self.tableView.selectRowIndexes(indexSet, byExtendingSelection: false) self.currentObject = obj self.modify(obj: obj!) } else { self.delete(obj: obj!) } } return cellView } func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { let rowView = KMTableRowView() return rowView } func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { if self.isBates { return KMHeaderFooterManager.defaultManager.onlyBatesObjects[row].cellHeight } else { return KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects[row].cellHeight } } func tableView(_ tableView: NSTableView, shouldSelect tableColumn: NSTableColumn?) -> Bool { return true } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { return true } func tableViewSelectionDidChange(_ notification: Notification) { let row = self.tableView.selectedRow updateActionButtonbackgroundColor() if row == -1 { return } if self.isBates { let bates = KMHeaderFooterManager.defaultManager.onlyBatesObjects[row] self.currentObject = bates for i in 0..<(self.files?.count ?? 0) { let file = self.files?[i] file?.addBatesInfo.pageChoice = KMBatchOperatePageChoice(rawValue: bates.pageRangeType.rawValue) ?? .All if file?.addBatesInfo.pageChoice == .Input { let arr = allPageNumbers(bates.pageRangeString) let sortedArray: NSArray = file!.pagesArrayIntersect(with: arr) as NSArray if sortedArray.count < 1 { file?.addBatesInfo.pageChoice = .All } else { file!.addBatesInfo.pageRangeString = sortedArray.componentsJoined(by: ",") } } } } else { let headerFooter = KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects[row] self.currentObject = headerFooter for i in 0..<(self.files?.count ?? 0) { let file = self.files?[i] file?.addHeaderFooterInfo.pageChoice = KMBatchOperatePageChoice(rawValue: headerFooter.pageRangeType.rawValue) ?? .All if file?.addHeaderFooterInfo.pageChoice == .Input { let arr = allPageNumbers(headerFooter.pageRangeString) let sortedArray: NSArray = file!.pagesArrayIntersect(with: arr) as NSArray if sortedArray.count < 1 { file?.addHeaderFooterInfo.pageChoice = .All } else { file!.addHeaderFooterInfo.pageRangeString = sortedArray.componentsJoined(by: ",") } } } } reloadTable() NotificationCenter.default.post(name: NSNotification.Name(rawValue: kNeedChangePageRangeNotification), object: nil) } } extension KMBatchOperateAddHeaderFooterViewController: NSMenuDelegate, NSMenuItemValidation{ func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { let action = menuItem.action if action == #selector(buttonItemClick_CleanAll(_:)) { if (self.isBates && KMHeaderFooterManager.defaultManager.onlyBatesObjects.count < 1) || (!self.isBates && KMHeaderFooterManager.defaultManager.onlyHeaderFooterObjects.count < 1){ return false } return true } return true } }