123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- //
- // KMPDFThumbnailView.swift
- // PDF Master
- //
- // Created by lxy on 2022/12/15.
- //
- class KMPDFThumbnailView: KMThumbnailView {
- var document: CPDFDocument?
- var isShowPageSize = false
- var thumbnailSzie = NSZeroSize
-
- //标记线
- var isNeedMarkerLine: Bool = false
- var markerLineView: NSView = NSView()
- var markBeginIndexes: IndexSet = IndexSet()
- //注释状态
- var annotationShowState: KMAnnotationViewShowType = .none {
- didSet {
- self.collectionView.reloadData()
- }
- }
- //悬浮
- var hoverIndex: Int = -1
-
- var filePromiseQueue: OperationQueue = {
- let queue = OperationQueue()
- return queue
- }()
- var provider: NSFilePromiseProvider?
-
- override func initDefaultValue() {
- super.initDefaultValue()
-
- self.wantsLayer = true
- self.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
-
- self.thumbnailSzie = CGSize(width: 120, height: 155)
- self.minimumLineSpacing = 8
- self.register(KMPDFThumbnailItem.self)
-
- self.collectionView.registerForDraggedTypes(NSFilePromiseReceiver.readableDraggedTypes.map { NSPasteboard.PasteboardType($0) })
- self.collectionView.setDraggingSourceOperationMask([.copy, .delete], forLocal: false)
- }
-
- override func initSubViews() {
- super.initSubViews()
-
- self.markerLineView.wantsLayer = true
- self.markerLineView.layer?.backgroundColor = NSColor(hex: "#1770F4").cgColor
- self.markerLineView.frame = CGRectMake(0, 0, 100, 2)
- self.collectionView.addSubview(markerLineView)
- self.markerLineView.isHidden = true
- }
- }
- extension KMPDFThumbnailView {
- override func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
- if let size_ = self.delegate?.thumbnailView?(thumbanView: self, sizeForItemAt: indexPath) {
- return size_
- }
-
- let page = self.document?.page(at: UInt(indexPath.item))
- let height = KMPDFThumbnailItem.sizeToFit(size: self.thumbnailSzie, page: page!, isShow: self.isShowPageSize)
-
- return NSMakeSize(collectionView.frame.size.width - 20, CGFloat(height))
- }
-
- override func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
- if let count = self.document?.pageCount {
- return Int(count)
- }
- return 0
- }
-
- override func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
- if let item = self.delegate?.thumbnailView?(thumbanView: self, itemForRepresentedObjectAt: indexPath) {
- return item
- }
- let cellView: KMPDFThumbnailItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: NSStringFromClass(KMPDFThumbnailItem.self)), for: indexPath) as! KMPDFThumbnailItem
- cellView.thumbnailView = self
- if let page_ = self.document?.page(at: UInt(indexPath.item)) {
- cellView.page = page_
- }
- cellView.annotationShowState = self.annotationShowState
- if indexPath.item == hoverIndex {
- cellView.hover = true
- } else {
- cellView.hover = false
- }
-
- cellView.mouseDownAction = { [unowned self] (view, event) in
- self.delegate?.thumbnailView?(thumbanView: self, didSelectItemAt: indexPath, object: event)
- }
-
- cellView.rightMouseDownAction = { [unowned self] (view, event) in
- self.delegate?.thumbnailView?(thumbanView: self, rightMouseDidClick: indexPath, item: view, object: event)
- }
-
- cellView.hoverCallBack = { [unowned self] view, mouseEntered in
- if let _ = self.collectionView.item(at: hoverIndex)?.view {
- let tempCell = self.collectionView.item(at: hoverIndex) as? KMPDFThumbnailItem
- tempCell!.hover = false
- }
-
- if mouseEntered {
- hoverIndex = indexPath.item
- if let _ = self.collectionView.item(at: hoverIndex)?.view {
- let tempCell = self.collectionView.item(at: hoverIndex) as? KMPDFThumbnailItem
- tempCell!.hover = true
- }
- } else {
- hoverIndex = -1
- }
- }
-
- return cellView
- }
-
- override func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexes: IndexSet) {
- if self.isNeedMarkerLine {
- self.markBeginIndexes = collectionView.selectionIndexes
- }
- return super.collectionView(collectionView, draggingSession: session, willBeginAt: screenPoint, forItemsAt: indexes)
- }
-
- override func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
- if self.isNeedMarkerLine {
- if self.markBeginIndexes.count != 0 {
- if !self.markBeginIndexes.contains(proposedDropIndexPath.pointee.item) {
- //标记线
- var rect = self.collectionView.frameForItem(at: proposedDropIndexPath.pointee.item)
- rect.size.height = 2
- self.markerLineView.frame = rect
- self.markerLineView.isHidden = false
- }
- }
- }
-
- return super.collectionView(collectionView, validateDrop: draggingInfo, proposedIndexPath: proposedDropIndexPath, dropOperation: proposedDropOperation)
- }
-
- override func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool {
- self.markerLineView.isHidden = true
- self.markBeginIndexes = IndexSet()
-
- let pboard = draggingInfo.draggingPasteboard
- if (pboard.availableType(from: [self.localForDraggedTypes]) != nil) {
- let dragIndexPath = self.dragedIndexPaths.first
- if (dragIndexPath == nil) {
- return false
- }
- let dragIndex = dragIndexPath!.item
- let toIndex = max(0, indexPath.item)
- if dragIndex < 0 || dragIndex > self.document!.pageCount {
- return false
- }
- if (toIndex >= self.document!.pageCount) {
- return false
- }
- if dragIndex == toIndex {
- return false
- }
- return super.collectionView(collectionView, acceptDrop: draggingInfo, indexPath: indexPath, dropOperation: dropOperation)
- } else if ((pboard.availableType(from: [.fileURL])) != nil) {
- //获取url
- var array: [URL] = []
- for item: NSPasteboardItem in pboard.pasteboardItems! {
- let string: String = item.string(forType: NSPasteboard.PasteboardType.fileURL)!
- let url = NSURL(string: string)
- /// MARK: 暂时支持PDF格式
- if (url?.pathExtension?.lowercased() == "pdf") {
- array.append(url! as URL)
- }
- }
-
- self.delegate?.thumbnailView?(thumbanView: self, didDragAddFiles: array, indexpath: indexPath)
- return true
- }
-
- return false
- }
-
- // func collectionView(_ collectionView: NSCollectionView,
- // pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
- // /** Here the sample provide a custom NSFilePromise#imageLiteral(resourceName: "_DSC9930.jpeg")#imageLiteral(resourceName: "_DSC9930.jpeg")Provider.
- // Here we provide a custom provider, offering the row to the drag object, and its URL.
- // */
- //// var provider: NSFilePromiseProvider?
- ////
- ////// guard let photoItem =
- ////// dataSource.itemIdentifier(for: IndexPath(item: indexPath.item, section: 0)) else { return provider }
- ////// let photoFileExtension = photoItem.fileURL.pathExtension
- //// let photoFileExtension = "pdf"
- ////
- //// if #available(macOS 11.0, *) {
- //// let typeIdentifier = UTType(filenameExtension: photoFileExtension)
- //// provider = FilePromiseProvider(fileType: typeIdentifier!.identifier, delegate: self)
- //// } else {
- //// let typeIdentifier =
- //// UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, photoFileExtension as CFString, nil)
- //// provider = FilePromiseProvider(fileType: typeIdentifier!.takeRetainedValue() as String, delegate: self)
- //// }
- ////
- //// // Send out the indexPath and photo's url dictionary.
- //// do {
- //// let data = try NSKeyedArchiver.archivedData(withRootObject: indexPath, requiringSecureCoding: false)
- //// provider!.userInfo = [FilePromiseProvider.UserInfoKeys.urlKey: self.document!.documentURL as Any,
- //// FilePromiseProvider.UserInfoKeys.indexPathKey: data]
- //// } catch {
- //// fatalError("failed to archive indexPath to pasteboard")
- //// }
- //// return provider
- //
- // guard let model = indexPath as? IndexPath else {
- // return nil
- // }
- //
- // let pastBoard = NSPasteboardItem.init()
- // pastBoard.setString(String(format: "%ld", model.item), forType: NSPasteboard.PasteboardType.string)
- // return pastBoard
- // }
- }
- class FilePromiseProvider: NSFilePromiseProvider {
-
- struct UserInfoKeys {
- static let indexPathKey = "indexPath"
- static let urlKey = "url"
- }
- /** Required:
- Return an array of UTI strings of data types the receiver can write to the pasteboard.
- By default, data for the first returned type is put onto the pasteboard immediately, with the remaining types being promised.
- To change the default behavior, implement -writingOptionsForType:pasteboard: and return
- NSPasteboardWritingPromised to lazily provided data for types, return no option to provide the data for that type immediately.
-
- Use the pasteboard argument to provide different types based on the pasteboard name, if desired.
- Do not perform other pasteboard operations in the function implementation.
- */
- override func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
- var types = super.writableTypes(for: pasteboard)
- types.append(kKMLocalForDraggedTypes) // Add our own internal drag type (row drag and drop reordering).
- types.append(.fileURL) // Add the .fileURL drag type (to promise files to other apps).
- return types
- }
-
- /** Required:
- Return the appropriate property list object for the provided type.
- This will commonly be the NSData for that data type. However, if this function returns either a string, or any other property-list type,
- the pasteboard will automatically convert these items to the correct NSData format required for the pasteboard.
- */
- override func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
- guard let userInfoDict = userInfo as? [String: Any] else { return nil }
- switch type {
- case .fileURL:
- // Incoming type is "public.file-url", return (from our userInfo) the item's URL.
- if let url = userInfoDict[FilePromiseProvider.UserInfoKeys.urlKey] as? NSURL {
- return url.pasteboardPropertyList(forType: type)
- }
- case kKMLocalForDraggedTypes:
- // Incoming type is "com.mycompany.mydragdrop", return (from our userInfo) the item's indexPath.
- let indexPathData = userInfoDict[FilePromiseProvider.UserInfoKeys.indexPathKey]
- return indexPathData
- default:
- break
- }
- return super.pasteboardPropertyList(forType: type)
- }
-
- /** Optional:
- Returns options for writing data of a type to a pasteboard.
- Use the pasteboard argument to provide different options based on the pasteboard name, if desired.
- Do not perform other pasteboard operations in the function implementation.
- */
- public override func writingOptions(forType type: NSPasteboard.PasteboardType, pasteboard: NSPasteboard)
- -> NSPasteboard.WritingOptions {
- return super.writingOptions(forType: type, pasteboard: pasteboard)
- }
- }
- // MARK: - NSFilePromiseProviderDelegate
- extension KMPDFThumbnailView: NSFilePromiseProviderDelegate {
-
- /** This function is called at drop time to provide the title of the file being dropped.
- This sample uses a hard-coded string for simplicity, but depending on your use case, you should take the fileType parameter into account.
- */
- func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String {
- // Return the photoItem's URL file name.
- // let photoItem = photoFromFilePromiserProvider(filePromiseProvider: filePromiseProvider)
- // return (photoItem?.fileURL.lastPathComponent)!
- // return (self.document?.documentURL.lastPathComponent)!
- return "Test.pdf"
- }
-
- func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider,
- writePromiseTo url: URL,
- completionHandler: @escaping (Error?) -> Void) {
- do {
- if let photoItem = photoFromFilePromiserProvider(filePromiseProvider: filePromiseProvider) {
- /** Copy the file to the location provided to you. You always do a copy, not a move.
- It's important you call the completion handler.
- */
- // try FileManager.default.copyItem(at: photoItem.fileURL, to: url)
- try FileManager.default.copyItem(at: (self.document?.documentURL)!, to: url)
- completionHandler(nil)
- }
- } catch let error {
- OperationQueue.main.addOperation {
- self.presentError(error, modalFor: self.window!,
- delegate: nil, didPresent: nil, contextInfo: nil)
- }
- completionHandler(error)
- }
- }
-
- /** You should provide a non main operation queue (e.g. one you create) via this function.
- This way you don't stall the main thread while writing the promise file.
- */
- func operationQueue(for filePromiseProvider: NSFilePromiseProvider) -> OperationQueue {
- return filePromiseQueue
- }
-
- // Utility function to return a PhotoItem object from the NSFilePromiseProvider.
- func photoFromFilePromiserProvider(filePromiseProvider: NSFilePromiseProvider) -> CPDFPage? {
- var returnPhoto: CPDFPage?
- if let userInfo = filePromiseProvider.userInfo as? [String: AnyObject] {
- do {
- if let indexPathData = userInfo[FilePromiseProvider.UserInfoKeys.indexPathKey] as? Data {
- if let indexPath =
- try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(indexPathData) as? IndexPath {
- returnPhoto = self.document?.page(at: UInt(indexPath.item))
- }
- }
- } catch {
- fatalError("failed to unarchive indexPath from promise provider.")
- }
- }
- return returnPhoto
- }
-
- }
|