// // KMMergeViewController.swift // PDF Reader Pro // // Created by tangchao on 2022/11/23. // import Cocoa import PDFKit import CoreGraphics class KMMergePage: CPDFPage { var drawingPage: CPDFPage! // override func draw(with box: PDFDisplayBox) { // super.draw(with: box) // // let context: CGContext = NSGraphicsContext.current?.graphicsPort as! CGContext // let pageSize = bounds(for: .cropBox).size // // drawPageWithContext(context: context, page: self.drawingPage, pageSize: pageSize) // } override func draw(with box: CPDFDisplayBox, to context: CGContext!) { super.draw(with: box, to: context) let pageSize = bounds(for: .cropBox).size drawPageWithContext(context: context, page: self.drawingPage, pageSize: pageSize) } func drawPageWithContext(context: CGContext, page: CPDFPage, pageSize: NSSize) { var originalSize = page.bounds(for: .cropBox).size /// 如果page的旋转角度为90,或者270,宽高交换 if ((page.rotation % 180) != 0) { originalSize = NSSize(width: originalSize.height, height: originalSize.width) } let wRadio: CGFloat = pageSize.width/originalSize.width let hRadio: CGFloat = pageSize.height/originalSize.height let radio = min(wRadio, hRadio) context.saveGState() var xTransform: CGFloat = (pageSize.width-originalSize.width * radio) / 2 var yTransform: CGFloat = (pageSize.height-originalSize.height * radio) / 2 context.translateBy(x: xTransform, y: yTransform) context.scaleBy(x: radio, y: radio) page.draw(with: .cropBox, to: context) page.transform(context, for: .cropBox) // var contextRef: CGContextRef = context } } class KMMergeTestWindow: NSWindow { private var mergeController: KMMergeViewController! override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) { super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag); isReleasedWhenClosed = false let controller: KMMergeViewController = KMMergeViewController() contentView?.addSubview(controller.view) controller.view.frame = CGRect(x: 0, y: 0, width: contentRect.size.width, height: contentRect.size.height) mergeController = controller } } protocol KMMergeViewController_collectionView_delegate : NSObjectProtocol { func collection_menu(for event: NSEvent) -> NSMenu? } class KMMergeViewController_collectionView: NSCollectionView { weak var myDelegate: KMMergeViewController_collectionView_delegate! override func menu(for event: NSEvent) -> NSMenu? { guard let myDelegate = self.myDelegate else { return super.menu(for: event) } return myDelegate.collection_menu(for: event) // return super.menu(for: event) } } class KMMergeViewController: NSViewController { @IBOutlet weak var titleBar: NSBox! @IBOutlet weak var contentBox: NSBox! private var titleBarView: KMMergeTitleBar? private var scrollView: NSScrollView? private var contentionView: KMMergeViewController_collectionView? private var zoomScale: CGFloat = 0 private var zoomMinScale: CGFloat = 0.5 private var zoomMaxScale: CGFloat = 8 private lazy var dataArray: [KMMergeFileModel] = [] private lazy var deleteArray: [Int] = [] private var dragedIndexPaths: [IndexPath] = [] private let localForDraggedTypes = NSPasteboard.PasteboardType(rawValue: "localForDraggedTypes") private var pageRangeWindowController: KMPageRangePickerWindowController! private var mergeSettingWindowController: KMMergeSettingWindowController! private var outPDFDocument = CPDFDocument() private var lockedFiles: [URL] = [] override func viewDidLoad() { super.viewDidLoad() initDefaultValue() initSubViews() } /** * MARK: - 初始化值 */ func initDefaultValue() { titleBar.titlePosition = NSBox.TitlePosition.noTitle titleBar.contentViewMargins = NSSize.zero titleBar.boxType = NSBox.BoxType.custom titleBar.borderWidth = 0 zoomScale = 0 } func initSubViews() { let titleBarView_: KMMergeTitleBar = KMMergeTitleBar() titleBar.contentView?.addSubview(titleBarView_) titleBarView_.frame = NSMakeRect(0, 0, NSWidth(titleBar.bounds), NSHeight(titleBar.bounds)) titleBarView_.autoresizingMask = NSView.AutoresizingMask(rawValue: 18) titleBarView_.delegate = self titleBarView_.wantsLayer = true titleBarView_.layer?.backgroundColor = NSColor.white.cgColor titleBarView = titleBarView_ titleBarView?.setDeleteItemEnable(enable: false) titleBarView?.setPageRangeComboBoxHidden(isHidden: true) titleBarView_.itemClick = { [self] (itemIndex: KMMergeTitleBarButtonID) -> () in switch itemIndex { case .add: self.showAddMenu() break case .delete: for index in deleteArray { if index < dataArray.count { dataArray.remove(at: index) } } deleteArray.removeAll() self.titleBarView?.setDeleteItemEnable(enable: false) self.contentionView?.reloadData() break case .zoomOut: if zoomScale > zoomMaxScale { titleBarView?.setZoomItemEnable(zoomIn: false, enable: false) return } titleBarView?.setZoomItemEnable(zoomIn: true, enable: true) zoomScale += 0.5 contentionView?.reloadData() break case .zoomIn: if zoomScale < zoomMinScale { titleBarView?.setZoomItemEnable(zoomIn: true, enable: false) return } titleBarView?.setZoomItemEnable(zoomIn: false, enable: true) zoomScale -= 0.5 contentionView?.reloadData() break case .merge: if dataArray.count == 0 { let alert = NSAlert() alert.messageText = NSLocalizedString("没有文件", comment: "") alert.runModal() return } let windowController = KMMergeSettingWindowController.init(windowNibName: "KMMergeSettingWindowController") self.view.window?.beginSheet(windowController.window!) mergeSettingWindowController = windowController windowController.odd_even_mergeComboBox.isEnabled = dataArray.count == 2 windowController.cancelClick = { [self] (window: NSWindow) -> () in self.view.window?.endSheet(window) } windowController.mergeClick = { [self] (windowController: NSWindowController) -> () in if mergeSettingWindowController.odd_even_mergeComboBox.state == .on { /// 奇偶数穿插合并 let document = CPDFDocument() outPDFDocument = document mergeActionForOddEven() let panel = NSSavePanel() panel.nameFieldStringValue = "[新文件].pdf" let button = NSButton.init(checkboxWithTitle: "保存后打开文档", target: nil, action: nil) button.state = .on panel.accessoryView = button panel.isExtensionHidden = true let response = panel.runModal() if response == .OK { let url = panel.url let result = outPDFDocument!.write(to: panel.url!) if result { if button.state == .on { /// 开启文档 } else { NSWorkspace.shared.openFile(url!.path) } } } } else { /// 正常合并 let document = CPDFDocument() outPDFDocument = document mergeAction() let panel = NSSavePanel() panel.nameFieldStringValue = "[新文件].pdf" let button = NSButton.init(checkboxWithTitle: "保存后打开文档", target: nil, action: nil) button.state = .on panel.accessoryView = button panel.isExtensionHidden = true let response = panel.runModal() if response == .OK { let url = panel.url let result = document!.write(to: panel.url!) if result { if button.state == .on { /// 开启文档 } else { NSWorkspace.shared.openFile(url!.path) } } } } self.view.window?.endSheet(windowController.window!) } break case .cancel: if dataArray.count == 0 { self.view.window?.close() } else { let alert = NSAlert() alert.messageText = NSLocalizedString("关闭将不会保存当前操作", comment: "") alert.addButton(withTitle: NSLocalizedString("关闭", comment: "")) alert.addButton(withTitle: NSLocalizedString("取消", comment: "")) let response = alert.runModal() if response == .alertFirstButtonReturn { self.view.window?.close() } } break } } titleBarView_.pageRangeClick = { [self] (index: Int, pageString: String) -> () in let model = self.titleBarView?.model if index == 0 { model?.pageRange = .all model?.updateChildPageRange(pageRange: .all) } else if index == 1 { model?.pageRange = .oddPages model?.updateChildPageRange(pageRange: .oddPages) } else if index == 2 { model?.pageRange = .evenPages model?.updateChildPageRange(pageRange: .evenPages) } else if index == 3 { if !isValidPagesString(pagesString: pageString) { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: "") alert.runModal() return } model?.pageRangeString = pageString model?.updateChildPageRange(pageRange: .custom) let pageNumbers = findSelectPage(fileModel: model!) for i in 0 ... (model?.pages.count)!-1 { let pageModel = model?.pages[i] if pageNumbers .contains(Int(pageModel!.pageID)+1) { pageModel!.selected = true } else { pageModel!.selected = false } } } self.contentionView?.reloadData() } let scrollView_ = NSScrollView() contentBox.contentView?.addSubview(scrollView_) scrollView_.frame = NSMakeRect(0, 0, NSWidth(contentBox.bounds), NSHeight(contentBox.bounds)) scrollView_.autoresizingMask = NSView.AutoresizingMask(rawValue: 18) scrollView = scrollView_ let layout = NSCollectionViewFlowLayout() layout.sectionInset = NSEdgeInsetsMake(20, 20, 20, 20) layout.minimumLineSpacing = 0 layout.minimumInteritemSpacing = 2 let contentionView_ = KMMergeViewController_collectionView() contentionView_.autoresizingMask = NSView.AutoresizingMask(rawValue: 18) contentionView_.collectionViewLayout = layout contentionView_.dataSource = self contentionView_.delegate = self contentionView_.register(KMMergeCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID")) contentionView_.register(KMMergeCollectionPageViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "PageCellID")) contentionView_.registerForDraggedTypes([localForDraggedTypes]) scrollView?.documentView = contentionView_ contentionView = contentionView_ contentionView?.isSelectable = true contentionView?.allowsMultipleSelection = true contentionView?.myDelegate = self contentionView?.reloadData() } func mergeActionForOddEven() { if (dataArray.count <= 1) { return } var rootPDFOutlineArray: [CPDFOutline] = [] var pdfDocument = outPDFDocument var mergeOutline: Bool = true for fileModel in dataArray { if fileModel.pageRange != .all { mergeOutline = false } var pdfOutlineArray: [CPDFOutline] = [] if (fileModel.document.outlineRoot != nil) { rootPDFOutlineArray.append(fileModel.document.outlineRoot()) fetchAllChildren(outline: fileModel.document.outlineRoot(), containers: &pdfOutlineArray) pdfOutlineArray.removeObject(fileModel.document.outlineRoot()) } else { var rootOutline = CPDFOutline() fileModel.document.setOutlineRoot(rootOutline) if fileModel.document.outlineRoot() != nil { rootPDFOutlineArray.append(fileModel.document.outlineRoot()) } } for outline in pdfOutlineArray { /// 这段代码主要是调用他的getter方法;outline显示正确 outline.destination?.page().document.dataRepresentation() } } let oddDocument = dataArray.first?.document let oddCount: Int = Int(oddDocument!.pageCount) let evenDocument = dataArray.last?.document! let evenCount: Int = Int(evenDocument!.pageCount) let count = min(oddCount-1, evenCount-1) for i in 0 ... count { pdfDocument!.insertPageObject((oddDocument?.page(at: UInt(i)))!, at: pdfDocument!.pageCount) pdfDocument!.insertPageObject((evenDocument?.page(at: UInt(i)))!, at: pdfDocument!.pageCount) } if oddCount > evenCount { for i in count ... oddCount-1 { pdfDocument!.insertPageObject((oddDocument?.page(at: UInt(i)))!, at: pdfDocument!.pageCount) } } else if oddCount < evenCount { for i in count ... evenCount-1 { pdfDocument!.insertPageObject((evenDocument?.page(at: UInt(i)))!, at: pdfDocument!.pageCount) } } if mergeOutline { pdfDocument?.setOutlineRoot(CPDFOutline()) var index = 0 for outlineRoot in rootPDFOutlineArray { if outlineRoot != nil && outlineRoot.numberOfChildren > 0 { for i in 0 ... outlineRoot.numberOfChildren-1 { pdfDocument!.outlineRoot().insertChild(outlineRoot.child(at: UInt(i)), at: UInt(index)) index += 1 } } } } handerReDraw() } func mergeAction() { if (dataArray.count <= 1) { // return } var rootPDFOutlineArray: [CPDFOutline] = [] var pdfDocument = outPDFDocument var mergeOutline: Bool = true for fileModel in dataArray { if fileModel.pageRange != .all { mergeOutline = false } var pdfOutlineArray: [CPDFOutline] = [] if (fileModel.document.outlineRoot() != nil) { rootPDFOutlineArray.append(fileModel.document.outlineRoot()) fetchAllChildren(outline: fileModel.document.outlineRoot(), containers: &pdfOutlineArray) pdfOutlineArray.removeObject(fileModel.document.outlineRoot()) } else { var rootOutline = CPDFOutline() fileModel.document.setOutlineRoot(rootOutline) if fileModel.document.outlineRoot() != nil { rootPDFOutlineArray.append(fileModel.document.outlineRoot()) } } for outline in pdfOutlineArray { /// 这段代码主要是调用他的getter方法;outline显示正确 outline.destination?.page().document.dataRepresentation() } if fileModel.pageRange == .all { for i in 0 ... fileModel.document.pageCount-1 { pdfDocument!.insertPageObject(fileModel.document.page(at: UInt(i))!, at: pdfDocument!.pageCount) } } else if fileModel.pageRange == .oddPages { for i in 0 ... fileModel.document.pageCount-1 { if i % 2 == 1 { continue } pdfDocument!.insertPageObject(fileModel.document.page(at: UInt(i))!, at: pdfDocument!.pageCount) } } else if fileModel.pageRange == .evenPages { for i in 0 ... fileModel.document.pageCount-1 { if i % 2 == 0 { continue } pdfDocument!.insertPageObject(fileModel.document.page(at: UInt(i))!, at: pdfDocument!.pageCount) } } else if fileModel.pageRange == .custom { let pageNumbers = self.findSelectPage(fileModel: fileModel) for i in pageNumbers { pdfDocument!.insertPageObject(fileModel.document.page(at: UInt(i-1))!, at: pdfDocument!.pageCount) } } } if mergeOutline { pdfDocument?.setOutlineRoot( CPDFOutline()) var index = 0 for outlineRoot in rootPDFOutlineArray { if outlineRoot != nil && outlineRoot.numberOfChildren > 0 { for i in 0 ... outlineRoot.numberOfChildren-1 { pdfDocument!.outlineRoot().insertChild(outlineRoot.child(at: UInt(i)), at: UInt(index)) index += 1 } } } } handerReDraw() } func handerReDraw() { if mergeSettingWindowController.selectedIndex == 0 { /// 原始尺寸 } else { var pageSize = NSMakeSize(595, 841) if mergeSettingWindowController.selectedIndex == 1 { /// A4 595 x 841 pageSize = NSMakeSize(595, 841) } else if mergeSettingWindowController.selectedIndex == 2 { /// A3 841 x 1190 pageSize = NSMakeSize(841, 1190) } else if mergeSettingWindowController.selectedIndex == 3 { /// U.S.Letter 612 x 792 pageSize = NSMakeSize(612, 792) } else if mergeSettingWindowController.selectedIndex == 4 { /// U.S.Legal 612 x 1108 pageSize = NSMakeSize(612, 1108) } else if mergeSettingWindowController.selectedIndex == 5 { /// 自定义 595 x 841 // pageSize = NSMakeSize(595, 841) pageSize.width = CGFloat(mergeSettingWindowController.customWidthTextField.floatValue) pageSize.height = CGFloat(mergeSettingWindowController.customHeightTextField.floatValue) } var pagesArray: [CPDFPage] = [] let pageCount = outPDFDocument!.pageCount for _ in 0 ... pageCount-1 { pagesArray.append(outPDFDocument!.page(at: 0)!) outPDFDocument!.removePage(at: 0) } for i in 0 ... pageCount-1 { var page = KMMergePage() page.drawingPage = pagesArray[Int(i)] page.setBounds(NSMakeRect(0, 0, pageSize.width, pageSize.height), for: .cropBox) outPDFDocument!.insertPageObject(page, at: i) } /// 如果是自定义大小,删除所有的outline,因为合并出来的outline是无效的 if mergeSettingWindowController.pageSizeComboBox.indexOfSelectedItem == 5 { /// 自定义 595 x 841 let childCount: Int = Int(outPDFDocument!.outlineRoot().numberOfChildren) var array: [CPDFOutline] = [] for i in 0 ... childCount-1 { array.append((outPDFDocument!.outlineRoot().child(at: UInt(i)))!) } for outline in array { outline.removeFromParent() } } } } func fetchAllChildren(outline: CPDFOutline, containers: inout [CPDFOutline]) { if !containers.contains(outline) { containers.append(outline) } if outline != nil && outline.numberOfChildren > 0 { for i in 0 ... (outline.numberOfChildren-1) { containers.append(outline.child(at: i)!) fetchAllChildren(outline: outline.child(at: i)!, containers: &containers) } } } func showAddMenu() { let menu: NSMenu = NSMenu() let addFileitem = menu.addItem(withTitle: NSLocalizedString("添加文件", comment: ""), action: #selector(itemAddFileAction), keyEquivalent: "") addFileitem.target = self let addFolderitem = menu.addItem(withTitle: NSLocalizedString("添加文件夹", comment: ""), action: #selector(itemAddFolderAction), keyEquivalent: "") addFolderitem.target = self let addOpenFileitem = menu.addItem(withTitle: NSLocalizedString("添加已打开文件", comment: ""), action: #selector(itemAddOpenFileAction), keyEquivalent: "") addOpenFileitem.target = self addOpenFileitem.action = nil let point: NSPoint = NSMakePoint(0, -10) menu.popUp(positioning: nil, at: point, in: titleBarView?.addItem) } @objc func itemAddFileAction() { let openPanel = NSOpenPanel() openPanel.title = NSLocalizedString("选择并合并文件。按下键盘上的命令按钮,逐次点击目标文件,即可选择多个文件", comment: "") openPanel.canChooseDirectories = false //是否允许选择目录 openPanel.canChooseFiles = true //是否可以选择文件 openPanel.allowsMultipleSelection = true //是否允许多选 openPanel.allowedFileTypes = ["jpg","cur","bmp","jpeg","gif","png","tiff","tif",/*@"pic",*/"ico","icns","tga","psd","eps","hdr","jp2","jpc","pict","sgi","heic","pdf"] openPanel.beginSheetModal(for: view.window!, completionHandler: { result in if result != .OK { } else { for documentURL in openPanel.urls { self.addModel(documentURL: documentURL) } self.contentionView?.reloadData() self.dealLockedFiles() } }) } @objc func itemAddFolderAction() { let openPanel = NSOpenPanel() openPanel.title = NSLocalizedString("选择并合并文件。按下键盘上的命令按钮,逐次点击目标文件,即可选择多个文件", comment: "") openPanel.canChooseDirectories = true openPanel.canChooseFiles = false openPanel.allowsMultipleSelection = true openPanel.beginSheetModal(for: view.window!, completionHandler: { [self] result in if result != .OK { } else { var result: [URL] = [] for folderURL in openPanel.urls { findAllFiles(folder: folderURL, result: &result) } for documentURL in result { addModel(documentURL: documentURL) } self.contentionView?.reloadData() self.dealLockedFiles() } }) } func findAllFiles(folder: URL, result: inout [URL]) { let fileManager = FileManager.default var isDirectory: ObjCBool = ObjCBool(false) fileManager.fileExists(atPath: folder.path, isDirectory: &isDirectory) if (!isDirectory.boolValue) { return } let contents = try?fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil) if contents?.count == 0 { return } let array = ["jpg","cur","bmp","jpeg","gif","png","tiff","tif",/*@"pic",*/"ico","icns","tga","psd","eps","hdr","jp2","jpc","pict","sgi","heic","pdf"] for documentURL in contents! { var isDirectory: ObjCBool = ObjCBool(false) fileManager.fileExists(atPath: documentURL.path, isDirectory: &isDirectory) if (isDirectory.boolValue) { findAllFiles(folder: documentURL, result: &result) } else { if !array.contains(documentURL.pathExtension.lowercased()) { continue } result.append(documentURL) } } } @objc func itemAddOpenFileAction() { } func addModel(documentURL: URL) { let model: KMMergeFileModel = KMMergeFileModel(); model.documentURL = documentURL if documentURL.pathExtension.lowercased() == "pdf" { model.document = CPDFDocument.init(url: documentURL) if model.document.isLocked { lockedFiles.append(documentURL) return } model.myDocument = CPDFDocument.init(url: documentURL) model.pageRange = .all model.page = model.document.page(at: 0) if model.document.pageCount > 1 { for i in 0 ... model.document.pageCount-1 { let pageModel = KMMergePageModel() pageModel.selected = true pageModel.pageID = Int(i) model.pages.append(pageModel) } } else { } } else { let doucument = CPDFDocument.init(url: documentURL) model.document = doucument model.page = doucument?.page(at: 0) model.document.insertPageObject(model.page, at: 0) } self.dataArray.append(model) } func replaceModel(documentURL: URL, index: Int) { if index >= 0 && index < dataArray.count { let model: KMMergeFileModel = KMMergeFileModel(); model.documentURL = documentURL if documentURL.pathExtension.lowercased() == "pdf" { model.document = CPDFDocument.init(url: documentURL) model.myDocument = CPDFDocument.init(url: documentURL) model.pageRange = .all model.page = model.document.page(at: 0) if model.document.pageCount > 1 { for i in 0 ... model.document.pageCount { let pageModel = KMMergePageModel() pageModel.selected = true pageModel.pageID = Int(i) model.pages.append(pageModel) } } else { } } else { let doucument = CPDFDocument.init(url: documentURL) model.document = doucument model.page = doucument?.page(at: 0) model.document.insertPageObject(model.page, at: 0) } self.dataArray[index] = model } } func isValidPagesString(pagesString: String)-> Bool { var valid = false for ch in pagesString { if ch != "0" && ch != "1" && ch != "2" && ch != "3" && ch != "4" && ch != "5" && ch != "6" && ch != "7" && ch != "8" && ch != "9" && ch != "," && ch != "-" { valid = false break } else { valid = true } } return valid } func findSelectPage(fileModel: KMMergeFileModel) -> ([Int]) { if !isValidPagesString(pagesString: fileModel.pageRangeString) { return [] } var result: [Int] = [] let array = fileModel.pageRangeString.components(separatedBy: ",") for string in array { if string.isEmpty { return [] } else { let pages = string .components(separatedBy: "-") if pages.count > 2 { return [] } else if pages.count == 1 { let page = pages[0] if page.isEmpty || Int(page)! > fileModel.document.pageCount || Int(page)! == 0 { return [] } else { var hasSame: Bool = false for i in result { if i == Int(page)! { hasSame = true return [] } } if !hasSame { result.append(Int(page)!) } } } else if pages.count == 2 { let page1 = pages[0] let page2 = pages[1] if page1.isEmpty || page2.isEmpty || Int(page1)! >= Int(page2)! || Int(page2)! > fileModel.myDocument.pageCount || Int(page1)! == 0 { return [] } else { var hasSame: Bool = false for i in Int(page1)! ... Int(page2)! { for j in result { if j == i { hasSame = true return [] } } } if !hasSame { for i in Int(page1)! ... Int(page2)! { result.append(i) } } } } } } return result } func findFileModel(indexPath: IndexPath) -> (fileModel: KMMergeFileModel, pageIndex: Int) { var count: Int = 0 var flagModel : KMMergeFileModel! var index: Int = NSNotFound for model in dataArray { if (flagModel != nil) { break } if model.open { for i in 0...(model.document.pageCount-1) { if count == indexPath.item { index = Int(i) flagModel = model break } count += 1 } } else { if count == indexPath.item { flagModel = model break } count += 1 } } return (flagModel, index) } func allItemIsExpanded() -> Bool { for fileModel in dataArray { if fileModel.canExpand() && !fileModel.open { return false } } return true } func allItemIsFolded() -> Bool { for fileModel in dataArray { if fileModel.open { return false } } return true } func dealLockedFiles() { if lockedFiles.count > 0 { let documentURL = lockedFiles.first let alert = NSAlert() alert.messageText = documentURL!.lastPathComponent.appending(", 已被保护") alert.addButton(withTitle: "输入密码") alert.addButton(withTitle: "关闭") let response = alert.runModal() if response == .alertFirstButtonReturn { let window = KMPasswordInputWindow.createWindow() window?.documentURL = documentURL! window?.type = .open window?.itemClick = { (_, index: Int, string: String) in if index == 1 { self.view.window?.endSheet(window!) self.lockedFiles.remove(at: 0) self.dealLockedFiles() } else { self.view.window?.endSheet(window!) let model = KMMergeFileModel() model.documentURL = documentURL model.document = CPDFDocument(url: documentURL!) model.document.unlock(withPassword: string) model.myDocument = CPDFDocument.init(url: documentURL) model.pageRange = .all model.page = model.document.page(at: 0) model.password = string if model.document.pageCount > 1 { for i in 0 ... model.document.pageCount-1 { let pageModel = KMMergePageModel() pageModel.selected = true pageModel.pageID = Int(i) model.pages.append(pageModel) } } self.dataArray.append(model) self.contentionView?.reloadData() self.lockedFiles.remove(at: 0) self.dealLockedFiles() } } self.view.window?.beginSheet(window!) } else { self.lockedFiles.remove(at: 0) self.dealLockedFiles() } } } /** * MARK: Menu Actions */ @objc func expandAllMenuAction() { for fileModel in dataArray { if fileModel.canExpand() { fileModel.open = true } } self.contentionView?.reloadData() deleteArray.removeAll() titleBarView?.setDeleteItemEnable(enable: false) } @objc func foldAllMenuAction() { for fileModel in dataArray { fileModel.open = false } self.contentionView?.reloadData() } @objc func expandMenuAction(sender: NSMenuItem) { let index = sender.tag if index >= 0 && index < dataArray.count { let fileModel: KMMergeFileModel = dataArray[index] if fileModel.canExpand() { fileModel.open = true } self.contentionView?.reloadData() // if index < deleteArray.count { // deleteArray.remove(at: index) // } deleteArray.removeAll() if deleteArray.count > 0 { titleBarView?.setDeleteItemEnable(enable: true) } else { titleBarView?.setDeleteItemEnable(enable: false) } } } @objc func addMenuAction() { itemAddFileAction() } @objc func removeMenuAction(sender: NSMenuItem) { let index = sender.tag if index >= 0 && index < dataArray.count { dataArray.remove(at: index) self.contentionView?.reloadData() } } @objc func replaceMenuAction(sender: NSMenuItem) { let index = sender.tag if index >= 0 && index < dataArray.count { let openPanel = NSOpenPanel() openPanel.title = NSLocalizedString("选择并合并文件。按下键盘上的命令按钮,逐次点击目标文件,即可选择多个文件", comment: "") openPanel.canChooseDirectories = false openPanel.canChooseFiles = true openPanel.allowsMultipleSelection = false openPanel.allowedFileTypes = ["jpg","cur","bmp","jpeg","gif","png","tiff","tif",/*@"pic",*/"ico","icns","tga","psd","eps","hdr","jp2","jpc","pict","sgi","heic","pdf"] openPanel.beginSheetModal(for: view.window!, completionHandler: { result in if result != .OK { } else { for documentURL in openPanel.urls { self.replaceModel(documentURL: documentURL, index: index) } self.contentionView?.reloadData() } }) } } } extension KMMergeViewController: KMMergeTitleBarDelegate { func titleBar(titleBar: KMMergeTitleBar, itemDidClick: KMMergeTitleBarButtonID) { KMPrint(); } } extension KMMergeViewController: NSCollectionViewDataSource { func numberOfSections(in collectionView: NSCollectionView) -> Int { return 1 } func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { var count: Int = 0 for model in dataArray { if model.open { count += Int(model.document.pageCount) } else { count += 1 } } return count } func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { var count: Int = 0 var flagModel : KMMergeFileModel! var index: Int = NSNotFound for model in dataArray { if (flagModel != nil) { break } if model.open { for i in 0...(model.document.pageCount-1) { if count == indexPath.item { index = Int(i) flagModel = model break } count += 1 } } else { if count == indexPath.item { flagModel = model break } count += 1 } } if index == NSNotFound { let cellView: KMMergeCollectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID"), for: indexPath) as! KMMergeCollectionViewItem cellView.model = flagModel cellView.doubleClick = { [self] (viewItem: AnyObject) -> () in let cellItemView: KMMergeCollectionViewItem = viewItem as! KMMergeCollectionViewItem let model = cellItemView.model if model.canExpand() { model.open = true self.titleBarView?.setPageRangeComboBoxHidden(isHidden: false) self.titleBarView?.model = model self.contentionView?.reloadData() // if deleteArray.contains(indexPath.item) { // deleteArray.removeObject(indexPath.item) // } deleteArray.removeAll() if deleteArray.count > 0 { titleBarView?.setDeleteItemEnable(enable: true) } else { titleBarView?.setDeleteItemEnable(enable: false) } } } cellView.pageRangeClick = { [self] (viewItem: AnyObject) -> () in let cellItemView: KMMergeCollectionViewItem = viewItem as! KMMergeCollectionViewItem let model = cellItemView.model let window = KMPageRangePickerWindowController.init(windowNibName: "KMPageRangePickerWindowController") window.fileModel = model self.view.window?.beginSheet(window.window!) pageRangeWindowController = window window.cancelClick = { [self] (window: NSWindow)->() in self.view.window?.endSheet(window) } window.confirmClick = { [self] (windowController: NSWindowController)->() in if pageRangeWindowController.selectIndex == 0 { model.pageRange = .all model.updateChildPageRange(pageRange: .all) } else if pageRangeWindowController.selectIndex == 1 { model.pageRange = .oddPages model.updateChildPageRange(pageRange: .oddPages) } else if pageRangeWindowController.selectIndex == 2 { model.pageRange = .evenPages model.updateChildPageRange(pageRange: .evenPages) } else if pageRangeWindowController.selectIndex == 3 { model.pageRange = .custom model.pageRangeString = pageRangeWindowController.customPagesTextField.stringValue model.updateChildPageRange(pageRange: .custom) let pageNumbers = findSelectPage(fileModel: model) for i in 0 ... (model.pages.count)-1 { let pageModel = model.pages[i] if pageNumbers .contains(Int(pageModel.pageID)+1) { pageModel.selected = true } else { pageModel.selected = false } } } self.contentionView?.reloadItems(at: [indexPath]) self.view.window?.endSheet(windowController.window!) } } return cellView } else { let cellView: KMMergeCollectionPageViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "PageCellID"), for: indexPath) as! KMMergeCollectionPageViewItem let model = flagModel.pages[index] model.page = flagModel.document.page(at: UInt(index)) cellView.fileModel = flagModel cellView.model = model cellView.flodClick = { [self] (viewItem: AnyObject) -> () in let cellItemView: KMMergeCollectionPageViewItem = viewItem as! KMMergeCollectionPageViewItem let model = cellItemView.fileModel model.open = false self.titleBarView?.setPageRangeComboBoxHidden(isHidden: true) self.contentionView?.reloadData() } cellView.selectedClick = { [self] (viewItem: AnyObject) -> () in let cellItemView: KMMergeCollectionPageViewItem = viewItem as! KMMergeCollectionPageViewItem let model = cellItemView.model model.selected = !model.selected self.titleBarView?.setPageRangeComboBoxHidden(isHidden: false) cellView.fileModel.updatePagePange() self.titleBarView?.model = cellView.fileModel self.contentionView?.reloadItems(at: [indexPath]) } cellView.view.wantsLayer = true cellView.view.layer?.backgroundColor = NSColor.lightGray.cgColor return cellView } return NSCollectionViewItem() } } extension KMMergeViewController: NSCollectionViewDelegateFlowLayout { /** * MARK: item大小 */ func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize { return NSMakeSize(246 * (1 + zoomScale), 322 * (1 + zoomScale)) } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 0.01 } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return -5 } } extension KMMergeViewController: NSCollectionViewDelegate { func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set) -> Set { return indexPaths } func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) { let index: IndexPath = indexPaths.first! var i = 0 for model in dataArray { if i == index.item { model.selected = true } else { model.selected = false } i += 1 } deleteArray.append(index.item) if collectionView.selectionIndexes.count > 0 { titleBarView?.setDeleteItemEnable(enable: true) } else { titleBarView?.setDeleteItemEnable(enable: false) } } func collectionView(_ collectionView: NSCollectionView, shouldDeselectItemsAt indexPaths: Set) -> Set { return indexPaths } func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set) { let index: IndexPath = indexPaths.first! deleteArray.removeObject(index.item) if collectionView.selectionIndexes.count > 0 { titleBarView?.setDeleteItemEnable(enable: true) } else { titleBarView?.setDeleteItemEnable(enable: false) titleBarView?.setPageRangeComboBoxHidden(isHidden: true) } } func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set, with event: NSEvent) -> Bool { for indexPath in indexPaths { let pageIndex = findFileModel(indexPath: indexPath).pageIndex if pageIndex != NSNotFound { /// 页面不用拖拽 return false } } return true } func collectionView(_ collectionView: NSCollectionView, writeItemsAt indexPaths: Set, to pasteboard: NSPasteboard) -> Bool { let data: Data = try! NSKeyedArchiver.archivedData(withRootObject: indexPaths, requiringSecureCoding: true) pasteboard.declareTypes([self.localForDraggedTypes], owner: self) pasteboard.setData(data, forType: self.localForDraggedTypes) dragedIndexPaths.removeAll() for indexPath in indexPaths { dragedIndexPaths.append(indexPath) } return true } func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer, dropOperation proposedDropOperation: UnsafeMutablePointer) -> NSDragOperation { let pboard = draggingInfo.draggingPasteboard if (pboard.availableType(from: [localForDraggedTypes]) != nil) { return .move } return NSDragOperation.generic } func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool { let pboard = draggingInfo.draggingPasteboard if (pboard.availableType(from: [localForDraggedTypes]) != nil) { // let indexPathsData: Data = pboard.data(forType: self.localForDraggedTypes)! // let set = try!NSKeyedUnarchiver.unarchivedObject(ofClasses: Set, from: indexPathsData)! let dragIndex: Int = dragedIndexPaths.first!.item let toIndex = max(0, indexPath.item-1) if dragIndex < 0 || dragIndex > dataArray.count { return false } if dragIndex == toIndex { return false } /// 交互数据 let dragFileModel = dataArray[dragIndex] dataArray.removeObject(dragFileModel) dataArray.insert(dragFileModel, at: toIndex) self.contentionView?.reloadData() return true } return false } } extension KMMergeViewController : KMMergeViewController_collectionView_delegate { func collection_menu(for event: NSEvent) -> NSMenu? { let point = event.locationInWindow let rigthIndex = self.contentionView!.indexPathForItem(at: self.contentionView!.convert(point, from: nil)) if rigthIndex == nil { /// 空白处 let menu = NSMenu(title: "") if dataArray.count == 0 { } else { if allItemIsExpanded() { let foldAllItem = NSMenuItem(title: NSLocalizedString("折叠全部文件", comment: ""), action: #selector(foldAllMenuAction), keyEquivalent: "") foldAllItem.target = self menu.addItem(foldAllItem) } else if allItemIsFolded() { let expandAllItem = NSMenuItem(title: NSLocalizedString("展开全部文件", comment: ""), action: #selector(expandAllMenuAction), keyEquivalent: "") expandAllItem.target = self menu.addItem(expandAllItem) } else { let expandAllItem = NSMenuItem(title: NSLocalizedString("展开全部文件", comment: ""), action: #selector(expandAllMenuAction), keyEquivalent: "") expandAllItem.target = self menu.addItem(expandAllItem) let foldAllItem = NSMenuItem(title: NSLocalizedString("折叠全部文件", comment: ""), action: #selector(foldAllMenuAction), keyEquivalent: "") foldAllItem.target = self menu.addItem(foldAllItem) } } let addItem = NSMenuItem(title: NSLocalizedString("添加文件", comment: ""), action: #selector(addMenuAction), keyEquivalent: "") addItem.target = self menu.addItem(addItem) return menu } let cellView = self.contentionView!.item(at: rigthIndex!) if ((cellView?.isKind(of: KMMergeCollectionViewItem.self))! == true) { let fileCellView: KMMergeCollectionViewItem = cellView! as! KMMergeCollectionViewItem var index = 0 var indexTag = NSNotFound for fileModel in dataArray { if fileModel.isEqual(to: fileCellView.model) { indexTag = index break } index += 1 } let menu = NSMenu(title: "") let expandItem = NSMenuItem(title: NSLocalizedString("展开文件", comment: ""), action: #selector(expandMenuAction), keyEquivalent: "") expandItem.target = self expandItem.tag = indexTag if fileCellView.model.canExpand() { expandItem.action = #selector(expandMenuAction) } else { expandItem.action = nil } menu.addItem(expandItem) let expandAllItem = NSMenuItem(title: NSLocalizedString("展开全部文件", comment: ""), action: #selector(expandAllMenuAction), keyEquivalent: "") expandAllItem.target = self menu.addItem(expandAllItem) menu.addItem(NSMenuItem.separator()) let removeItem = NSMenuItem(title: NSLocalizedString("移除文件", comment: ""), action: #selector(removeMenuAction), keyEquivalent: "") removeItem.target = self removeItem.tag = indexTag menu.addItem(removeItem) menu.addItem(NSMenuItem.separator()) let replaceItem = NSMenuItem(title: NSLocalizedString("替换文件", comment: ""), action: #selector(replaceMenuAction), keyEquivalent: "") replaceItem.target = self replaceItem.tag = indexTag menu.addItem(replaceItem) let addItem = NSMenuItem(title: NSLocalizedString("添加文件", comment: ""), action: #selector(addMenuAction), keyEquivalent: "") addItem.target = self menu.addItem(addItem) return menu } return nil } }