|
@@ -0,0 +1,477 @@
|
|
|
+//
|
|
|
+// KMNBaseViewController.swift
|
|
|
+// PDF Reader Pro
|
|
|
+//
|
|
|
+// Created by tangchao on 2023/5/5.
|
|
|
+//
|
|
|
+
|
|
|
+import Cocoa
|
|
|
+
|
|
|
+// 基类 [抽象类]
|
|
|
+class KMNBaseViewController: NSViewController {
|
|
|
+ // 是否需要菜单
|
|
|
+ var needMenu = false {
|
|
|
+ didSet {
|
|
|
+ if (self.needMenu) {
|
|
|
+ self.addMenu(to: self.view)
|
|
|
+ } else {
|
|
|
+ self.removeMenu(to: self.view)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ deinit {
|
|
|
+ Swift.debugPrint(self.className + " 已释放")
|
|
|
+
|
|
|
+ self.removeNotifations()
|
|
|
+ }
|
|
|
+
|
|
|
+ override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+
|
|
|
+ if (self.needMenu) {
|
|
|
+ self.addMenu(to: self.view)
|
|
|
+ } else {
|
|
|
+ self.removeMenu(to: self.view)
|
|
|
+ }
|
|
|
+
|
|
|
+ self.addNotifations()
|
|
|
+ }
|
|
|
+
|
|
|
+ // Noti
|
|
|
+
|
|
|
+ func addNotifations() { }
|
|
|
+
|
|
|
+ func removeNotifations() {
|
|
|
+ NotificationCenter.default.removeObserver(self)
|
|
|
+ }
|
|
|
+
|
|
|
+ func km_add_office_multi(fileUrls: [URL], completionBlock:@escaping ([String])->Void) -> Void {
|
|
|
+ var fileUrlStrings: [String] = []
|
|
|
+
|
|
|
+ let dispatchGroup = Dispatch.DispatchGroup()
|
|
|
+ for (index, fileUrl) in fileUrls.enumerated() {
|
|
|
+ let filePath = fileUrl.path
|
|
|
+ let folderPath = "convertToPDF_\(index).pdf"
|
|
|
+ let savePath: String? = folderPath.kUrlToPDFFolderPath() as String
|
|
|
+ if (savePath == nil) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ dispatchGroup.enter()
|
|
|
+ KMConvertPDFManager.convertFile(filePath, savePath: savePath!) { success, errorDic in
|
|
|
+ if errorDic != nil || !success || !FileManager.default.fileExists(atPath: savePath!) {
|
|
|
+ dispatchGroup.leave()
|
|
|
+
|
|
|
+ if FileManager.default.fileExists(atPath: savePath!) {
|
|
|
+ try?FileManager.default.removeItem(atPath: savePath!)
|
|
|
+ }
|
|
|
+ let alert = NSAlert.init()
|
|
|
+ alert.alertStyle = .critical
|
|
|
+ var infoString = ""
|
|
|
+ if errorDic != nil {
|
|
|
+ for key in (errorDic! as Dictionary).keys {
|
|
|
+ infoString = infoString.appendingFormat("%@\n", errorDic![key] as! CVarArg)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ alert.informativeText = NSLocalizedString("Please install Microsoft Office to create PDFs from Office files", comment: "")
|
|
|
+ alert.messageText = NSLocalizedString("Failed to Create PDF", comment: "")
|
|
|
+ alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
|
|
|
+ alert.runModal()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if !savePath!.isPDFValid() {
|
|
|
+ dispatchGroup.leave()
|
|
|
+
|
|
|
+ let alert = NSAlert()
|
|
|
+ alert.alertStyle = .critical
|
|
|
+ alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
|
|
|
+ alert.runModal()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ fileUrlStrings.append(savePath!)
|
|
|
+ dispatchGroup.leave()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dispatchGroup.notify(queue: DispatchQueue.main) {
|
|
|
+ completionBlock(fileUrlStrings)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Open Password Files
|
|
|
+
|
|
|
+ private var lockedFiles: [URL] = []
|
|
|
+ func km_open_pdf_multi(type: KMPasswordInputWindowType = .open, progressBlock: ((_ index: Int, _ params: Any...)->Void)? = nil, completionBlock:@escaping ([CPDFDocument])->Void) {
|
|
|
+ NSPanel.km_open_pdf_multi_success(self.view.window!, panel: nil) { urls in
|
|
|
+ self.km_add_pdf_multi(fileUrls: urls, type: type, progressBlock: progressBlock, completionBlock: completionBlock)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func km_open_file_multi(type: KMPasswordInputWindowType = .open, progressBlock: ((_ index: Int, _ params: Any...)->Void)? = nil, completionBlock:@escaping ([CPDFDocument])->Void) {
|
|
|
+ NSPanel.km_open_multi_success(self.view.window!) { panel in
|
|
|
+ var array: [String] = []
|
|
|
+ for fileType in KMConvertPDFManager.supportFileType() {
|
|
|
+ array.append(fileType)
|
|
|
+ }
|
|
|
+ panel.allowedFileTypes = KMTools.pdfExtensions + array
|
|
|
+ } completion: { urls in
|
|
|
+ self.km_add_file_multi(fileUrls: urls, type: type, progressBlock: progressBlock, completionBlock: completionBlock)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func km_add_pdf_multi(fileUrlStrings: [String] ,type: KMPasswordInputWindowType = .open, progressBlock: ((_ index: Int, _ params: Any...)->Void)? = nil, completionBlock:@escaping ([CPDFDocument])->Void) {
|
|
|
+ var urls: [URL] = []
|
|
|
+ for string in fileUrlStrings {
|
|
|
+ urls.append(URL(fileURLWithPath: string))
|
|
|
+ }
|
|
|
+ self.km_add_pdf_multi(fileUrls: urls, type: type, progressBlock: progressBlock, completionBlock: completionBlock)
|
|
|
+ }
|
|
|
+
|
|
|
+ func km_add_pdf_multi(fileUrls: [URL] ,type: KMPasswordInputWindowType = .open, progressBlock: ((_ index: Int, _ params: Any...)->Void)? = nil, completionBlock:@escaping ([CPDFDocument])->Void) {
|
|
|
+ var results: [CPDFDocument] = []
|
|
|
+
|
|
|
+ self.lockedFiles.removeAll()
|
|
|
+ var index = 0
|
|
|
+ for url in fileUrls {
|
|
|
+ let document = CPDFDocument(url: url)
|
|
|
+ if (document!.isLocked) {
|
|
|
+ self.lockedFiles.append(url)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if let _document = document {
|
|
|
+ results.append(_document)
|
|
|
+ }
|
|
|
+
|
|
|
+ index += 1
|
|
|
+ if let _callback = progressBlock {
|
|
|
+ _callback(index, ((document != nil) ? document : CPDFDocument()) as Any, url)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (self.lockedFiles.count == 0) {
|
|
|
+ completionBlock(results)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+// if let _callback = progressBlock {
|
|
|
+// _callback(0, results)
|
|
|
+// }
|
|
|
+
|
|
|
+ self._openPasswordWindow_loop(fileUrl: self.lockedFiles.first!, type: type) { params in
|
|
|
+ index += 1
|
|
|
+ if (params.count <= 2) { // 参数错误
|
|
|
+ if let _callback = progressBlock { // 回调进度
|
|
|
+ _callback(index)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let fileUrl = params[0] as! URL
|
|
|
+ let result = params[1] as! KMPasswordInputWindowResult
|
|
|
+ let password = params[2] as? String
|
|
|
+ if (result == .cancel) {
|
|
|
+ if let _callback = progressBlock { // 回调进度
|
|
|
+ _callback(index, CPDFDocument() as Any, fileUrl, result)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let document = CPDFDocument(url: fileUrl)
|
|
|
+ if let _password = password { // 将文档进行解密
|
|
|
+ document?.unlock(withPassword: _password)
|
|
|
+ }
|
|
|
+ if let _callback = progressBlock { // 回调进度
|
|
|
+ _callback(index, document as Any, fileUrl, result, password as Any)
|
|
|
+ }
|
|
|
+ // 将文档加入返回数据
|
|
|
+ if let _document = document {
|
|
|
+ results.append(_document)
|
|
|
+ }
|
|
|
+ } completionBlock: {
|
|
|
+ completionBlock(results)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func km_add_file_multi(fileUrls: [URL] ,type: KMPasswordInputWindowType = .open, progressBlock: ((_ index: Int, _ params: Any...)->Void)? = nil, completionBlock:@escaping ([CPDFDocument])->Void) {
|
|
|
+ var pdfUrls: [URL] = []
|
|
|
+ var imageUrls: [URL] = []
|
|
|
+ var officeUrls: [URL] = []
|
|
|
+ for url in fileUrls {
|
|
|
+ let type = url.pathExtension.lowercased()
|
|
|
+ if (KMTools.isPDFType(type)) {
|
|
|
+ pdfUrls.append(url)
|
|
|
+ }
|
|
|
+ if (KMTools.isImageType(type)) {
|
|
|
+ imageUrls.append(url)
|
|
|
+ }
|
|
|
+ if (KMTools.isOfficeType(type)) {
|
|
|
+ officeUrls.append(url)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (officeUrls.count == 0) {
|
|
|
+ self.km_add_pdf_multi(fileUrls: pdfUrls, type: type, progressBlock: progressBlock) { documents in
|
|
|
+ var index = documents.count
|
|
|
+ var _documents: [CPDFDocument] = []
|
|
|
+ for imageUrl in imageUrls {
|
|
|
+ index += 1
|
|
|
+ let document = CPDFDocument()
|
|
|
+ let image = NSImage(contentsOfFile: imageUrl.path)
|
|
|
+// document?.insertPage(image!.size, withImage: imageUrl.path, at: 0)
|
|
|
+ document?.km_insertPage(image?.size ?? .zero, withImage: imageUrl.path, at: 0)
|
|
|
+ _documents.append(document!)
|
|
|
+
|
|
|
+ if let _callback = progressBlock { // 回调进度
|
|
|
+ _callback(index, document as Any, imageUrl)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ completionBlock(documents + _documents)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ self.km_add_office_multi(fileUrls: officeUrls) { [unowned self] fileUrlStrings in
|
|
|
+ var officeDocuments: [CPDFDocument] = []
|
|
|
+ var index = 0
|
|
|
+ for fileUrlString in fileUrlStrings {
|
|
|
+ index += 1
|
|
|
+ let document = CPDFDocument(url: URL(fileURLWithPath: fileUrlString))
|
|
|
+ officeDocuments.append(document!)
|
|
|
+
|
|
|
+ if let _callback = progressBlock { // 回调进度
|
|
|
+ _callback(index, document as Any, URL(fileURLWithPath: fileUrlString))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.km_add_pdf_multi(fileUrls: pdfUrls) { documents in
|
|
|
+ var index = documents.count + officeDocuments.count
|
|
|
+ var _documents: [CPDFDocument] = []
|
|
|
+ for imageUrl in imageUrls {
|
|
|
+ index += 1
|
|
|
+ let document = CPDFDocument()
|
|
|
+ let image = NSImage(contentsOfFile: imageUrl.path)
|
|
|
+// document?.insertPage(image!.size, withImage: imageUrl.path, at: 0)
|
|
|
+ document?.km_insertPage(image!.size, withImage: imageUrl.path, at: 0)
|
|
|
+ _documents.append(document!)
|
|
|
+
|
|
|
+ if let _callback = progressBlock { // 回调进度
|
|
|
+ _callback(index, document as Any, imageUrl)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ completionBlock(officeDocuments + documents + _documents)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - ProgressBlock Params Fetch
|
|
|
+
|
|
|
+ func fetchProgressBlockParamsForDocument(params: Any...) -> CPDFDocument? {
|
|
|
+ return params.first as? CPDFDocument
|
|
|
+ }
|
|
|
+ func fetchProgressBlockParamsForFileUrl(params: Any...) -> URL? {
|
|
|
+ if (params.count < 2) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return params[1] as? URL
|
|
|
+ }
|
|
|
+
|
|
|
+ func fetchProgressBlockParamsForResult(params: Any...) -> KMPasswordInputWindowResult? {
|
|
|
+ if (params.count <= 2) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return params[2] as? KMPasswordInputWindowResult
|
|
|
+ }
|
|
|
+ func fetchProgressBlockParamsForPassword(params: Any...) -> String? {
|
|
|
+ if (params.count <= 2) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return params.last as? String
|
|
|
+ }
|
|
|
+ func fetchProgressBlockParamsIsPasswordFile(params: Any...) -> Bool {
|
|
|
+ if (params.count <= 2) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Open Password Window
|
|
|
+ // 留意:
|
|
|
+ // -会直接弹密码弹窗,不会判断文档是否加密
|
|
|
+ // -在使用前最好判断下文件是否已加密
|
|
|
+
|
|
|
+ func openPasswordWindow(fileUrlString: String, type: KMPasswordInputWindowType = .open, completionBlock:@escaping (KMPasswordInputWindowResult, String?)->Void) {
|
|
|
+ self.openPasswordWindow(fileUrl: URL(fileURLWithPath: fileUrlString), type: type, completionBlock: completionBlock)
|
|
|
+ }
|
|
|
+
|
|
|
+ func openPasswordWindow(fileUrl: URL, type: KMPasswordInputWindowType = .open, completionBlock:@escaping (KMPasswordInputWindowResult, String?)->Void) {
|
|
|
+ KMPasswordInputWindow.openWindow(window: self.view.window!, type: type, url: fileUrl, callback: completionBlock)
|
|
|
+ }
|
|
|
+
|
|
|
+ func openPasswordWindow_success(fileUrlString: String, type: KMPasswordInputWindowType = .open, completionBlock:@escaping (String)->Void) {
|
|
|
+ self.openPasswordWindow_success(fileUrl: URL(fileURLWithPath: fileUrlString), type: type, completionBlock: completionBlock)
|
|
|
+ }
|
|
|
+
|
|
|
+ func openPasswordWindow_success(fileUrl: URL, type: KMPasswordInputWindowType = .open, completionBlock:@escaping (String)->Void) {
|
|
|
+ KMPasswordInputWindow.success_openWindow(window: self.view.window!, url: fileUrl, callback: completionBlock)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ fileprivate func _openPasswordWindow_loop(fileUrl: URL, type: KMPasswordInputWindowType, progressBlock: ((_ params: Any...)->Void)?, completionBlock:@escaping ()->Void) {
|
|
|
+ KMPasswordInputWindow.openWindow(window: self.view.window!, type: type, url: fileUrl) { [weak self] result, password in
|
|
|
+ // 将结果返回
|
|
|
+ if let _callback = progressBlock {
|
|
|
+ _callback(fileUrl, result, password as Any)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 进行下一个
|
|
|
+ self?.lockedFiles.removeFirst()
|
|
|
+ if let _fileUrl = self?.lockedFiles.first {
|
|
|
+ self?._openPasswordWindow_loop(fileUrl: _fileUrl, type: type, progressBlock: progressBlock, completionBlock: completionBlock)
|
|
|
+ } else {
|
|
|
+ completionBlock()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Progress Window
|
|
|
+
|
|
|
+ var progressC: SKProgressController?
|
|
|
+ func showProgressWindow(message: String = "") {
|
|
|
+ if (self.progressC != nil) {
|
|
|
+ self.hiddenProgressWindow()
|
|
|
+ }
|
|
|
+
|
|
|
+ let progressC = SKProgressController()
|
|
|
+ progressC.window?.backgroundColor = NSColor.km_init(hex: "#36383B")
|
|
|
+ progressC.window?.contentView?.wantsLayer = true
|
|
|
+ progressC.window?.contentView?.layer?.backgroundColor = NSColor.km_init(hex: "#36383B").cgColor
|
|
|
+ progressC.progressField.textColor = NSColor.white
|
|
|
+ progressC.showClose = false
|
|
|
+ progressC.message = message
|
|
|
+
|
|
|
+ self.progressC = progressC
|
|
|
+ self.view.window?.beginSheet(progressC.window!)
|
|
|
+ }
|
|
|
+
|
|
|
+ func hiddenProgressWindow() {
|
|
|
+ if let _progressC = self.progressC {
|
|
|
+ if let _window = _progressC.window {
|
|
|
+ self.view.window?.endSheet(_window)
|
|
|
+ }
|
|
|
+ self.progressC = nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Menu Add & Remove
|
|
|
+
|
|
|
+ public func addMenu(to view: NSView?) {
|
|
|
+ if let menuView = view {
|
|
|
+ self.addMenu(to: menuView)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ self.addMenu(to: self.view)
|
|
|
+ }
|
|
|
+
|
|
|
+ public func removeMenu(to view: NSView?) {
|
|
|
+ if let menuView = view {
|
|
|
+ self.removeMenu(to: menuView)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ self.removeMenu(to: self.view)
|
|
|
+ }
|
|
|
+
|
|
|
+ private func addMenu(to view: NSView) {
|
|
|
+ // 先移除
|
|
|
+ self.removeMenu(to: view)
|
|
|
+
|
|
|
+ let menu = NSMenu()
|
|
|
+ menu.delegate = self
|
|
|
+ view.menu = menu
|
|
|
+ }
|
|
|
+ private func removeMenu(to view: NSView) {
|
|
|
+ view.menu?.delegate = nil
|
|
|
+ view.menu = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Document isDocumentEdited
|
|
|
+
|
|
|
+ public func setDocumentEditedState(window: NSWindow? = nil) {
|
|
|
+ var _win = window
|
|
|
+ if (_win == nil) {
|
|
|
+ _win = self.view.window
|
|
|
+ }
|
|
|
+ guard let _window = _win else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ guard let _document = NSDocumentController.shared.document(for: _window) else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ self.setDocumentEditedState(document: _document)
|
|
|
+ }
|
|
|
+
|
|
|
+ public func setDocumentEditedState(url: URL? = nil) {
|
|
|
+ if let _url = url {
|
|
|
+ KMTools.setDocumentEditedState(url: _url)
|
|
|
+ } else {
|
|
|
+ self.setDocumentEditedState(window: self.view.window)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func setDocumentEditedState(document: NSDocument) {
|
|
|
+ km_synchronized(document) {
|
|
|
+ if let _document = document as? KMMainDocument {
|
|
|
+ _document.km_updateChangeCount(.changeDone)
|
|
|
+ } else {
|
|
|
+ document.updateChangeCount(.changeDone)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func clearDocumentEditedState(window: NSWindow? = nil) {
|
|
|
+ var _win = window
|
|
|
+ if (_win == nil) {
|
|
|
+ _win = self.view.window
|
|
|
+ }
|
|
|
+ guard let _window = _win else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ guard let _document = NSDocumentController.shared.document(for: _window) else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ self.clearDocumentEditedState(document: _document)
|
|
|
+ }
|
|
|
+
|
|
|
+ public func clearDocumentEditedState(url: URL? = nil) {
|
|
|
+ if let _url = url {
|
|
|
+ KMTools.clearDocumentEditedState(url: _url)
|
|
|
+ } else {
|
|
|
+ self.clearDocumentEditedState(window: self.view.window)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func clearDocumentEditedState(document: NSDocument) {
|
|
|
+ km_synchronized(document) {
|
|
|
+ if let _document = document as? KMMainDocument {
|
|
|
+ _document.km_updateChangeCount(.changeCleared)
|
|
|
+ } else {
|
|
|
+ document.updateChangeCount(.changeCleared)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension KMNBaseViewController: NSMenuDelegate, NSMenuItemValidation {
|
|
|
+ func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ func menuNeedsUpdate(_ menu: NSMenu) {
|
|
|
+ menu.removeAllItems()
|
|
|
+ }
|
|
|
+}
|