123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- //
- // KMBookMarkViewController.swift
- // PDF Reader Pro
- //
- // Created by lxy on 2022/10/10.
- //
- import Cocoa
- import PDFKit
- typealias KMBookMarkViewControllerBookMarkDidChange = (_ controller: KMBookMarkViewController, _ bookMarks: [KMBookMarkItem]) -> Void
- class KMBookMarkViewController: NSViewController, NSTextFieldDelegate {
- @IBOutlet weak var addBookButton: NSButton!
- @IBOutlet weak var titleTextField: NSTextField!
-
- @IBOutlet var topSeplineView: NSView!
-
- @IBOutlet weak var bookTableView: NSTableView!
- @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!
- var listView: CPDFListView!
- let pdfView = PDFView.init()
-
- var isLocalEvent: Bool = false //区分外部点击还是内部点击
-
- var selectItems: [KMBookMarkItem] = []
-
- var bookMarkDidChange: KMBookMarkViewControllerBookMarkDidChange?
-
- func dealloc() {
- NotificationCenter.default.removeObserver(self)
- }
-
- deinit {
- NotificationCenter.default.removeObserver(self)
- }
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- self.view.wantsLayer = true
- self.view.layer?.backgroundColor = NSColor(red: 247.0/255.0, green: 248.0/255.0, blue: 250.0/255.0, alpha: 1).cgColor
-
- self.topSeplineView.wantsLayer = true
- self.topSeplineView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.1).cgColor
-
- self.bookTableView.backgroundColor = NSColor.km_init(hex: "#F7F8FA")
- if #available(macOS 11, *) {
- self.bookTableView.style = NSTableView.Style.plain
- }
- self.bookTableView.allowsMultipleSelection = true
- self.bookTableView.doubleAction = #selector(renameBookAction)
- // self.bookTableView.selectionHighlightStyle = NSTableView.SelectionHighlightStyle.none;
-
-
- self.refreshUI()
- self.reloadData()
-
- self.initNotification()
- }
-
- func refreshUI() {
- self.titleTextField.font = NSFont.SFProTextSemiboldFont(14.0)
- self.titleTextField.textColor = NSColor.km_init(hex: "#252629")
- self.titleTextField.stringValue = NSLocalizedString("Bookmarks", comment: "")
- self.addBookButton.toolTip = NSLocalizedString("Add Bookmark", comment: "")
-
-
- self.bigTipLabel.font = NSFont.SFProTextRegularFont(14.0)
- self.bigTipLabel.textColor = NSColor.km_init(hex: "#616469")
- self.bigTipLabel.stringValue = NSLocalizedString("No Bookmarks", comment: "")
-
- let title = NSLocalizedString("To create a bookmark, please right-click on the selected page and choose \"Add Bookmark\", or click \"Add\" button in the upper right corner.", comment: "")
- let paragraphStyle = NSMutableParagraphStyle()
- paragraphStyle.lineHeightMultiple = 1.32
- paragraphStyle.alignment = .center
- self.tipLabel.attributedStringValue = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor.km_init(hex: "#94989C")])
- }
-
- func reloadData() {
- let array = self.listView.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
-
- self.dataSource.sort(){$0.bookMark.pageIndex < $1.bookMark.pageIndex}
- 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
- }
- }
-
- func initNotification() {
- 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 removeNotification() {
- NotificationCenter.default.removeObserver(self)
- }
-
- private func addMenuTitle(view: NSView, event: NSEvent) {
- let menus : NSMenu = NSMenu(title: "")
- menus.delegate = self
- let addItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameBookAction))
- let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeLocationAction))
- let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteBookAction))
- menus.addItem(addItem)
- menus.addItem(addChildItem)
- menus.addItem(addHigherItem)
- let point = view.convert(event.locationInWindow, from: nil)
- menus.popUp(positioning: nil, at: point, in: view)
- // self.bookTableView.menu = menus
- }
- func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem {
- let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "")
- menuItem.target = self
- return menuItem
- }
-
- //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) {
- if self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex)) == nil {
- let label = "\(NSLocalizedString("Page", comment:"")) \(self.listView.currentPageIndex + 1)"
- let bookMark = KMBookMarkItem()
- bookMark.label = label
- bookMark.index = UInt(self.listView.currentPageIndex)
- self.addBookMark(bookMarks: [bookMark])
- } else {
- let bookMark = self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex))
- self.dataSource.contains { KMBookMarkItem in
- if KMBookMarkItem.bookMark == bookMark {
- let index = Int(KMBookMarkItem.index)
- self.bookTableView.selectRowIndexes(IndexSet(integer: index), byExtendingSelection: false)
- self.didSelectItem(row: index, event: NSEvent())
- return true
- }
- return false
- }
- }
- }
-
- @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 = NSLocalizedString("Are you sure you want to set the target location of the selected outline to the current location", comment: "")
- alter.addButton(withTitle: NSLocalizedString("YES", comment:""))
- alter.addButton(withTitle: NSLocalizedString("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(self.listView.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.bookTitle
- self.renameTextField.delegate = self
- self.renameTextField.isEditable = true
- self.renameTextField.becomeFirstResponder()
- }
-
- //MARK: Noti
- @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) {
- if notification.object is CPDFDocument {
- let pdfdocument : CPDFDocument = notification.object as! CPDFDocument
- if pdfdocument.isEqual(self.listView.document) {
- if !isLocalEvent {
- var containSelIndex:Bool = false
- for (index, value) in self.dataSource.enumerated() {
- if value.bookMark == self.listView.document.bookmark(forPageIndex: UInt(self.listView.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?.listView?.document) {
- self?.reloadData()
- }
- }
- }
- }
-
- //MARK: - NSTextFieldDelegate
- func controlTextDidEndEditing(_ obj: Notification) {
- if (self.renameTextField.isEqual(obj.object)) {
- let textField : NSTextField = obj.object as! NSTextField
- self.renamePDFBook(bookmark: self.renamePDFBook, label: textField.stringValue)
- self.renameTextField.isEditable = false
- }
- }
- }
- extension KMBookMarkViewController : NSTableViewDelegate,NSTableViewDataSource {
- func numberOfRows(in tableView: NSTableView) -> Int {
- let count = self.dataSource?.count ?? 0
- 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
- cell.bookTitle.stringValue = item.bookMark.label
- return cell
- }
- return nil
- }
-
- func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
- let rowView = KMBookMarkTableRowView()
- if row < self.dataSource.count {
- rowView.model = self.dataSource[row]
-
- 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 self.bookTableView.rowView(atRow: row, makeIfNecessary: false) != nil {
- let tempView = self.bookTableView.rowView(atRow: row, makeIfNecessary: false)
- self.addMenuTitle(view: tempView!, event: event)
- }
- }
-
- rowView.hoverCallback = { [unowned self] (mouseEntered, mouseBox) in
- self.bookTableView.enumerateAvailableRowViews { view, row in
- if view is KMBookMarkTableRowView {
- (view as? KMBookMarkTableRowView)?.model.hover = false
- (view as? KMBookMarkTableRowView)?.reloadData()
- }
- }
- if mouseEntered {
- rowView.model.hover = true
- } else {
- rowView.model.hover = false
- }
- }
- }
- return rowView
- }
-
- 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) {
- 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() {
- // self.addBookButton.isEnabled = self.canAddBorkMark()
- }
-
- func canAddBorkMark() -> Bool {
- if self.listView.document.bookmarks() != nil && self.listView.document.bookmarks()?.count != 0 {
- for bookMark in self.listView.document.bookmarks() {
- if bookMark.pageIndex == self.listView.currentPageIndex {
- return false
- }
- }
- }
- return true
- }
- }
- //MARK: undoRedo
- extension KMBookMarkViewController {
- func changeLocation(oldBookMark: KMBookMarkItem, newBookMark: KMBookMarkItem) {
- self.listView.document.removeBookmark(forPageIndex: oldBookMark.index)
- self.listView.document.addBookmark(newBookMark.label, forPageIndex: newBookMark.index)
-
- self.reloadData()
- self.listView.setNeedsDisplayForVisiblePages()
-
- self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
- self.changeLocation(oldBookMark: newBookMark, newBookMark: oldBookMark)
- }
- }
-
- 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)
-
- self.listView.undoManager?.registerUndo(withTarget: self) { [weak self] targetType in
- bookmark.bookMark.label = label
- self?.renamePDFBook(bookmark: bookmark, label: temp ?? bookmark.label)
- }
- }
-
- func deleteBookMark(bookMarks: [KMBookMarkItem]) {
- for bookMark in bookMarks {
- if self.listView.document.removeBookmark(forPageIndex: bookMark.index) {
- KMPrint("删除标签成功")
- }
- }
- self.listView.setNeedsDisplayForVisiblePages()
- self.reloadData()
-
- //undo redo
- var saveBooks:[KMBookMarkItem] = bookMarks
- self.listView.undoManager?.registerUndo(withTarget: self) { [weak self] targetType in
- saveBooks.sort(){$0.index > $1.index}
- self?.addBookMark(bookMarks: bookMarks)
- }
-
- guard let callBack = bookMarkDidChange else { return }
-
- callBack(self, bookMarks)
- }
-
- func addBookMark(bookMarks: [KMBookMarkItem]) {
- for bookMark in bookMarks {
- self.listView.document.addBookmark(bookMark.label, forPageIndex: UInt(bookMark.index))
- }
- self.listView.setNeedsDisplayForVisiblePages()
- self.reloadData()
-
- if bookMarks.count == 1 {
- DispatchQueue.main.async {
- if self.listView.document.bookmark(forPageIndex: UInt(bookMarks.first!.index)) != nil {
- let item = KMBookMarkItem()
- item.bookMark = self.listView.document.bookmark(forPageIndex: UInt(bookMarks.first!.index))
- item.label = item.bookMark.label
- item.index = UInt(item.bookMark.pageIndex)
- self.addBookMarkAndEdit(newBookMark: item)
- }
- }
- }
-
- //undo redo
- var saveBooks:[KMBookMarkItem] = bookMarks
- self.listView.undoManager?.registerUndo(withTarget: self) { [weak self] targetType in
- saveBooks.sort(){$0.index > $1.index}
- self?.deleteBookMark(bookMarks: saveBooks)
- }
-
- guard let callBack = bookMarkDidChange else { return }
-
- callBack(self, bookMarks)
- }
-
- @IBAction func undo(_ sender: Any) {
- if (self.listView.undoManager?.canUndo ?? false) {
- self.listView.undoManager?.undo()
- }
- }
-
- @IBAction func redo(_ sender: Any) {
- if (self.listView.undoManager?.canRedo ?? false) {
- self.listView.undoManager?.redo()
- }
- }
- }
- extension KMBookMarkViewController: NSMenuDelegate, NSMenuItemValidation {
- func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
- let action = menuItem.action
- if (action == #selector(undo)) {
- return self.listView.undoManager?.canUndo ?? false
- }
- if (action == #selector(redo)) {
- return self.listView.undoManager?.canRedo ?? false
- }
-
- 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) {
- // if self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex)) != nil {
- // return false
- // }
- }
- } else {
- return false
- }
- }
- }
- return true
- }
- }
- class KMBookMarkItem: NSObject {
- var label: String = ""
- var index: UInt = 0
- var bookMark: CPDFBookmark = CPDFBookmark()
- var select: Bool = false
- var hover: Bool = false
- }
|