KMNThumbnailBaseViewController.swift 29 KB

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