KMBookMarkViewController.swift 34 KB

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