// // KMTableView.swift // PDF Reader Pro // // Created by tangchao on 2023/11/16. // import Cocoa /* @protocol SKTableViewDelegate - (NSArray *)tableView:(NSTableView *)aTableView typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)aTypeSelectHelper; - (void)tableView:(NSTableView *)aTableView typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString; - (void)tableView:(NSTableView *)aTableView typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper updateSearchString:(NSString *)searchString; @end */ @objc protocol KMBotaTableViewDelegate: NSTableViewDelegate { @objc optional func tableView(_ aTableView: NSTableView, deleteRowsWithIndexes rowIndexes: IndexSet) @objc optional func tableView(_ aTableView: NSTableView, canDeleteRowsWithIndexes rowIndexes: IndexSet) -> Bool @objc optional func tableView(_ aTableView: NSTableView, copyRowsWithIndexes rowIndexes: IndexSet) @objc optional func tableView(_ aTableView: NSTableView, canCopyRowsWithIndexes rowIndexes: IndexSet) -> Bool @objc optional func tableView(_ aTableView: NSTableView, pasteFromPasteboard pboard: NSPasteboard) @objc optional func tableView(_ aTableView: NSTableView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool @objc optional func tableViewMoveLeft(_ aTableView: NSTableView) @objc optional func tableViewMoveRight(_ aTableView: NSTableView) @objc optional func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject? @objc optional func tableView(_ aTableView: NSTableView, typeSelectHelperSelectionStrings aTypeSelectHelper: SKTypeSelectHelper) -> NSArray @objc optional func tableView(_ aTableView: NSTableView, typeSelectHelper aTypeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) @objc optional func tableView(_ aTableView: NSTableView, typeSelectHelper aTypeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String) } class KMBotaTableView: NSTableView, SKTypeSelectHelperDelegate { var kmTrackingAreas: NSMutableSet? /* @interface SKTableView : NSTableView { SKTypeSelectHelper *typeSelectHelper; } @property (nonatomic) supportsQuickLook; @property (nonatomic, retain) SKTypeSelectHelper *typeSelectHelper; */ var typeSelectHelper: SKTypeSelectHelper? weak var botaDelegate: KMBotaTableViewDelegate? { didSet { self._rebuildTrackingAreas() } } var hasImageToolTips: Bool { get { return self.kmTrackingAreas != nil } set { if self.kmTrackingAreas == nil && newValue { self.kmTrackingAreas = NSMutableSet() if self.window != nil { self._rebuildTrackingAreas() } } else if self.kmTrackingAreas != nil && newValue == false { if self.window != nil { self._removeTrackingAreas() } } } } func setTypeSelectHelper(_ newTypeSelectHelper: SKTypeSelectHelper) { if typeSelectHelper != newTypeSelectHelper { typeSelectHelper?.delegate = nil typeSelectHelper = newTypeSelectHelper typeSelectHelper?.delegate = self } } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Drawing code here. } override func reloadData() { super.reloadData() self._rebuildTrackingAreas() // [typeSelectHelper rebuildTypeSelectSearchCache]; typeSelectHelper?.rebuildTypeSelectSearchCache() } // MARK: - Tracking override func updateTrackingAreas() { super.updateTrackingAreas() self._rebuildTrackingAreas() } override func noteNumberOfRowsChanged() { super.noteNumberOfRowsChanged() self._rebuildTrackingAreas() } override func mouseEntered(with event: NSEvent) { if (self.kmTrackingAreas == nil) { return } // let userInfo = event.userData as? [String : Any] let userInfo = event.trackingArea?.userInfo as? [String : Any] let row = userInfo?["row"] as? Int if row != nil { let context = self.botaDelegate?.tableView?(self, imageContextForRow: row!) if context is KMImageToolTipContext { KMImageToolTipWindow.shared.showForImageContext(context as! KMImageToolTipContext, at: .zero) } } } override func mouseExited(with event: NSEvent) { if (self.kmTrackingAreas == nil) { return } let userInfo = event.userData as? [String : Any] let row = userInfo?["row"] as? Int if row != nil { KMImageToolTipWindow.shared.fadeOut() } } func scrollToBeginningOfDocument(_ sender: AnyObject?) { if self.numberOfRows > 0 { self.scrollRowToVisible(0) } } func scrollToEndOfDocument(_ sender: AnyObject?) { if self.numberOfRows > 0 { self.scrollRowToVisible(self.numberOfRows-1) } } func moveLeft(_ sender: AnyObject?) { self.botaDelegate?.tableViewMoveLeft?(self) } func moveRight(_ sender: AnyObject?) { self.botaDelegate?.tableViewMoveRight?(self) } func canDelete() -> Bool { let indexes = self.selectedRowIndexes if indexes.isEmpty { return false } return self.botaDelegate?.tableView?(self, canDeleteRowsWithIndexes: indexes) ?? false } @objc func delete(_ sender: AnyObject?) { if self.canDelete() { self.botaDelegate?.tableView?(self, deleteRowsWithIndexes: self.selectedRowIndexes) } } func canCopy() -> Bool { let indexes = self.selectedRowIndexes if indexes.isEmpty { return false } return self.botaDelegate?.tableView?(self, canCopyRowsWithIndexes: indexes) ?? false } @objc func copy(_ sender: AnyObject?) { if self.canCopy() { self.botaDelegate?.tableView?(self, copyRowsWithIndexes: self.selectedRowIndexes) } } func canPaste() -> Bool { return self.botaDelegate?.tableView?(self, canPasteFromPasteboard: NSPasteboard.general) ?? false } @objc func paste(_ sender: AnyObject?) { if self.canPaste() { self.botaDelegate?.tableView?(self, pasteFromPasteboard: NSPasteboard.general) } } override func keyDown(with event: NSEvent) { // let eventChar = event.PDFListViewFirstCharacter() // let modifierFlags = event.deviceIndependen // NSUInteger modifierFlags = [theEvent deviceIndependentModifierFlags]; // // if ((eventChar == NSNewlineCharacter || eventChar == NSEnterCharacter || eventChar == NSCarriageReturnCharacter) && modifierFlags == 0) { // if ([self doubleAction] == NULL || [self sendAction:[self doubleAction] to:[self target]] == NO) // NSBeep(); // } else if ((eventChar == NSDeleteCharacter || eventChar == NSDeleteFunctionKey) && modifierFlags == 0 && [self canDelete]) { // [self delete:self]; // } else if ((eventChar == SKSpaceCharacter) && modifierFlags == 0) { // if (supportsQuickLook == NO) // [[self enclosingScrollView] pageDown:nil]; // else if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) // [[QLPreviewPanel sharedPreviewPanel] orderOut:nil]; // else // [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil]; // } else if ((eventChar == SKSpaceCharacter) && modifierFlags == NSShiftKeyMask) { // if (supportsQuickLook == NO) // [[self enclosingScrollView] pageUp:nil]; // } else if (eventChar == NSHomeFunctionKey && (modifierFlags & ~NSFunctionKeyMask) == 0) { // [self scrollToBeginningOfDocument:nil]; // } else if (eventChar == NSEndFunctionKey && (modifierFlags & ~NSFunctionKeyMask) == 0) { // [self scrollToEndOfDocument:nil]; // } else if (eventChar == NSLeftArrowFunctionKey && modifierFlags == 0) { // [self moveLeft:nil]; // } else if (eventChar == NSRightArrowFunctionKey && modifierFlags == 0) { // [self moveRight:nil]; // } else if ([typeSelectHelper handleEvent:theEvent] == NO) { // [super keyDown:theEvent]; // } super.keyDown(with: event) } override var font: NSFont? { get { for tc in self.tableColumns { if let cell = tc.dataCell as? NSCell { if cell.type == .textCellType { return cell.font } } } return nil } set { for tc in self.tableColumns { if let cell = tc.dataCell as? NSCell { if cell.type == .textCellType { cell.font = newValue } } } var rowHeight = font?.defaultViewLineHeight() ?? 34 if self.selectionHighlightStyle == .sourceList { rowHeight += 2.0 } self.rowHeight = rowHeight self.noteHeightOfRows(withIndexesChanged: IndexSet(0.. NSArray { return self.botaDelegate?.tableView?(self, typeSelectHelperSelectionStrings: typeSelectHelper) ?? NSArray() } func typeSelectHelperCurrentlySelectedIndex(_ typeSelectHelper: SKTypeSelectHelper) -> Int { return self.selectedRowIndexes.last ?? 0 } func typeSelectHelper(_ typeSelectHelper: SKTypeSelectHelper, selectItemAtIndex itemIndex: Int) { self.selectRowIndexes(NSIndexSet(index: itemIndex) as IndexSet, byExtendingSelection: false) self.scrollRowToVisible(itemIndex) } func typeSelectHelper(_ typeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) { self.botaDelegate?.tableView?(self, typeSelectHelper: typeSelectHelper, didFailToFindMatchForSearchString: searchString) } func typeSelectHelper(_ typeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String) { self.botaDelegate?.tableView?(self, typeSelectHelper: typeSelectHelper, updateSearchString: searchString) } /* - (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper { if (typeSelectHelper != newTypeSelectHelper) { if ([typeSelectHelper delegate] == self) [typeSelectHelper setDelegate:nil]; [typeSelectHelper release]; typeSelectHelper = [newTypeSelectHelper retain]; [typeSelectHelper setDelegate:self]; } } - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent *)dragEvent offset:(NSPointPointer)dragImageOffset{ return [super dragImageForRowsWithIndexes:dragRows tableColumns:[[self tableColumns] subarrayWithRange:NSMakeRange(0, 1)] event:dragEvent offset:dragImageOffset]; } #pragma mark SKTypeSelectHelper datasource protocol - (NSArray *)typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)aTypeSelectHelper { if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelperSelectionStrings:)]) return [[self delegate] tableView:self typeSelectHelperSelectionStrings:aTypeSelectHelper]; return nil; } - (NSUInteger)typeSelectHelperCurrentlySelectedIndex:(SKTypeSelectHelper *)aTypeSelectHelper { return [[self selectedRowIndexes] lastIndex]; } - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper selectItemAtIndex:(NSUInteger)itemIndex { [self selectRowIndexes:[NSIndexSet indexSetWithIndex:itemIndex] byExtendingSelection:NO]; [self scrollRowToVisible:itemIndex]; } - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString { if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelper:didFailToFindMatchForSearchString:)]) [[self delegate] tableView:self typeSelectHelper:aTypeSelectHelper didFailToFindMatchForSearchString:searchString]; } - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper updateSearchString:(NSString *)searchString { if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelper:updateSearchString:)]) [[self delegate] tableView:self typeSelectHelper:aTypeSelectHelper updateSearchString:searchString]; } */ } // MARK: - Private Methods extension KMBotaTableView { private func _rebuildTrackingAreas() { if self.kmTrackingAreas == nil { return } let context = self.botaDelegate?.tableView?(self, imageContextForRow: 0) if context == nil { return } self._removeTrackingAreas() if self.window != nil { let visibleRect = self.visibleRect let rowRange = self.rows(in: visibleRect) var row: UInt = 0 for i in rowRange.location ..< NSMaxRange(rowRange) { self._addTrackingAreaForRow(Int(i)) } } } private func _removeTrackingAreas() { if (self.kmTrackingAreas == nil) { return } for area in self.kmTrackingAreas! { self.removeTrackingArea(area as! NSTrackingArea) } self.kmTrackingAreas?.removeAllObjects() } private func _addTrackingAreaForRow(_ row: Int) { if (self.kmTrackingAreas == nil) { return } let userInfo = ["row" : row] let area = NSTrackingArea(rect: self.rect(ofRow: row), options: [.mouseEnteredAndExited, .activeInActiveApp], owner: self, userInfo: userInfo) self.addTrackingArea(area) self.kmTrackingAreas?.add(area) } /* */ } extension KMBotaTableView: NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { if menuItem.action == #selector(delete) { return self.canDelete() } else if menuItem.action == KMSelectorCopy { return self.canCopy() } else if menuItem.action == #selector(paste) { return self.canPaste() } else if menuItem.action == #selector(selectAll) { return self.allowsMultipleSelection } else if menuItem.action == #selector(deselectAll) { return self.allowsEmptySelection } // else if ([[SKTableView superclass] instancesRespondToSelector:@selector(validateMenuItem:)]) // return [super validateMenuItem:menuItem]; return true } }