KMBotaSearchViewController.swift 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. //
  2. // KMBotaSearchViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/11/16.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. extension KMNSearchKey.wholeWords {
  10. static let botaSearch = "BotaSearchWholeWordsKey"
  11. }
  12. extension KMNSearchKey.caseSensitive {
  13. static let botaSearch = "BotaSearchCaseSensitiveKey"
  14. }
  15. enum KMNBotaSearchType: Int {
  16. case search = 1
  17. case replace = 2
  18. }
  19. class KMBotaSearchViewController: KMNBotaBaseViewController {
  20. @IBOutlet weak var searchField: NSSearchField!
  21. @IBOutlet weak var segmentedControl: KMSegmentedControl!
  22. @IBOutlet weak var topView: NSBox!
  23. @IBOutlet weak var topHeightConst: NSLayoutConstraint!
  24. var contentView: NSView? {
  25. didSet {
  26. if let view = self.contentView {
  27. self.box.contentView = view
  28. }
  29. }
  30. }
  31. @IBOutlet weak var emptyBox: NSBox!
  32. @IBOutlet weak var searchBox: KMBox!
  33. @IBOutlet weak var searchResultsView: NSView!
  34. @IBOutlet weak var searchResultsLabel: NSTextField!
  35. @IBOutlet weak var searchDomeButton: NSButton!
  36. @IBOutlet weak var box: NSBox!
  37. @IBOutlet weak var emptySearchLabel: NSTextField!
  38. @IBOutlet weak var searchLabel: NSTextField!
  39. @IBOutlet weak var searchTips: NSTextField!
  40. @IBOutlet weak var pageLabel: NSTextField!
  41. @IBOutlet var scrollView: NSScrollView!
  42. @IBOutlet weak var tableView: KMBotaTableView!
  43. private lazy var topContentView_: KMNBotaSearchTopView? = {
  44. let view = KMNBotaSearchTopView.createFromNib()
  45. return view
  46. }()
  47. private var emptyView_: ComponentEmpty = {
  48. let view = ComponentEmpty()
  49. // , image: NSImage(named: "KMImageNameOutlineEmpty")
  50. view.properties = ComponentEmptyProperty(emptyType: .noSearch, state: .normal, text: KMLocalizedString("No Results"), subText: KMLocalizedString(""))
  51. return view
  52. }()
  53. private var menuGroupView_: ComponentGroup?
  54. private var menuSections_: [CPDFSelection] = []
  55. private var menuType_: CAnnotationType = .circle
  56. var handdler = KMNSearchHanddler()
  57. var searchResults : [KMBotaSearchSectionModel] = [] {
  58. didSet {
  59. self.updataLeftSideFindView()
  60. }
  61. }
  62. private var datas: [Any] = []
  63. deinit {
  64. KMPrint("KMBotaSearchViewController deinit.")
  65. NotificationCenter.default.removeObserver(self)
  66. }
  67. override func loadView() {
  68. super.loadView()
  69. topView.borderWidth = 0
  70. topView.fillColor = .clear
  71. topView.contentView = topContentView_
  72. topContentView_?.itemClick = { [unowned self] idx, params in
  73. if idx == KMNBotaSearchTopItemKey.search.rawValue {
  74. if let data = params.first as? ComponentButton {
  75. showSearchGroupView(sender: data)
  76. }
  77. } else if idx == KMNBotaSearchTopItemKey.replace.rawValue {
  78. if handdler.type == .search {
  79. handdler.type = .replace
  80. showReplaceView()
  81. } else {
  82. handdler.type = .search
  83. showSearchView()
  84. }
  85. } else if idx == KMNBotaSearchTopItemKey.switch.rawValue {
  86. } else if idx == KMNBotaSearchTopItemKey.previous.rawValue {
  87. tableViewMoveUp(tableView)
  88. } else if idx == KMNBotaSearchTopItemKey.next.rawValue {
  89. tableViewMoveDown(tableView)
  90. }
  91. }
  92. showSearchView()
  93. topContentView_?.valueDidChange = { [unowned self] sender, info in
  94. guard let string = info?[.newKey] as? String else {
  95. return
  96. }
  97. let isCase = KMDataManager.ud_bool(forKey: KMNSearchKey.caseSensitive.botaSearch)
  98. let isWholeWord = KMDataManager.ud_bool(forKey: KMNSearchKey.wholeWords.botaSearch)
  99. handdler.search(keyword: string, isCase: isCase, isWholeWord: isWholeWord) { [unowned self] results in
  100. searchResults = results ?? []
  101. showResult()
  102. tableView.reloadData()
  103. }
  104. }
  105. emptyBox.contentView?.addSubview(emptyView_)
  106. emptyView_.km_add_top_constraint(constant: 232)
  107. emptyView_.km_add_bottom_constraint()
  108. emptyView_.km_add_leading_constraint()
  109. emptyView_.km_add_trailing_constraint()
  110. self.emptySearchLabel.stringValue = KMLocalizedString("")
  111. self.emptySearchLabel.textColor = KMAppearance.Layout.h1Color()
  112. self.emptyBox.fillColor = KMAppearance.Layout.l0Color()
  113. // self.searchLabel.stringValue = KMLocalizedString("Search")
  114. // self.searchLabel.textColor = KMAppearance.Layout.h0Color()
  115. // self.searchTips.stringValue = KMLocalizedString("Search")
  116. // self.searchTips.textColor = KMAppearance.Layout.h2Color()
  117. // self.searchResultsLabel.textColor = KMAppearance.Layout.h1Color()
  118. // self.pageLabel.stringValue = KMLocalizedString("Page")
  119. // self.pageLabel.textColor = KMAppearance.Layout.h1Color()
  120. // self.searchResultsView.isHidden = true
  121. // self.searchDomeButton.title = KMLocalizedString("Done")
  122. // self.searchDomeButton.toolTip = KMLocalizedString("Done")
  123. // self.searchDomeButton.setTitleColor(KMAppearance.Layout.w0Color())
  124. // self.searchDomeButton.wantsLayer = true
  125. // self.searchDomeButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor
  126. // self.searchDomeButton.layer?.cornerRadius = 4.0
  127. // self.searchDomeButton.hidden = YES;
  128. // self.searchField.wantsLayer = true
  129. // self.searchField.layer.backgroundColor = [KMAppearance KMColor_Layout_L1].CGColor;
  130. // self.searchField.layer?.cornerRadius = 1.0
  131. // self.searchField.layer?.borderWidth = 1.0
  132. // self.searchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor
  133. // self.searchBox.fillColor = KMAppearance.Interactive.s0Color()
  134. // self.searchField.hidden = YES;
  135. // self.searchBox.downCallback = { [unowned self] downEntered, box, _ in
  136. // if (downEntered) {
  137. // self.searchField.isHidden = false
  138. // self.searchDomeButton.isHidden = false
  139. // self.searchBox.isHidden = true
  140. // self.searchField.becomeFirstResponder()
  141. // }
  142. // }
  143. // self.searchBox.isHidden = true
  144. contentView = tableView.enclosingScrollView
  145. tableView.menuClickedAction = { [unowned self] point in
  146. let idxs = self.tableView.selectedRowIndexes.count
  147. let convertP = self.tableView.convert(point, from: nil)
  148. let row = self.tableView.row(at: convertP)
  149. if row == -1 {
  150. return NSMenu()
  151. }
  152. guard let model = self.datas[row] as? KMSearchMode else {
  153. return NSMenu()
  154. }
  155. var viewHeight: CGFloat = 0
  156. let items: [String] = ["Add New Circle", "Add New Rectangle", "Add New Highlight", "Add New Underline", "Add New Strikethrough"]
  157. var menuItemArr: [ComponentMenuitemProperty] = []
  158. for value in items {
  159. let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
  160. itemSelected: false,
  161. isDisabled: false,
  162. keyEquivalent: nil,
  163. text: KMLocalizedString(value),
  164. identifier: value)
  165. menuItemArr.append(properties_Menuitem)
  166. viewHeight += 36
  167. }
  168. self.menuGroupView_ = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  169. self.menuGroupView_?.clickedAutoHide = false
  170. self.menuGroupView_?.groupDelegate = self
  171. self.menuGroupView_?.frame = CGRectMake(0, 0, 180, viewHeight)
  172. self.menuGroupView_?.updateGroupInfo(menuItemArr)
  173. self.menuSections_ = [model.selection]
  174. self.menuGroupView_?.showWithPoint(CGPoint(x: point.x, y: point.y - viewHeight), relativeTo: self.tableView)
  175. return NSMenu()
  176. }
  177. // var rowIndexes = self.findTableView.selectedRowIndexes
  178. // let row = self.findTableView.clickedRow
  179. // if (row != -1) {
  180. // if rowIndexes.contains(row) == false {
  181. // rowIndexes = IndexSet(integer: row)
  182. // }
  183. //
  184. // var selections: [CPDFSelection] = []
  185. // for (i, data) in self.searchResults.enumerated() {
  186. // if rowIndexes.contains(i) {
  187. // selections.append(data.selection)
  188. // }
  189. // }
  190. // let hideNotes = self.hideNotes()
  191. // let allowsNotes = self.allowsNotes()
  192. // if hideNotes == false && allowsNotes {
  193. // item = menu.addItem(withTitle: KMLocalizedString("Add New Circle"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.circle.rawValue)
  194. // item?.representedObject = selections
  195. // item = menu.addItem(withTitle: KMLocalizedString("Add New Rectangle"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.square.rawValue)
  196. // item?.representedObject = selections
  197. // item = menu.addItem(withTitle: KMLocalizedString("Add New Highlight"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.highlight.rawValue)
  198. // item?.representedObject = selections
  199. // item = menu.addItem(withTitle: KMLocalizedString("Add New Underline"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.underline.rawValue)
  200. // item?.representedObject = selections
  201. // item = menu.addItem(withTitle: KMLocalizedString("Add New Strikethrough"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.strikeOut.rawValue)
  202. // item?.representedObject = selections
  203. // }
  204. // }
  205. }
  206. override func viewDidLoad() {
  207. super.viewDidLoad()
  208. self.tableView.delegate = self
  209. self.tableView.dataSource = self
  210. self.tableView.botaDelegate = self
  211. // self.tableView.menu?.delegate = self
  212. // self.mwcFlags.wholeWordSearch = KMDataManager.ud_integer(forKey: SKWholeWordSearchKey)
  213. // self.mwcFlags.caseInsensitiveSearch = KMDataManager.ud_integer(forKey: SKCaseInsensitiveSearchKey)
  214. // self.tableView.backgroundColor = KMAppearance.Layout.l0Color()
  215. self.tableView.tableColumn(withIdentifier: kPageColumnId)?.headerCell.title = KMLocalizedString("Page")
  216. }
  217. override func viewDidAppear() {
  218. super.viewDidAppear()
  219. // self.searchField.becomeFirstResponder()
  220. self.updateViewColor()
  221. DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged), name: NSApplication.interfaceThemeChangedNotification, object: nil)
  222. }
  223. override func updateUILanguage() {
  224. super.updateUILanguage()
  225. KMMainThreadExecute {
  226. self.topContentView_?.resultLabel.stringValue = KMLocalizedString("Result:") + " " + "\(self.handdler.searchResults.count)"
  227. self.tableView.reloadData()
  228. }
  229. }
  230. override func updateUIThemeColor() {
  231. super.updateUIThemeColor()
  232. KMMainThreadExecute {
  233. self.view.wantsLayer = true
  234. let color = KMNColorTools.colorBg_layoutMiddle()
  235. self.view.layer?.backgroundColor = color.cgColor
  236. self.tableView.backgroundColor = color
  237. self.topContentView_?.resultLabel.textColor = KMNColorTools.colorText_3()
  238. self.tableView.reloadData()
  239. }
  240. }
  241. func updateViewColor() {
  242. if (KMAppearance.isDarkMode()) {
  243. // self.searchField.layer?.backgroundColor = NSColor(red: 57.0/255.0, green: 60.0/255.0, blue: 62.0/255.0, alpha: 1).cgColor
  244. } else {
  245. // self.searchField.layer?.backgroundColor = .white
  246. }
  247. }
  248. func showSearchView() {
  249. topContentView_?.showSearch()
  250. topHeightConst.constant = topContentView_?.fetchContentHeight(type: handdler.type, hasResult: handdler.searchResults.isEmpty == false) ?? 0
  251. }
  252. func showReplaceView() {
  253. topContentView_?.showReplace()
  254. topHeightConst.constant = topContentView_?.fetchContentHeight(type: handdler.type, hasResult: handdler.searchResults.isEmpty == false) ?? 0
  255. }
  256. func showResult() {
  257. topContentView_?.showResult(type: handdler.type)
  258. topContentView_?.resultLabel.stringValue = KMLocalizedString("Result:") + " " + "\(self.handdler.searchResults.count)"
  259. topHeightConst.constant = topContentView_?.fetchContentHeight(type: handdler.type, hasResult: searchResults.isEmpty == false) ?? 0
  260. }
  261. // MARK: - Group View
  262. func showSearchGroupView(sender: ComponentButton) {
  263. var viewHeight: CGFloat = 8
  264. var menuItemArr: [ComponentMenuitemProperty] = []
  265. let titles = ["Search", "Find and Replace", "", "Whole Words", "Case Sensitive"]
  266. for i in titles {
  267. if i.isEmpty {
  268. let menuI = ComponentMenuitemProperty.divider()
  269. menuItemArr.append(menuI)
  270. viewHeight += 8
  271. } else {
  272. let menuI = ComponentMenuitemProperty(text: KMLocalizedString(i))
  273. menuItemArr.append(menuI)
  274. viewHeight += 36
  275. }
  276. }
  277. if handdler.type == .search {
  278. menuItemArr.first?.righticon = NSImage(named: "KMNImageNameMenuSelect")
  279. } else if handdler.type == .replace {
  280. let info = menuItemArr.safe_element(for: 1) as? ComponentMenuitemProperty
  281. info?.righticon = NSImage(named: "KMNImageNameMenuSelect")
  282. }
  283. if let info = menuItemArr.safe_element(for: 3) as? ComponentMenuitemProperty {
  284. if KMDataManager.ud_bool(forKey: KMNSearchKey.wholeWords.botaSearch) {
  285. info.righticon = NSImage(named: "KMNImageNameMenuSelect")
  286. }
  287. }
  288. if let info = menuItemArr.last {
  289. if KMDataManager.ud_bool(forKey: KMNSearchKey.caseSensitive.botaSearch) {
  290. info.righticon = NSImage(named: "KMNImageNameMenuSelect")
  291. }
  292. }
  293. let groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  294. searchGroupView = groupView
  295. groupView?.groupDelegate = self
  296. groupView?.frame = CGRectMake(310, 0, 200, viewHeight)
  297. groupView?.updateGroupInfo(menuItemArr)
  298. var point = sender.convert(sender.frame.origin, to: nil)
  299. point.y -= viewHeight
  300. groupView?.showWithPoint(point, relativeTo: sender)
  301. searchGroupTarget = sender
  302. }
  303. @objc func themeChanged(_ notification: NSNotification) {
  304. DispatchQueue.main.asyncAfter(deadline: .now()+0.3) {
  305. self.updateViewColor()
  306. }
  307. }
  308. @IBAction func searchDomeButtonAtion(_ sender: AnyObject) {
  309. self.searchField.isHidden = true
  310. self.searchDomeButton.isHidden = true
  311. self.searchBox.isHidden = false
  312. }
  313. @objc func toggleWholeWordSearch(_ sender: AnyObject?) {
  314. // if self.mwcFlags.wholeWordSearch == 1 {
  315. // self.mwcFlags.wholeWordSearch = 0
  316. // } else {
  317. // self.mwcFlags.wholeWordSearch = 1
  318. // }
  319. // if self.searchField.stringValue.isEmpty == false {
  320. // self.search(self.searchField)
  321. // }
  322. // KMDataManager.ud_set(self.mwcFlags.wholeWordSearch, forKey: SKWholeWordSearchKey)
  323. }
  324. @objc func toggleCaseInsensitiveSearch(_ sender: AnyObject?) {
  325. // if self.mwcFlags.caseInsensitiveSearch == 0 {
  326. // self.mwcFlags.caseInsensitiveSearch = 1
  327. // } else {
  328. // self.mwcFlags.caseInsensitiveSearch = 0
  329. // }
  330. //
  331. // if self.searchField.stringValue.isEmpty == false {
  332. // self.search(self.searchField)
  333. // }
  334. // KMDataManager.ud_set(self.mwcFlags.caseInsensitiveSearch, forKey: SKCaseInsensitiveSearchKey)
  335. }
  336. @objc func goToSelectedFindResults(_ sender: AnyObject?) {
  337. // guard let olView = sender as? NSTableView, olView.clickedRow != -1 else {
  338. // NSSound.beep()
  339. // return
  340. // }
  341. // self.updateFindResultHighlightsForDirection(.directSelection)
  342. }
  343. @objc func searchAction(_ sender: NSSearchField) {
  344. // if sender.stringValue.isEmpty {
  345. // self.applySearchTableHeader("")
  346. // }
  347. // self.delegate?.searchAction?(searchString: sender.stringValue, isCase: self.mwcFlags.caseInsensitiveSearch == 1)
  348. handdler.search(keyword: sender.stringValue, isCase: false, isWholeWord: false) { [weak self] results in
  349. self?.searchResults = results ?? []
  350. self?.tableView.reloadData()
  351. }
  352. }
  353. @objc func addAnnotationsForSelections(_ sender: NSMenuItem) {
  354. for selection in menuSections_ {
  355. // self.listView?.addAnnotation(with: CAnnotationType(rawValue: sender.tag) ?? .circle, selection: selection, page: selection.page, bounds: selection.bounds)
  356. }
  357. }
  358. func updataLeftSideFindView() {
  359. if (self.searchResults.count > 0) {
  360. self.emptyBox.isHidden = true
  361. // self.searchResultsView.isHidden = false
  362. // self.searchResultsLabel.stringValue = String(format: KMLocalizedString("%ld Results"), self.searchResults.count)
  363. } else {
  364. self.emptyBox.isHidden = false
  365. // self.searchResultsView.isHidden = true
  366. }
  367. }
  368. }
  369. // MARK: - NSTableViewDelegate, NSTableViewDataSource
  370. extension KMBotaSearchViewController: NSTableViewDelegate, NSTableViewDataSource {
  371. func numberOfRows(in tableView: NSTableView) -> Int {
  372. var datas: [Any] = []
  373. for sectionM in self.handdler.searchResults {
  374. if sectionM.items.count > 0 {
  375. datas.append(sectionM)
  376. if sectionM.isExpand == false {
  377. continue
  378. }
  379. for item in sectionM.items {
  380. datas.append(item)
  381. }
  382. }
  383. }
  384. self.datas = datas
  385. return datas.count
  386. }
  387. func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  388. let model = self.datas[row]
  389. if let data = model as? KMBotaSearchSectionModel {
  390. var cell = tableView.makeView(withIdentifier: KMSectionCellView.km_identifier, owner: nil) as? KMSectionCellView
  391. if cell == nil {
  392. cell = KMSectionCellView.createFromNib()
  393. }
  394. cell?.titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  395. cell?.titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular")
  396. cell?.titleLabel.textColor = KMNColorTools.colorText_1()
  397. cell?.countLabel.textColor = KMNColorTools.colorText_3()
  398. cell?.isExpand = data.isExpand
  399. let pageIndex = data.pageIndex
  400. cell?.titleLabel.stringValue = KMLocalizedString("Page") + " \(pageIndex + 1)"
  401. cell?.countLabel.stringValue = "\(data.itemCount)"
  402. cell?.bottomLine.wantsLayer = true
  403. cell?.bottomLine.layer?.backgroundColor = KMNColorTools.colorBorder_divider().cgColor
  404. cell?.itemClick = { [weak self] idx, _ in
  405. if idx == 1 { // 收取 & 展开
  406. data.isExpand = !data.isExpand
  407. self?.tableView.reloadData()
  408. }
  409. }
  410. return cell
  411. }
  412. if let data = model as? KMSearchMode {
  413. var cell = tableView.makeView(withIdentifier: KMNBotaSearchCellView.km_identifier, owner: self) as? KMNBotaSearchCellView
  414. if cell == nil {
  415. cell = KMNBotaSearchCellView()
  416. }
  417. cell?.label.attributedStringValue = data.attributedString
  418. return cell
  419. }
  420. return nil
  421. }
  422. func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
  423. let model = datas[row]
  424. if model is KMBotaSearchSectionModel {
  425. return 40.0
  426. }
  427. if let data = model as? KMSearchMode {
  428. let width = NSWidth(self.view.frame)
  429. let rect = data.attributedString.boundingRect(with: .init(width: width-24*2, height: CGFLOAT_MAX), options: [.usesLineFragmentOrigin, .usesLineFragmentOrigin])
  430. return rect.size.height + 12 * 2
  431. }
  432. return 40.0
  433. }
  434. func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
  435. let rowView = KMBotaTableRowView()
  436. return rowView
  437. }
  438. func tableViewSelectionDidChange(_ notification: Notification) {
  439. // if self.stopRepeatLoad == true {
  440. //
  441. // } else {
  442. // self.delegate?.controller?(controller: self, listViewSelectionDidChange: notification.object, info: nil)
  443. // }
  444. // [self updateFindResultHighlightsForDirection:NSDirectSelection];
  445. let row = self.tableView.selectedRow
  446. if row >= 0 {
  447. let model = handdler.searchResults[row]
  448. // let isEditing = self.listView?.isEditing() ?? false
  449. // if isEditing {
  450. // self.mainViewController?.srHanddler.showSelection(model.selection)
  451. // return
  452. // }
  453. // self.listView?.go(to: model.selection, animated: true)
  454. // self.listView?.setHighlightedSelection(model.selection, animated: true)
  455. DispatchQueue.main.asyncAfter(deadline: .now()+0.3) {
  456. // self.listView?.setHighlightedSelections([model.selection])
  457. // self.listView?.setNeedsDisplayAnnotationViewForVisiblePages()
  458. }
  459. }
  460. }
  461. func tableView(_ aTableView: NSTableView, copyRowsWithIndexes rowIndexes: IndexSet) {
  462. if IAPProductsManager.default().isAvailableAllFunction() == false {
  463. KMPurchaseCompareWindowController.sharedInstance().showWindow(nil)
  464. return
  465. }
  466. var string = ""
  467. for idx in rowIndexes {
  468. // let match = handdler.searchResults[idx].selection
  469. // string.append("* ")
  470. // [string appendFormat:NSLocalizedString(@"Page %@", @""), [match firstPageLabel]];
  471. // string = string.appendingFormat(KMLocalizedString("Page %@"), "\(match.safeFirstPage()?.pageIndex() ?? 0)")
  472. // [string appendFormat:@"", [[match contextString] string]];
  473. // string = string.appendingFormat(": %@\n", match.string() ?? "")
  474. }
  475. let pboard = NSPasteboard.general
  476. pboard.clearContents()
  477. pboard.writeObjects([string as NSPasteboardWriting])
  478. }
  479. }
  480. // MARK: - KMBotaTableViewDelegate
  481. extension KMBotaSearchViewController: KMBotaTableViewDelegate {
  482. func tableView(_ aTableView: NSTableView, canCopyRowsWithIndexes rowIndexes: IndexSet) -> Bool {
  483. return rowIndexes.count > 0
  484. }
  485. func tableViewMoveRight(_ aTableView: NSTableView) {
  486. // self.updateFindResultHighlightsForDirection(.selectingNext)
  487. }
  488. func tableViewMoveUp(_ aTableView: NSTableView) {
  489. self.tableView.km_safe_selectRowIndexes(.init(integer: self.tableView.selectedRow-1), byExtendingSelection: false)
  490. self.tableView.scrollRowToVisible(self.tableView.selectedRow)
  491. }
  492. func tableViewMoveDown(_ aTableView: NSTableView) {
  493. self.tableView.km_safe_selectRowIndexes(.init(integer: self.tableView.selectedRow+1), byExtendingSelection: false)
  494. self.tableView.scrollRowToVisible(self.tableView.selectedRow)
  495. }
  496. func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject? {
  497. if rowIndex >= self.searchResults.count {
  498. return nil
  499. }
  500. // let model = self.searchResults[rowIndex]
  501. // let selection = model.selection
  502. // let x = selection.bounds.origin.x + NSWidth(selection.bounds) * 0.5
  503. // let y = selection.bounds.origin.y + NSHeight(selection.bounds) * 0.5
  504. // let point = NSPoint(x: x, y: y)
  505. // return CPDFDestination(document: self.pdfDocument(), pageIndex: Int(model.selectionPageIndex), at: point, zoom: self.scaleFactor().cgFloat)
  506. return nil
  507. }
  508. }
  509. //MARK: - ComponentGroupDelegate
  510. extension KMBotaSearchViewController: ComponentGroupDelegate {
  511. func componentGroupDidDismiss(group: ComponentGroup?) {
  512. // if group == groupView_ {
  513. // removeGroupView()
  514. // } else if group == menuGroupView_ {
  515. // group?.removeFromSuperview()
  516. // menuGroupView_ = nil
  517. // } else
  518. if group == searchGroupView {
  519. // searchGroupView_ = nil
  520. searchGroupTarget?.properties.state = .normal
  521. searchGroupTarget?.reloadData()
  522. searchGroupTarget = nil
  523. }
  524. }
  525. func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) {
  526. // if group == groupView_ {
  527. // if let selItem = menuItemProperty {
  528. // let index = group?.menuItemArr.firstIndex(of: selItem)
  529. // if index == 0 {
  530. // expandAllComments(item: NSMenuItem())
  531. // } else if index == 1 {
  532. // collapseAllComments(item: NSMenuItem())
  533. // } else if index == 2 {
  534. // removeAllOutlineItem(item: NSMenuItem())
  535. // }
  536. // }
  537. // } else
  538. if group == menuGroupView_ {
  539. if let selItem = menuItemProperty {
  540. let index = group?.menuItemArr.firstIndex(of: selItem)
  541. if index == 0 {
  542. menuType_ = .circle
  543. addAnnotationsForSelections(NSMenuItem())
  544. } else if index == 1 {
  545. menuType_ = .square
  546. addAnnotationsForSelections(NSMenuItem())
  547. } else if index == 2 {
  548. menuType_ = .highlight
  549. addAnnotationsForSelections(NSMenuItem())
  550. } else if index == 3 {
  551. menuType_ = .underline
  552. addAnnotationsForSelections(NSMenuItem())
  553. } else if index == 4 {
  554. menuType_ = .strikeOut
  555. addAnnotationsForSelections(NSMenuItem())
  556. }
  557. group?.removeFromSuperview()
  558. }
  559. } else if group == searchGroupView {
  560. guard let menuI = menuItemProperty else {
  561. return
  562. }
  563. let idx = group?.menuItemArr.firstIndex(of: menuI)
  564. if idx == 0 { // search
  565. } else if idx == 1 { // replace
  566. } else if idx == 3 {
  567. let key = KMNSearchKey.wholeWords.botaSearch
  568. let value = KMDataManager.ud_bool(forKey: key)
  569. KMDataManager.ud_set(!value, forKey: key)
  570. // BOTAOutlineView.wholeWords = !value
  571. } else if idx == 4 {
  572. let key = KMNSearchKey.caseSensitive.botaSearch
  573. let value = KMDataManager.ud_bool(forKey: key)
  574. KMDataManager.ud_set(!value, forKey: key)
  575. // BOTAOutlineView.caseSensitive = !value
  576. }
  577. }
  578. }
  579. }