KMNSearchHanddler.swift 6.6 KB

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