KMNThumbnailBaseViewController.swift 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. //
  2. // KMNThumbnailBaseViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by 丁林圭 on 2024/10/21.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. @objc protocol KMNThumbnailBaseViewDelegate: AnyObject {
  10. @objc optional func clickThumbnailViewControlle(pageEditVC:KMNThumbnailBaseViewController?,currentIndex:Int)
  11. @objc optional func insertPDFThumbnailViewControlle(pageEditVC:KMNThumbnailBaseViewController?,pdfDocment:CPDFDocument?)
  12. @objc optional func changeIndexPathsThumbnailViewControlle(pageEditVC:KMNThumbnailBaseViewController?,selectionIndexPaths: Set<IndexPath>,selectionStrings:String )
  13. }
  14. enum KMNThumbnailChoosePageStyle: Int {
  15. case odd
  16. case even
  17. case horizontal
  18. case vertical
  19. case allPage
  20. case custom
  21. }
  22. internal let kmnThumLocalForDraggedTypes = NSPasteboard.PasteboardType(rawValue: "kmnThumLocalForDraggedTypes")
  23. let topThumOffset: CGFloat = 12.0 // 缩图顶部高度
  24. let infoThumTitleHeight: CGFloat = 16.0 //文字高度
  25. let infoThumTitleBottom: CGFloat = 16.0 // 底部高度
  26. struct KMNMenuStruct {
  27. var menuitems: [ComponentMenuitemProperty]
  28. var viewHeight: CGFloat // 不可变属性
  29. }
  30. class KMNThumbnailBaseViewController: KMNBaseViewController,NSCollectionViewDelegate, NSCollectionViewDataSource,NSCollectionViewDelegateFlowLayout {
  31. let maxCellHeight: CGFloat = 320.0 - infoThumTitleBottom - infoThumTitleHeight - topThumOffset * 2
  32. let minCellHeight: CGFloat = 80.0 - infoThumTitleBottom - infoThumTitleHeight - topThumOffset - topThumOffset * 2
  33. weak open var thumbnailBaseViewDelegate: KMNThumbnailBaseViewDelegate?
  34. @IBOutlet var backViewBox: NSBox!
  35. @IBOutlet var scrollView: NSScrollView!
  36. @IBOutlet var collectionView: KMNThumbnailCollectionView!
  37. @IBOutlet var flowLayout: KMNThumCustomCollectionViewFlowLayout!
  38. private var currentDocument:CPDFDocument?
  39. private var isChangeIndexPaths = false
  40. var lockedFiles: [URL] = []
  41. var filePromiseQueue: OperationQueue = {
  42. let queue = OperationQueue()
  43. return queue
  44. }()
  45. fileprivate var dragFilePath: String?
  46. fileprivate var dragFlag = false
  47. public var thumbnails:[KMNThumbnail] = []
  48. public var dragLocalityPages: [CPDFPage] = []
  49. public var currentUndoManager:UndoManager?
  50. public var showDocument: CPDFDocument? {
  51. return currentDocument
  52. }
  53. public var changeDocument:CPDFDocument? {
  54. didSet {
  55. if(changeDocument != nil && changeDocument != currentDocument) {
  56. currentDocument = changeDocument
  57. refreshDatas()
  58. collectionView.reloadData()
  59. }
  60. }
  61. }
  62. public var thumbnailChoosePageStyle:KMNThumbnailChoosePageStyle = .custom {
  63. didSet {
  64. var tSelectionIndexPaths: Set<IndexPath> = []
  65. let pageCount = currentDocument?.pageCount ?? 0
  66. switch thumbnailChoosePageStyle {
  67. case .even:
  68. for i in 0 ..< pageCount {
  69. if(i % 2 == 0) {
  70. tSelectionIndexPaths.insert(IndexPath(item: Int(i), section: 0))
  71. }
  72. }
  73. case .odd:
  74. for i in 0 ..< pageCount {
  75. if(i % 2 != 0) {
  76. tSelectionIndexPaths.insert(IndexPath(item: Int(i), section: 0))
  77. }
  78. }
  79. case .allPage:
  80. for i in 0 ..< pageCount {
  81. tSelectionIndexPaths.insert(IndexPath(item: Int(i), section: 0))
  82. }
  83. case .vertical:
  84. for i in 0 ..< pageCount {
  85. let page = showDocument?.page(at: i)
  86. if(page != nil) {
  87. if(page!.rotation % 180 != 0) {
  88. tSelectionIndexPaths.insert(IndexPath(item: Int(i), section: 0))
  89. }
  90. }
  91. }
  92. case .horizontal:
  93. for i in 0 ..< pageCount {
  94. let page = showDocument?.page(at: i)
  95. if(page != nil) {
  96. if(page!.rotation % 180 == 0) {
  97. tSelectionIndexPaths.insert(IndexPath(item: Int(i), section: 0))
  98. }
  99. }
  100. }
  101. default: break
  102. }
  103. isChangeIndexPaths = true
  104. collectionView.selectionIndexPaths = tSelectionIndexPaths
  105. isChangeIndexPaths = false
  106. }
  107. }
  108. var selectionIndexPaths: Set<IndexPath> = [] {
  109. didSet {
  110. var indexpaths: Set<IndexPath> = []
  111. for indexpath in selectionIndexPaths {
  112. if (indexpath.section >= collectionView.numberOfSections) {
  113. continue
  114. }
  115. if indexpath.section < 0 {
  116. continue
  117. }
  118. if (indexpath.item >= collectionView.numberOfItems(inSection: indexpath.section)) {
  119. continue
  120. }
  121. if indexpath.item < 0 {
  122. continue
  123. }
  124. indexpaths.insert(indexpath)
  125. }
  126. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {//页面跳转进入时未直接滚动和选中
  127. if indexpaths.count > 0 {
  128. self.collectionView.scrollToItems(at: indexpaths, scrollPosition: .top)
  129. }
  130. self.isChangeIndexPaths = true
  131. self.collectionView.selectionIndexPaths = indexpaths
  132. self.isChangeIndexPaths = false
  133. }
  134. }
  135. }
  136. public var isShowPageSize:Bool = false {
  137. didSet {
  138. if oldValue != isShowPageSize {
  139. var pageSize = pageThumbnailSize
  140. if(isShowPageSize) {
  141. pageSize.height += infoThumTitleHeight
  142. } else {
  143. pageSize.height -= infoThumTitleHeight
  144. }
  145. pageThumbnailSize = pageSize
  146. collectionView.reloadData()
  147. }
  148. }
  149. }
  150. public var pageThumbnailSize:CGSize = CGSizeMake(185.0, 260) {
  151. didSet {
  152. collectionView.reloadData()
  153. }
  154. }
  155. public let defaultItemSize = NSMakeSize(185.0, 260)
  156. deinit {
  157. thumbnailBaseViewDelegate = nil
  158. KMPrint("KMNThumbnailBaseViewController deinit.")
  159. }
  160. init(_ document: CPDFDocument?) {
  161. super.init(nibName: "KMNThumbnailBaseViewController", bundle: nil)
  162. currentDocument = document
  163. }
  164. init(_ filePath: String,password:String?) {
  165. super.init(nibName: "KMNThumbnailBaseViewController", bundle: nil)
  166. let document = CPDFDocument.init(url: URL(fileURLWithPath: filePath))
  167. if password != nil {
  168. document?.unlock(withPassword: password as String?)
  169. }
  170. if document?.allowsCopying == false || document?.allowsPrinting == false {
  171. exitCurrentView()
  172. } else {
  173. currentDocument = document
  174. }
  175. }
  176. required init?(coder: NSCoder) {
  177. fatalError("init(coder:) has not been implemented")
  178. }
  179. override func viewDidLoad() {
  180. super.viewDidLoad()
  181. collectionView.delegate = self
  182. collectionView.dataSource = self
  183. collectionView.isSelectable = true //支持拖拽需设置未True
  184. collectionView.allowsMultipleSelection = true
  185. collectionView.enclosingScrollView?.hasVerticalScroller = false
  186. collectionView.enclosingScrollView?.hasHorizontalScroller = false
  187. scrollView.scrollerStyle = .overlay
  188. collectionView.register(KMNThumbnailCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "thumbnailCollectionViewItem"))
  189. collectionView.registerForDraggedTypes([.fileURL,.string,.pdf])
  190. collectionView.setDraggingSourceOperationMask(.every, forLocal: false)
  191. collectionView.setDraggingSourceOperationMask(.every, forLocal: true)
  192. collectionView.collectionSelectChanges = {[weak self] in
  193. if self?.isChangeIndexPaths == false {
  194. let indexpathsz = self?.collectionView.selectionIndexPaths
  195. let dex:IndexSet = KMNTools.indexpathsToIndexs(indexpaths: indexpathsz ?? [])
  196. let selectedIndexPathsString = KMNTools.parseIndexSet(indexSet: dex)
  197. self?.thumbnailBaseViewDelegate?.changeIndexPathsThumbnailViewControlle?(pageEditVC: self, selectionIndexPaths: indexpathsz ?? [], selectionStrings: selectedIndexPathsString)
  198. }
  199. }
  200. refreshDatas()
  201. }
  202. public func exitCurrentView() {
  203. let minIndexPath = selectionIndexPaths.max(by: { $0.item < $1.item })
  204. thumbnailBaseViewDelegate?.clickThumbnailViewControlle?(pageEditVC: self, currentIndex: minIndexPath?.item ?? 0)
  205. }
  206. public func supportDragFileTypes()->[String] {
  207. let supportFiles = KMNConvertTool.pdfExtensions + KMConvertPDFManager.supportFileType()
  208. return supportFiles
  209. }
  210. public func refreshDatas() {
  211. thumbnails = []
  212. if currentDocument != nil {
  213. for i in 0 ..< currentDocument!.pageCount {
  214. let thumbnail = KMNThumbnail.init(document: currentDocument!, currentPageIndex: Int(i))
  215. thumbnails.append(thumbnail)
  216. }
  217. }
  218. }
  219. public func reloadDatas() {
  220. refreshDatas()
  221. collectionView.reloadData()
  222. }
  223. public func reloadDataWithIndexs(pageIndexs: IndexSet) {
  224. if currentDocument != nil {
  225. for i in pageIndexs {
  226. if i < currentDocument?.pageCount ?? 0 {
  227. let thumbnail = KMNThumbnail.init(document: currentDocument!, currentPageIndex: i)
  228. thumbnail.removeCacheImage()
  229. }
  230. }
  231. reloadDatas()
  232. }
  233. }
  234. // MARK: - private
  235. public func clickMenu(point:NSPoint)->KMNMenuStruct {
  236. let copyPages: [CPDFPage] = KMNThumbnailManager.manager.copyPages
  237. let selectedIndexPaths = collectionView.selectionIndexPaths
  238. var viewHeight: CGFloat = 8
  239. var menuItemArr: [ComponentMenuitemProperty] = []
  240. var items: [(String, String)] = []
  241. if selectedIndexPaths.count > 0 {
  242. items.append(("Copy", ThumbnailMenuIdentifier_Copy))
  243. items.append(("Cut", ThumbnailMenuIdentifier_Cut))
  244. if(copyPages.count > 0) {
  245. items.append(("Paste", ThumbnailMenuIdentifier_Paste))
  246. }
  247. items.append(("Delete", ThumbnailMenuIdentifier_Delete))
  248. items.append(("", ""))
  249. items.append(("90° CW", ThumbnailMenuIdentifier_RotateRight))
  250. items.append(("90° CCW", ThumbnailMenuIdentifier_RotateLeft))
  251. items.append(("", ""))
  252. if(selectedIndexPaths.count == 1) {
  253. items.append(("Insert File", ThumbnailMenuIdentifier_InsertFile))
  254. items.append(("Insert a Blank Page", ThumbnailMenuIdentifier_InsertBlank))
  255. items.append(("Replace", ThumbnailMenuIdentifier_Replace))
  256. }
  257. items.append(("Extract", ThumbnailMenuIdentifier_Export))
  258. items.append(("Share", ThumbnailMenuIdentifier_Share))
  259. items.append(("", ""))
  260. items.append(("Display Page Size", ThumbnailMenuIdentifier_FileShowSize))
  261. } else {
  262. if(copyPages.count > 0) {
  263. items.append(("Paste", ThumbnailMenuIdentifier_Paste))
  264. items.append(("", ""))
  265. } else {
  266. items.append(("Paste", ThumbnailMenuIdentifier_PastNull))
  267. items.append(("", ""))
  268. }
  269. items.append(("Display Page Size", ThumbnailMenuIdentifier_FileShowSize))
  270. }
  271. for (i, value) in items {
  272. if value.count == 0 {
  273. let property: ComponentMenuitemProperty = ComponentMenuitemProperty.divider()
  274. menuItemArr.append(property)
  275. viewHeight += 8
  276. } else {
  277. var isDisabled = false
  278. if(value == ThumbnailMenuIdentifier_PastNull) {
  279. isDisabled = true
  280. }
  281. let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
  282. itemSelected: false,
  283. isDisabled: isDisabled,
  284. keyEquivalent: nil,
  285. text: KMLocalizedString(i),
  286. identifier: value,representedObject: point)
  287. if value == ThumbnailMenuIdentifier_Copy {
  288. properties_Menuitem.keyEquivalent = "⌘ C"
  289. } else if value == ThumbnailMenuIdentifier_Cut {
  290. properties_Menuitem.keyEquivalent = "⌘ x"
  291. } else if value == ThumbnailMenuIdentifier_Paste {
  292. properties_Menuitem.keyEquivalent = "⌘ v"
  293. } else if value == ThumbnailMenuIdentifier_PastNull {
  294. properties_Menuitem.keyEquivalent = "⌘ v"
  295. } else if value == ThumbnailMenuIdentifier_Delete {
  296. properties_Menuitem.keyEquivalent = "⌘ " + "⌫"
  297. } else if value == ThumbnailMenuIdentifier_RotateRight {
  298. properties_Menuitem.keyEquivalent = "⌥ ⌘ R"
  299. } else if value == ThumbnailMenuIdentifier_RotateLeft {
  300. properties_Menuitem.keyEquivalent = "⌥ ⌘ L"
  301. }
  302. if(value == ThumbnailMenuIdentifier_FileShowSize && isShowPageSize) {
  303. properties_Menuitem.righticon = NSImage(named: "KMNImageNameMenuSelect")
  304. }
  305. menuItemArr.append(properties_Menuitem)
  306. viewHeight += 36
  307. }
  308. }
  309. let menuStruct = KMNMenuStruct(menuitems: menuItemArr, viewHeight: viewHeight)
  310. return menuStruct
  311. }
  312. // MARK: - NSCollectionViewDataSource
  313. func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
  314. return thumbnails.count
  315. }
  316. func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
  317. let item: KMNThumbnailCollectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "thumbnailCollectionViewItem"), for: indexPath) as! KMNThumbnailCollectionViewItem
  318. item.isShowFileSize = isShowPageSize
  319. item.doubleClickBack = { [weak self] in
  320. self?.thumbnailBaseViewDelegate?.clickThumbnailViewControlle?(pageEditVC: self, currentIndex: indexPath.item)
  321. }
  322. item.thumbnailMode = thumbnails[indexPath.item]
  323. return item
  324. }
  325. func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
  326. if isChangeIndexPaths == false {
  327. let indexpathsz = collectionView.selectionIndexPaths
  328. let dex:IndexSet = KMNTools.indexpathsToIndexs(indexpaths: indexpathsz)
  329. let selectedIndexPathsString = KMNTools.parseIndexSet(indexSet: dex)
  330. thumbnailBaseViewDelegate?.changeIndexPathsThumbnailViewControlle?(pageEditVC: self, selectionIndexPaths: indexpathsz, selectionStrings: selectedIndexPathsString)
  331. }
  332. }
  333. func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set<IndexPath>) {
  334. if isChangeIndexPaths == false {
  335. let indexpathsz = self.collectionView.selectionIndexPaths
  336. let dex:IndexSet = KMNTools.indexpathsToIndexs(indexpaths: indexpathsz)
  337. let selectedIndexPathsString = KMNTools.parseIndexSet(indexSet: dex)
  338. thumbnailBaseViewDelegate?.changeIndexPathsThumbnailViewControlle?(pageEditVC: self, selectionIndexPaths: indexpathsz, selectionStrings: selectedIndexPathsString)
  339. }
  340. }
  341. // MARK: - NSCollectionViewDelegateFlowLayout
  342. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
  343. return pageThumbnailSize
  344. }
  345. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
  346. return 16.0
  347. }
  348. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
  349. return 24.0
  350. }
  351. public func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets {
  352. return NSEdgeInsetsMake(24.0, 24.0, 24.0, 24.0)
  353. }
  354. //MARK: - NSCollectionViewDelegate
  355. func collectionView(_ collectionView: NSCollectionView,
  356. pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
  357. var provider: NSFilePromiseProvider?
  358. // 创建数据提供者
  359. let fileExtension = "pdf"
  360. if #available(macOS 11.0, *) {
  361. if let typeIdentifier = UTType(filenameExtension: fileExtension) {
  362. provider = KMFilePromiseProvider(fileType: typeIdentifier.identifier, delegate: self)
  363. }
  364. } else {
  365. if let typeIdentifier =
  366. UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil) {
  367. provider = KMFilePromiseProvider(fileType: typeIdentifier.takeRetainedValue() as String, delegate: self)
  368. }
  369. }
  370. do {
  371. if let _url = showDocument?.documentURL {
  372. let data = try NSKeyedArchiver.archivedData(withRootObject: indexPath, requiringSecureCoding: false)
  373. provider?.userInfo = [KMFilePromiseProvider.UserInfoKeys.urlKey: _url,
  374. KMFilePromiseProvider.UserInfoKeys.indexPathKey: data]
  375. } else {
  376. let data = try NSKeyedArchiver.archivedData(withRootObject: indexPath, requiringSecureCoding: false)
  377. provider?.userInfo = [KMFilePromiseProvider.UserInfoKeys.indexPathKey: data]
  378. }
  379. } catch {
  380. fatalError("failed to archive indexPath to pasteboard")
  381. }
  382. return provider
  383. }
  384. func collectionView(_ collectionView: NSCollectionView,
  385. draggingSession session: NSDraggingSession,
  386. willBeginAt screenPoint: NSPoint,
  387. forItemsAt indexPaths: Set<IndexPath>) {
  388. let sortedIndexPaths = indexPaths.sorted { (ip1, ip2) -> Bool in
  389. if ip1.section == ip2.section {
  390. return ip1.item < ip2.item
  391. }
  392. return ip1.section < ip2.section
  393. }
  394. dragLocalityPages = []
  395. for fromIndexPath in sortedIndexPaths {
  396. let page = thumbnails[fromIndexPath.item].thumbnaiPage
  397. if(page != nil) {
  398. dragLocalityPages.append(page!)
  399. }
  400. }
  401. var docmentName = currentDocument?.documentURL.lastPathComponent.deletingPathExtension ?? ""
  402. let pagesName = indexPaths.count > 1 ? " pages" : " page"
  403. var tFileName = pagesName + KMNTools.parseIndexPathsSet(indexSets: collectionView.selectionIndexPaths)
  404. if tFileName.count > 50 {
  405. tFileName = String(tFileName.prefix(50))
  406. }
  407. var cachesDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  408. cachesDir = cachesDir.appendingPathComponent("PageEdit_Pasteboard")
  409. let fileManager = FileManager.default
  410. if !fileManager.fileExists(atPath: cachesDir.path) {
  411. try? FileManager.default.createDirectory(atPath: cachesDir.path, withIntermediateDirectories: true, attributes: nil)
  412. }
  413. docmentName = "\(docmentName)\(tFileName)"
  414. if docmentName.count > 50 {
  415. docmentName = String(docmentName.prefix(50))
  416. }
  417. // 将拖拽的page插入临时路径(文档)
  418. var indexs = IndexSet()
  419. for indexpath in indexPaths {
  420. indexs.insert(indexpath.item)
  421. }
  422. // 重置拖拽标识
  423. self.dragFlag = false
  424. if (indexs.count > 0) {
  425. let writePDFDocument = CPDFDocument()
  426. writePDFDocument?.importPages(indexs, from: self.showDocument, at: 0)
  427. let filePathURL = cachesDir.appendingPathComponent(docmentName).appendingPathExtension("pdf")
  428. let success = writePDFDocument?.write(to: filePathURL, isSaveFontSubset:false)
  429. if(success == true) {
  430. self.dragFilePath = filePathURL.path
  431. }
  432. }
  433. }
  434. func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
  435. let pboard = draggingInfo.draggingPasteboard
  436. if dragLocalityPages.count != 0 {
  437. if proposedDropOperation.pointee == .on {
  438. proposedDropOperation.pointee = .before
  439. }
  440. return .move
  441. } else if ((pboard.availableType(from: [.fileURL])) != nil) {
  442. guard let pbItems = pboard.pasteboardItems else {
  443. return NSDragOperation(rawValue: 0)
  444. }
  445. var hasValidFile = false
  446. for item in pbItems {
  447. guard let data = item.string(forType: .fileURL), let url = URL(string: data) else {
  448. continue
  449. }
  450. let type = url.pathExtension.lowercased()
  451. if (supportDragFileTypes().contains(type)) {
  452. hasValidFile = true
  453. break
  454. }
  455. }
  456. if (!hasValidFile) {
  457. return NSDragOperation(rawValue: 0)
  458. }
  459. }
  460. return .copy
  461. }
  462. func collectionView(_ collectionView: NSCollectionView,
  463. acceptDrop draggingInfo: NSDraggingInfo,
  464. indexPath: IndexPath,
  465. dropOperation: NSCollectionView.DropOperation) -> Bool {
  466. let pboard = draggingInfo.draggingPasteboard
  467. if dragLocalityPages.count != 0 {
  468. movePages(dragPages: dragLocalityPages, destinationDex: indexPath.item)
  469. return true
  470. } else if ((pboard.availableType(from: [.fileURL])) != nil) {
  471. let index = indexPath.item
  472. guard let pbItems = pboard.pasteboardItems else {
  473. return false
  474. }
  475. //获取url
  476. var fileNames: [String] = []
  477. for item in pbItems {
  478. guard let data = item.string(forType: .fileURL), let url = URL(string: data) else {
  479. continue
  480. }
  481. let type = url.pathExtension.lowercased()
  482. if (supportDragFileTypes().contains(type)) {
  483. if(FileManager.default.fileExists(atPath: url.path)) {
  484. fileNames.append(url.path)
  485. }
  486. }
  487. }
  488. if(fileNames.count > 0) {
  489. insertFromFilePath(fileNames: fileNames, formDex: 0, indexDex: UInt(index), selectIndexs: []) { newSelectIndexs in
  490. }
  491. return true
  492. }
  493. }
  494. return false
  495. }
  496. func collectionView(_ collectionView: NSCollectionView,
  497. draggingSession session: NSDraggingSession,
  498. endedAt screenPoint: NSPoint,
  499. dragOperation operation: NSDragOperation) {
  500. dragLocalityPages = []
  501. }
  502. }
  503. // MARK: - NSFilePromiseProviderDelegate
  504. extension KMNThumbnailBaseViewController: NSFilePromiseProviderDelegate {
  505. func operationQueue(for filePromiseProvider: NSFilePromiseProvider) -> OperationQueue {
  506. return self.filePromiseQueue
  507. }
  508. func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider,
  509. writePromiseTo url: URL,
  510. completionHandler: @escaping (Error?) -> Void) {
  511. do {
  512. /** Copy the file to the location provided to you. You always do a copy, not a move.
  513. It's important you call the completion handler.
  514. */
  515. if let _urlString = dragFilePath, !dragFlag {
  516. dragFlag = true
  517. try FileManager.default.copyItem(at: URL(fileURLWithPath: _urlString), to: url)
  518. }
  519. completionHandler(nil)
  520. } catch let error {
  521. OperationQueue.main.addOperation {
  522. if let win = self.view.window {
  523. self.presentError(error, modalFor: win,
  524. delegate: nil, didPresent: nil, contextInfo: nil)
  525. }
  526. }
  527. completionHandler(error)
  528. }
  529. }
  530. func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String {
  531. var fileName: String = "Untitle"
  532. if let _string = showDocument?.documentURL.deletingPathExtension().lastPathComponent {
  533. fileName = _string
  534. }
  535. fileName.append(" pages")
  536. var indexs = IndexSet()
  537. for page in dragLocalityPages {
  538. indexs.insert(Int(page.pageIndex()))
  539. }
  540. fileName.append(" ")
  541. fileName.append(KMPageRangeTools.newParseSelectedIndexs(selectedIndex: indexs.sorted()))
  542. fileName.append(".pdf")
  543. return fileName
  544. }
  545. }