// // KMThumbnailViewController.swift // PDF Master // // Created by lxy on 2022/10/10. // import Cocoa @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: NSViewController { 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 copysPages : [CPDFPage] = [] //注释状态 var annotationShowState: KMAnnotationViewShowType = .none { didSet { if self.thumbnailView != nil { self.thumbnailView.annotationShowState = self.annotationShowState } } } func dealloc() { NotificationCenter.default.removeObserver(self) } deinit { NotificationCenter.default.removeObserver(self) print("KMThumbnailViewController 释放") } override func viewWillAppear() { super.viewWillAppear() self.thumbnailView.reloadData() } override func viewDidLoad() { super.viewDidLoad() self.view.wantsLayer = true self.view.layer?.backgroundColor = NSColor.clear.cgColor self.lineView.backgroundColor(NSColor(hex: "#EDEEF0")) self.headerView.wantsLayer = true self.headerView.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor self.titleLabel.stringValue = NSLocalizedString("Thumbnails", comment: "") self.titleLabel.font = NSFont.SFProTextSemibold(14.0) self.titleLabel.textColor = NSColor(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 self.doublePageBtn.toolTip = NSLocalizedString("Single/Double column display", comment: "") self.pdfCurrentPageChange() self.initNotification() } func initNotification() { 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") } else { self.thumbnailView.thumbnailSzie = CGSize(width: 156, height: 180) self.doublePageBtn.image = NSImage(named: "KMImageNameThumbnailDoublePage") } 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<IndexPath>() 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() { self.view.window?.makeFirstResponder(self.listView) } //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) } } } 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 var insertIndex: Int = self!.thumbnailView.collectionView.selectionIndexes.last! let doucument = CPDFDocument(url: panel.url) if ((doucument?.isLocked)!) { 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<CPDFPage>, indexs: IndexSet) { self.insertPages(pages: pages, indexs: indexs) /// 选中插入的页面 self.selectPages(indexs: indexs) } private func selectPages(indexs: IndexSet, needScroll: Bool = true) { var selectPages: [Int] = [] for index in indexs { selectPages.append(index+1) } DispatchQueue.main.async { var data = Set<IndexPath>() for index in selectPages { data.insert(IndexPath(item: index-1, section: 0)) } 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<CPDFPage>, indexs: IndexSet) { var count: Int = 0 for index in indexs { if (count >= pages.count) { break } let newPage = pages[count].copy() as? CPDFPage 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) self.listView?.layoutDocumentView() self.thumbnailView.reloadData() } /// MARK: indexs 需要降序排序后 @objc private func deletePages(indexs: IndexSet) { let newIndexs = indexs.sorted() var array: Array<CPDFPage> = [] 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 if await (KMLightMemberManager.manager.canUseAdvanced() == false) { let _ = KMComparativeTableViewController.show(window: self.view.window!) return } 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 if await (KMLightMemberManager.manager.canUseAdvanced() == false) { let _ = KMComparativeTableViewController.show(window: self.view.window!) return } 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 var 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 if await (KMLightMemberManager.manager.canUseAdvanced() == false) { let _ = KMComparativeTableViewController.show(window: self.view.window!) return } 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) } } } @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 if await (KMLightMemberManager.manager.canUseAdvanced() == false) { let _ = KMComparativeTableViewController.show(window: self.view.window!) return } } let indexs = self.thumbnailView.collectionView.selectionIndexes if indexs.count >= 0 { self.copysPages = [] for i in indexs { let page = self.thumbnailView.document?.page(at: UInt(i)).copy() as? CPDFPage self.copysPages.append(page ?? CPDFPage()) } } } @IBAction func cutItemAction(menu:NSMenuItem) { Task { @MainActor in if await (KMLightMemberManager.manager.canUseAdvanced() == false) { let _ = KMComparativeTableViewController.show(window: self.view.window!) return } let indexs = self.thumbnailView.collectionView.selectionIndexes if indexs.count >= 0 { self.copysPages = [] for i in indexs { let page = self.thumbnailView.document?.page(at: UInt(i)).copy() as? CPDFPage self.copysPages.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 if await (KMLightMemberManager.manager.canUseAdvanced() == false) { let _ = KMComparativeTableViewController.show(window: self.view.window!) return } var index = (self.thumbnailView.collectionView.selectionIndexes).last ?? -1 if Int(index) >= 0 && self.copysPages.count > 0 { var pageItems: [KMThumbnailPageItem] = [] for page in self.copysPages { 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) } } } @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: KMPDFThumbnailViewDelegate extension KMThumbnailViewController : KMPDFThumbnailViewDelegate { func thumbnailView(thumbanView: KMPDFThumbnailView, didSelectPageAtIndex index: UInt, event: NSEvent) { NSApp.mainWindow?.makeFirstResponder(self) //是本地触发事件 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: index) self.listView.go(toPageIndex: Int(page.pageIndex()), animated: true) self.thumbnailView.collectionView.deselectAll(nil) self.thumbnailView.collectionView.selectionIndexes = IndexSet(integer: IndexSet.Element(index)) } var indexs: [Int] = [] for index in self.thumbnailView.collectionView.selectionIndexes { indexs.append(index) } self.delegate?.pageDidSelect?(controller: self, pages: indexs) } func thumbnailView(thumbanView: KMPDFThumbnailView, item: KMPDFThumbnailItem, rightMouseDidSelect index: UInt, event: NSEvent) { self.addRightMenuItem(view: item, event: event) } func thumbnailView(thumbanView: KMPDFThumbnailView, sizeForItemAt indexpath: IndexPath) -> NSSize { if self.doublePageMode { return NSMakeSize(86, 147) } else { return NSMakeSize(156, 226) } } func thumbnailView(thumbanView: KMPDFThumbnailView, didDragPages pages: [Int], indexpath: IndexPath) { let toIndex = max(0, indexpath.item) var indexs = IndexSet() for page in pages { indexs.insert(page) } self.dragPagesForDescSort(indexs, toIndex) } func thumbnailView(thumbanView: KMPDFThumbnailView, 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) } } } //MARK: NSMenuDelegate extension KMThumbnailViewController : NSMenuDelegate,NSMenuItemValidation { 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 self.copysPages.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("Delete Page", comment: ""), action: #selector(deletePageItemAction), target: self) item.keyEquivalent = String(Unicode.Scalar(NSBackspaceCharacter)!) item.keyEquivalentModifierMask = [] 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()) 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" 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<IndexPath>) { 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(message: NSLocalizedString("Unable to delete all pages", comment: ""), from: self.thumbnailView, with: .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)) } // 刷新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? }