// // KMToolbarCustomViewController.swift // PDF Reader Pro // // Created by tangchao on 2023/10/26. // import Cocoa private let KMPasteboardTypeAllowedIndexSet: NSPasteboard.PasteboardType = NSPasteboard.PasteboardType(rawValue: "KMPasteboardTypeAllowedIndexSet") private let KMPasteboardTypeDefaultIndexSet: NSPasteboard.PasteboardType = NSPasteboard.PasteboardType(rawValue: "KMPasteboardTypeDefaultIndexSet") private let KMToolbarImageToPDFItemIdentifier = "KMToolbarImageToPDFItemIdentifier" private let KMToolbarRotateRightItemIdentifier = "KMToolbarRotateRightItemIdentifier" private let KMToolbarRotateLeftItemIdentifier = "KMToolbarRotateLeftItemIdentifier" private let KMToolbarPageBreaksItemIdentifier = "KMToolbarPageBreaksItemIdentifier" private let KMToolbarViewModeItemIdentifier = "KMToolbarViewModeItemIdentifier" private let KMToolbarDisplayModeItemIdentifier = "KMToolbarDisplayModeItemIdentifier" private let KMToolbarSpaceItemIdentifier = "KMToolbarSpaceItemIdentifier" //private let KMImageNameToolbarSpace = "KMImageNameToolbarSpace" private let KMToolbarFormAlignmentIdentifier = "KMToolbarFormAlignmentIdentifier" let KMToolbarCustomChangeNotification: Notification.Name = Notification.Name(rawValue: "KMToolbarCustomChangeNotification") class KMToolbarCellView: NSTableCellView { @IBOutlet weak var tickImageView: NSImageView! } class KMToolbarCustomViewController: NSViewController { @IBOutlet weak var titleLabel: NSTextField! @IBOutlet weak var leftLabel: NSTextField! @IBOutlet weak var rightLabel: NSTextField! @IBOutlet weak var subTitleLabel: NSTextField! @IBOutlet weak var okButton: NSButton! @IBOutlet weak var cancelButton: NSButton! @IBOutlet weak var resetButton: NSButton! @IBOutlet weak var addButton: NSButton! @IBOutlet weak var removeButton: NSButton! @IBOutlet weak var allowedItemsTableView: NSTableView! @IBOutlet weak var defaultItemsTableView: NSTableView! private var _allowedItems: [String] = [] private var _defaultItems: [String] = [] weak var toolbar: KMToolbarView? override func loadView() { super.loadView() self.titleLabel.stringValue = KMLocalizedString("Customize Toolbar", nil) self.subTitleLabel.stringValue = String(format: "%@", KMLocalizedString("Drag-and-drop tools to change their order", nil)) self.leftLabel.stringValue = KMLocalizedString("Choose tools to add:", nil) self.rightLabel.stringValue = KMLocalizedString("Tools to show in Toolbar:", nil) self.okButton.title = KMLocalizedString("Done", nil) self.cancelButton.title = KMLocalizedString("Cancel", nil) self.resetButton.title = KMLocalizedString("Reset", nil) self.resetButton.toolTip = KMLocalizedString("Reset Toolbars", nil) self.addButton.title = String(format: "%@ >>", KMLocalizedString("Add", nil)) self.addButton.toolTip = KMLocalizedString("Add to Toolbar", nil) self.removeButton.title = String(format: "<< %@", KMLocalizedString("Remove", nil)) self.removeButton.toolTip = KMLocalizedString("Remove from Toolbar", nil) var color = NSColor(red: 51.0/255.0, green: 51.0/255.0, blue: 51.0/255.0, alpha: 1.0) if (KMAppearance.isSupportNewColor()) { if (KMAppearance.isDarkMode()) { color = NSColor(red: 1, green: 1, blue: 1, alpha: 0.5) } } self.titleLabel.textColor = color self.subTitleLabel.textColor = color self.leftLabel.textColor = color self.rightLabel.textColor = color self.addButton.wantsLayer = true self.addButton.layer?.borderColor = NSColor(red: 153.0/255.0, green: 153.0/255.0, blue: 153.0/255.0, alpha: 1).cgColor self.addButton.layer?.borderWidth = 1.0 self.addButton.layer?.cornerRadius = 4.0 self.removeButton.wantsLayer = true self.removeButton.layer?.borderColor = NSColor(red: 153.0/255.0, green: 153.0/255.0, blue: 153.0/255.0, alpha: 1).cgColor self.removeButton.layer?.borderWidth = 1.0 self.removeButton.layer?.cornerRadius = 4.0 self.addButton.isEnabled = false self.removeButton.isEnabled = false self.defaultItemsTableView.registerForDraggedTypes([KMPasteboardTypeAllowedIndexSet, KMPasteboardTypeDefaultIndexSet]) self._loadAllowedItems() self._loadDefaultItems() } override func viewDidLoad() { super.viewDidLoad() // Do view setup here. } @IBAction @objc func okButtonAction(_ sender: NSButton) { NSApp.endSheet(self.view.window!) self.view.window?.close() if self.toolbar?.toolbarIdentifier != nil && self._defaultItems.count > 0 { if let data = self.toolbar?.toolbarIdentifier { UserDefaults.standard.set(self._defaultItems, forKey: data) } NotificationCenter.default.post(name: KMToolbarCustomChangeNotification, object: self.toolbar) } } @IBAction @objc func cancelButtonAction(_ sender: NSButton) { NSApp.endSheet(self.view.window!) self.view.window?.close() } @IBAction @objc func resetButtonAction(_ sender: NSButton) { if let data = self.toolbar?.toolbarIdentifier { UserDefaults.standard.removeObject(forKey: data) NotificationCenter.default.post(name: KMToolbarCustomChangeNotification, object: self.toolbar) } self._loadDefaultItems() self.allowedItemsTableView.reloadData() self.defaultItemsTableView.reloadData() } @IBAction @objc func addButtonAction(_ sender: NSButton) { let selectedRowIndexes = self.allowedItemsTableView.selectedRowIndexes if (selectedRowIndexes.count <= 0) { return } var itemIdentifiers: [String] = [] for index in selectedRowIndexes { itemIdentifiers.append(self._allowedItems[index]) } if (itemIdentifiers.count > 0) { var isAdded = false for itemIdentifier in itemIdentifiers { if self._defaultItems.contains(itemIdentifier) == false || itemIdentifier == KMToolbarSpaceItemIdentifier { self._defaultItems.append(itemIdentifier) isAdded = true } } if (isAdded) { self.allowedItemsTableView.reloadData() self.defaultItemsTableView.reloadData() self.defaultItemsTableView.scrollRowToVisible(self._defaultItems.count-1) self.addButton.isEnabled = false } else { } } } @IBAction @objc func removeButtonAction(_ sender: NSButton) { let selectedRowIndexes = self.defaultItemsTableView.selectedRowIndexes if (selectedRowIndexes.count <= 0) { return } for index in selectedRowIndexes { if index < self._defaultItems.count { self._defaultItems.remove(at: index) } } self.allowedItemsTableView.reloadData() self.defaultItemsTableView.reloadData() self.removeButton.isEnabled = false } @objc func delete(_ sender: Any?) { if self.defaultItemsTableView.isEqual(to: self.view.window?.firstResponder) { self.removeButtonAction(self.removeButton) } } } extension KMToolbarCustomViewController: NSTableViewDelegate, NSTableViewDataSource { func numberOfRows(in tableView: NSTableView) -> Int { var rows = 0 if (self.allowedItemsTableView.isEqual(to: tableView)) { rows = self._allowedItems.count } else if (self.defaultItemsTableView.isEqual(to: tableView)) { rows = self._defaultItems.count } return rows } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { var cellView: NSTableCellView? var itemIdentifier: String? if (self.allowedItemsTableView == tableView) { cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMAllowedItemsCell"), owner: self) as! NSTableCellView itemIdentifier = self._allowedItems[row] } else if (self.defaultItemsTableView == tableView) { cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMDefaultItemsCell"), owner: self) as! NSTableCellView itemIdentifier = self._defaultItems[row] } if itemIdentifier == KMToolbarSpaceItemIdentifier || itemIdentifier == KMNewToolbarSpaceItemIdentifier { cellView?.textField?.stringValue = KMLocalizedString("", nil) cellView?.imageView?.image = NSImage(named: KMImageNameToolbarSpace) } else { if let data = self.toolbar?.delegate?.toolbar?(self.toolbar!, itemFor: itemIdentifier!) { if itemIdentifier == KMToolbarFormAlignmentIdentifier { cellView?.textField?.stringValue = KMLocalizedString("Alignment", nil) } else { cellView?.textField?.stringValue = data.titleName ?? "" } cellView?.imageView?.image = data.image } } if let data = cellView?.isKind(of: KMToolbarCellView.self), data { if self._defaultItems.contains(itemIdentifier!) && itemIdentifier != KMToolbarSpaceItemIdentifier { (cellView as? KMToolbarCellView)?.tickImageView.image = NSImage(named: "KMImageNameToolbarSelected") } else { (cellView as? KMToolbarCellView)?.tickImageView.image = nil } } return cellView; } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { if (self.allowedItemsTableView == tableView) { let itemIdentifier = self._allowedItems[row] if (self._defaultItems.contains(itemIdentifier) && itemIdentifier != KMToolbarSpaceItemIdentifier) { return false } } return true } func tableViewSelectionDidChange(_ notification: Notification) { self.allowedItemsTableView.delegate = nil self.defaultItemsTableView.delegate = nil let tableView = notification.object as? NSTableView if (self.allowedItemsTableView == tableView) { self.addButton.isEnabled = tableView!.selectedRowIndexes.count > 0 ? true : false let set = NSIndexSet() self.defaultItemsTableView.selectRowIndexes(set as IndexSet, byExtendingSelection: false) self.removeButton.isEnabled = false } else if (self.defaultItemsTableView == tableView) { self.removeButton.isEnabled = tableView!.selectedRowIndexes.count > 0 ? true : false let set = NSIndexSet() self.allowedItemsTableView.selectRowIndexes(set as IndexSet, byExtendingSelection: false) self.addButton.isEnabled = false } self.allowedItemsTableView.delegate = self self.defaultItemsTableView.delegate = self } func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { let indexSetData = NSKeyedArchiver.archivedData(withRootObject: rowIndexes) if (self.allowedItemsTableView == tableView) { pboard.declareTypes([KMPasteboardTypeAllowedIndexSet], owner: self) pboard.setData(indexSetData, forType: KMPasteboardTypeAllowedIndexSet) } else if (self.defaultItemsTableView == tableView) { pboard.declareTypes([KMPasteboardTypeDefaultIndexSet], owner: self) pboard.setData(indexSetData, forType: KMPasteboardTypeDefaultIndexSet) } return true } func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { if (self.defaultItemsTableView == tableView) { if (dropOperation == .on) { return NSDragOperation(rawValue: 0) } return .move } return NSDragOperation(rawValue: 0) } func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool { if (self.defaultItemsTableView == tableView) { let pboard = info.draggingPasteboard if pboard.availableType(from: [KMPasteboardTypeAllowedIndexSet]) != nil { let rowData = pboard.data(forType: KMPasteboardTypeAllowedIndexSet) let rowIndexes: IndexSet = NSKeyedUnarchiver.unarchiveObject(with: rowData!) as! IndexSet var isAdded = false for idx in rowIndexes.reversed() { let itemIdentifier = self._allowedItems[idx] if self._defaultItems.contains(itemIdentifier) == false || itemIdentifier == KMToolbarSpaceItemIdentifier { self._defaultItems.insert(itemIdentifier, at: row) isAdded = true } } if (isAdded) { self.allowedItemsTableView.reloadData() self.defaultItemsTableView.reloadData() return true } else { } } else if pboard.availableType(from: [KMPasteboardTypeDefaultIndexSet]) != nil { let rowData = pboard.data(forType: KMPasteboardTypeDefaultIndexSet) let indexes: IndexSet = NSKeyedUnarchiver.unarchiveObject(with: rowData!) as! IndexSet var items: [String] = [] for item in self._defaultItems { items.append(item) } var moveItems: [String] = [] for index in indexes { moveItems.append(self._defaultItems[index]) } var index = 0 for index in indexes { self._defaultItems.remove(at: index) } var _row = row if (_row > 0) { var item: String? = items[row-1] while (moveItems.contains(item ?? "")) { _row -= 1 if (_row < 0) { item = nil break } else { item = items[_row] } } if (item != nil) { index = self._defaultItems.firstIndex(of: item!)!+1 } } for i in 0 ..< moveItems.count { self._defaultItems.insert(moveItems[i], at: index+i) } self.allowedItemsTableView.reloadData() self.defaultItemsTableView.reloadData() return true } } return false } } extension KMToolbarCustomViewController: NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { if (menuItem.action == #selector(delete)) { if (self.defaultItemsTableView != self.view.window!.firstResponder) { return false } } return true } } // MARK: - Private Methods extension KMToolbarCustomViewController { private func _loadAllowedItems() { if let data = self.toolbar?.delegate?.toolbarAllowedItemIdentifiers?(self.toolbar!) { self._allowedItems = data } let itemArray = [KMToolbarImageToPDFItemIdentifier, KMToolbarRotateRightItemIdentifier, KMToolbarRotateLeftItemIdentifier, KMToolbarPageBreaksItemIdentifier, KMToolbarViewModeItemIdentifier, KMToolbarDisplayModeItemIdentifier, KMToolbarDividerItemIdentifier, KMToolbarShowToolbarItemIdentifier] for itemStr in itemArray { if (self._allowedItems.contains(itemStr)) { self._allowedItems.removeObject(itemStr) } } } private func _loadDefaultItems() { var itemIdentifiers: [String]? = self.toolbar?.toolbarIdentifier != nil ? (UserDefaults.standard.object(forKey: self.toolbar!.toolbarIdentifier!) as? [String]) : [] if itemIdentifiers == nil || itemIdentifiers!.count <= 0 { if let items = self.toolbar?.delegate?.toolbarDefaultItemIdentifiers?(self.toolbar!) { itemIdentifiers = items } } self._defaultItems.removeAll() if (itemIdentifiers != nil) { for itemI in itemIdentifiers! { if itemI != KMToolbarDividerItemIdentifier { self._defaultItems.append(itemI) } } } else { self._defaultItems = [] } } }