KMBookMarkViewController.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. //
  2. // KMBookMarkViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lxy on 2022/10/10.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. typealias KMBookMarkViewControllerBookMarkDidChange = (_ controller: KMBookMarkViewController, _ bookMarks: [KMBookmarkItem]) -> Void
  10. @objc protocol KMBookMarkViewControllerDelegate: NSObjectProtocol {
  11. @objc optional func bkControllerAddAction(controller: KMBookMarkViewController, bookmark: CPDFBookmark?, info: [String : Any]?)
  12. }
  13. extension KMNSearchKey.wholeWords {
  14. static let bookmark = "BookmarkSearchWholeWordsKey"
  15. }
  16. extension KMNSearchKey.caseSensitive {
  17. static let bookmark = "BookmarkSearchCaseSensitiveKey"
  18. }
  19. class KMBookMarkViewController: KMNBotaBaseViewController {
  20. @IBOutlet weak var topView: NSView!
  21. @IBOutlet weak var addBookButton: NSButton!
  22. @IBOutlet weak var titleTextField: NSTextField!
  23. @IBOutlet weak var sortButton: NSButton!
  24. @IBOutlet var topSeplineView: NSView!
  25. @IBOutlet weak var bookTableView: KMBotaTableView!
  26. @IBOutlet weak var emptyView: NSView!
  27. @IBOutlet weak var bigTipLabel: NSTextField!
  28. @IBOutlet weak var tipLabel: NSTextField!
  29. private lazy var addButton_: ComponentButton = {
  30. let view = ComponentButton()
  31. view.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "KMBookmarkAdd"))
  32. return view
  33. }()
  34. private var emptyView_: ComponentEmpty = {
  35. let view = ComponentEmpty()
  36. view.properties = ComponentEmptyProperty(emptyType: .noBookmark, state: .normal, image: NSImage(named: "KMBookmarkEmpty"), text: KMLocalizedString("No Bookmarks"), subText: KMLocalizedString(""))
  37. return view
  38. }()
  39. private var groupView: ComponentGroup? = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  40. private var menuGroupView_: ComponentGroup?
  41. var dataSource: [KMBookmarkItem] = []
  42. var renameTextField: NSTextField?
  43. var renamePDFBook: KMBookmarkItem?
  44. var renameCellView: KMBookCellView?
  45. weak var document: CPDFDocument?
  46. var isLocalEvent: Bool = false //区分外部点击还是内部点击
  47. var selectItems: [KMBookmarkItem] = []
  48. var bookMarkDidChange: KMBookMarkViewControllerBookMarkDidChange?
  49. weak var delegate: KMBookMarkViewControllerDelegate?
  50. private lazy var handdler_ = KMNBookmarkHanddler()
  51. var handdler: KMNBookmarkHanddler {
  52. get {
  53. return handdler_
  54. }
  55. }
  56. convenience init() {
  57. self.init(nibName: "KMBookMarkViewController", bundle: nil)
  58. }
  59. override func viewDidLoad() {
  60. super.viewDidLoad()
  61. handdler.delegate = self
  62. titleTextField.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-bold")
  63. titleTextField.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1")
  64. addBookButton.addSubview(addButton_)
  65. addButton_.frame = addBookButton.bounds
  66. addButton_.autoresizingMask = [.width, .height]
  67. addButton_.setTarget(self, action: #selector(addBookmarkAction))
  68. bookTableView.style = NSTableView.Style.plain
  69. bookTableView.allowsMultipleSelection = true
  70. bookTableView.doubleAction = #selector(renameBookAction)
  71. bookTableView.hasImageToolTips = true
  72. bookTableView.botaDelegate = self
  73. bookTableView.menuClickedAction = { [unowned self] point in
  74. let idxs = self.bookTableView.selectedRowIndexes.count
  75. let convertP = self.bookTableView.convert(point, from: nil)
  76. let row = self.bookTableView.row(at: convertP)
  77. if row == -1 {
  78. var viewHeight: CGFloat = 0
  79. let items: [String] = ["Add Bookmark", "Select All", "Delete"]
  80. var menuItemArr: [ComponentMenuitemProperty] = []
  81. for value in items {
  82. let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
  83. itemSelected: false,
  84. isDisabled: false,
  85. keyEquivalent: nil,
  86. text: KMLocalizedString(value),
  87. identifier: value)
  88. if value == "Delete" {
  89. properties_Menuitem.keyEquivalent = "⌘ " + String(Unicode.Scalar(NSBackspaceCharacter)!)
  90. properties_Menuitem.isDisabled = idxs == 0
  91. }
  92. if value == "Select All" {
  93. properties_Menuitem.keyEquivalent = "⌘ A"
  94. if self.dataSource.count == 0{
  95. properties_Menuitem.isDisabled = true
  96. } else {
  97. properties_Menuitem.isDisabled = false
  98. }
  99. }
  100. menuItemArr.append(properties_Menuitem)
  101. viewHeight += 36
  102. }
  103. self.menuGroupView_ = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  104. self.menuGroupView_?.groupDelegate = self
  105. self.menuGroupView_?.frame = CGRectMake(0, 0, 180, viewHeight)
  106. self.menuGroupView_?.updateGroupInfo(menuItemArr)
  107. self.menuGroupView_?.showWithPoint(CGPoint(x: point.x, y: point.y - viewHeight), relativeTo: nil)
  108. return NSMenu()
  109. }
  110. return NSMenu()
  111. }
  112. topView.addSubview(searchButton)
  113. searchButton.km_add_size_constraint(size: NSMakeSize(24, 24))
  114. searchButton.km_add_centerY_constraint(constant: 1)
  115. searchButton.km_add_trailing_constraint(equalTo: addBookButton, attribute: .leading, constant: -4)
  116. searchButton.setTarget(self, action: #selector(_searchAction))
  117. emptyView.addSubview(emptyView_)
  118. emptyView_.km_add_top_constraint(constant: 232)
  119. emptyView_.km_add_bottom_constraint()
  120. emptyView_.km_add_leading_constraint()
  121. emptyView_.km_add_trailing_constraint()
  122. if let data = headerSearchView {
  123. topView.addSubview(data)
  124. headerSearchView?.frame = topView.bounds
  125. headerSearchView?.autoresizingMask = [.width, .height]
  126. }
  127. hideHeaderSearch()
  128. headerSearchView?.itemClick = { [weak self] idx, params in
  129. if idx == 1 { // 显示搜索限制条件
  130. guard let button = params.first as? ComponentButton else {
  131. return
  132. }
  133. self?.showSearchGroupView(sender: button)
  134. } else if idx == 2 { // 关闭搜索
  135. self?.hideHeaderSearch()
  136. self?.reloadData()
  137. }
  138. }
  139. headerSearchView?.valueDidChange = { [weak self] sender, info in
  140. let value = info?[.newKey] as? String ?? ""
  141. self?.handdler.searchKey = value
  142. self?.reloadData()
  143. }
  144. self.reloadData()
  145. }
  146. override func updateUILanguage() {
  147. super.updateUILanguage()
  148. KMMainThreadExecute {
  149. self.titleTextField.stringValue = KMLocalizedString("Bookmarks")
  150. }
  151. }
  152. override func updateUIThemeColor() {
  153. super.updateUIThemeColor()
  154. KMMainThreadExecute {
  155. self.view.wantsLayer = true
  156. let color = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle")
  157. self.view.layer?.backgroundColor = color.cgColor
  158. self.topSeplineView.wantsLayer = true
  159. self.topSeplineView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/divider").cgColor
  160. self.titleTextField.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  161. self.addButton_.properties.icon = NSImage(named: "KMImageNameBookMarksAdd")
  162. self.addButton_.reloadData()
  163. self.searchButton.properties.icon = NSImage(named: "KMImageNameOutlineSearch")
  164. self.searchButton.reloadData()
  165. self.bookTableView.reloadData()
  166. }
  167. }
  168. func reloadData() {
  169. var bookMarks: [KMBookmarkItem] = []
  170. for bookmark in handdler.bookmarks() {
  171. let item = KMBookmarkItem()
  172. item.bookMark = bookmark
  173. item.index = UInt(bookmark.pageIndex)
  174. item.label = bookmark.label
  175. bookMarks.append(item)
  176. }
  177. self.dataSource = bookMarks
  178. self.dataSource.sort(){
  179. guard let index = $0.bookMark?.pageIndex else {
  180. return false
  181. }
  182. guard let otherIndex = $1.bookMark?.pageIndex else {
  183. return false
  184. }
  185. return index < otherIndex
  186. }
  187. if handdler.isValidSearchMode() {
  188. var items: [KMBookmarkItem] = []
  189. for item in dataSource {
  190. guard let bookmark = item.bookMark else {
  191. continue
  192. }
  193. if hasContainString(handdler.searchKey, bookmark: bookmark) {
  194. items.append(item)
  195. }
  196. }
  197. self.dataSource = items
  198. }
  199. self.bookTableView.reloadData()
  200. self.updateAddBookMarkState()
  201. }
  202. func addBookMarkAndEdit(newBookMark: KMBookmarkItem) {
  203. _ = self.dataSource.contains { item in
  204. if handdler.bookmarkIsEqual(bookmark: item.bookMark, otherBookmark: newBookMark.bookMark) {
  205. let index = KMOCToolClass.arrayIndexOf(array: self.dataSource, item: item) ?? 0
  206. self.didSelectItem(row: index, event: NSEvent())
  207. self.renameBookWithRow(row: index)
  208. return true
  209. }
  210. return false
  211. }
  212. }
  213. func showSearchGroupView(sender: ComponentButton) {
  214. var viewHeight: CGFloat = 8
  215. var menuItemArr: [ComponentMenuitemProperty] = []
  216. let titles = ["Whole Words","Case Sensitive"]
  217. for i in titles {
  218. let menuI = ComponentMenuitemProperty(text: KMLocalizedString(i))
  219. menuItemArr.append(menuI)
  220. viewHeight += 36
  221. }
  222. if let info = menuItemArr.first {
  223. if KMDataManager.ud_bool(forKey: KMNSearchKey.wholeWords.botaSearch) {
  224. info.righticon = NSImage(named: "KMNImageNameMenuSelect")
  225. }
  226. }
  227. if let info = menuItemArr.last {
  228. if KMDataManager.ud_bool(forKey: KMNSearchKey.caseSensitive.botaSearch) {
  229. info.righticon = NSImage(named: "KMNImageNameMenuSelect")
  230. }
  231. }
  232. let groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  233. searchGroupView = groupView
  234. groupView?.groupDelegate = self
  235. groupView?.frame = CGRectMake(310, 0, 200, viewHeight)
  236. groupView?.updateGroupInfo(menuItemArr)
  237. var point = sender.convert(sender.frame.origin, to: nil)
  238. point.y -= viewHeight
  239. groupView?.showWithPoint(point, relativeTo: sender)
  240. searchGroupTarget = sender
  241. }
  242. override func showHeaderSearch() {
  243. super.showHeaderSearch()
  244. handdler.isSearchMode = true
  245. }
  246. override func hideHeaderSearch() {
  247. super.hideHeaderSearch()
  248. handdler.isSearchMode = false
  249. }
  250. func hasContainString(_ searchString: String, bookmark: CPDFBookmark) -> Bool {
  251. var label = bookmark.label ?? ""
  252. var searchLabel = searchString
  253. if handdler.caseSensitive == false {
  254. label = label.lowercased()
  255. searchLabel = searchLabel.lowercased()
  256. }
  257. if label.contains(searchLabel) {
  258. if handdler.wholeWords {
  259. let words = label.words()
  260. return words.contains(searchLabel)
  261. }
  262. return true
  263. }
  264. return false
  265. }
  266. func didSelectItem(row: Int, event: NSEvent?, needJump: Bool = true) {
  267. //当选中一个时
  268. if self.bookTableView.selectedRowIndexes.count == 1 || (event != nil &&
  269. (!event!.modifierFlags.contains(NSEvent.ModifierFlags.command) &&
  270. !event!.modifierFlags.contains(NSEvent.ModifierFlags.shift))) {
  271. self.bookTableView.selectRowIndexes(IndexSet(integer: IndexSet.Element(row)), byExtendingSelection: false)
  272. }
  273. //原始数据置空
  274. for model in self.selectItems {
  275. for (idx, item) in self.dataSource.enumerated() {
  276. if item.bookMark != model.bookMark {
  277. continue
  278. }
  279. item.select = false
  280. let rowView = self.bookTableView.rowView(atRow: idx, makeIfNecessary: false) as? KMBookMarkTableRowView
  281. rowView?.reloadData()
  282. }
  283. }
  284. //获取最新数据
  285. var items: [KMBookmarkItem] = []
  286. for index in self.bookTableView.selectedRowIndexes {
  287. if index < self.dataSource.count {
  288. let model = self.dataSource[index]
  289. model.select = true
  290. let rowView = self.bookTableView.rowView(atRow: index, makeIfNecessary: false) as? KMBookMarkTableRowView
  291. rowView?.reloadData()
  292. items.append(model)
  293. }
  294. }
  295. self.selectItems = items
  296. //刷新数据
  297. if needJump {
  298. self.updateListViewData()
  299. }
  300. }
  301. func updateListViewData() {
  302. let selectedRowIndexes = self.bookTableView.selectedRowIndexes
  303. if selectedRowIndexes.count != 1 {
  304. return
  305. }
  306. let idx = selectedRowIndexes.first ?? 0
  307. guard let item = self.dataSource.safe_element(for: idx) as? KMBookmarkItem else {
  308. return
  309. }
  310. handdler.goBookmark(item.bookMark, animated: true)
  311. }
  312. func cancelSelect() {
  313. self.bookTableView.deselectAll(nil)
  314. for model in self.selectItems {
  315. model.select = false
  316. if let index = self.dataSource.firstIndex(of: model) {
  317. let rowView = self.bookTableView.rowView(atRow: index, makeIfNecessary: false) as? KMBookMarkTableRowView
  318. rowView?.reloadData()
  319. }
  320. }
  321. }
  322. func updateAddBookMarkState() {
  323. // addButton_.properties.isDisabled = !canAddBorkMark()
  324. // addButton_.properties.state = .normal
  325. // addButton_.reloadData()
  326. }
  327. // MARK: - Public Methods
  328. public func selectIndex(index: Int) {
  329. if index < 0 || index >= self.dataSource.count {
  330. return
  331. }
  332. self.bookTableView.selectRowIndexes(IndexSet(integer: IndexSet.Element(index)), byExtendingSelection: false)
  333. self.didSelectItem(row: index, event: NSEvent(), needJump: false)
  334. }
  335. public func currentPageDidChangedAction(document: CPDFDocument?) {
  336. guard let theDocument = document, theDocument.isEqual(to: handdler.document) else {
  337. return
  338. }
  339. if !isLocalEvent {
  340. var containSelIndex:Bool = false
  341. for (index, value) in self.dataSource.enumerated() {
  342. let currentBookmark = handdler.bookmark(for: handdler.currentPageIndex)
  343. if handdler.bookmarkIsEqual(bookmark: value.bookMark, otherBookmark: currentBookmark) {
  344. containSelIndex = true
  345. self.didSelectItem(row: index, event: NSEvent())
  346. break
  347. }
  348. }
  349. if !containSelIndex {
  350. self.cancelSelect()
  351. }
  352. }
  353. isLocalEvent = false
  354. self.updateAddBookMarkState()
  355. }
  356. public func canAddBorkMark() -> Bool {
  357. return handdler.canAddBorkmark()
  358. }
  359. public func addCurrentBookmark(callback: ((CPDFBookmark?)->Void)?) {
  360. handdler.addCurrentBookmark(callback: callback)
  361. }
  362. public func removeBookmark(for index: Int) -> Bool {
  363. return handdler.removeBookmark(for: index)
  364. }
  365. public func removeBookmarks(for indexs: IndexSet) -> Bool {
  366. return handdler.removeBookmarks(for: indexs)
  367. }
  368. public func removeAllBookmarks() -> Bool {
  369. return handdler.removeAllBookmarks()
  370. }
  371. public func rename(bookmark: CPDFBookmark, label: String) {
  372. handdler.rename(bookmark: bookmark, label: label)
  373. }
  374. //MARK: - Menu Action
  375. @objc func renameBookAction() {
  376. if self.bookTableView.selectedRowIndexes.count == 1 {
  377. self.renameBookWithRow(row: self.bookTableView.selectedRowIndexes.first!)
  378. } else {
  379. __NSBeep()
  380. }
  381. }
  382. @IBAction func escButtonAction(_ sender: Any) {
  383. self.bookTableView.deselectAll(nil)
  384. }
  385. @IBAction func addBookmarkAction(_ sender: Any) {
  386. addButton_.properties.state = .normal
  387. addButton_.reloadData()
  388. let currentPageIndex = handdler.currentPageIndex
  389. if let data = handdler.bookmark(for: currentPageIndex) {
  390. delegate?.bkControllerAddAction?(controller: self, bookmark: data, info: ["result" : false])
  391. return
  392. }
  393. handdler.addCurrentBookmark(callback: { [unowned self] bookmark in
  394. self.delegate?.bkControllerAddAction?(controller: self, bookmark: bookmark, info: nil)
  395. self.reloadData()
  396. DispatchQueue.main.async {
  397. if let bookmark = self.handdler.bookmark(for: Int(currentPageIndex)) {
  398. let item = KMBookmarkItem()
  399. item.bookMark = bookmark
  400. item.label = bookmark.label
  401. item.index = UInt(bookmark.pageIndex)
  402. self.addBookMarkAndEdit(newBookMark: item)
  403. }
  404. }
  405. })
  406. }
  407. @objc func changeLocationAction() {
  408. if self.bookTableView.selectedRowIndexes.count == 1 {
  409. let item = self.dataSource[self.bookTableView.selectedRowIndexes.first!]
  410. let alter = NSAlert()
  411. alter.alertStyle = NSAlert.Style.informational
  412. alter.messageText = KMLocalizedString("Are you sure you want to set the selected page as the bookmark location?", comment: "")
  413. alter.addButton(withTitle: KMLocalizedString("Yes", comment:""))
  414. alter.addButton(withTitle: KMLocalizedString("No", comment:""))
  415. let modlres = alter.runModal()
  416. if modlres == .alertFirstButtonReturn {
  417. let bookMark = KMBookmarkItem()
  418. bookMark.bookMark = item.bookMark
  419. bookMark.label = item.label
  420. bookMark.index = UInt(handdler.currentPageIndex)
  421. self.changeLocation(oldBookMark: item,
  422. newBookMark: bookMark)
  423. }
  424. } else {
  425. __NSBeep()
  426. }
  427. }
  428. @objc func deleteBookAction() {
  429. if self.bookTableView.selectedRowIndexes.count != 0 {
  430. var bookMarks:[KMBookmarkItem] = []
  431. for index in self.bookTableView.selectedRowIndexes {
  432. let item = self.dataSource[index]
  433. bookMarks.append(item)
  434. }
  435. self.deleteBookMark(bookMarks: bookMarks)
  436. } else {
  437. __NSBeep()
  438. }
  439. }
  440. private func renameBookWithRow(row: Int) {
  441. self.renamePDFBook = self.dataSource[row]
  442. self.renameCellView = self.bookTableView.view(atColumn: 0, row: row, makeIfNecessary: true) as? KMBookCellView
  443. self.renameTextField = self.renameCellView?.inputTF
  444. self.renameTextField?.delegate = self
  445. self.renameTextField?.isEditable = true
  446. self.renameTextField?.becomeFirstResponder()
  447. }
  448. @objc private func _searchAction() {
  449. searchButton.properties.state = .normal
  450. searchButton.reloadData()
  451. showHeaderSearch()
  452. }
  453. }
  454. // MARK: - NSTextFieldDelegate
  455. extension KMBookMarkViewController: NSTextFieldDelegate {
  456. func controlTextDidEndEditing(_ obj: Notification) {
  457. guard let bookmark = renamePDFBook?.bookMark else {
  458. return
  459. }
  460. if (self.renameTextField!.isEqual(obj.object)) {
  461. let textField : NSTextField = obj.object as! NSTextField
  462. handdler.rename(bookmark: bookmark, label: textField.stringValue)
  463. self.renameTextField?.isEditable = false
  464. renameCellView?.inputBox.borderWidth = 0
  465. renameCellView?.inputBox.fillColor = .clear
  466. }
  467. }
  468. }
  469. // MARK: - NSTableViewDelegate,NSTableViewDataSource
  470. extension KMBookMarkViewController : NSTableViewDelegate,NSTableViewDataSource {
  471. func numberOfRows(in tableView: NSTableView) -> Int {
  472. let count = self.dataSource.count
  473. if count == 0 {
  474. self.emptyView.isHidden = false
  475. self.searchButton.properties.isDisabled = true
  476. } else {
  477. self.emptyView.isHidden = true
  478. self.searchButton.properties.isDisabled = false
  479. }
  480. self.searchButton.reloadData()
  481. return count
  482. }
  483. func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  484. if row < self.dataSource.count {
  485. let item: KMBookmarkItem = self.dataSource[row]
  486. let cell : KMBookCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMBookCellView"), owner: self) as! KMBookCellView
  487. if handdler.isValidSearchMode() {
  488. var label = item.bookMark?.label ?? ""
  489. let attri = NSMutableAttributedString(string: label, attributes: [
  490. .font : NSFont.SFProTextRegularFont(13),
  491. .foregroundColor : KMNColorTools.colorText_1()])
  492. var _searchKey = handdler.searchKey
  493. if handdler.caseSensitive == false {
  494. label = label.lowercased()
  495. _searchKey = _searchKey.lowercased()
  496. }
  497. let ranges = label.ranges(of: _searchKey)
  498. for range in ranges.nsRnage {
  499. attri.addAttributes([.font : NSFont.SFProTextBoldFont(13), .foregroundColor: KMNColorTools.colorPrimary_textLight()], range: range)
  500. }
  501. cell.inputTF.attributedStringValue = attri
  502. } else {
  503. cell.inputTF.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular")
  504. cell.inputTF.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1")
  505. cell.inputTF.stringValue = item.bookMark?.label ?? ""
  506. cell.bookTitle.stringValue = "\(item.bookMark?.pageIndex ?? 0 + 1)"
  507. cell.inputBox.borderWidth = 0
  508. if let data = item.bookMark?.date {
  509. cell.dateLabel.stringValue = KMTools.timeString(timeDate: data)
  510. } else {
  511. cell.dateLabel.stringValue = ""
  512. }
  513. }
  514. cell.textFieldDidEndEditingCallback = { [weak self] textF in
  515. self?.renameCellView = cell
  516. if let bookmark = item.bookMark {
  517. self?.handdler.rename(bookmark: bookmark, label: textF.stringValue)
  518. }
  519. }
  520. return cell
  521. }
  522. return nil
  523. }
  524. func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
  525. let rowView = KMBookMarkTableRowView()
  526. rowView.selectionHighlightStyle = .none
  527. if row < self.dataSource.count {
  528. rowView.model = self.dataSource[row]
  529. rowView.menuClickedAction = { [weak self] point in
  530. let idxs = self?.bookTableView.selectedRowIndexes.count ?? 0
  531. let tempView = self?.bookTableView.rowView(atRow: row, makeIfNecessary: false)
  532. var viewHeight: CGFloat = 0
  533. var items: [String] = ["Rename", "Setting Destination", "Delete"]
  534. if idxs > 1 {
  535. items = ["Delete"]
  536. }
  537. var menuItemArr: [ComponentMenuitemProperty] = []
  538. for value in items {
  539. let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
  540. itemSelected: false,
  541. isDisabled: false,
  542. keyEquivalent: nil,
  543. text: KMLocalizedString(value),
  544. identifier: value)
  545. if value == "Delete" {
  546. properties_Menuitem.keyEquivalent = "⌘ " + String(Unicode.Scalar(NSBackspaceCharacter)!)
  547. }
  548. menuItemArr.append(properties_Menuitem)
  549. viewHeight += 36
  550. }
  551. if self?.groupView != nil {
  552. self?.groupView?.groupDelegate = self
  553. self?.groupView?.frame = CGRectMake(0, 0, 180, viewHeight)
  554. self?.groupView?.updateGroupInfo(menuItemArr)
  555. self?.groupView?.showWithPoint(CGPoint(x: point.x, y: point.y - viewHeight), relativeTo: tempView)
  556. }
  557. return NSMenu()
  558. }
  559. rowView.mouseDownAction = { [unowned self] (view, event) in
  560. self.didSelectItem(row: row, event: event)
  561. }
  562. rowView.rightMouseDownAction = { [unowned self] (view, event) in
  563. if !KMOCToolClass.arrayContains(array: self.selectItems, annotation: rowView.model) ||
  564. self.selectItems.count == 1 {
  565. self.selectIndex(index: row)
  566. }
  567. if row < 0 || row >= self.dataSource.count {
  568. return
  569. }
  570. }
  571. rowView.hoverCallback = { [unowned self] (mouseEntered, mouseBox) in
  572. if let value = ComponentLibrary.shared.getComponentValueFromKey("radius/xs") {
  573. let currentValue = value as? CGFloat ?? 0
  574. rowView.box?.cornerRadius = currentValue
  575. }
  576. self.bookTableView.enumerateAvailableRowViews { view, row in
  577. if let data = view as? KMBookMarkTableRowView {
  578. data.model.hover = false
  579. data.reloadData()
  580. }
  581. }
  582. if mouseEntered {
  583. rowView.model.hover = true
  584. rowView.reloadData()
  585. } else {
  586. rowView.model.hover = false
  587. rowView.reloadData()
  588. }
  589. }
  590. }
  591. return rowView
  592. }
  593. func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
  594. return 36
  595. }
  596. func tableView(_ tableView: NSTableView, shouldSelect tableColumn: NSTableColumn?) -> Bool {
  597. self.isLocalEvent = true
  598. return true
  599. }
  600. func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
  601. self.isLocalEvent = true
  602. return true
  603. }
  604. func tableViewSelectionDidChange(_ notification: Notification) {
  605. if self.bookTableView.selectedRow == -1 {
  606. self.cancelSelect()
  607. }
  608. }
  609. }
  610. // MARK: - KMBotaTableViewDelegate
  611. extension KMBookMarkViewController: KMBotaTableViewDelegate {
  612. func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject? {
  613. if aTableView.isEqual(to: self.bookTableView) {
  614. let cnt = self.dataSource.count
  615. if rowIndex >= cnt {
  616. return nil
  617. }
  618. let model = self.dataSource[rowIndex]
  619. return model.bookMark
  620. }
  621. return nil
  622. }
  623. }
  624. //MARK: - undoRedo
  625. extension KMBookMarkViewController {
  626. func changeLocation(oldBookMark: KMBookmarkItem, newBookMark: KMBookmarkItem) {
  627. document?.removeBookmark(forPageIndex: oldBookMark.index)
  628. document?.addBookmark(newBookMark.label, forPageIndex: newBookMark.index)
  629. reloadData()
  630. }
  631. func renamePDFBook(bookmark : KMBookmarkItem! , label:String) {
  632. if bookmark.bookMark?.label == label {
  633. return
  634. }
  635. let temp = bookmark.bookMark?.label
  636. bookmark.bookMark?.label = label
  637. self.reloadData()
  638. var indexSet = IndexSet()
  639. indexSet.insert(self.bookTableView.row(for: self.renameCellView!))
  640. self.bookTableView.selectRowIndexes(indexSet, byExtendingSelection: false)
  641. }
  642. func deleteBookMark(bookMarks: [KMBookmarkItem]) {
  643. for bookMark in bookMarks {
  644. if ((document?.removeBookmark(forPageIndex: bookMark.index)) != nil) {
  645. KMPrint("删除标签成功")
  646. }
  647. }
  648. self.reloadData()
  649. guard let callBack = bookMarkDidChange else { return }
  650. callBack(self, bookMarks)
  651. }
  652. func addBookMark(bookMarks: [KMBookmarkItem]) {
  653. for bookmark in bookMarks {
  654. document?.addBookmark(bookmark.label, forPageIndex: UInt(bookmark.index))
  655. }
  656. self.reloadData()
  657. if bookMarks.count == 1 {
  658. DispatchQueue.main.async {
  659. if let bookmark = self.handdler.bookmark(for: Int(bookMarks.first!.index)) {
  660. let item = KMBookmarkItem()
  661. item.bookMark = bookmark
  662. item.label = bookmark.label
  663. item.index = UInt(bookmark.pageIndex)
  664. self.addBookMarkAndEdit(newBookMark: item)
  665. }
  666. }
  667. }
  668. guard let callBack = bookMarkDidChange else { return }
  669. callBack(self, bookMarks)
  670. }
  671. @IBAction func undo(_ sender: Any) {
  672. handdler.undo()
  673. }
  674. @IBAction func redo(_ sender: Any) {
  675. handdler.redo()
  676. }
  677. }
  678. // MARK: - NSMenuDelegate, NSMenuItemValidation
  679. extension KMBookMarkViewController: NSMenuDelegate, NSMenuItemValidation {
  680. func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  681. let action = menuItem.action
  682. if (action == #selector(undo)) {
  683. return handdler.canUndo()
  684. }
  685. if (action == #selector(redo)) {
  686. return handdler.canRedo()
  687. }
  688. if action == #selector(renameBookAction) ||
  689. action == #selector(changeLocationAction) ||
  690. action == #selector(deleteBookAction) {
  691. if self.bookTableView.selectedRowIndexes.count > 1 {
  692. if action == #selector(changeLocationAction) {
  693. return false
  694. } else if action == #selector(renameBookAction) {
  695. return false
  696. }
  697. } else if self.bookTableView.selectedRowIndexes.count == 1 {
  698. return true
  699. } else {
  700. if self.bookTableView.selectedRowIndexes.count == 0 {
  701. if action == #selector(changeLocationAction) {}
  702. } else {
  703. return false
  704. }
  705. }
  706. }
  707. return true
  708. }
  709. }
  710. // MARK: - KMNBookmarkHanddlerDelegate
  711. extension KMBookMarkViewController: KMNBookmarkHanddlerDelegate {
  712. func handdler(_ handdler: KMNBookmarkHanddler, didAdd bookmark: CPDFBookmark?, info: [String : Any]?) {
  713. KMMainThreadExecute {
  714. self.reloadData()
  715. }
  716. }
  717. func handdler(_ handdler: KMNBookmarkHanddler, didRemove bookmark: CPDFBookmark?, info: [String : Any]?) {
  718. KMMainThreadExecute {
  719. self.reloadData()
  720. }
  721. }
  722. func handdler(_ handdler: KMNBookmarkHanddler, didRemoveAll info: [String : Any]?) {
  723. KMMainThreadExecute {
  724. self.reloadData()
  725. }
  726. }
  727. func handdler(_ handdler: KMNBookmarkHanddler, didRename bookmark: CPDFBookmark?, info: [String : Any]?) {
  728. KMMainThreadExecute {
  729. self.reloadData()
  730. }
  731. }
  732. }
  733. // MARK: - ComponentGroupDelegate
  734. extension KMBookMarkViewController: ComponentGroupDelegate {
  735. func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) {
  736. if group == groupView {
  737. if let selItem = menuItemProperty {
  738. if selItem.text == KMLocalizedString("Delete") {
  739. var pageIndexs = IndexSet()
  740. for i in bookTableView.selectedRowIndexes {
  741. guard let item = dataSource.safe_element(for: i) as? KMBookmarkItem else {
  742. continue
  743. }
  744. if let bookmark = item.bookMark {
  745. pageIndexs.insert(bookmark.pageIndex)
  746. }
  747. }
  748. _ = handdler.removeBookmarks(for: pageIndexs)
  749. } else if selItem.text == KMLocalizedString("Rename") {
  750. renameBookAction()
  751. } else if selItem.text == KMLocalizedString("Setting Destination") {
  752. changeLocationAction()
  753. }
  754. }
  755. } else if group == searchGroupView {
  756. guard let menuI = menuItemProperty else {
  757. return
  758. }
  759. let idx = group?.menuItemArr.firstIndex(of: menuI)
  760. if idx == 0 {
  761. let key = KMNSearchKey.wholeWords.botaSearch
  762. let value = KMDataManager.ud_bool(forKey: key)
  763. KMDataManager.ud_set(!value, forKey: key)
  764. handdler.wholeWords = !value
  765. } else if idx == 1 {
  766. let key = KMNSearchKey.caseSensitive.botaSearch
  767. let value = KMDataManager.ud_bool(forKey: key)
  768. KMDataManager.ud_set(!value, forKey: key)
  769. handdler.caseSensitive = !value
  770. }
  771. } else if group == menuGroupView_ {
  772. guard let menuI = menuItemProperty else {
  773. return
  774. }
  775. let idx = group?.menuItemArr.firstIndex(of: menuI)
  776. if idx == 0 {
  777. addBookmarkAction(addButton_)
  778. } else if idx == 1 {
  779. var indexs = IndexSet()
  780. for (i, _) in dataSource.enumerated() {
  781. indexs.insert(i)
  782. }
  783. bookTableView.km_safe_selectRowIndexes(indexs, byExtendingSelection: false)
  784. for i in indexs {
  785. didSelectItem(row: i, event: nil)
  786. }
  787. } else if idx == 2 {
  788. deleteBookAction()
  789. }
  790. }
  791. }
  792. func componentGroupDidDismiss(group: ComponentGroup?) {
  793. if group == searchGroupView {
  794. searchGroupTarget?.properties.state = .normal
  795. searchGroupTarget?.reloadData()
  796. searchGroupTarget = nil
  797. }
  798. }
  799. }