KMNThumbnailBaseViewController.swift 27 KB

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