// // KMBatchOperateBaseViewController.swift // PDF Reader Pro // // Created by kdanmobile on 2023/10/30. // import Cocoa //var kBatchQueueOperationsChanged = "kBatchQueueOperationsChanged" private var kBatchQueueOperationsChanged = 0x000101 class KMBatchOperateBaseViewController: NSViewController, KMBatchOperateProtocol{ var operateType: KMBatchOperationType? var convertType: KMConvertWithPDFType? var files: [KMBatchOperateFile]? lazy var queue: KMOperationQueue? = { var queue: KMOperationQueue? if !((self.view.window as? KMBatchWindow)?.isBatch ?? false) { queue = KMOperationQueue() } else { if self.operateType == .Convert { queue = KMConvertOperationQueue.sharedQueue } else if self.operateType == .Compress { queue = KMCompressOperationQueue.sharedQueue } else if self.operateType == .AddPassword { queue = KMAddPasswordOperationQueue.sharedQueue } else if self.operateType == .RemovePassword { queue = KMRemovePasswordOperationQueue.sharedQueue } else if self.operateType == .AddWatermark { queue = KMAddWatermarkOprationQueue.sharedQueue } else if self.operateType == .RemoveWatermark { queue = KMRemoveWatermarkOperationQueue.sharedQueue } else if self.operateType == .AddBackground { queue = KMAddBackgroundOperationQueue.sharedQueue } else if self.operateType == .RemoveBackground { queue = KMRemoveBackgroundOperationQueue.sharedQueue } else if self.operateType == .AddHeaderFooter { queue = KMAddHeaderFooterOperationQueue.sharedQueue } else if self.operateType == .RemoveHeaderFooter { queue = KMRemoveHeaderFooterQueue.sharedQueue } else if self.operateType == .AddBates { queue = KMAddBatesOperationQueue.sharedQueue } else if self.operateType == .RemoveBates { queue = KMRemoveBatesOperationQueue.sharedQueue } } return queue }() lazy var successFilePathURLArray: [URL]? = { let arr = NSMutableArray() return (arr as! [URL]) }() var name: String = "" var interfaceStatus: KMBatchOperateInterfaceStatus? var choosePath: String = "" init(files: [KMBatchOperateFile]) { super.init(nibName: String(describing: type(of: self)), bundle: nil) self.files = (files) } required init?(coder: NSCoder) { super.init(coder: coder) } deinit { // queue?.removeObserver(self, forKeyPath: "operations") } override func viewDidLoad() { super.viewDidLoad() self.queue?.addObserver(self, forKeyPath: "operations", options: .new, context: &kBatchQueueOperationsChanged) } func operateCompleted() { showWindowCloseButton() if successFilePathURLArray!.count > 0 { NSWorkspace.shared.activateFileViewerSelecting(successFilePathURLArray!) } self.interfaceStatus = .PrepareProcess } func showWindowCloseButton() { let btn = self.view.window?.standardWindowButton(NSWindow.ButtonType.closeButton) btn?.isHidden = false } func beginBatchOperation() { for i in 0.. KMBatchOperateLeftViewController? { guard let vc = self.view.window?.contentViewController as? KMBatchOperateSplitViewController, let viewController = vc.splitViewItems.first?.viewController as? KMBatchOperateLeftViewController else { return nil } return viewController } func beginQueueOperation() {//业务逻辑转移到数据处理层 // hiddenWindowCloseButtonIfNeeded() self.successFilePathURLArray?.removeAll() for i in 0.. 0 { self.interfaceStatus = .Processing } } func operationWithFile(_ file: KMBatchOperateFile) -> KMBatchOperation { var operation: KMBatchOperation? if self.operateType == .Convert { return KMBatchConvertOperation(file: file, convertType: self.convertType!) } else if self.operateType == .Compress { return KMCompressOperation(file: file, compressValue: 60) } else if self.operateType == .AddPassword { // handle AddPassword operation } else if self.operateType == .RemovePassword { return KMBatchRemovePasswordOperation(file: file) } else if self.operateType == .AddWatermark { // handle AddWatermark operation } else if self.operateType == .RemoveWatermark { return KMBatchRemoveWatermarkOperation(file: file) } else if self.operateType == .AddBackground { // handle AddBackground operation } else if self.operateType == .RemoveBackground { return KMBatchRemoveBackgroundOperation(file: file) } else if self.operateType == .AddHeaderFooter { // handle AddHeaderFooter operation } else if self.operateType == .RemoveHeaderFooter { return KMBatchRemoveHeaderFooterOperation(file: file) } else if self.operateType == .AddBates { // operation = KMBatchRemoveHeaderFooterOperation(file: file) } else if self.operateType == .RemoveBates { var op = KMBatchRemoveHeaderFooterOperation(file: file) op.isBates = true return op } else { } return operation ?? KMBatchOperation(file: file) } func cancelBatchOperation() { for i in 0..<(self.queue?.operations.count)! { let operation = self.queue?.operations[i] if !operation!.isExecuting { operation?.cancel() } else { // Do nothing } } self.interfaceStatus = .PrepareProcess } func hiddenWindowCloseButtonIfNeeded() { let btn = self.view.window?.standardWindowButton(NSWindow.ButtonType.closeButton) btn?.isHidden = true } func allPageNumbers(_ pageString: String) -> [NSNumber] { let array = pageString.components(separatedBy: ",") let pageNumbers = NSMutableArray() var isInvalid = false for s in array { if s == "" { isInvalid = true break } else { let pages = s.components(separatedBy: "-") if pages.count > 2 { isInvalid = true break } else if pages.count == 1 { let p = pages[0] if p == "" || Int(p) == 0 { isInvalid = true break } else { var isEqual = false for pageNumber in pageNumbers { if (pageNumber as AnyObject).intValue == Int(p) { isEqual = true isInvalid = true break } } if !isEqual { pageNumbers.add(NSNumber(value: Int(p)!)) } } } else if pages.count == 2 { let p1 = pages[0] let p2 = pages[1] if p1 == "" || p2 == "" || Int(p1)! >= Int(p2)! || Int(p1) == 0 { isInvalid = true break } else { var isEqual = false for i in Int(p1)!...Int(p2)! { for pageNumber in pageNumbers { if (pageNumber as AnyObject).intValue == i { isEqual = true isInvalid = true break } } } if !isEqual { for i in Int(p1)!...Int(p2)! { pageNumbers.add(NSNumber(value: i)) } } } } } } return pageNumbers as! [NSNumber] } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "operations" && context == &kBatchQueueOperationsChanged { if self.queue?.operations.count == 0 { DispatchQueue.main.async { self.operateCompleted() } } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } } extension KMBatchOperateBaseViewController{ func fileBeginOperate(_ file: KMBatchOperateFile, info: KMBatchBaseParameter) { DispatchQueue.main.async { info.status = .processing self.fetchFileListViewController()?.reloadFile(file) } } func fileOperating(_ file: KMBatchOperateFile, progress: CGFloat, info: KMBatchBaseParameter) { DispatchQueue.main.async { info.progress = Float(progress) self.fetchFileListViewController()?.reloadFile(file) } } func fileOperateSuccessed(_ file: KMBatchOperateFile, info: KMBatchBaseParameter) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { info.status = .Success if info.outPutPath?.count ?? 0 > 0{ let url: URL = URL(fileURLWithPath: info.outPutPath!) self.successFilePathURLArray?.append(url) } self.fetchFileListViewController()?.reloadFile(file) } } func fileOperateFailed(_ file: KMBatchOperateFile, error: NSError, info: KMBatchBaseParameter) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { info.status = .Failed info.error = error self.fetchFileListViewController()?.reloadData() } } func fileOperateCanceled(_ file: KMBatchOperateFile, info: KMBatchBaseParameter) { DispatchQueue.main.async { info.status = .Waiting info.error = nil self.fetchFileListViewController()?.reloadFile(file) } } }