Jelajahi Sumber

Merge branch 'develop_2025' of git.kdan.cc:Mac_PDF/PDF_Office into develop_2025

niehaoyu 4 bulan lalu
induk
melakukan
c86b68eb8f

+ 1 - 0
PDF Office/PDF Master/Class/Purchase/IAPProductsManager.m

@@ -528,6 +528,7 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
 }
 
 - (BOOL)isAvailableAllFunction {
+    return YES;
 #if VERSION_DMG
     // 桌机版
     VerificationManager *tManager = [VerificationManager manager];

+ 2 - 506
PDF Office/PDF Master/KMClass/KMNPDFPageEdit/KMNThumbnailView/KMNPageEditViewController.swift

@@ -15,6 +15,8 @@ import KMComponentLibrary
 }
 
 class KMNPageEditViewController: KMNThumbnailBaseViewController {
+    
+    weak open var pageEditViewController: KMNPageEditViewViewDelegate?
 
      override func viewDidLoad() {
         super.viewDidLoad()
@@ -26,87 +28,6 @@ class KMNPageEditViewController: KMNThumbnailBaseViewController {
          }
     }
     
-    // MARK: - private
-    private func clickMenu(point:NSPoint)->NSMenu {
-        let copyPages: [CPDFPage] = KMNThumbnailManager.manager.copyPages
-
-        let menu = NSMenu()
-        // 根据 clickPoint 创建菜单项
-        let copyMenuItem = NSMenuItem(title: KMLocalizedString("Copy"), action: #selector(copyMenuItemAciton), target: self)
-        copyMenuItem.keyEquivalent = "c"
-                
-        let pastMenuItem = NSMenuItem(title: KMLocalizedString("Paste"), action: #selector(pastMenuItemAciton), target: self)
-        pastMenuItem.representedObject = point
-        pastMenuItem.keyEquivalent = "v"
-        
-        let pastNullMenuItem = NSMenuItem(title: KMLocalizedString("Paste"), action: nil, target: self)
-        pastNullMenuItem.keyEquivalent = "v"
-        
-        let cutMenuItem = NSMenuItem(title: KMLocalizedString("Cut"), action: #selector(cutMenuItemAciton), target: self)
-        cutMenuItem.keyEquivalent = "x"
-        
-        let deleteMenuItem = NSMenuItem(title: KMLocalizedString("Delete"), action: #selector(deleteMenuItemAciton), target: self)
-        deleteMenuItem.keyEquivalent = String(Unicode.Scalar(NSBackspaceCharacter)!)
-
-        let rotateLeftMenuItem = NSMenuItem(title: KMLocalizedString("90° CCW"), action: #selector(rotatePageLeftAction), target: self)
-        rotateLeftMenuItem.keyEquivalent = "l" // 设置为字母 l
-        rotateLeftMenuItem.keyEquivalentModifierMask = [.option, .command] // 设置为 Option + Command
-
-        let rotateRightMenuItem = NSMenuItem(title: KMLocalizedString("90° CW"), action: #selector(rotatePageRightAction), target: self)
-        rotateRightMenuItem.keyEquivalent = "r" // 设置为字母 r
-        rotateRightMenuItem.keyEquivalentModifierMask = [.option, .command] // 设置为 Option + Command
-
-        let insertFileMenuItem = NSMenuItem(title: KMLocalizedString("Insert File"), action: #selector(insertFromPDFAction), target: self)
-
-        let insertBlankMenuItem = NSMenuItem(title: KMLocalizedString("Insert a Blank Page"), action: #selector(insertFromBlankAction), target: self)
-        
-        let replaceMenuItem = NSMenuItem(title: KMLocalizedString("Replace"), action: #selector(replacePDFAction), target: self)
-        
-        let extractMenuItem = NSMenuItem(title: KMLocalizedString("Export"), action: #selector(extractPDFAction), target: self)
-        
-        let shareMenuItem = NSMenuItem(title: KMLocalizedString("Share"), action: nil, target: self)
-        
-        shareMenuItem.submenu = NSSharingServicePicker.menu(forSharingItems: [showDocument?.documentURL ?? ""], subjectContext: "", withTarget: self, selector: #selector(sharePageItemAction), serviceDelegate: nil)
-        
-        let showFileSizeMenuItem = NSMenuItem(title: KMLocalizedString("Display Page Size"), action: #selector(displayPageSizeAction), target: self)
-        showFileSizeMenuItem.state = isShowPageSize ? .on : .off
-        
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-
-        if(selectedIndexPaths.count > 0) {
-            menu.addItem(copyMenuItem)
-            menu.addItem(cutMenuItem)
-            if(copyPages.count > 0) {
-                menu.addItem(pastMenuItem)
-            }
-            menu.addItem(deleteMenuItem)
-            menu.addItem(NSMenuItem.separator())
-            menu.addItem(rotateRightMenuItem)
-            menu.addItem(rotateLeftMenuItem)
-            menu.addItem(NSMenuItem.separator())
-            if(selectedIndexPaths.count == 1) {
-                menu.addItem(insertFileMenuItem)
-                menu.addItem(insertBlankMenuItem)
-                menu.addItem(replaceMenuItem)
-            }
-            menu.addItem(extractMenuItem)
-            menu.addItem(shareMenuItem)
-            menu.addItem(NSMenuItem.separator())
-            menu.addItem(showFileSizeMenuItem)
-        } else {
-            if(copyPages.count > 0) {
-                menu.addItem(pastMenuItem)
-                menu.addItem(NSMenuItem.separator())
-            } else {
-                menu.addItem(pastNullMenuItem)
-                menu.addItem(NSMenuItem.separator())
-            }
-            menu.addItem(showFileSizeMenuItem)
-        }
-
-        return menu
-    }
-    
     private func insertFilePath(filePath:String,pdfPassword:String?) {
         let selectedIndexPaths = collectionView.selectionIndexPaths
         
@@ -134,134 +55,6 @@ class KMNPageEditViewController: KMNThumbnailBaseViewController {
     }
     
     // MARK: - public
-    func updateThumnailItem(updateIndexPaths: Set<IndexPath>,isSelect:Bool) {
-        
-        for targetIndexPath in updateIndexPaths {
-            let cellView = collectionView.item(at: targetIndexPath) as? KMNThumbnailCollectionViewItem
-            if(cellView != nil) {
-                cellView?.thumbnailMode.removeCacheImage()
-            }
-        }
-        
-        collectionView.reloadItems(at: updateIndexPaths)
-        if(isSelect) {
-            if updateIndexPaths.isEmpty { return }
-            let firstIndexPath = updateIndexPaths.first
-            collectionView.scrollToItems(at: [firstIndexPath ?? IndexPath(item: 0, section: 0)], scrollPosition: .top)
-
-            collectionView.selectionIndexPaths = updateIndexPaths
-        }
-    }
-    
-    func updateAllThumnailItems() {
-        KMNThumbnailManager.clearCacheFilePath(filePath: showDocument?.documentURL.path ?? "")
-        
-        collectionView.reloadData()
-    }
-    
-    @objc public func insertFromPDFAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        let openPanel = NSOpenPanel()
-        openPanel.allowedFileTypes = supportDragFileTypes()
-        openPanel.allowsMultipleSelection = false
-        openPanel.beginSheetModal(for: NSWindow.currentWindow()) {[weak self] result in
-            if result == NSApplication.ModalResponse.OK {
-                let fileURL = openPanel.url
-                if(fileURL?.pathExtension == "pdf") {
-                    let pdfDoc = CPDFDocument(url: fileURL)
-                    if let data = pdfDoc?.isLocked, data {
-                        DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
-                            KMNBaseWindowController.checkPassword(url: fileURL ?? NSURL.fileURL(withPath: ""), type: .owner) { result, pwd in
-                                if (pwd.isEmpty == false) {
-                                    self?.insertFilePath(filePath: fileURL?.path ?? "", pdfPassword: pwd)
-                                }
-                            }
-                        }
-                    } else {
-                        self?.insertFilePath(filePath: fileURL?.path ?? "", pdfPassword: nil)
-                    }
-                } else {
-                    self?.insertFilePath(filePath: fileURL?.path ?? "", pdfPassword: nil)
-                }
-            }
-        }
-    }
-    
-    @objc public func insertFromBlankAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-
-        let insertPDF = KMNPDFInsertBlankWindowController(self.showDocument, selectionIndexPaths: selectedIndexPaths)
-        insertPDF.callback = { [weak self] pageSize, insertIdx in
-            self?.insertBlankSize(pageSize: pageSize, pageDex: insertIdx)
-        }
-        insertPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
-    }
-    
-    public func insertFromClipboardAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-        var maxmumIndex = 1
-        if(selectedIndexPaths.count > 0) {
-            let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
-            maxmumIndex = (maxmumIndexPath?.item ?? 0) + 1
-        }
-        
-        var error: NSError?
-        guard let document: CPDFDocument = KMNConvertTool.openDocumentWithImageFromPasteboard(NSPasteboard.general, error: &error) else {
-            return
-        }
-        if let page: CPDFPage = (document.page(at: 0)) {
-            insertFormPages(insertPages: [page], pageDex: maxmumIndex)
-        }
-        
-        self.thumbnailBaseViewDelegate?.insertPDFThumbnailViewControlle?(pageEditVC: self, pdfDocment: document)
-        let ips: Set<IndexPath> = [IndexPath(item: maxmumIndex, section: 0)]
-        
-        refreshDatas()
-        collectionView.selectionIndexPaths = ips
-        collectionView.scrollToItems(at: ips, scrollPosition: .centeredVertically)
-    }
-    
-    public func insertFromScannerAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-        let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
-
-        let vc = KMDeviceBrowserWindowController.shared
-        vc.type = .scanner
-        vc.importScannerFileCallback = { [weak self] (url: NSURL) -> Void in
-            if let imag = NSImage(contentsOfFile: url.path! ) {
-                let index = (maxmumIndexPath?.item ?? 0) + 1
-                _ = self?.showDocument?.km_insertPage(imag.size, withImage: url.path! , at:UInt(index))
-                
-                self?.refreshDatas()
-                
-                let ips: Set<IndexPath> = [IndexPath(item: index, section: 0)]
-
-                self?.collectionView.selectionIndexPaths = ips
-                self?.collectionView.scrollToItems(at: ips, scrollPosition: .centeredVertically)
-            }
-        }
-        vc.showWindow(nil)
-        vc.window?.center()
-    }
-    
     public func canZoomInPageSize()->Bool {
         if pageThumbnailSize.width > defaultItemSize.width {
             return false
@@ -276,162 +69,6 @@ class KMNPageEditViewController: KMNThumbnailBaseViewController {
         return true
     }
     
-    public func canUndo()->Bool {
-        return true
-    }
-    
-    public func undoPDFAction() {
-        currentUndoManager?.undo()
-    }
-    
-    public func canRodo()->Bool {
-        return true
-    }
-    
-    public func redoPDFAction() {
-        currentUndoManager?.redo()
-    }
-        
-    @objc public func extractPDFAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-
-        if selectedIndexPaths.count < 1 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-        }
-                
-        let extractPDF = KMNExtractPDFWindowController(self.showDocument, selectionIndexPaths: collectionView.selectionIndexPaths)
-        extractPDF.callback = { [weak self] oneDocumentPerPage, isDeletePage in
-            extractPDF.own_closeEndSheet()
-            if let _ = self?.showDocument {
-                self?.extractPages(indexpaths: selectedIndexPaths, oneDocumentPerPage: oneDocumentPerPage, callback: { [weak self] result, params in
-                    if (result == .failure || result == .cancel) {
-                        return
-                    }
-                    if (isDeletePage) {
-                        self?.deletePages(indexpaths: selectedIndexPaths)
-                    }
-                })
-            }
-        }
-        extractPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
-    }
-    
-    @objc public func replacePDFAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-        
-        if selectedIndexPaths.count < 1 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-        }
-        
-        self.km_open_file_multi {  [unowned self] index, params in
-            if (self.fetchProgressBlockParamsIsPasswordFile(params: params)) { // 加密文档进度回调
-                return
-            }
-            
-            let tFileUrl = self.fetchProgressBlockParamsForFileUrl(params: params)
-            let pdfExtensions = KMNConvertTool.supportPDFFileType()
-            if let exn = tFileUrl?.pathExtension, pdfExtensions.contains(exn)  {
-                if (tFileUrl!.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
-            self.replacePages(of: selectedIndexPaths, with: documents)
-        }
-    }
-    
-    public func splitPDFAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-        
-        if collectionView.selectionIndexPaths.count < 1 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.bounds.origin.x + self.view.bounds.size.width/2, self.view.bounds.size.height - 30))
-            return
-        }
-        
-        let splitPDF = KMNSplitPDFWindowController(self.showDocument,selectionIndexPaths: selectedIndexPaths)
-        splitPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
-    }
-    
-    public func reversePDFAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-        
-        if selectedIndexPaths.count < 2 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("No page selected. Please select at least two pages to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-        }
-        reversePages(indexs:KMNTools.indexpathsToIndexs(indexpaths: selectedIndexPaths))
-    }
-    
-    @objc public func rotatePageLeftAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        if collectionView.selectionIndexPaths.count < 1 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-        }
-        rotatePages(indexPaths: collectionView.selectionIndexPaths, rotateAngle: -90)
-    }
-    
-    @objc public func rotatePageRightAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        if collectionView.selectionIndexPaths.count < 1 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-        }
-        rotatePages(indexPaths: collectionView.selectionIndexPaths, rotateAngle: 90)
-    }
-    
-    public func deletePageAction() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
-            return
-        }
-        
-        let selectedIndexPaths = collectionView.selectionIndexPaths
-
-        if selectedIndexPaths.count < 1 {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-        } else if selectedIndexPaths.count == (showDocument?.pageCount ?? 0) {
-            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Can not delete all pages."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
-            return
-
-        }
-        
-        deletePages(indexpaths: selectedIndexPaths)
-    }
-    
     public func zoomInPageAction() { //放大
         if (canZoomInPageSize() == false) {
             return
@@ -463,146 +100,5 @@ class KMNPageEditViewController: KMNThumbnailBaseViewController {
         collectionView.reloadData()
         collectionView.selectionIndexPaths = indexpaths
     }
-    
-    // MARK: - MenuItem
-    @objc public func copyMenuItemAciton() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            let winC = KMPurchaseCompareWindowController.sharedInstance()
-            winC?.showWindow(nil)
-            return
-        }
-        KMNThumbnailManager.manager.copyPages = []
-
-        let indexpaths = collectionView.selectionIndexPaths
-        for indexpath in indexpaths.sorted() {
-            guard let page = showDocument?.page(at: UInt(indexpath.item)).copy() as? CPDFPage else {
-                continue
-            }
-            
-            KMNThumbnailManager.manager.copyDocument.append(showDocument ?? CPDFDocument())
-            KMNThumbnailManager.manager.copyPages.append(page)
-        }
-    }
-    
-    @objc public func pastMenuItemAciton(menuItem:NSMenuItem) {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            let winC = KMPurchaseCompareWindowController.sharedInstance()
-            winC?.showWindow(nil)
-            return
-        }
-        
-        var pastIndex = 1
-        let point = menuItem.representedObject as? NSPoint
-        
-        if(point != nil) {
-            let pointInCollectionView = collectionView.convert(point!, from: nil)
-            
-            let visibleItems = collectionView.visibleItems()
-            let mouseX = pointInCollectionView.x
-            let mouseY = pointInCollectionView.y
-            
-            // 获取当前行的所有 cell
-            let currentRowItems = visibleItems.filter { item in
-                if let indexPath = collectionView.indexPath(for: item),
-                   let cellAttributes = collectionView.layoutAttributesForItem(at: indexPath) {
-                    return cellAttributes.frame.minY <= mouseY && cellAttributes.frame.maxY >= mouseY
-                }
-                return false
-            }
-            if(mouseX < 24) { //点击区域在最左边
-                // 找到最近右边的 cell
-                let rightMostCell = currentRowItems.compactMap { collectionView.indexPath(for: $0) }
-                    .filter { indexPath in
-                        let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame
-                        return cellRect?.minX ?? 0 > mouseX // 只选择右侧的 cell
-                    }
-                    .sorted { ($0.item < $1.item) } // 按 item 的顺序排列
-                    .first // 选择第一个,即最右边的 cell
-                pastIndex = (rightMostCell?.item ?? (Int(showDocument?.pageCount ?? 1)))
-            } else {
-                // 找到最近右边的 cell
-                let leftMostCell = currentRowItems.compactMap { collectionView.indexPath(for: $0) }
-                    .filter { indexPath in
-                        let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame
-                        return cellRect?.maxX ?? 0 < mouseX // 只选择左侧的 cell
-                    }
-                    .sorted { $0.item > $1.item } // 按 item 的逆序排列,以选择最近的左侧 cell
-                    .first // 选择第一个,即最近的左边的 cell
-                
-                pastIndex = (leftMostCell?.item ?? (Int(showDocument?.pageCount ?? 1)) - 1) + 1
-            }
-        } else {
-            let selectedIndexPaths = collectionView.selectionIndexPaths
-            if(selectedIndexPaths.count > 0) {
-                let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
-                pastIndex = ((maxmumIndexPath?.item ?? 0) + 1)
-            } else {
-                pastIndex = Int(showDocument?.pageCount ?? 0)
-            }
-        }
-        let copyPages = KMNThumbnailManager.manager.copyPages
-
-        insertFormPages(insertPages: copyPages, pageDex: pastIndex)
-    }
-
-    @objc public func cutMenuItemAciton() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            let winC = KMPurchaseCompareWindowController.sharedInstance()
-            winC?.showWindow(nil)
-            return
-        }
-        
-        let indexpaths = collectionView.selectionIndexPaths
-        for indexpath in indexpaths.sorted() {
-            guard let page = showDocument?.page(at: UInt(indexpath.item)).copy() as? CPDFPage else {
-                continue
-            }
-            
-            KMNThumbnailManager.manager.copyDocument.append(showDocument ?? CPDFDocument())
-            KMNThumbnailManager.manager.copyPages.append(page)
-        }
-
-        deletePages(indexpaths: indexpaths)
-    }
-    
-    @objc public func deleteMenuItemAciton() {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            let winC = KMPurchaseCompareWindowController.sharedInstance()
-            winC?.showWindow(nil)
-            return
-        }
-        let indexpaths = collectionView.selectionIndexPaths
-        deletePages(indexpaths: indexpaths)
-    }
-    
-    @objc func sharePageItemAction(menuItem:NSMenuItem) {
-        if IAPProductsManager.default().isAvailableAllFunction() == false {
-            let winC = KMPurchaseCompareWindowController.sharedInstance()
-            winC?.showWindow(nil)
-            return
-        }
-        let indexpaths = collectionView.selectionIndexPaths
-
-        let doucument = showDocument
-        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(KMNTools.indexpathsToIndexs(indexpaths: indexpaths), from: doucument, at: 0) ?? false
-        let url = URL(fileURLWithPath: folderPath)
-        if ret {
-            let success = pdfdocument?.write(toFile: folderPath)
-            let represent = menuItem.representedObject as? NSSharingService
-            represent?.perform(withItems: [url])
-        }
-    }
-
-    @objc public func displayPageSizeAction() {
-        let indexpaths = collectionView.selectionIndexPaths
-        isShowPageSize = !isShowPageSize
-        collectionView.selectionIndexPaths = indexpaths
-    }
 
 }

+ 454 - 0
PDF Office/PDF Master/KMClass/KMNPDFPageEdit/KMNThumbnailView/KMNThumbnailBaseViewController+Action.swift

@@ -440,4 +440,458 @@ extension KMNThumbnailBaseViewController {
             }
         }
     }
+    
+    // MARK: - MenuItem
+    @objc public func copyMenuItemAciton() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            let winC = KMPurchaseCompareWindowController.sharedInstance()
+            winC?.showWindow(nil)
+            return
+        }
+        KMNThumbnailManager.manager.copyPages = []
+
+        let indexpaths = collectionView.selectionIndexPaths
+        for indexpath in indexpaths.sorted() {
+            guard let page = showDocument?.page(at: UInt(indexpath.item)).copy() as? CPDFPage else {
+                continue
+            }
+            
+            KMNThumbnailManager.manager.copyDocument.append(showDocument ?? CPDFDocument())
+            KMNThumbnailManager.manager.copyPages.append(page)
+        }
+    }
+    
+    @objc public func pastMenuItemAciton(menuItem:NSMenuItem) {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            let winC = KMPurchaseCompareWindowController.sharedInstance()
+            winC?.showWindow(nil)
+            return
+        }
+        
+        var pastIndex = 1
+        let point = menuItem.representedObject as? NSPoint
+        
+        if(point != nil) {
+            let pointInCollectionView = collectionView.convert(point!, from: nil)
+            
+            let visibleItems = collectionView.visibleItems()
+            let mouseX = pointInCollectionView.x
+            let mouseY = pointInCollectionView.y
+            
+            // 获取当前行的所有 cell
+            let currentRowItems = visibleItems.filter { item in
+                if let indexPath = collectionView.indexPath(for: item),
+                   let cellAttributes = collectionView.layoutAttributesForItem(at: indexPath) {
+                    return cellAttributes.frame.minY <= mouseY && cellAttributes.frame.maxY >= mouseY
+                }
+                return false
+            }
+            if(mouseX < 24) { //点击区域在最左边
+                // 找到最近右边的 cell
+                let rightMostCell = currentRowItems.compactMap { collectionView.indexPath(for: $0) }
+                    .filter { indexPath in
+                        let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame
+                        return cellRect?.minX ?? 0 > mouseX // 只选择右侧的 cell
+                    }
+                    .sorted { ($0.item < $1.item) } // 按 item 的顺序排列
+                    .first // 选择第一个,即最右边的 cell
+                pastIndex = (rightMostCell?.item ?? (Int(showDocument?.pageCount ?? 1)))
+            } else {
+                // 找到最近右边的 cell
+                let leftMostCell = currentRowItems.compactMap { collectionView.indexPath(for: $0) }
+                    .filter { indexPath in
+                        let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame
+                        return cellRect?.maxX ?? 0 < mouseX // 只选择左侧的 cell
+                    }
+                    .sorted { $0.item > $1.item } // 按 item 的逆序排列,以选择最近的左侧 cell
+                    .first // 选择第一个,即最近的左边的 cell
+                
+                pastIndex = (leftMostCell?.item ?? (Int(showDocument?.pageCount ?? 1)) - 1) + 1
+            }
+        } else {
+            let selectedIndexPaths = collectionView.selectionIndexPaths
+            if(selectedIndexPaths.count > 0) {
+                let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
+                pastIndex = ((maxmumIndexPath?.item ?? 0) + 1)
+            } else {
+                pastIndex = Int(showDocument?.pageCount ?? 0)
+            }
+        }
+        let copyPages = KMNThumbnailManager.manager.copyPages
+
+        insertFormPages(insertPages: copyPages, pageDex: pastIndex)
+    }
+
+    @objc public func cutMenuItemAciton() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            let winC = KMPurchaseCompareWindowController.sharedInstance()
+            winC?.showWindow(nil)
+            return
+        }
+        
+        let indexpaths = collectionView.selectionIndexPaths
+        for indexpath in indexpaths.sorted() {
+            guard let page = showDocument?.page(at: UInt(indexpath.item)).copy() as? CPDFPage else {
+                continue
+            }
+            
+            KMNThumbnailManager.manager.copyDocument.append(showDocument ?? CPDFDocument())
+            KMNThumbnailManager.manager.copyPages.append(page)
+        }
+
+        deletePages(indexpaths: indexpaths)
+    }
+    
+    @objc public func deleteMenuItemAciton() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            let winC = KMPurchaseCompareWindowController.sharedInstance()
+            winC?.showWindow(nil)
+            return
+        }
+        let indexpaths = collectionView.selectionIndexPaths
+        deletePages(indexpaths: indexpaths)
+    }
+    
+    @objc func sharePageItemAction(menuItem:NSMenuItem) {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            let winC = KMPurchaseCompareWindowController.sharedInstance()
+            winC?.showWindow(nil)
+            return
+        }
+        let indexpaths = collectionView.selectionIndexPaths
+
+        let doucument = showDocument
+        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(KMNTools.indexpathsToIndexs(indexpaths: indexpaths), from: doucument, at: 0) ?? false
+        let url = URL(fileURLWithPath: folderPath)
+        if ret {
+            let success = pdfdocument?.write(toFile: folderPath)
+            let represent = menuItem.representedObject as? NSSharingService
+            represent?.perform(withItems: [url])
+        }
+    }
+
+    @objc public func displayPageSizeAction() {
+        let indexpaths = collectionView.selectionIndexPaths
+        isShowPageSize = !isShowPageSize
+        collectionView.selectionIndexPaths = indexpaths
+    }
+    
+    private func insertFilePath(filePath:String,pdfPassword:String?) {
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+        
+        let insertPDF = KMNPDFInsertPDFWindowController(showDocument, filePath: filePath, password: pdfPassword, selectionIndexPaths: selectedIndexPaths)
+        insertPDF.pdfCallback = { [weak self] fileAttribute, insertIdx in
+            let doc = fileAttribute.pdfDocument
+            
+            var insertPages: [CPDFPage] = []
+            
+            for number in fileAttribute.fetchSelectPages() {
+                if let page = doc?.page(at: UInt(number-1)) {
+                    insertPages.append(page)
+                }
+            }
+            self?.thumbnailBaseViewDelegate?.insertPDFThumbnailViewControlle?(pageEditVC: self, pdfDocment: doc)
+            
+            self?.insertFormPages(insertPages: insertPages, pageDex: insertIdx)
+        }
+        insertPDF.fileCallback = { [weak self] filePath, insertIdx in
+            self?.insertFromFilePath(fileNames: [filePath], formDex: 0, indexDex: UInt(insertIdx), selectIndexs: [], completionBlock: { newSelectIndexs in
+            })
+        }
+        
+        insertPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
+    }
+    
+    // MARK: - public
+    func updateThumnailItem(updateIndexPaths: Set<IndexPath>,isSelect:Bool) {
+        
+        for targetIndexPath in updateIndexPaths {
+            let cellView = collectionView.item(at: targetIndexPath) as? KMNThumbnailCollectionViewItem
+            if(cellView != nil) {
+                cellView?.thumbnailMode.removeCacheImage()
+            }
+        }
+        
+        collectionView.reloadItems(at: updateIndexPaths)
+        if(isSelect) {
+            if updateIndexPaths.isEmpty { return }
+            let firstIndexPath = updateIndexPaths.first
+            collectionView.scrollToItems(at: [firstIndexPath ?? IndexPath(item: 0, section: 0)], scrollPosition: .top)
+
+            collectionView.selectionIndexPaths = updateIndexPaths
+        }
+    }
+    
+    func updateAllThumnailItems() {
+        KMNThumbnailManager.clearCacheFilePath(filePath: showDocument?.documentURL.path ?? "")
+        
+        collectionView.reloadData()
+    }
+    
+    @objc public func insertFromPDFAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        let openPanel = NSOpenPanel()
+        openPanel.allowedFileTypes = supportDragFileTypes()
+        openPanel.allowsMultipleSelection = false
+        openPanel.beginSheetModal(for: NSWindow.currentWindow()) {[weak self] result in
+            if result == NSApplication.ModalResponse.OK {
+                let fileURL = openPanel.url
+                if(fileURL?.pathExtension == "pdf") {
+                    let pdfDoc = CPDFDocument(url: fileURL)
+                    if let data = pdfDoc?.isLocked, data {
+                        DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
+                            KMNBaseWindowController.checkPassword(url: fileURL ?? NSURL.fileURL(withPath: ""), type: .owner) { result, pwd in
+                                if (pwd.isEmpty == false) {
+                                    self?.insertFilePath(filePath: fileURL?.path ?? "", pdfPassword: pwd)
+                                }
+                            }
+                        }
+                    } else {
+                        self?.insertFilePath(filePath: fileURL?.path ?? "", pdfPassword: nil)
+                    }
+                } else {
+                    self?.insertFilePath(filePath: fileURL?.path ?? "", pdfPassword: nil)
+                }
+            }
+        }
+    }
+    
+    @objc public func insertFromBlankAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+
+        let insertPDF = KMNPDFInsertBlankWindowController(self.showDocument, selectionIndexPaths: selectedIndexPaths)
+        insertPDF.callback = { [weak self] pageSize, insertIdx in
+            self?.insertBlankSize(pageSize: pageSize, pageDex: insertIdx)
+        }
+        insertPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
+    }
+    
+    public func insertFromClipboardAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+        var maxmumIndex = 1
+        if(selectedIndexPaths.count > 0) {
+            let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
+            maxmumIndex = (maxmumIndexPath?.item ?? 0) + 1
+        }
+        
+        var error: NSError?
+        guard let document: CPDFDocument = KMNConvertTool.openDocumentWithImageFromPasteboard(NSPasteboard.general, error: &error) else {
+            return
+        }
+        if let page: CPDFPage = (document.page(at: 0)) {
+            insertFormPages(insertPages: [page], pageDex: maxmumIndex)
+        }
+        
+        self.thumbnailBaseViewDelegate?.insertPDFThumbnailViewControlle?(pageEditVC: self, pdfDocment: document)
+        let ips: Set<IndexPath> = [IndexPath(item: maxmumIndex, section: 0)]
+        
+        refreshDatas()
+        collectionView.selectionIndexPaths = ips
+        collectionView.scrollToItems(at: ips, scrollPosition: .centeredVertically)
+    }
+    
+    public func insertFromScannerAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+        let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
+
+        let vc = KMDeviceBrowserWindowController.shared
+        vc.type = .scanner
+        vc.importScannerFileCallback = { [weak self] (url: NSURL) -> Void in
+            if let imag = NSImage(contentsOfFile: url.path! ) {
+                let index = (maxmumIndexPath?.item ?? 0) + 1
+                _ = self?.showDocument?.km_insertPage(imag.size, withImage: url.path! , at:UInt(index))
+                
+                self?.refreshDatas()
+                
+                let ips: Set<IndexPath> = [IndexPath(item: index, section: 0)]
+
+                self?.collectionView.selectionIndexPaths = ips
+                self?.collectionView.scrollToItems(at: ips, scrollPosition: .centeredVertically)
+            }
+        }
+        vc.showWindow(nil)
+        vc.window?.center()
+    }
+    
+    
+    public func canUndo()->Bool {
+        return true
+    }
+    
+    public func undoPDFAction() {
+        currentUndoManager?.undo()
+    }
+    
+    public func canRodo()->Bool {
+        return true
+    }
+    
+    public func redoPDFAction() {
+        currentUndoManager?.redo()
+    }
+        
+    @objc public func extractPDFAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+
+        if selectedIndexPaths.count < 1 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+        }
+                
+        let extractPDF = KMNExtractPDFWindowController(self.showDocument, selectionIndexPaths: collectionView.selectionIndexPaths)
+        extractPDF.callback = { [weak self] oneDocumentPerPage, isDeletePage in
+            extractPDF.own_closeEndSheet()
+            if let _ = self?.showDocument {
+                self?.extractPages(indexpaths: selectedIndexPaths, oneDocumentPerPage: oneDocumentPerPage, callback: { [weak self] result, params in
+                    if (result == .failure || result == .cancel) {
+                        return
+                    }
+                    if (isDeletePage) {
+                        self?.deletePages(indexpaths: selectedIndexPaths)
+                    }
+                })
+            }
+        }
+        extractPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
+    }
+    
+    @objc public func replacePDFAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+        
+        if selectedIndexPaths.count < 1 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+        }
+        
+        self.km_open_file_multi {  [unowned self] index, params in
+            if (self.fetchProgressBlockParamsIsPasswordFile(params: params)) { // 加密文档进度回调
+                return
+            }
+            
+            let tFileUrl = self.fetchProgressBlockParamsForFileUrl(params: params)
+            let pdfExtensions = KMNConvertTool.supportPDFFileType()
+            if let exn = tFileUrl?.pathExtension, pdfExtensions.contains(exn)  {
+                if (tFileUrl!.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
+            self.replacePages(of: selectedIndexPaths, with: documents)
+        }
+    }
+    
+    public func splitPDFAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+        
+        if collectionView.selectionIndexPaths.count < 1 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.bounds.origin.x + self.view.bounds.size.width/2, self.view.bounds.size.height - 30))
+            return
+        }
+        
+        let splitPDF = KMNSplitPDFWindowController(self.showDocument,selectionIndexPaths: selectedIndexPaths)
+        splitPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
+    }
+    
+    public func reversePDFAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+        
+        if selectedIndexPaths.count < 2 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("No page selected. Please select at least two pages to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+        }
+        reversePages(indexs:KMNTools.indexpathsToIndexs(indexpaths: selectedIndexPaths))
+    }
+    
+    @objc public func rotatePageLeftAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        if collectionView.selectionIndexPaths.count < 1 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+        }
+        rotatePages(indexPaths: collectionView.selectionIndexPaths, rotateAngle: -90)
+    }
+    
+    @objc public func rotatePageRightAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        if collectionView.selectionIndexPaths.count < 1 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+        }
+        rotatePages(indexPaths: collectionView.selectionIndexPaths, rotateAngle: 90)
+    }
+    
+    public func deletePageAction() {
+        if IAPProductsManager.default().isAvailableAllFunction() == false {
+            KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
+            return
+        }
+        
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+
+        if selectedIndexPaths.count < 1 {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+        } else if selectedIndexPaths.count == (showDocument?.pageCount ?? 0) {
+            _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Can not delete all pages."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
+            return
+
+        }
+        
+        deletePages(indexpaths: selectedIndexPaths)
+    }
+    
 }

+ 108 - 33
PDF Office/PDF Master/KMClass/KMNPDFPageEdit/KMNThumbnailView/KMNThumbnailBaseViewController.swift

@@ -26,14 +26,19 @@ enum KMNThumbnailChoosePageStyle: Int {
 
 internal let kmnThumLocalForDraggedTypes = NSPasteboard.PasteboardType(rawValue: "kmnThumLocalForDraggedTypes")
 
+let topThumOffset: CGFloat = 12.0 // 缩图顶部高度
+let infoThumTitleHeight: CGFloat = 16.0 //文字高度
+let infoThumTitleBottom: CGFloat = 16.0 // 底部高度
+
 class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDelegate, NSCollectionViewDataSource,NSCollectionViewDelegateFlowLayout {
-    let subTitleHeight: CGFloat = 20.0
-    let maxCellHeight: CGFloat = 280.0
-    let minCellHeight: CGFloat = 40.0
+
+    let maxCellHeight: CGFloat = 320.0 - infoThumTitleBottom - infoThumTitleHeight - topThumOffset * 2
+    let minCellHeight: CGFloat = 80.0 - infoThumTitleBottom - infoThumTitleHeight - topThumOffset - topThumOffset * 2
 
     weak open var thumbnailBaseViewDelegate: KMNThumbnailBaseViewDelegate?
     
     @IBOutlet var backViewBox: NSBox!
+    @IBOutlet var scrollView: NSScrollView!
     @IBOutlet var collectionView: KMNThumbnailCollectionView!
         
     private var currentDocument:CPDFDocument?
@@ -45,9 +50,7 @@ class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDele
     public var dragLocalityPages: [CPDFPage] = []
 
     public var currentUndoManager:UndoManager?
-    
-    public var isAdjustWidth:Bool = false
-    
+        
     public var showDocument: CPDFDocument? {
         return currentDocument
     }
@@ -133,7 +136,18 @@ class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDele
             }
             
             collectionView.selectionIndexPaths = indexpaths
-            collectionView.scrollToItems(at: indexpaths, scrollPosition: .top)
+            
+            if(indexpaths.count > 0) {
+                let firstIndexPath = indexpaths.first
+                
+                let itemFrame = collectionView.frameForItem(at: firstIndexPath!.item)
+                let itemFrameInCollectionView = collectionView.convert(itemFrame, to: self.view)
+                
+                if collectionView.bounds.contains(itemFrameInCollectionView) {
+                } else {
+                    collectionView.scrollToItems(at: indexpaths, scrollPosition: .bottom)
+                }
+            }
         }
     }
         
@@ -142,9 +156,9 @@ class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDele
             if oldValue != isShowPageSize {
                 var pageSize = pageThumbnailSize
                 if(isShowPageSize) {
-                    pageSize.height += subTitleHeight
+                    pageSize.height += infoThumTitleHeight
                 } else {
-                    pageSize.height -= subTitleHeight
+                    pageSize.height -= infoThumTitleHeight
                     
                 }
                 pageThumbnailSize = pageSize
@@ -195,6 +209,9 @@ class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDele
         collectionView.dataSource = self
         collectionView.isSelectable = true //支持拖拽需设置未True
         collectionView.allowsMultipleSelection = true
+        collectionView.enclosingScrollView?.hasVerticalScroller = false
+        collectionView.enclosingScrollView?.hasHorizontalScroller = false
+        scrollView.scrollerStyle = .overlay
         
         collectionView.register(KMNThumbnailCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "thumbnailCollectionViewItem"))
         
@@ -230,6 +247,87 @@ class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDele
         collectionView.reloadData()
     }
     
+    // MARK: - private
+    public func clickMenu(point:NSPoint)->NSMenu {
+        let copyPages: [CPDFPage] = KMNThumbnailManager.manager.copyPages
+
+        let menu = NSMenu()
+        // 根据 clickPoint 创建菜单项
+        let copyMenuItem = NSMenuItem(title: KMLocalizedString("Copy"), action: #selector(copyMenuItemAciton), target: self)
+        copyMenuItem.keyEquivalent = "c"
+                
+        let pastMenuItem = NSMenuItem(title: KMLocalizedString("Paste"), action: #selector(pastMenuItemAciton), target: self)
+        pastMenuItem.representedObject = point
+        pastMenuItem.keyEquivalent = "v"
+        
+        let pastNullMenuItem = NSMenuItem(title: KMLocalizedString("Paste"), action: nil, target: self)
+        pastNullMenuItem.keyEquivalent = "v"
+        
+        let cutMenuItem = NSMenuItem(title: KMLocalizedString("Cut"), action: #selector(cutMenuItemAciton), target: self)
+        cutMenuItem.keyEquivalent = "x"
+        
+        let deleteMenuItem = NSMenuItem(title: KMLocalizedString("Delete"), action: #selector(deleteMenuItemAciton), target: self)
+        deleteMenuItem.keyEquivalent = String(Unicode.Scalar(NSBackspaceCharacter)!)
+
+        let rotateLeftMenuItem = NSMenuItem(title: KMLocalizedString("90° CCW"), action: #selector(rotatePageLeftAction), target: self)
+        rotateLeftMenuItem.keyEquivalent = "l" // 设置为字母 l
+        rotateLeftMenuItem.keyEquivalentModifierMask = [.option, .command] // 设置为 Option + Command
+
+        let rotateRightMenuItem = NSMenuItem(title: KMLocalizedString("90° CW"), action: #selector(rotatePageRightAction), target: self)
+        rotateRightMenuItem.keyEquivalent = "r" // 设置为字母 r
+        rotateRightMenuItem.keyEquivalentModifierMask = [.option, .command] // 设置为 Option + Command
+
+        let insertFileMenuItem = NSMenuItem(title: KMLocalizedString("Insert File"), action: #selector(insertFromPDFAction), target: self)
+
+        let insertBlankMenuItem = NSMenuItem(title: KMLocalizedString("Insert a Blank Page"), action: #selector(insertFromBlankAction), target: self)
+        
+        let replaceMenuItem = NSMenuItem(title: KMLocalizedString("Replace"), action: #selector(replacePDFAction), target: self)
+        
+        let extractMenuItem = NSMenuItem(title: KMLocalizedString("Export"), action: #selector(extractPDFAction), target: self)
+        
+        let shareMenuItem = NSMenuItem(title: KMLocalizedString("Share"), action: nil, target: self)
+        
+        shareMenuItem.submenu = NSSharingServicePicker.menu(forSharingItems: [showDocument?.documentURL ?? ""], subjectContext: "", withTarget: self, selector: #selector(sharePageItemAction), serviceDelegate: nil)
+        
+        let showFileSizeMenuItem = NSMenuItem(title: KMLocalizedString("Display Page Size"), action: #selector(displayPageSizeAction), target: self)
+        showFileSizeMenuItem.state = isShowPageSize ? .on : .off
+        
+        let selectedIndexPaths = collectionView.selectionIndexPaths
+
+        if(selectedIndexPaths.count > 0) {
+            menu.addItem(copyMenuItem)
+            menu.addItem(cutMenuItem)
+            if(copyPages.count > 0) {
+                menu.addItem(pastMenuItem)
+            }
+            menu.addItem(deleteMenuItem)
+            menu.addItem(NSMenuItem.separator())
+            menu.addItem(rotateRightMenuItem)
+            menu.addItem(rotateLeftMenuItem)
+            menu.addItem(NSMenuItem.separator())
+            if(selectedIndexPaths.count == 1) {
+                menu.addItem(insertFileMenuItem)
+                menu.addItem(insertBlankMenuItem)
+                menu.addItem(replaceMenuItem)
+            }
+            menu.addItem(extractMenuItem)
+            menu.addItem(shareMenuItem)
+            menu.addItem(NSMenuItem.separator())
+            menu.addItem(showFileSizeMenuItem)
+        } else {
+            if(copyPages.count > 0) {
+                menu.addItem(pastMenuItem)
+                menu.addItem(NSMenuItem.separator())
+            } else {
+                menu.addItem(pastNullMenuItem)
+                menu.addItem(NSMenuItem.separator())
+            }
+            menu.addItem(showFileSizeMenuItem)
+        }
+
+        return menu
+    }
+    
     // MARK: - NSCollectionViewDataSource
     func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
         return thumbnails.count
@@ -269,30 +367,7 @@ class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDele
     // MARK: - NSCollectionViewDelegateFlowLayout
     
     func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
-        if isAdjustWidth == false {
-            return pageThumbnailSize
-        } else {
-            let thumbnailMode: KMNThumbnail = thumbnails[indexPath.item]
-            
-            var orgPageHeight = thumbnailMode.pageSize.height
-            var orgPagewidth = thumbnailMode.pageSize.width
-            
-            if (thumbnailMode.thumbnaiPage?.rotation ?? 0) % 180 != 0 {
-                orgPageHeight = thumbnailMode.pageSize.width
-                orgPagewidth = thumbnailMode.pageSize.height
-            }
-
-            var cellWidth = self.view.bounds.width - 24 * 2
-            var cellHeight = cellWidth * (orgPageHeight / orgPagewidth)
-            
-            if cellHeight > maxCellHeight * (cellWidth / 264) {
-                cellHeight = maxCellHeight * (cellWidth / 264)
-            } else if(cellHeight < minCellHeight * (cellWidth / 264)) {
-                cellHeight = minCellHeight * (cellWidth / 264)
-            }
-            
-            return CGSize(width: cellWidth, height: cellHeight + 16 + 32.0)
-        }
+        return pageThumbnailSize
     }
     
     func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {

+ 2 - 1
PDF Office/PDF Master/KMClass/KMNPDFPageEdit/KMNThumbnailView/KMNThumbnailBaseViewController.xib

@@ -10,6 +10,7 @@
             <connections>
                 <outlet property="backViewBox" destination="CuB-Dn-dzz" id="dxa-1R-qFC"/>
                 <outlet property="collectionView" destination="zKK-EM-MTi" id="9vL-hb-IJk"/>
+                <outlet property="scrollView" destination="MtL-9j-REw" id="0rS-ia-yAU"/>
                 <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
             </connections>
         </customObject>
@@ -25,7 +26,7 @@
                         <rect key="frame" x="0.0" y="0.0" width="861" height="501"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
-                            <scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MtL-9j-REw">
+                            <scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MtL-9j-REw">
                                 <rect key="frame" x="0.0" y="0.0" width="861" height="501"/>
                                 <clipView key="contentView" drawsBackground="NO" id="jUQ-uk-I3w">
                                     <rect key="frame" x="0.0" y="0.0" width="861" height="501"/>

+ 2 - 2
PDF Office/PDF Master/KMClass/KMNPDFPageEdit/KMNThumbnailView/KMNThumbnailCollectionViewItem.swift

@@ -33,11 +33,11 @@ class KMNThumbnailCollectionViewItem: NSCollectionViewItem {
                                                                 markIcon:NSImage.init(named: "KMNImageNameThumbnailBookMark"))
             cardFileView.properties.isShowSubText = isShowFileSize
             
-            cardFileView.properties.propertyInfo.infoLabelHeight = 16.0
+            cardFileView.properties.propertyInfo.infoLabelHeight =  infoThumTitleHeight
             cardFileView.properties.isShowMarkIcon = thumbnailMode.isHoveBookMark
             cardFileView.reloadData()
             
-            let maxThumCellWidth = self.view.frame.size.width - 32.0
+            let maxThumCellWidth = self.view.frame.size.width - infoThumTitleBottom * 2
             
             var maxThumCellHeight = self.view.frame.size.height
             

+ 24 - 5
PDF Office/PDF Master/KMClass/KMPDFViewController/KMMainViewController.swift

@@ -446,7 +446,7 @@ import Cocoa
         if botaViewController == nil {
             botaViewController = KMNLeftSideViewController(listView.document)
         }
-
+        botaViewController?.leftSideViewDelegate = self
         botaViewController?.view.frame = infoSplitLeftView.bounds
         botaViewController?.view.autoresizingMask = [.width, .height]
         if botaViewController != nil {
@@ -808,9 +808,7 @@ extension KMMainViewController: NSSplitViewDelegate {
     func splitViewDidResizeSubviews(_ notification: Notification) {
         let splitView = notification.object as? NSSplitView
         if((splitView?.isEqual(to: infoContendSplitView)) == true) {
-            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(leftSplitViewResizeFinish), object: nil)
-            self.perform(#selector(leftSplitViewResizeFinish), with: nil, afterDelay: 0.15)
-
+            leftSplitViewResizeFinish()
         }
         NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(splitViewResizeFinish), object: nil)
         self.perform(#selector(splitViewResizeFinish), with: nil, afterDelay: 0.15)
@@ -1217,6 +1215,11 @@ extension KMMainViewController: CPDFViewDelegate,CPDFListViewDelegate {
         
         sideBarController?.reloadPageTurnerData()
         
+        var indexpaths: Set<IndexPath> = []
+        indexpaths.insert(IndexPath(item: listView.currentPageIndex, section: 0))
+        
+        botaViewController?.thumnailViewController?.selectionIndexPaths =  indexpaths
+        
         reloadPDFBottomToolbar()
         //分屏视图
         if viewManager.splitSyncScroll {
@@ -2160,7 +2163,23 @@ extension KMMainViewController: KMNThumbnailBaseViewDelegate {
     
 }
 
-
+extension KMMainViewController: KMNLeftSideViewControllerDelegate {
+    func enterPageEditLeftSideViewController(leftSideViewController: KMNLeftSideViewController) {
+        if viewManager.isPageEditMode == false {
+            viewManager.isPageEditMode = true
+            if(pdfToolbarController != nil) {
+                kmPDFToolbarControllerDidToolbarItemClicked(pdfToolbarController!, KMPDFToolbar_PageEdit_Identifier)
+                pdfToolbarController?.reloadSecondToolbar()
+            }
+        }
+    }
+    
+    func changeSelectePageLeftSideViewController(leftSideViewController: KMNLeftSideViewController, pageIndex: Int) {
+        if(listView.currentPageIndex != pageIndex) {
+            listView.go(toPageIndex: pageIndex, animated: true)
+        }
+    }
+}
 
 
 //MARK: -

+ 42 - 14
PDF Office/PDF Master/KMClass/Left/KMNLeftSideViewController.swift

@@ -9,10 +9,19 @@ import Cocoa
 
 import KMComponentLibrary
 
+@objc protocol KMNLeftSideViewControllerDelegate: AnyObject {
+    @objc optional func enterPageEditLeftSideViewController(leftSideViewController:KMNLeftSideViewController)
+    
+    @objc optional func changeSelectePageLeftSideViewController(leftSideViewController:KMNLeftSideViewController,pageIndex:Int)
+
+}
+
 class KMNLeftSideViewController: KMNBaseViewController {
     
-    @IBOutlet var backContentBox: NSBox!
+    weak open var leftSideViewDelegate: KMNLeftSideViewControllerDelegate?
 
+    @IBOutlet var backContentBox: NSBox!
+    
     @IBOutlet var headerBox: NSBox!
     
     @IBOutlet var bottomBox: NSBox!
@@ -20,26 +29,25 @@ class KMNLeftSideViewController: KMNBaseViewController {
     private var currentDocument:CPDFDocument?
     
     public var thumnailViewController:KMNThumnailViewController?
-
-    lazy var thumnailHeaderViewController:KMNThumnailHeaderViewController = {
-    let thumnailHeaderViewController:KMNThumnailHeaderViewController = KMNThumnailHeaderViewController.init(nibName: "KMNThumnailHeaderViewController", bundle: nil)
-        thumnailHeaderViewController.view.bounds = headerBox.bounds
-        thumnailHeaderViewController.view.autoresizingMask = [.width, .height]
-        return thumnailHeaderViewController
+    
+    lazy var thumbnailHeaderViewController: KMNThumnailHeaderViewController = {
+        let controller = KMNThumnailHeaderViewController(nibName: "KMNThumnailHeaderViewController", bundle: nil)
+        controller.view.bounds = headerBox.bounds
+        controller.view.autoresizingMask = [.width, .height] // Use flexible for better resizing
+        return controller
     }()
-
+    
     public var leftsideType:KMPDFSidebarType = .none {
         didSet {
             switch leftsideType {
             case .search: break
             case .thumbnail:
-                headerBox.contentView = thumnailHeaderViewController.view
+                headerBox.contentView = thumbnailHeaderViewController.view
                 bottomBox.contentView = thumnailViewController?.view
-                 break
             case .bookmark:  break
                 
             case .outline: break
-              
+                
             case .annotation: break
                 
             default: break
@@ -50,7 +58,7 @@ class KMNLeftSideViewController: KMNBaseViewController {
     public func changeDocument(document: CPDFDocument?) {
         currentDocument = document
     }
-
+    
     public func changeLeftSideBounds() {
         thumnailViewController?.updateThumbnailSize()
     }
@@ -60,7 +68,7 @@ class KMNLeftSideViewController: KMNBaseViewController {
         
         currentDocument = document
     }
-        
+    
     required init?(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
@@ -77,12 +85,32 @@ class KMNLeftSideViewController: KMNBaseViewController {
         super.updateUIThemeColor()
         
         headerBox.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/divider")
+        headerBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle")
+        bottomBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle")
     }
     
-   private func initThumnailView() {
+    private func initThumnailView() {
         thumnailViewController = KMNThumnailViewController.init(currentDocument)
         thumnailViewController?.view.frame = bottomBox.bounds
+        thumnailViewController?.thumnailViewDelegate = self
+        thumnailViewController?.thumbnailBaseViewDelegate = self
         thumnailViewController?.view.autoresizingMask = [.width, .height]
     }
     
 }
+
+extension KMNLeftSideViewController: KMNThumbnailBaseViewDelegate {
+    func changeIndexPathsThumbnailViewControlle(pageEditVC:KMNThumbnailBaseViewController?,selectionIndexPaths: Set<IndexPath>,selectionStrings:String ) {
+        
+        if(selectionIndexPaths.count == 1) {
+            leftSideViewDelegate?.changeSelectePageLeftSideViewController?(leftSideViewController: self, pageIndex: selectionIndexPaths.first?.item ?? 0)
+        }
+    }
+
+}
+
+extension KMNLeftSideViewController: KMNThumnailViewControllerDelegate {
+    func enterPageEditThumnailViewController(thumnailViewController: KMNThumnailViewController) {
+        leftSideViewDelegate?.enterPageEditLeftSideViewController?(leftSideViewController: self)
+    }
+}

+ 69 - 2
PDF Office/PDF Master/KMClass/Left/ThumnailView/KMNThumnailViewController.swift

@@ -7,14 +7,26 @@
 
 import Cocoa
 
+@objc protocol KMNThumnailViewControllerDelegate: AnyObject {
+    @objc optional func enterPageEditThumnailViewController(thumnailViewController:KMNThumnailViewController)
+}
+
+let subThumleftOffser: CGFloat = 40.0 //左右间隔
+let suborgWidthOff: CGFloat = 264.0
+
 class KMNThumnailViewController: KMNThumbnailBaseViewController {
    
+    weak open var thumnailViewDelegate: KMNThumnailViewControllerDelegate?
+
     override func viewDidLoad() {
-        isAdjustWidth = true
-        
         super.viewDidLoad()
+        
+        collectionView.menuClickedAction = { point in
+            return self.clickMenu(point: point)
+        }
     }
     
+    // MARK: - public
     public func changeDocument(document: CPDFDocument) {
         changeDocument = document
     }
@@ -22,5 +34,60 @@ class KMNThumnailViewController: KMNThumbnailBaseViewController {
     public func updateThumbnailSize() {
         collectionView.reloadData()
     }
+    
+     override func clickMenu(point:NSPoint)->NSMenu {
+        let menu = super.clickMenu(point: point)
+         let pageEditMenuItem = NSMenuItem(title: KMLocalizedString("Page Edit"), action: #selector(pageEditMenuItemAction), target: self)
+         
+         menu.addItem(NSMenuItem.separator())
+         menu.addItem(pageEditMenuItem)
+
+        return menu
+    }
+    
+    @objc public func pageEditMenuItemAction() {
+        thumnailViewDelegate?.enterPageEditThumnailViewController?(thumnailViewController: self)
+    }
+    
+    // MARK: - NSCollectionViewDelegateFlowLayout
+    override func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
+        let thumbnailMode: KMNThumbnail = thumbnails[indexPath.item]
+        
+        var orgPageHeight = thumbnailMode.pageSize.height
+        var orgPagewidth = thumbnailMode.pageSize.width
+        
+        if (thumbnailMode.thumbnaiPage?.rotation ?? 0) % 180 != 0 {
+            orgPageHeight = thumbnailMode.pageSize.width
+            orgPagewidth = thumbnailMode.pageSize.height
+        }
+        
+        let cellWidth = self.view.bounds.width - subThumleftOffser * 2 - infoThumTitleBottom * 2
+        var cellHeight = cellWidth * (orgPageHeight / orgPagewidth)
+        let scal = cellWidth / (suborgWidthOff - subThumleftOffser * 2 - infoThumTitleBottom * 2)
+
+        if cellHeight > maxCellHeight * scal {
+            cellHeight = maxCellHeight * scal
+        } else if(cellHeight < minCellHeight * scal) {
+            cellHeight = minCellHeight * scal
+        }
+
+        if(isShowPageSize) {
+            cellHeight += infoThumTitleHeight
+        }
+        
+        return CGSize(width: cellWidth + infoThumTitleBottom * 2, height: cellHeight + infoThumTitleBottom + infoThumTitleHeight + topThumOffset * 2)
+    }
+
+    override func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
+        return 20.0
+    }
+    
+    override func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
+        return 40.0
+    }
+    
+    public override func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets {
+        return NSEdgeInsetsMake(8.0, 0, 8.0, 0)
+    }
         
 }