// // 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") self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) } else { self.actionButton.tag = 0 self.actionButton.title = KMLocalizedString("Cancel") 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") self.actionButton.title = KMLocalizedString("Apply") self.titleLabel.stringValue = KMLocalizedString("Apply") } 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") 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] if let data = files?.first { if data.currentOperateType != self.operateType { return } } let viewC = notification.userInfo?[kObjectKey] as? NSViewController if let data = self.view.window?.isEqual(to: viewC?.view.window), data == false { return } 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) { let filePath: String = Bundle.main.path(forResource: NSLocalizedString("Quick Start Guide.pdf", comment: ""), ofType: "") ?? "" 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 = { [weak self] controller in self?.km_endSheet() } windowController.operateCallBack = { [weak 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) { var filePath: String = Bundle.main.path(forResource: NSLocalizedString("Quick Start Guide.pdf", comment: ""), ofType: "") ?? "" 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 = { [weak 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 } }