KMNSearchHanddler.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. //
  2. // KMNSearchHanddler.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by User-Tangchao on 2024/12/1.
  6. //
  7. import Cocoa
  8. class KMNSearchHanddler: NSObject {
  9. weak var pdfView: CPDFView?
  10. var type: KMNBotaSearchType = .search
  11. var searchKey: String?
  12. var replaceKey: String?
  13. var showIdx = 0
  14. var resultCount = 0
  15. var searchSectionResults: [KMBotaSearchSectionModel] = []
  16. var searchResults: [KMSearchMode] = []
  17. // 创建一个串行队列
  18. private let searchQueue = DispatchQueue(label: "com.pdfkit.searchQueue")
  19. func hideNotes() -> Bool {
  20. let listView = pdfView as? CPDFListView
  21. return listView?.hideNotes ?? false
  22. }
  23. func allowsNotes() -> Bool {
  24. let listView = pdfView as? CPDFListView
  25. return listView?.allowsNotes() ?? false
  26. }
  27. func pdfDocument() -> CPDFDocument? {
  28. return pdfView?.document
  29. }
  30. func scaleFactor() -> CGFloat? {
  31. return pdfView?.scaleFactor
  32. }
  33. func isEditing() -> Bool {
  34. return pdfView?.isEditing() ?? false
  35. }
  36. private var searchTimer: Timer?
  37. private let debounceInterval: TimeInterval = 1.0 // 0.3秒的延迟
  38. func search(keyword: String, isCase: Bool, isWholeWord: Bool, isEdit: Bool = false, callback: @escaping (([KMBotaSearchSectionModel]) -> Void)) {
  39. searchTimer?.invalidate()
  40. searchTimer = Timer.scheduledTimer(withTimeInterval: debounceInterval, repeats: false) { [weak self] _ in
  41. self?.searchQueue.sync {
  42. // 在这里执行同步任务
  43. print("Executing task synchronously in searchQueue")
  44. self?._search(keyword: keyword, isCase: isCase, isWholeWord: isWholeWord, isEdit: isEdit, callback: callback)
  45. }
  46. }
  47. }
  48. func _search(keyword: String, isCase: Bool, isWholeWord: Bool, isEdit: Bool = false, callback: @escaping (([KMBotaSearchSectionModel]) -> Void)) {
  49. guard let document = self.pdfView?.document else {
  50. NSSound.beep()
  51. self.clear()
  52. callback([])
  53. return
  54. }
  55. if document.isFinding {
  56. document.cancelFindString()
  57. }
  58. if keyword.isEmpty {
  59. self.clear()
  60. callback([])
  61. return
  62. }
  63. let theKeyword = keyword.decomposedStringWithCompatibilityMapping
  64. var opt = CPDFSearchOptions(rawValue: 0)
  65. if isCase {
  66. opt.insert(.caseSensitive)
  67. }
  68. if isWholeWord {
  69. opt.insert(.matchWholeWord)
  70. }
  71. DispatchQueue.global().async {
  72. var result: [[CPDFSelection]] = []
  73. if isEdit {
  74. result = document.findEditAllPageString(theKeyword, with: opt) ?? []
  75. } else {
  76. result = document.findString(theKeyword, with: opt) ?? []
  77. }
  78. self.clear()
  79. for sels in result {
  80. let sectionM = KMBotaSearchSectionModel()
  81. guard let page = sels.first?.page else { continue }
  82. sectionM.pageIndex = Int(page.pageIndex())
  83. for sel in sels {
  84. let mode : KMSearchMode = KMSearchMode()
  85. mode.selection = sel
  86. mode.attributedString = KMOCToolClass.getAttributedString(selection: sel, keyword: theKeyword)
  87. mode.selectionPageIndex = document.index(for: sel.page)
  88. sectionM.items.append(mode)
  89. sel.setColor(KMNColorTools.colorWarning_base().withAlphaComponent(0.5))
  90. self.searchResults.append(mode)
  91. }
  92. self.searchSectionResults.append(sectionM)
  93. }
  94. DispatchQueue.main.async {
  95. self.searchKey = theKeyword
  96. self.showIdx = 0
  97. self.resultCount = self.searchResults.count
  98. callback(self.searchSectionResults)
  99. }
  100. }
  101. }
  102. func clear() {
  103. self.searchResults.removeAll()
  104. self.searchSectionResults.removeAll()
  105. self.searchKey = ""
  106. self.showIdx = 0
  107. self.resultCount = 0
  108. }
  109. func startFindEditText(page: CPDFPage, searchString: String, options: CPDFSearchOptions) -> [[CPDFSelection]] {
  110. let datas = self.pdfView?.document.startFindEditText(from: page, with: searchString, options: options) ?? []
  111. return datas
  112. }
  113. func replace(searchS: String, replaceS: String?, sel: CPDFSelection, isEdit: Bool = false, callback: @escaping ((CPDFSelection?)->Void)) {
  114. self.pdfView?.document.replace(with: sel, search: searchS, toReplace: replaceS, completionHandler: { newSel in
  115. callback(newSel)
  116. })
  117. }
  118. func showSelection(_ sel: CPDFSelection?) {
  119. guard let theSel = sel else {
  120. let isEditing = self.pdfView?.isEditing() ?? false
  121. if isEditing {
  122. self.pdfView?.setHighlightedSelection(nil, animated: true)
  123. } else {
  124. self.pdfView?.setHighlightedSelections([])
  125. }
  126. self.pdfView?.setNeedsDisplayAnnotationViewForVisiblePages()
  127. return
  128. }
  129. guard let document = self.pdfView?.document else {
  130. return
  131. }
  132. let pageIdx = document.index(for: theSel.page)
  133. self.pdfView?.go(toPageIndex: Int(pageIdx), animated: false)
  134. self.pdfView?.go(to: theSel.bounds, on: theSel.page)
  135. let isEditing = self.pdfView?.isEditing() ?? false
  136. if isEditing {
  137. self.pdfView?.setHighlightedSelection(theSel, animated: true)
  138. } else {
  139. self.pdfView?.setHighlightedSelections([theSel])
  140. }
  141. self.pdfView?.setNeedsDisplayAnnotationViewForVisiblePages()
  142. }
  143. func clearData() {
  144. let isEditing = self.pdfView?.isEditing() ?? false
  145. if isEditing {
  146. self.pdfView?.setHighlightedSelection(nil, animated: false)
  147. } else {
  148. self.pdfView?.setHighlightedSelections([])
  149. }
  150. self.pdfView?.setNeedsDisplayAnnotationViewForVisiblePages()
  151. }
  152. }
  153. extension KMNSearchHanddler {
  154. func next() -> Int {
  155. var index = self.showIdx
  156. index = index + 1
  157. index = min(index, self.searchResults.count - 1)
  158. self.showIdx = index
  159. return index
  160. }
  161. func previous() -> Int {
  162. var index = self.showIdx
  163. index = index - 1
  164. index = max(index, 0)
  165. self.showIdx = index
  166. return index
  167. }
  168. }