// // KMBookMarkViewController.swift // PDF Reader Pro // // Created by lxy on 2022/10/10. // import Cocoa import KMComponentLibrary typealias KMBookMarkViewControllerBookMarkDidChange = (_ controller: KMBookMarkViewController, _ bookMarks: [KMBookMarkItem]) -> Void @objc protocol KMBookMarkViewControllerDelegate: NSObjectProtocol { @objc optional func bkControllerAddAction(controller: KMBookMarkViewController, bookmark: CPDFBookmark?, info: [String : Any]?) } extension KMNSearchKey.wholeWords { static let bookmark = "BookmarkSearchWholeWordsKey" } extension KMNSearchKey.caseSensitive { static let bookmark = "BookmarkSearchCaseSensitiveKey" } class KMBookMarkViewController: KMNBotaBaseViewController { @IBOutlet weak var topView: NSView! @IBOutlet weak var addBookButton: NSButton! @IBOutlet weak var titleTextField: NSTextField! @IBOutlet weak var sortButton: NSButton! @IBOutlet var topSeplineView: NSView! @IBOutlet weak var bookTableView: KMBotaTableView! @IBOutlet weak var emptyView: NSView! @IBOutlet weak var bigTipLabel: NSTextField! @IBOutlet weak var tipLabel: NSTextField! var dataSource: [KMBookMarkItem] = [] var renameTextField: NSTextField? var renamePDFBook: KMBookMarkItem? var renameCellView: KMBookCellView? weak var document: CPDFDocument? var isLocalEvent: Bool = false //区分外部点击还是内部点击 var selectItems: [KMBookMarkItem] = [] var bookMarkDidChange: KMBookMarkViewControllerBookMarkDidChange? weak var delegate: KMBookMarkViewControllerDelegate? private lazy var handdler_ = KMNBookmarkHanddler() // 升序 private var sortType_: KMSortMode = .ascending private lazy var addButton_: ComponentButton = { let view = ComponentButton() view.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "KMBookmarkAdd")) return view }() private var emptyView_: ComponentEmpty = { let view = ComponentEmpty() view.properties = ComponentEmptyProperty(emptyType: .noBookmark, state: .normal, image: NSImage(named: "KMBookmarkEmpty"), text: KMLocalizedString("No Bookmark"), subText: KMLocalizedString("Here is the description.")) return view }() var handdler: KMNBookmarkHanddler { get { return handdler_ } } private var groupView: ComponentGroup? = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle()) convenience init() { self.init(nibName: "KMBookMarkViewController", bundle: nil) } override func viewDidLoad() { super.viewDidLoad() handdler.delegate = self titleTextField.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-bold") addBookButton.addSubview(addButton_) addButton_.frame = addBookButton.bounds addButton_.autoresizingMask = [.width, .height] addButton_.setTarget(self, action: #selector(addBookmarkAction)) bookTableView.style = NSTableView.Style.plain bookTableView.allowsMultipleSelection = true bookTableView.doubleAction = #selector(renameBookAction) bookTableView.hasImageToolTips = true bookTableView.botaDelegate = self sortButton.image = NSImage(named: "KMImageNameBotaBookmarkSortIcon") sortButton.target = self sortButton.action = #selector(sortAction) topView.addSubview(searchButton) searchButton.km_add_size_constraint(size: NSMakeSize(24, 24)) searchButton.km_add_centerY_constraint(constant: 1) searchButton.km_add_trailing_constraint(equalTo: addBookButton, attribute: .leading, constant: -4) searchButton.setTarget(self, action: #selector(_searchAction)) emptyView.addSubview(emptyView_) emptyView_.km_add_top_constraint(constant: 232) emptyView_.km_add_bottom_constraint() emptyView_.km_add_leading_constraint() emptyView_.km_add_trailing_constraint() if let data = headerSearchView { topView.addSubview(data) headerSearchView?.frame = topView.bounds headerSearchView?.autoresizingMask = [.width, .height] } hideHeaderSearch() headerSearchView?.itemClick = { [weak self] idx, params in if idx == 1 { // 显示搜索限制条件 guard let button = params.first as? ComponentButton else { return } self?.showSearchGroupView(sender: button) } else if idx == 2 { // 关闭搜索 self?.hideHeaderSearch() self?.reloadData() } } headerSearchView?.valueDidChange = { [weak self] sender, info in let value = info?[.newKey] as? String ?? "" self?.handdler.searchKey = value self?.reloadData() } self.reloadData() } override func updateUILanguage() { super.updateUILanguage() KMMainThreadExecute { self.titleTextField.stringValue = KMLocalizedString("Bookmarks") } } override func updateUIThemeColor() { super.updateUIThemeColor() KMMainThreadExecute { self.view.wantsLayer = true let color = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle") self.view.layer?.backgroundColor = color.cgColor self.topSeplineView.wantsLayer = true self.topSeplineView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/divider").cgColor self.titleTextField.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") self.headerSearchView?.wantsLayer = true self.headerSearchView?.layer?.backgroundColor = color.cgColor self.headerSearchView?.bottomLine.wantsLayer = true self.headerSearchView?.bottomLine.layer?.backgroundColor = KMNColorTools.colorPrimary_border1().cgColor self.addButton_.properties.icon = NSImage(named: "KMBookmarkAdd") self.addButton_.reloadData() self.searchButton.properties.icon = NSImage(named: "KMImageNameOutlineSearch") self.searchButton.reloadData() } } func reloadData() { let array = document?.bookmarks() ?? [CPDFBookmark]() var bookMarks: [KMBookMarkItem] = [] for bookMark in array { let item = KMBookMarkItem() item.bookMark = bookMark item.index = UInt(bookMark.pageIndex) item.label = bookMark.label bookMarks.append(item) } self.dataSource = bookMarks if self.sortType_ == .descending { self.dataSource.sort(){$0.bookMark.pageIndex >= $1.bookMark.pageIndex} } else { self.dataSource.sort(){$0.bookMark.pageIndex < $1.bookMark.pageIndex} } if handdler.isValidSearchMode() { var items: [KMBookMarkItem] = [] for item in dataSource { if hasContainString(handdler.searchKey, bookmark: item.bookMark) { items.append(item) } } self.dataSource = items } self.bookTableView.reloadData() self.updateAddBookMarkState() } func addBookMarkAndEdit(newBookMark: KMBookMarkItem) { _ = self.dataSource.contains { KMBookMarkItem in if KMBookMarkItem.bookMark == newBookMark.bookMark { let index = KMOCToolClass.arrayIndexOf(array: self.dataSource, item: KMBookMarkItem) ?? 0 self.didSelectItem(row: index, event: NSEvent()) self.renameBookWithRow(row: index) return true } return false } } override func addNotifations() { super.addNotifations() NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(documentPageCountChangedNotification), name: NSNotification.Name.init(rawValue: "CPDFDocumentPageCountChangedNotification"), object: nil) } func showSearchGroupView(sender: ComponentButton) { var viewHeight: CGFloat = 8 var menuItemArr: [ComponentMenuitemProperty] = [] let titles = ["Whole Words","Case Sensitive"] for i in titles { let menuI = ComponentMenuitemProperty(text: KMLocalizedString(i)) menuItemArr.append(menuI) viewHeight += 36 } if let info = menuItemArr.first { if KMDataManager.ud_bool(forKey: KMNSearchKey.wholeWords.bookmark) { info.righticon = NSImage(named: "KMNImageNameMenuSelect") } } if let info = menuItemArr.last { if KMDataManager.ud_bool(forKey: KMNSearchKey.caseSensitive.bookmark) { info.righticon = NSImage(named: "KMNImageNameMenuSelect") } } let groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle()) searchGroupView = groupView groupView?.groupDelegate = self groupView?.frame = CGRectMake(310, 0, 200, viewHeight) groupView?.updateGroupInfo(menuItemArr) var point = sender.convert(sender.frame.origin, to: nil) point.y -= viewHeight groupView?.showWithPoint(point, relativeTo: sender) searchGroupTarget = sender } override func showHeaderSearch() { super.showHeaderSearch() handdler.isSearchMode = true } override func hideHeaderSearch() { super.hideHeaderSearch() handdler.isSearchMode = false } func hasContainString(_ searchString: String, bookmark: CPDFBookmark) -> Bool { var label = bookmark.label ?? "" var searchLabel = searchString if handdler.caseSensitive == false { label = label.lowercased() searchLabel = searchLabel.lowercased() } if label.contains(searchLabel) { if handdler.wholeWords { let words = label.words() return words.contains(searchLabel) } return true } return false } //MARK: - Menu Action @objc func renameBookAction() { if self.bookTableView.selectedRowIndexes.count == 1 { self.renameBookWithRow(row: self.bookTableView.selectedRowIndexes.first!) } else { __NSBeep() } } @IBAction func escButtonAction(_ sender: Any) { self.bookTableView.deselectAll(nil) } @IBAction func addBookmarkAction(_ sender: Any) { let currentPageIndex = handdler.currentPageIndex if let data = handdler.bookmark(for: currentPageIndex) { delegate?.bkControllerAddAction?(controller: self, bookmark: data, info: ["result" : false]) return } handdler.addCurrentBookmark(callback: { [unowned self] bookmark in self.delegate?.bkControllerAddAction?(controller: self, bookmark: bookmark, info: nil) self.reloadData() }) } @objc func changeLocationAction() { if self.bookTableView.selectedRowIndexes.count == 1 { let item = self.dataSource[self.bookTableView.selectedRowIndexes.first!] let alter = NSAlert() alter.alertStyle = NSAlert.Style.informational alter.messageText = KMLocalizedString("Are you sure you want to set the selected page as the bookmark location?", comment: "") alter.addButton(withTitle: KMLocalizedString("Yes", comment:"")) alter.addButton(withTitle: KMLocalizedString("No", comment:"")) let modlres = alter.runModal() if modlres == NSApplication.ModalResponse.alertFirstButtonReturn { let bookMark = KMBookMarkItem() bookMark.bookMark = item.bookMark bookMark.label = item.label bookMark.index = UInt(handdler.currentPageIndex) self.changeLocation(oldBookMark: item, newBookMark: bookMark) } } else { __NSBeep() } } @objc func deleteBookAction() { if self.bookTableView.selectedRowIndexes.count != 0 { var bookMarks:[KMBookMarkItem] = [] for index in self.bookTableView.selectedRowIndexes { let item = self.dataSource[index] bookMarks.append(item) } self.deleteBookMark(bookMarks: bookMarks) } else { __NSBeep() } } private func renameBookWithRow(row: Int) { self.renamePDFBook = self.dataSource[row] self.renameCellView = self.bookTableView.view(atColumn: 0, row: row, makeIfNecessary: true) as? KMBookCellView self.renameTextField = self.renameCellView?.inputTF self.renameTextField?.delegate = self self.renameTextField?.isEditable = true self.renameTextField?.becomeFirstResponder() } @objc func sortAction(_ sender: NSButton) { let type = self.sortType_ if type == .ascending { self.sortType_ = .descending } else { self.sortType_ = .ascending } self.reloadData() } @objc private func _searchAction() { searchButton.properties.state = .normal searchButton.reloadData() showHeaderSearch() } //MARK: - Noti @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) { if notification.object is CPDFDocument { let pdfdocument : CPDFDocument = notification.object as! CPDFDocument if pdfdocument.isEqual(document) { if !isLocalEvent { var containSelIndex:Bool = false for (index, value) in self.dataSource.enumerated() { if value.bookMark == document?.bookmark(forPageIndex: UInt(handdler.currentPageIndex)) { containSelIndex = true self.didSelectItem(row: index, event: NSEvent()) break } } if !containSelIndex { self.cancelSelect() } } isLocalEvent = false } self.updateAddBookMarkState() } } @objc func documentPageCountChangedNotification(notification: NSNotification) { if notification.object is CPDFDocument { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { [weak self] in let pdfdocument : CPDFDocument = notification.object as! CPDFDocument if pdfdocument.isEqual(self?.document) { self?.reloadData() } } } } } // MARK: - NSTextFieldDelegate extension KMBookMarkViewController: NSTextFieldDelegate { func controlTextDidEndEditing(_ obj: Notification) { if (self.renameTextField!.isEqual(obj.object)) { let textField : NSTextField = obj.object as! NSTextField handdler.rename(bookmark: renamePDFBook!.bookMark, label: textField.stringValue) self.renameTextField?.isEditable = false } } } // MARK: - NSTableViewDelegate,NSTableViewDataSource extension KMBookMarkViewController : NSTableViewDelegate,NSTableViewDataSource { func numberOfRows(in tableView: NSTableView) -> Int { let count = self.dataSource.count if count == 0 { self.emptyView.isHidden = false } else { self.emptyView.isHidden = true } return count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { if row < self.dataSource.count { let item: KMBookMarkItem = self.dataSource[row] let cell : KMBookCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMBookCellView"), owner: self) as! KMBookCellView if handdler.isValidSearchMode() { var label = item.bookMark.label ?? "" let attri = NSMutableAttributedString(string: label, attributes: [ .font : NSFont.SFProTextRegularFont(13), .foregroundColor : KMNColorTools.colorText_1()]) var _searchKey = handdler.searchKey if handdler.caseSensitive == false { label = label.lowercased() _searchKey = _searchKey.lowercased() } let ranges = label.ranges(of: _searchKey) for range in ranges.nsRnage { attri.addAttributes([.font : NSFont.SFProTextBoldFont(13), .foregroundColor: KMNColorTools.colorPrimary_textLight()], range: range) } cell.inputTF.attributedStringValue = attri } else { cell.inputTF.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular") cell.inputTF.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1") cell.inputTF.stringValue = item.bookMark.label cell.bookTitle.stringValue = "\(item.bookMark.pageIndex + 1)" cell.inputBox.borderWidth = 0 if let data = item.bookMark.date { cell.dateLabel.stringValue = KMTools.timeString(timeDate: data) } else { cell.dateLabel.stringValue = "" } } cell.textFieldDidEndEditingCallback = { [weak self] textF in self?.renameCellView = cell self?.handdler.rename(bookmark: item.bookMark, label: textF.stringValue) } return cell } return nil } func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { let rowView = KMBookMarkTableRowView() rowView.selectionHighlightStyle = .none if row < self.dataSource.count { rowView.model = self.dataSource[row] rowView.menuClickedAction = { [weak self] point in let idxs = self?.bookTableView.selectedRowIndexes.count ?? 0 let tempView = self?.bookTableView.rowView(atRow: row, makeIfNecessary: false) var viewHeight: CGFloat = 0 let items: [String] = ["Delete", "Add", "Rename", "Delete All"] var menuItemArr: [ComponentMenuitemProperty] = [] for value in items { let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false, itemSelected: false, isDisabled: false, keyEquivalent: nil, text: KMLocalizedString(value), identifier: value) menuItemArr.append(properties_Menuitem) viewHeight += 36 } if idxs > 1 { (menuItemArr.safe_element(for: 1) as? ComponentMenuitemProperty)?.isDisabled = true (menuItemArr.safe_element(for: 2) as? ComponentMenuitemProperty)?.isDisabled = true } if self?.groupView != nil { self?.groupView?.clickedAutoHide = false self?.groupView?.groupDelegate = self self?.groupView?.frame = CGRectMake(0, 0, 180, viewHeight) self?.groupView?.updateGroupInfo(menuItemArr) self?.groupView?.showWithPoint(CGPoint(x: point.x, y: point.y - viewHeight), relativeTo: tempView) } return NSMenu() } rowView.mouseDownAction = { [unowned self] (view, event) in self.didSelectItem(row: row, event: event) } rowView.rightMouseDownAction = { [unowned self] (view, event) in if !KMOCToolClass.arrayContains(array: self.selectItems, annotation: rowView.model) || self.selectItems.count == 1 { self.selectIndex(index: row) } if row < 0 || row >= self.dataSource.count { return } } rowView.hoverCallback = { [unowned self] (mouseEntered, mouseBox) in if let value = ComponentLibrary.shared.getComponentValueFromKey("radius/xs") { let currentValue = value as? CGFloat ?? 0 rowView.box?.cornerRadius = currentValue } self.bookTableView.enumerateAvailableRowViews { view, row in if let data = view as? KMBookMarkTableRowView { data.model.hover = false data.reloadData() } } if mouseEntered { rowView.model.hover = true rowView.reloadData() } else { rowView.model.hover = false rowView.reloadData() } } } return rowView } func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { return 36 } func tableView(_ tableView: NSTableView, shouldSelect tableColumn: NSTableColumn?) -> Bool { self.isLocalEvent = true return true } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { self.isLocalEvent = true return true } func tableViewSelectionDidChange(_ notification: Notification) { // if self.bookTableView.selectedRow == -1 { // self.cancelSelect() // } } func selectIndex(index: Int) { if index < 0 || index >= self.dataSource.count { return } self.bookTableView.selectRowIndexes(IndexSet(integer: IndexSet.Element(index)), byExtendingSelection: false) self.didSelectItem(row: index, event: NSEvent(), needJump: false) } func didSelectItem(row: Int, event: NSEvent?, needJump: Bool = true) { //当选中一个时 if self.bookTableView.selectedRowIndexes.count == 1 || (event != nil && (!event!.modifierFlags.contains(NSEvent.ModifierFlags.command) && !event!.modifierFlags.contains(NSEvent.ModifierFlags.shift))) { self.bookTableView.selectRowIndexes(IndexSet(integer: IndexSet.Element(row)), byExtendingSelection: false) } //原始数据置空 for model in self.selectItems { self.dataSource.contains { KMBookMarkItem in if KMBookMarkItem.bookMark == model.bookMark { let index = KMOCToolClass.arrayIndexOf(array: self.dataSource, item: KMBookMarkItem) ?? 0 if index != nil { KMBookMarkItem.select = false if self.bookTableView.rowView(atRow: index, makeIfNecessary: false) != nil { let rowView: KMBookMarkTableRowView = self.bookTableView.rowView(atRow: index, makeIfNecessary: false) as! KMBookMarkTableRowView rowView.reloadData() } } return true } return false } } //获取最新数据 var items: [KMBookMarkItem] = [] for index in self.bookTableView.selectedRowIndexes { if index < self.dataSource.count { let model: KMBookMarkItem = self.dataSource[index] model.select = true if self.bookTableView.rowView(atRow: index, makeIfNecessary: false) != nil { let rowView: KMBookMarkTableRowView = self.bookTableView.rowView(atRow: index, makeIfNecessary: false) as! KMBookMarkTableRowView rowView.reloadData() } items.append(model) } } self.selectItems = items //刷新数据 if needJump { self.updateListViewData() } } func updateListViewData() { if self.bookTableView.selectedRowIndexes.count == 1 && self.bookTableView.selectedRowIndexes.first! < self.dataSource.count { let index = self.bookTableView.selectedRowIndexes.first let selectBookMark = self.dataSource[index!] // self.listView.go(toPageIndex: selectBookMark.bookMark.pageIndex, animated: true) } } func cancelSelect() { self.bookTableView.deselectAll(nil) for model in self.selectItems { model.select = false let index = self.dataSource.firstIndex(of: model) if index != nil { if (self.bookTableView.rowView(atRow: index!, makeIfNecessary: false) != nil) { let rowView: KMBookMarkTableRowView = self.bookTableView.rowView(atRow: index!, makeIfNecessary: false) as! KMBookMarkTableRowView rowView.reloadData() } } } } func updateAddBookMarkState() { addButton_.properties.isDisabled = !canAddBorkMark() addButton_.reloadData() } func canAddBorkMark() -> Bool { if document?.bookmarks() != nil && document?.bookmarks()?.count != 0 { for bookMark in document?.bookmarks() ?? [] { if bookMark.pageIndex == handdler.currentPageIndex { return false } } } return true } } // MARK: - KMBotaTableViewDelegate extension KMBookMarkViewController: KMBotaTableViewDelegate { func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject? { if aTableView.isEqual(to: self.bookTableView) { let cnt = self.dataSource.count if rowIndex >= cnt { return nil } let model = self.dataSource[rowIndex] return model.bookMark } return nil } } //MARK: - undoRedo extension KMBookMarkViewController { func changeLocation(oldBookMark: KMBookMarkItem, newBookMark: KMBookMarkItem) { document?.removeBookmark(forPageIndex: oldBookMark.index) document?.addBookmark(newBookMark.label, forPageIndex: newBookMark.index) reloadData() } func renamePDFBook(bookmark : KMBookMarkItem! , label:String) { if bookmark.bookMark.label == label { return } let temp = bookmark.bookMark.label bookmark.bookMark.label = label self.reloadData() var indexSet = IndexSet() indexSet.insert(self.bookTableView.row(for: self.renameCellView!)) self.bookTableView.selectRowIndexes(indexSet, byExtendingSelection: false) } func deleteBookMark(bookMarks: [KMBookMarkItem]) { for bookMark in bookMarks { if ((document?.removeBookmark(forPageIndex: bookMark.index)) != nil) { KMPrint("删除标签成功") } } self.reloadData() guard let callBack = bookMarkDidChange else { return } callBack(self, bookMarks) } func addBookMark(bookMarks: [KMBookMarkItem]) { for bookMark in bookMarks { document?.addBookmark(bookMark.label, forPageIndex: UInt(bookMark.index)) } self.reloadData() if bookMarks.count == 1 { DispatchQueue.main.async { [self] in if document?.bookmark(forPageIndex: UInt(bookMarks.first!.index)) != nil { let item = KMBookMarkItem() item.bookMark = (document?.bookmark(forPageIndex: UInt(bookMarks.first!.index)))! item.label = item.bookMark.label item.index = UInt(item.bookMark.pageIndex) self.addBookMarkAndEdit(newBookMark: item) } } } guard let callBack = bookMarkDidChange else { return } callBack(self, bookMarks) } @IBAction func undo(_ sender: Any) { handdler.undo() } @IBAction func redo(_ sender: Any) { handdler.redo() } } // MARK: - NSMenuDelegate, NSMenuItemValidation extension KMBookMarkViewController: NSMenuDelegate, NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { let action = menuItem.action if (action == #selector(undo)) { return handdler.canUndo() } if (action == #selector(redo)) { return handdler.canRedo() } if action == #selector(renameBookAction) || action == #selector(changeLocationAction) || action == #selector(deleteBookAction) { if self.bookTableView.selectedRowIndexes.count > 1 { if action == #selector(changeLocationAction) { return false } else if action == #selector(renameBookAction) { return false } } else if self.bookTableView.selectedRowIndexes.count == 1 { return true } else { if self.bookTableView.selectedRowIndexes.count == 0 { if action == #selector(changeLocationAction) {} } else { return false } } } return true } } extension KMBookMarkViewController: KMNBookmarkHanddlerDelegate { func handdler(_ handdler: KMNBookmarkHanddler, didAdd bookmark: CPDFBookmark?, info: [String : Any]?) { KMMainThreadExecute { self.reloadData() } } func handdler(_ handdler: KMNBookmarkHanddler, didRemove bookmark: CPDFBookmark?, info: [String : Any]?) { KMMainThreadExecute { self.reloadData() } } func handdler(_ handdler: KMNBookmarkHanddler, didRemoveAll info: [String : Any]?) { KMMainThreadExecute { self.reloadData() } } func handdler(_ handdler: KMNBookmarkHanddler, didRename bookmark: CPDFBookmark?, info: [String : Any]?) { KMMainThreadExecute { self.reloadData() } } } extension KMBookMarkViewController: ComponentGroupDelegate { func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) { if group == groupView { if let selItem = menuItemProperty { let index = group?.menuItemArr.firstIndex(of: selItem) if index == 0 { group?.removeFromSuperview() var pageIndexs = IndexSet() for i in bookTableView.selectedRowIndexes { if let item = dataSource.safe_element(for: i) as? KMBookMarkItem { pageIndexs.insert(item.bookMark.pageIndex) } } _ = handdler.removeBookmarks(for: pageIndexs) } else if index == 1 { } else if index == 2 { group?.removeFromSuperview() renameBookAction() } else if index == 3 { Task { let resp = await KMAlertTool.runModel(message: KMLocalizedString("此操作将删除文档中的所有书签,是否继续?"), buttons: [KMLocalizedString("OK"), KMLocalizedString("No")]) if resp == .alertFirstButtonReturn { _ = handdler.removeAllBookmarks() } } } } } else if group == searchGroupView { guard let menuI = menuItemProperty else { return } let idx = group?.menuItemArr.firstIndex(of: menuI) if idx == 0 { let key = KMNSearchKey.wholeWords.bookmark let value = KMDataManager.ud_bool(forKey: key) KMDataManager.ud_set(!value, forKey: key) handdler.wholeWords = !value } else if idx == 1 { let key = KMNSearchKey.caseSensitive.bookmark let value = KMDataManager.ud_bool(forKey: key) KMDataManager.ud_set(!value, forKey: key) handdler.caseSensitive = !value } } } func componentGroupDidDismiss(group: ComponentGroup?) { if group == groupView { groupView?.removeFromSuperview() } else if group == searchGroupView { searchGroupTarget?.properties.state = .normal searchGroupTarget?.reloadData() searchGroupTarget = nil } } } class KMBookMarkItem: NSObject { var label: String = "" var index: UInt = 0 var bookMark: CPDFBookmark = CPDFBookmark() var select: Bool = false var hover: Bool = false }