// // KMThumbnailViewController.swift // PDF Reader Pro // // Created by lxy on 2022/10/10. // @objc protocol KMThumbnailViewControllerDelegate { @objc optional func gotoPageEdit(thumbnailViewController:KMThumbnailViewController, pages: [Int]) @objc optional func pageDidSelect(controller: KMThumbnailViewController, pages: [Int]) @objc optional func controller(controller: KMThumbnailViewController, itemClick item: Any?, itemKey: KMItemKey, params: Any?) } class KMThumbnailViewController: KMBaseViewController { open weak var delegate: KMThumbnailViewControllerDelegate? @IBOutlet weak var headerView: NSView! @IBOutlet weak var titleLabel: NSTextField! @IBOutlet weak var thumbnailView: KMPDFThumbnailView! @IBOutlet var doublePageBtn: NSButton! @IBOutlet weak var lineView: NSView! var doublePageMode : Bool! = false var isLocalEvent: Bool = false //是否为本视图事件 var listView : CPDFListView! //注释状态 var annotationShowState: KMAnnotationViewShowType = .none { didSet { if self.thumbnailView != nil { self.thumbnailView.annotationShowState = self.annotationShowState } } } deinit { NotificationCenter.default.removeObserver(self) } override func viewWillAppear() { super.viewWillAppear() self.thumbnailView.reloadData() self.thumbnailView.collectionView.selectionIndexes = IndexSet(integer: IndexSet.Element(self.listView.currentPageIndex)) self.thumbnailView.collectionView.mouseDownCancelSelected = false } override func viewDidLoad() { super.viewDidLoad() self.view.wantsLayer = true self.view.layer?.backgroundColor = NSColor.clear.cgColor self.lineView.backgroundColor(NSColor.km_init(hex: "#EDEEF0")) self.headerView.wantsLayer = true self.headerView.layer?.backgroundColor = NSColor.km_init(hex: "#F7F8FA").cgColor self.titleLabel.stringValue = NSLocalizedString("Thumbnails", comment: "") self.titleLabel.font = NSFont.SFProTextSemiboldFont(14.0) self.titleLabel.textColor = NSColor.km_init(hex: "#252629") if self.listView.document != nil { self.thumbnailView.document = self.listView.document } // self.doublePageBtn.isHidden = true self.thumbnailView.delegate = self self.thumbnailView.collectionView.isSelectable = true self.thumbnailView.collectionView.allowsMultipleSelection = true self.thumbnailView.isShowPageSize = true self.thumbnailView.isNeedMarkerLine = true // self.thumbnailView.collectionView.allowsEmptySelection = false var allowedFileTypes: [String] = [] // if let _types = KMConvertPDFManagerOC.supportFileType() as? [String] { allowedFileTypes = KMTools.pdfExtensions + KMConvertPDFManager.supportFileType() // } else { // allowedFileTypes = KMTools.pdfExtensions + KMTools.imageExtensions // } self.thumbnailView.kmAllowedFileTypes = allowedFileTypes self.doublePageBtn.toolTip = NSLocalizedString("Single/Double column display", comment: "") self.pdfCurrentPageChange() self.initNotification() } func initNotification() { NotificationCenter.default.addObserver(self, selector: #selector(PDFViewEditingAreaDidChangedNotification), name: NSNotification.Name.init(rawValue: "kPDFViewEditingAreaDidChanged"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewPageCountChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewRotatePage"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewPageCountChangedNotification), name: NSNotification.Name.init(rawValue: "CPDFDocumentPageCountChangedNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(AnnotationDidChangeNoti), name: NSNotification.Name.init(rawValue: "CPDFPageDidAddAnnotationNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(AnnotationDidChangeNoti), name: NSNotification.Name.init(rawValue: "CPDFPageDidRemoveAnnotationNotification"), object: nil) } func removeNotification() { NotificationCenter.default.removeObserver(self) } //MARK: Public Method func reloadData () { if self.doublePageMode { self.thumbnailView.thumbnailSzie = CGSize(width: 86, height: 101) self.doublePageBtn.image = NSImage(named: "KMImageNameThumbnailPage") // self.thumbnailView.isNeedMarkerLine = false self.thumbnailView.hasDragLineView = false } else { self.thumbnailView.thumbnailSzie = CGSize(width: 156, height: 180) self.doublePageBtn.image = NSImage(named: "KMImageNameThumbnailDoublePage") // self.thumbnailView.isNeedMarkerLine = false } self.thumbnailView.reloadData() // self.km_resignFirstResponder() } func reloadDataAtIndexs (indexs: IndexSet) { var selectPages: [Int] = [] for index in indexs { selectPages.append(index+1) } DispatchQueue.main.async { var data = Set() for index in selectPages { data.insert(IndexPath(item: index-1, section: 0)) } self.thumbnailView.collectionView.reloadItems(at: data) // self.km_resignFirstResponder() } } //MARK: Private Method @IBAction func doublePageAction(_ sender: Any) { self.doublePageMode = !self.doublePageMode self.reloadData() } private func dragPagesForDescSort(_ indexs: IndexSet, _ toIndex: Int) { if (indexs.count <= 0) { return } /// 数据降序排序 let indexArray = indexs.sorted { index1, index2 in return index1 > index2 } var newIndexs: IndexSet = [] for index in indexArray { newIndexs.insert(index) } self.dragPages(newIndexs, toIndex) } @objc private func dragPages(_ indexs: IndexSet, _ toIndex: Int) { if (indexs.count <= 0) { return } //获取原有page的下标 和 开始添加的下标 var indexTemp: Int = toIndex var pageItems: [KMThumbnailPageItem] = [] for index in indexs { let newPage: CPDFPage = self.listView?.document.page(at: UInt(index)).copy() as! CPDFPage let pageItem = KMThumbnailPageItem(page: newPage, index: index, oldIndex: index) pageItems.append(pageItem) if (toIndex > index) { /// 删除了插入位置前面的页面,需要改变 toIndex indexTemp -= 1 } } //建立模型 var tPageItems: [KMThumbnailPageItem] = [] for index in 0...pageItems.count - 1 { let pageItem: KMThumbnailPageItem = pageItems[index] let tItem = KMThumbnailPageItem(page: pageItem.page, index: indexTemp + index, oldIndex: pageItem.index) tPageItems.append(tItem) } //进行移动 self.dragPageItems(pageItems: tPageItems) } private func km_resignFirstResponder() { NSApp.mainWindow?.makeFirstResponder(self) } //MARK: Noti @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) { if notification.object is CPDFDocument { let pdfdocument : CPDFDocument = notification.object as! CPDFDocument if pdfdocument.isEqual(self.listView.document) { if !isLocalEvent { // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { [unowned self] in self.pdfCurrentPageChange() self.delegate?.pageDidSelect?(controller: self, pages: [self.listView.currentPageIndex]) // } } isLocalEvent = false } } } @objc func KMPDFViewPageCountChangedNotification(notification: NSNotification) { if notification.object is CPDFDocument { let pdfdocument : CPDFDocument = notification.object as! CPDFDocument if pdfdocument.isEqual(self.listView.document) { if !isLocalEvent { // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { [unowned self] in self.reloadData() // } } isLocalEvent = false } } } @objc func AnnotationDidChangeNoti(notification: NSNotification) { if notification.object is CPDFAnnotation { let pdfAnnotation : CPDFAnnotation = notification.object as! CPDFAnnotation if (pdfAnnotation.page != nil) { var indexs: IndexSet = IndexSet() indexs.insert(IndexSet.Element(pdfAnnotation.page.pageIndex())) self.reloadDataAtIndexs(indexs: indexs) } } } @objc func PDFViewEditingAreaDidChangedNotification(notification: NSNotification) { if notification.object is CPDFDocument { let pdfdocument : CPDFDocument = notification.object as! CPDFDocument if pdfdocument.isEqual(self.listView.document) { var indexs: IndexSet = IndexSet() indexs.insert(IndexSet.Element(self.listView.currentPageIndex)) self.reloadDataAtIndexs(indexs: indexs) } } } func pdfCurrentPageChange () { if self.listView.document.pageCount == 0 { return } let insertIndex: Int = self.listView.currentPageIndex; var indexs: IndexSet = IndexSet() indexs.insert(insertIndex) self.selectPages(indexs: indexs) // self.thumbnailView.collectionView.scrollToItems(at: self.thumbnailView.collectionView.selectionIndexPaths, scrollPosition: NSCollectionView.ScrollPosition.centeredVertically) } //MARK: menu Action @IBAction func insertPageItemAction(menu:NSMenuItem) { if menu.tag == 0 { self.item_insertFile(sender: menu) } else if menu.tag == 1 { self.item_insertBankPage(sender: menu) } else if menu.tag == 2 { self.item_insertPage(sender: menu) } } @objc private func item_insertFile(sender: NSMenuItem?) { let panel = NSOpenPanel() panel.allowedFileTypes = ["pdf"] panel.beginSheetModal(for: self.view.window!) { [weak self] response in if (response == .cancel) { return } /// 处理page let insertIndex: Int = self!.thumbnailView.collectionView.selectionIndexes.last! let doucument = CPDFDocument(url: panel.url) if let data = doucument?.isLocked, data { KMPasswordInputWindow.openWindow(window: self!.view.window!, url: panel.url!) { [weak self] result, password in if (result == .cancel) { return } doucument?.unlock(withPassword: password) var newInsertIndex: Int = insertIndex + 1 var pages: [CPDFPage] = [] var indexs: IndexSet = IndexSet() for i in 0 ..< doucument!.pageCount { let page = doucument?.page(at: i) pages.append(page!) indexs.insert(newInsertIndex) newInsertIndex += 1 } self?.insertPagesForDescSort(pages: pages, indexs: indexs) } } else { var newInsertIndex: Int = insertIndex + 1 var pages: [CPDFPage] = [] var indexs: IndexSet = IndexSet() for i in 0 ..< doucument!.pageCount { let page = doucument?.page(at: i) pages.append(page!) indexs.insert(newInsertIndex) newInsertIndex += 1 } self?.insertPagesForDescSort(pages: pages, indexs: indexs) } } } @objc private func item_insertBankPage(sender: NSMenuItem?) { let insertIndex: Int = self.thumbnailView.collectionView.selectionIndexes.last! let page = self.listView?.document.page(at: UInt(insertIndex)) let document = CPDFDocument() document?.insertPage((page?.bounds.size)!, at: 0) let newPage: CPDFPage = (document?.page(at: 0))! self.insertPagesForDescSort(pages: [newPage], indexs: IndexSet(integer: insertIndex+1)) } @objc private func item_insertPage(sender: NSMenuItem?) { let windowController = KMPageEditInsertCustomPageWindowController(windowNibName: "KMPageEditInsertCustomPageWindowController") let insertIndex: Int = self.thumbnailView.collectionView.selectionIndexes.last! let page = self.listView?.document.page(at: UInt(insertIndex)) if (self.thumbnailView.collectionView.selectionIndexes.count > 0) { windowController.selectedPageSize = page!.bounds.size } self.view.window?.beginSheet(windowController.window!, completionHandler: { response in}) windowController.itemClick = { [weak self] (index: Int) in if (index == 1) { /// 取消 self?.view.window?.endSheet((windowController.window)!) return } /// 插入 /// 样式 let windowController_Insert: KMPageEditInsertCustomPageWindowController = windowController let type = windowController_Insert.typeIndex /// 页面大小 let pageSize = windowController_Insert.pageSize /// 方向 let direction = windowController_Insert.direction /// 插入位置 let insertIndex: Int = insertIndex if (type == 1) { /// 空白页 let document = CPDFDocument() document?.insertPage(pageSize, at: 0) let page: CPDFPage = (document?.page(at: 0))! if (direction == 0) { /// 纵向 page.rotation = 90 } self?.insertPagesForDescSort(pages: [page], indexs: IndexSet(integer: insertIndex+1)) } else { let document = CPDFDocument() var imageName = "plaid" if (type == 2) { imageName = "horizontal_line" } else if (type == 3) { imageName = "five_line_score" } let imagePath = Bundle.main.pathForImageResource(imageName) document?.insertPage(pageSize, withImage: imagePath, at: 0) let page: CPDFPage = (document?.page(at: 0))! if (direction == 0) { /// 纵向 page.rotation = 90 } self?.insertPagesForDescSort(pages: [page], indexs: IndexSet(integer: insertIndex+1)) } self?.view.window?.endSheet((windowController.window)!) } } @objc private func insertPagesForDescSort(pages: Array, indexs: IndexSet) { self.insertPages(pages: pages, indexs: indexs) /// 选中插入的页面 self.selectPages(indexs: indexs) } func selectPages(indexs: IndexSet, needScroll: Bool = true) { var selectPages: [Int] = [] for index in indexs { selectPages.append(index+1) } DispatchQueue.main.async { var data = Set() for index in selectPages { data.insert(IndexPath(item: index-1, section: 0)) } if self.thumbnailView != nil { self.thumbnailView.collectionView.deselectAll(nil) if needScroll { self.thumbnailView.collectionView.selectItems(at: data, scrollPosition: .centeredVertically) } else { self.thumbnailView.collectionView.selectItems(at: data, scrollPosition: NSCollectionView.ScrollPosition()) } } } } /// MARK: indexs 需要降序排序后 @objc private func insertPages(pages: Array, indexs: IndexSet) { var count: Int = 0 for index in indexs { if (count >= pages.count) { break } // let newPage = pages[count].copy() as? CPDFPage let newPage = pages[count] // if (newPage == nil) { // continue // } self.listView?.document.insertPageObject(newPage, at: UInt(index)) count += 1 } // self.listView.undoManager?.registerUndo(withTarget: self, selector: #selector(deletePages), object: indexs) // 设置文档已编辑 DispatchQueue.main.asyncAfter(deadline: .now() + 0.7, execute: { self.setDocumentEditedState(window: self.view.window) }) (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).deletePages(indexs: indexs) self.listView?.layoutDocumentView() self.thumbnailView.reloadData() } /// MARK: indexs 需要降序排序后 /// dynamic 动态派发 @objc private dynamic func deletePages(indexs: IndexSet) { var array: Array = [] for i in indexs { array.append((self.listView?.document.page(at: UInt(i)))!) } self.listView?.document.removePage(at: indexs) self.listView.undoManager?.registerUndo(withTarget: self) { target in target.insertPages(pages: array, indexs: indexs) } self.listView?.layoutDocumentView() self.thumbnailView.reloadData() } @IBAction func rotatePageItemAction(menu:NSMenuItem) { Task { @MainActor in let indexs = self.thumbnailView.collectionView.selectionIndexes let indexPaths = self.thumbnailView.collectionView.selectionIndexPaths if indexs.count >= 0 { var pageItems: [KMThumbnailPageItem] = [] let newDocument = self.thumbnailView.document for index in indexs { let page : CPDFPage = (newDocument?.page(at: UInt(index)))! var rotation = page.rotation if rotation == 0 { rotation = 90 } else if rotation == 90 { rotation = 180 } else if rotation == 180 { rotation = 270 } else if rotation == 270 { rotation = 0 } let pageItem = KMThumbnailPageItem(page: page, rotate: rotation, oldRotate: page.rotation) pageItems.append(pageItem) } self.rotatePageItems(pageItems: pageItems, indexPaths: indexPaths) // 事件外面传递 self.delegate?.controller?(controller: self, itemClick: menu, itemKey: .rightRotate, params: nil) } } } @IBAction func leftRotatePageItemAction(menu:NSMenuItem) { Task { @MainActor in let indexs = self.thumbnailView.collectionView.selectionIndexes let indexPaths = self.thumbnailView.collectionView.selectionIndexPaths if indexs.count >= 0 { var pageItems: [KMThumbnailPageItem] = [] let newDocument = self.thumbnailView.document for index in indexs { let page : CPDFPage = (newDocument?.page(at: UInt(index)))! var rotation = page.rotation if rotation == 0 { rotation = 270 } else if rotation == 90 { rotation = 0 } else if rotation == 180 { rotation = 90 } else if rotation == 270 { rotation = 180 } let pageItem = KMThumbnailPageItem(page: page, rotate: rotation, oldRotate: page.rotation) pageItems.append(pageItem) } self.rotatePageItems(pageItems: pageItems, indexPaths: indexPaths) // 事件外面传递 self.delegate?.controller?(controller: self, itemClick: menu, itemKey: .leftRotate, params: nil) } } } @IBAction func extractPageItemAction(menu:NSMenuItem) { let index = (self.thumbnailView.collectionView.selectionIndexes).first ?? -1 if Int(index) >= 0 { let newDocument = self.thumbnailView.document let page : CPDFPage = (newDocument?.page(at: UInt(index)))! let filename : String = (newDocument?.documentURL.lastPathComponent)! let pageIndex = newDocument!.index(for: page) + 1 var newfileName : String = "\(NSString(string: filename).deletingPathExtension) page \(pageIndex)" newfileName = "\(newfileName)\(filename.extension)" let panel = NSSavePanel() panel.nameFieldStringValue = newfileName panel.canCreateDirectories = true panel.allowedFileTypes = ["pdf"] panel.isExtensionHidden = true panel.beginSheetModal(for: self.view.window!) { result in if result == .OK { let pdfdocument = CPDFDocument() let ret = pdfdocument!.importPages((self.thumbnailView.collectionView.selectionIndexes), from: self.thumbnailView.document, at: 0) if ret { let success = pdfdocument!.write(to:panel.url) if success { let workspace = NSWorkspace.shared workspace.activateFileViewerSelecting([panel.url!]) } } } } } } @IBAction func pageEditItemAction(menu:NSMenuItem) { var indexs: [Int] = [] for index in self.thumbnailView.collectionView.selectionIndexes { indexs.append(index) } self.delegate?.gotoPageEdit?(thumbnailViewController: self, pages: indexs) } @IBAction func deletePageItemAction(menu:NSMenuItem) { Task { @MainActor in let indexs = self.thumbnailView.collectionView.selectionIndexes if indexs.count > 0 { self.deletePageItemWithIndexs(indexs: indexs) // 事件外面传递 self.delegate?.controller?(controller: self, itemClick: menu, itemKey: .delete, params: nil) } } } @objc func showPageSizeItemAction(menu: NSMenuItem) { self.thumbnailView.isShowPageSize = !self.thumbnailView.isShowPageSize let indexpaths = self.thumbnailView.selectionIndexPaths self.reloadData() self.thumbnailView.selectionIndexPaths = indexpaths } @IBAction func sharePageItemAction(menu:NSMenuItem) { // let item = menu.parent! let index = (self.thumbnailView.collectionView.selectionIndexes).first ?? -1 if Int(index) >= 0 { let doucument = self.thumbnailView.document // let page = doucument?.page(at: UInt(index)) let filename : String = (doucument?.documentURL.lastPathComponent)! let folderPath = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(filename))! try? FileManager.default.removeItem(atPath: folderPath) let pdfdocument = CPDFDocument() let ret = pdfdocument!.importPages((self.thumbnailView.collectionView.selectionIndexes), from: self.thumbnailView.document, at: 0) let url = URL(fileURLWithPath: folderPath) if ret { let success = pdfdocument!.write(to:url) if success { let workspace = NSWorkspace.shared workspace.activateFileViewerSelecting([url]) } } let represent : NSSharingService = menu.representedObject as! NSSharingService represent.perform(withItems: [url]) } } @IBAction func copyItemAction(menu:NSMenuItem) { Task { @MainActor in KMThumbnailManager.manager.copyPages.removeAll() let indexs = self.thumbnailView.collectionView.selectionIndexes //文件存储用于跨文件page处理 _ = KMThumbnailManager.manager.copyPages(document: self.thumbnailView.document!, indexs: indexs) //本地page记录用于本文件page移动处理 if indexs.count >= 0 { for i in indexs { let page = self.thumbnailView.document?.page(at: UInt(i)).copy() as? CPDFPage KMThumbnailManager.manager.copyPages.append(page ?? CPDFPage()) } } } } @IBAction func cutItemAction(menu:NSMenuItem) { Task { @MainActor in KMThumbnailManager.manager.copyPages.removeAll() let indexs = self.thumbnailView.collectionView.selectionIndexes _ = KMThumbnailManager.manager.copyPages(document: self.thumbnailView.document!, indexs: indexs) if indexs.count >= 0 { for i in indexs { let page = self.thumbnailView.document?.page(at: UInt(i)).copy() as? CPDFPage KMThumbnailManager.manager.copyPages.append(page ?? CPDFPage()) } self.deletePageItemWithIndexs(indexs: indexs) // 事件传递到外面 self.delegate?.controller?(controller: self, itemClick: menu, itemKey: .cut, params: nil) } } } @IBAction func pastePageItemAction(menu:NSMenuItem) { Task { @MainActor in let copyPages = KMThumbnailManager.manager.copyPages let page = copyPages.first if page?.document == self.thumbnailView.document { var index = (self.thumbnailView.collectionView.selectionIndexes).last ?? -1 if Int(index) >= 0 && copyPages.count > 0 { var pageItems: [KMThumbnailPageItem] = [] for page in copyPages { index = index + 1 let pageItem = KMThumbnailPageItem(page: page, index: index, oldIndex: index) pageItems.append(pageItem) } self.addPageItems(pageItems: pageItems) self.selectPages(indexs: IndexSet(integer: index)) self.listView.go(toPageIndex: index, animated: true) // 事件传递到外面 self.delegate?.controller?(controller: self, itemClick: menu, itemKey: .paste, params: nil) } } else { let index = ((self.thumbnailView.collectionView.selectionIndexes).last ?? self.thumbnailView.collectionView.numberOfItems(inSection: 0) - 1) + 1 debugPrint("不是同一个document") let url = URL(fileURLWithPath: KMThumbnailManager.manager.tempCopyFilePath) self.thumbnailView(thumbanView: self.thumbnailView, didDragAddFiles: [url], indexpath: NSIndexPath(forItem: index, inSection: 0) as IndexPath) } } } @IBAction func printItemAction(menu:NSMenuItem) { var indexs:[Int] = [] for index in self.thumbnailView.collectionView.selectionIndexes { indexs.append(index) } if indexs.count > 0 { self.delegate?.controller?(controller: self, itemClick: menu, itemKey: .print, params: indexs) } } } protocol KMThumbnailViewControllerAction {} extension KMThumbnailViewController: KMThumbnailViewControllerAction { @IBAction func cut(_ sender: Any) { self.cutItemAction(menu: NSMenuItem()) } @IBAction func copy(_ sender: Any) { self.copyItemAction(menu: NSMenuItem()) } @IBAction func paste(_ sender: Any) { self.pastePageItemAction(menu: NSMenuItem()) } @IBAction func delete(_ sender: Any) { self.deletePageItemAction(menu: NSMenuItem()) } @IBAction func escButtonAction(_ sender: NSButton) { self.cancelSelect() } func cancelSelect() { self.selectPages(indexs: []) // self.km_resignFirstResponder() } } // MARK: - KMThumbnailViewDelegate extension KMThumbnailViewController : KMThumbnailViewDelegate { func thumbnailView(thumbanView: KMThumbnailView, didSelectItemAt indexpath: IndexPath, object: AnyObject?) { guard let event = object as? NSEvent else { return } self.km_resignFirstResponder() //是本地触发事件 self.isLocalEvent = true if thumbanView.collectionView.selectionIndexes.count == 1 || (!event.modifierFlags.contains(NSEvent.ModifierFlags.command) && !event.modifierFlags.contains(NSEvent.ModifierFlags.shift)) { let page : CPDFPage = self.listView.document.page(at: UInt(indexpath.item)) self.listView.go(toPageIndex: Int(page.pageIndex()), animated: true) self.thumbnailView.collectionView.deselectAll(nil) self.thumbnailView.collectionView.selectionIndexes = IndexSet(integer: IndexSet.Element(indexpath.item)) } var indexs: [Int] = [] for index in self.thumbnailView.collectionView.selectionIndexes { indexs.append(index) } self.delegate?.pageDidSelect?(controller: self, pages: indexs) } func thumbnailView(thumbanView: KMThumbnailView, rightMouseDidClick indexpath: IndexPath, item: NSCollectionViewItem?, object: AnyObject?) { guard let event = object as? NSEvent else { return } self.addRightMenuItem(view: item as! KMPDFThumbnailItem, event: event) } func thumbnailView(thumbanView: KMThumbnailView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, dragOperation operation: NSDragOperation) { self.isLocalEvent = false } func thumbnailView(thumbanView: KMThumbnailView, sizeForItemAt indexpath: IndexPath) -> NSSize { if self.doublePageMode { return NSMakeSize(86, 147) } else { return NSMakeSize(156, 226) } } func thumbnailView(thumbanView: KMThumbnailView, shouldAcceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool { return true } func thumbnailView(thumbanView: KMThumbnailView, shouldPasteboardWriterForItemAt indexPath: IndexPath) -> Bool { return true } func thumbnailView(thumbanView: KMThumbnailView, didDrag dragedIndexPaths: [IndexPath], indexpath: IndexPath, dragInfo: [KMThumbnailViewDragInfoKey.RawValue : Any]) { let toIndex = max(0, indexpath.item) var indexs = IndexSet() for indexpath in dragedIndexPaths { indexs.insert(indexpath.item) } self.dragPagesForDescSort(indexs, toIndex) self.delegate?.pageDidSelect?(controller: self, pages: [self.listView.currentPageIndex]) } func thumbnailView(thumbanView: KMThumbnailView, insetForSectionAt section: Int) -> NSEdgeInsets { if self.doublePageMode { return NSEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) } else { return NSEdgeInsets(top: 8, left: 24, bottom: 8, right: 24) } } func thumbnailView(thumbanView: KMThumbnailView, didDragAddFiles files: [URL], indexpath: IndexPath) { self.km_add_file_multi(fileUrls: files) { [unowned self] index, params in if (self.fetchProgressBlockParamsIsPasswordFile(params: params)) { // 加密文档进度回调 return } let _fileUrl = self.fetchProgressBlockParamsForFileUrl(params: params) if let exn = _fileUrl?.pathExtension, KMTools.isPDFType(exn) { if (_fileUrl!.path.isPDFValid() == false) { 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() } } } completionBlock: { [unowned self] documents in debugPrint(indexpath) var insertIndex: Int = indexpath.item var pages: Array = [] var indexs = IndexSet() for document in documents { for i in 0 ..< document.pageCount { let page = document.page(at: i) pages.append(page!) indexs.insert(insertIndex) insertIndex += 1 } } self.insertPagesForDescSort(pages: pages, indexs: indexs) } } } //MARK: NSMenuDelegate extension KMThumbnailViewController { override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { let action = menuItem.action if action == #selector(insertPageItemAction) || action == #selector(insertPageItemAction) || action == #selector(deletePageItemAction) || action == #selector(rotatePageItemAction) || action == #selector(leftRotatePageItemAction) || action == #selector(copyItemAction) || action == #selector(cutItemAction) || action == #selector(printItemAction) || action == #selector(sharePageItemAction) || action == #selector(extractPageItemAction) { if self.thumbnailView.collectionView.selectionIndexes.count > 0 { return true } else { return false } } if action == #selector(pastePageItemAction) { if KMThumbnailManager.manager.copyPages.count != 0 { return true } else { return false } } if (action == #selector(undo)) { return self.listView.undoManager?.canUndo ?? false } if (action == #selector(redo)) { return self.listView.undoManager?.canRedo ?? false } return true } func addRightMenuItem(view: KMPDFThumbnailItem, event: NSEvent) { let index = view.page.pageIndex() if !self.thumbnailView.collectionView.selectionIndexes.contains(IndexSet.Element(index)) { var indexs: IndexSet = IndexSet() indexs.insert(IndexSet.Element(index)) self.selectPages(indexs: indexs, needScroll: false) // // //PDFView跳转 self.isLocalEvent = true self.listView.go(toPageIndex: Int(view.page.pageIndex()), animated: true) } DispatchQueue.main.async { [unowned self] in let menu = NSMenu() var item = NSMenuItem() // item = menu.addItem(withTitle: NSLocalizedString("Insert", comment: ""), action: nil, target: self) // item.representedObject = row // // var subMenu = NSMenu() // var subitem = NSMenuItem() // subitem = subMenu.addItem(withTitle: NSLocalizedString("Insert File", comment: ""), action: #selector(insertPageItemAction), target: self, tag:0) // subitem.representedObject = row // subitem = subMenu.addItem(withTitle: NSLocalizedString("Insert blank page", comment: ""), action: #selector(insertPageItemAction), target: self, tag:1) // subitem.representedObject = row // subitem = subMenu.addItem(withTitle: NSLocalizedString("Insert page", comment: ""), action: #selector(insertPageItemAction), target: self, tag:2) // subitem.representedObject = row // item.submenu = subMenu // item = menu.addItem(withTitle: NSLocalizedString("Extract", comment: ""), action: #selector(extractPageItemAction), target: self) item = menu.addItem(withTitle: NSLocalizedString("Copy", comment: ""), action: #selector(copyItemAction), target: self)! item.keyEquivalent = "c" item = menu.addItem(withTitle: NSLocalizedString("Cut", comment: ""), action: #selector(cutItemAction), target: self)! item.keyEquivalent = "x" item = menu.addItem(withTitle: NSLocalizedString("Paste", comment: ""), action: #selector(pastePageItemAction), target: self)! item.keyEquivalent = "v" item = menu.addItem(withTitle: NSLocalizedString("Delete Page", comment: ""), action: #selector(deletePageItemAction), target: self)! item.keyEquivalent = String(Unicode.Scalar(NSBackspaceCharacter)!) item.keyEquivalentModifierMask = [] menu.addItem(NSMenuItem.separator()) item = menu.addItem(withTitle: NSLocalizedString("Page Edit", comment: ""), action: #selector(pageEditItemAction), target: self)! menu.addItem(NSMenuItem.separator()) item = menu.addItem(withTitle: NSLocalizedString("Rotate Clockwise", comment: ""), action: #selector(rotatePageItemAction), target: self)! item = menu.addItem(withTitle: NSLocalizedString("Rotate Counterclockwise", comment: ""), action: #selector(leftRotatePageItemAction), target: self)! menu.addItem(NSMenuItem.separator()) if (!self.thumbnailView.isShowPageSize) { item = menu.addItem(withTitle: NSLocalizedString("Hide Page Size", comment: ""), action: #selector(showPageSizeItemAction), target: self)! } else { item = menu.addItem(withTitle: NSLocalizedString("Display Page Size", comment: ""), action: #selector(showPageSizeItemAction), target: self)! } menu.addItem(NSMenuItem.separator()) item = menu.addItem(withTitle: NSLocalizedString("Print", comment: ""), action: #selector(printItemAction), target: self)! let point = view.contentBox?.convert(event.locationInWindow, from: nil) menu.popUp(positioning: nil, at: point!, in: view.contentBox) // item = menu.addItem(withTitle: NSLocalizedString("Share", comment: ""), action: nil, target: self) // item.representedObject = row // item.submenu = NSSharingServicePicker.menu(forSharingItems: [self.listView.document.documentURL ?? ""], subjectContext: "", withTarget: self, selector: #selector(sharePageItemAction), serviceDelegate: nil) } } } //MARK: undoRedo extension KMThumbnailViewController { func rotatePageItems(pageItems: [KMThumbnailPageItem], indexPaths: Set) { let tempPageItems = pageItems for pageItem in tempPageItems { pageItem.page.rotation = pageItem.rotate } self.listView.layoutDocumentView() self.thumbnailView.reloadData() self.thumbnailView.collectionView.selectItems(at: indexPaths, scrollPosition: .centeredVertically) self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in self.isLocalEvent = true //替换下标顺序 var tempPageItems: [KMThumbnailPageItem] = [] for pageItem in pageItems { let item = KMThumbnailPageItem(page: pageItem.page, rotate: pageItem.oldRotate, oldRotate: pageItem.rotate) tempPageItems.append(item) } self.rotatePageItems(pageItems: tempPageItems, indexPaths: indexPaths) } } func deletePageItemWithIndexs(indexs: IndexSet) { if (indexs.count == (self.listView?.document.pageCount)!) { let _ = CustomAlertView.alertView(message: NSLocalizedString("Unable to delete all pages", comment: ""), fromView: self.thumbnailView, withStyle: .blue) return } var pageItems: [KMThumbnailPageItem] = [] for index in indexs { let page = self.listView.document.page(at: UInt(index))?.copy() as? CPDFPage if (page == nil) { continue } let pageItem = KMThumbnailPageItem(page: page!, index: index, oldIndex: index, bookMark: self.listView.document.bookmark(forPageIndex: UInt(index))) pageItems.append(pageItem) } self.deletePageItems(pageItems: pageItems) } func deletePageItems(pageItems: [KMThumbnailPageItem]) { //移除现有 var tempPageItems = pageItems tempPageItems.sort(){$0.oldIndex > $1.oldIndex} for pageItem in tempPageItems { self.listView?.document.removePage(at: UInt(pageItem.oldIndex)) } self.listView.layoutDocumentView() self.thumbnailView.reloadData() self.pdfCurrentPageChange() self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in self.isLocalEvent = true self.addPageItems(pageItems: tempPageItems) } } func addPageItems(pageItems: [KMThumbnailPageItem]) { var tempPageItems = pageItems tempPageItems.sort(){$0.oldIndex < $1.oldIndex} for pageItem in tempPageItems { let newPage = pageItem.page self.listView?.document.insertPageObject(newPage, at: UInt(pageItem.oldIndex)) } for pageItem in tempPageItems { if pageItem.bookMark != nil && pageItem.oldIndex < self.listView.document.pageCount { self.listView?.document.addBookmark(pageItem.bookMark!.label, forPageIndex: UInt(pageItem.oldIndex)) } } self.listView.layoutDocumentView() self.thumbnailView.reloadData() self.pdfCurrentPageChange() self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in self.isLocalEvent = true self.deletePageItems(pageItems: tempPageItems) } } func dragPageItems(pageItems: [KMThumbnailPageItem]) { var tempPageItems = pageItems //按照原下标顺序 tempPageItems.sort(){$0.oldIndex > $1.oldIndex} //移除现有 for pageItem in tempPageItems { self.listView?.document.removePage(at: UInt(pageItem.oldIndex)) } //在原位置添加 tempPageItems.sort(){$0.index < $1.index} for pageItem in tempPageItems { let newPage = pageItem.page self.listView?.document.insertPageObject(newPage, at: UInt(pageItem.index)) } if pageItems.count > 0 { self.isLocalEvent = true self.selectPages(indexs: IndexSet(integer: pageItems.first!.index)) self.listView.go(toPageIndex: pageItems.first!.index, animated: true) } // 刷新UI self.listView?.layoutDocumentView() self.thumbnailView.reloadData() self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in //替换下标顺序 var tempPageItems: [KMThumbnailPageItem] = [] for pageItem in pageItems { let item = KMThumbnailPageItem(page: pageItem.page, index: pageItem.oldIndex, oldIndex: pageItem.index) tempPageItems.append(item) } self.dragPageItems(pageItems: tempPageItems) } } @IBAction func undo(_ sender: Any) { if (self.listView.undoManager?.canUndo ?? false) { self.listView.undoManager?.undo() } } @IBAction func redo(_ sender: Any) { if (self.listView.undoManager?.canRedo ?? false) { self.listView.undoManager?.redo() } } } struct KMThumbnailPageItem { var page: CPDFPage var index: Int = 0 var oldIndex: Int = 0 var rotate: Int = 0 var oldRotate: Int = 0 var bookMark: CPDFBookmark? }