KMBotaTableView.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. //
  2. // KMTableView.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/11/16.
  6. //
  7. import Cocoa
  8. /*
  9. @protocol SKTableViewDelegate <NSTableViewDelegate>
  10. - (NSArray *)tableView:(NSTableView *)aTableView typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)aTypeSelectHelper;
  11. - (void)tableView:(NSTableView *)aTableView typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString;
  12. - (void)tableView:(NSTableView *)aTableView typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper updateSearchString:(NSString *)searchString;
  13. @end
  14. */
  15. @objc protocol KMBotaTableViewDelegate: NSTableViewDelegate {
  16. @objc optional func tableView(_ aTableView: NSTableView, deleteRowsWithIndexes rowIndexes: IndexSet)
  17. @objc optional func tableView(_ aTableView: NSTableView, canDeleteRowsWithIndexes rowIndexes: IndexSet) -> Bool
  18. @objc optional func tableView(_ aTableView: NSTableView, copyRowsWithIndexes rowIndexes: IndexSet)
  19. @objc optional func tableView(_ aTableView: NSTableView, canCopyRowsWithIndexes rowIndexes: IndexSet) -> Bool
  20. @objc optional func tableView(_ aTableView: NSTableView, pasteFromPasteboard pboard: NSPasteboard)
  21. @objc optional func tableView(_ aTableView: NSTableView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool
  22. @objc optional func tableViewMoveLeft(_ aTableView: NSTableView)
  23. @objc optional func tableViewMoveRight(_ aTableView: NSTableView)
  24. @objc optional func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject?
  25. @objc optional func tableView(_ aTableView: NSTableView, typeSelectHelperSelectionStrings aTypeSelectHelper: SKTypeSelectHelper) -> NSArray
  26. @objc optional func tableView(_ aTableView: NSTableView, typeSelectHelper aTypeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String)
  27. @objc optional func tableView(_ aTableView: NSTableView, typeSelectHelper aTypeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String)
  28. }
  29. class KMBotaTableView: NSTableView, SKTypeSelectHelperDelegate {
  30. var kmTrackingAreas: NSMutableSet?
  31. /*
  32. @interface SKTableView : NSTableView <SKTypeSelectDelegate> {
  33. SKTypeSelectHelper *typeSelectHelper;
  34. }
  35. @property (nonatomic) supportsQuickLook;
  36. @property (nonatomic, retain) SKTypeSelectHelper *typeSelectHelper;
  37. */
  38. var typeSelectHelper: SKTypeSelectHelper?
  39. weak var botaDelegate: KMBotaTableViewDelegate? {
  40. didSet {
  41. self._rebuildTrackingAreas()
  42. }
  43. }
  44. var hasImageToolTips: Bool {
  45. get {
  46. return self.kmTrackingAreas != nil
  47. }
  48. set {
  49. if self.kmTrackingAreas == nil && newValue {
  50. self.kmTrackingAreas = NSMutableSet()
  51. if self.window != nil {
  52. self._rebuildTrackingAreas()
  53. }
  54. } else if self.kmTrackingAreas != nil && newValue == false {
  55. if self.window != nil {
  56. self._removeTrackingAreas()
  57. }
  58. }
  59. }
  60. }
  61. func setTypeSelectHelper(_ newTypeSelectHelper: SKTypeSelectHelper) {
  62. if typeSelectHelper != newTypeSelectHelper {
  63. typeSelectHelper?.delegate = nil
  64. typeSelectHelper = newTypeSelectHelper
  65. typeSelectHelper?.delegate = self
  66. }
  67. }
  68. override func draw(_ dirtyRect: NSRect) {
  69. super.draw(dirtyRect)
  70. // Drawing code here.
  71. }
  72. override func reloadData() {
  73. super.reloadData()
  74. self._rebuildTrackingAreas()
  75. // [typeSelectHelper rebuildTypeSelectSearchCache];
  76. typeSelectHelper?.rebuildTypeSelectSearchCache()
  77. }
  78. // MARK: - Tracking
  79. override func updateTrackingAreas() {
  80. super.updateTrackingAreas()
  81. self._rebuildTrackingAreas()
  82. }
  83. override func noteNumberOfRowsChanged() {
  84. super.noteNumberOfRowsChanged()
  85. self._rebuildTrackingAreas()
  86. }
  87. override func mouseEntered(with event: NSEvent) {
  88. if (self.kmTrackingAreas == nil) {
  89. return
  90. }
  91. // let userInfo = event.userData as? [String : Any]
  92. let userInfo = event.trackingArea?.userInfo as? [String : Any]
  93. let row = userInfo?["row"] as? Int
  94. if row != nil {
  95. let context = self.botaDelegate?.tableView?(self, imageContextForRow: row!)
  96. if context is KMImageToolTipContext {
  97. KMImageToolTipWindow.shared.showForImageContext(context as! KMImageToolTipContext, at: .zero)
  98. }
  99. }
  100. }
  101. override func mouseExited(with event: NSEvent) {
  102. if (self.kmTrackingAreas == nil) {
  103. return
  104. }
  105. let userInfo = event.userData as? [String : Any]
  106. let row = userInfo?["row"] as? Int
  107. if row != nil {
  108. KMImageToolTipWindow.shared.fadeOut()
  109. }
  110. }
  111. func scrollToBeginningOfDocument(_ sender: AnyObject?) {
  112. if self.numberOfRows > 0 {
  113. self.scrollRowToVisible(0)
  114. }
  115. }
  116. func scrollToEndOfDocument(_ sender: AnyObject?) {
  117. if self.numberOfRows > 0 {
  118. self.scrollRowToVisible(self.numberOfRows-1)
  119. }
  120. }
  121. func moveLeft(_ sender: AnyObject?) {
  122. self.botaDelegate?.tableViewMoveLeft?(self)
  123. }
  124. func moveRight(_ sender: AnyObject?) {
  125. self.botaDelegate?.tableViewMoveRight?(self)
  126. }
  127. func canDelete() -> Bool {
  128. let indexes = self.selectedRowIndexes
  129. if indexes.isEmpty {
  130. return false
  131. }
  132. return self.botaDelegate?.tableView?(self, canDeleteRowsWithIndexes: indexes) ?? false
  133. }
  134. @objc func delete(_ sender: AnyObject?) {
  135. if self.canDelete() {
  136. self.botaDelegate?.tableView?(self, deleteRowsWithIndexes: self.selectedRowIndexes)
  137. }
  138. }
  139. func canCopy() -> Bool {
  140. let indexes = self.selectedRowIndexes
  141. if indexes.isEmpty {
  142. return false
  143. }
  144. return self.botaDelegate?.tableView?(self, canCopyRowsWithIndexes: indexes) ?? false
  145. }
  146. @objc func copy(_ sender: AnyObject?) {
  147. if self.canCopy() {
  148. self.botaDelegate?.tableView?(self, copyRowsWithIndexes: self.selectedRowIndexes)
  149. }
  150. }
  151. func canPaste() -> Bool {
  152. return self.botaDelegate?.tableView?(self, canPasteFromPasteboard: NSPasteboard.general) ?? false
  153. }
  154. @objc func paste(_ sender: AnyObject?) {
  155. if self.canPaste() {
  156. self.botaDelegate?.tableView?(self, pasteFromPasteboard: NSPasteboard.general)
  157. }
  158. }
  159. override func keyDown(with event: NSEvent) {
  160. // let eventChar = event.PDFListViewFirstCharacter()
  161. // let modifierFlags = event.deviceIndependen
  162. // NSUInteger modifierFlags = [theEvent deviceIndependentModifierFlags];
  163. //
  164. // if ((eventChar == NSNewlineCharacter || eventChar == NSEnterCharacter || eventChar == NSCarriageReturnCharacter) && modifierFlags == 0) {
  165. // if ([self doubleAction] == NULL || [self sendAction:[self doubleAction] to:[self target]] == NO)
  166. // NSBeep();
  167. // } else if ((eventChar == NSDeleteCharacter || eventChar == NSDeleteFunctionKey) && modifierFlags == 0 && [self canDelete]) {
  168. // [self delete:self];
  169. // } else if ((eventChar == SKSpaceCharacter) && modifierFlags == 0) {
  170. // if (supportsQuickLook == NO)
  171. // [[self enclosingScrollView] pageDown:nil];
  172. // else if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible])
  173. // [[QLPreviewPanel sharedPreviewPanel] orderOut:nil];
  174. // else
  175. // [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
  176. // } else if ((eventChar == SKSpaceCharacter) && modifierFlags == NSShiftKeyMask) {
  177. // if (supportsQuickLook == NO)
  178. // [[self enclosingScrollView] pageUp:nil];
  179. // } else if (eventChar == NSHomeFunctionKey && (modifierFlags & ~NSFunctionKeyMask) == 0) {
  180. // [self scrollToBeginningOfDocument:nil];
  181. // } else if (eventChar == NSEndFunctionKey && (modifierFlags & ~NSFunctionKeyMask) == 0) {
  182. // [self scrollToEndOfDocument:nil];
  183. // } else if (eventChar == NSLeftArrowFunctionKey && modifierFlags == 0) {
  184. // [self moveLeft:nil];
  185. // } else if (eventChar == NSRightArrowFunctionKey && modifierFlags == 0) {
  186. // [self moveRight:nil];
  187. // } else if ([typeSelectHelper handleEvent:theEvent] == NO) {
  188. // [super keyDown:theEvent];
  189. // }
  190. super.keyDown(with: event)
  191. }
  192. override var font: NSFont? {
  193. get {
  194. for tc in self.tableColumns {
  195. if let cell = tc.dataCell as? NSCell {
  196. if cell.type == .textCellType {
  197. return cell.font
  198. }
  199. }
  200. }
  201. return nil
  202. }
  203. set {
  204. for tc in self.tableColumns {
  205. if let cell = tc.dataCell as? NSCell {
  206. if cell.type == .textCellType {
  207. cell.font = newValue
  208. }
  209. }
  210. }
  211. var rowHeight = font?.defaultViewLineHeight() ?? 34
  212. if self.selectionHighlightStyle == .sourceList {
  213. rowHeight += 2.0
  214. }
  215. self.rowHeight = rowHeight
  216. self.noteHeightOfRows(withIndexesChanged: IndexSet(0..<self.numberOfRows))
  217. }
  218. }
  219. func typeSelectHelperSelectionStrings(_ typeSelectHelper: SKTypeSelectHelper) -> NSArray {
  220. return self.botaDelegate?.tableView?(self, typeSelectHelperSelectionStrings: typeSelectHelper) ?? NSArray()
  221. }
  222. func typeSelectHelperCurrentlySelectedIndex(_ typeSelectHelper: SKTypeSelectHelper) -> Int {
  223. return self.selectedRowIndexes.last ?? 0
  224. }
  225. func typeSelectHelper(_ typeSelectHelper: SKTypeSelectHelper, selectItemAtIndex itemIndex: Int) {
  226. self.selectRowIndexes(NSIndexSet(index: itemIndex) as IndexSet, byExtendingSelection: false)
  227. self.scrollRowToVisible(itemIndex)
  228. }
  229. func typeSelectHelper(_ typeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) {
  230. self.botaDelegate?.tableView?(self, typeSelectHelper: typeSelectHelper, didFailToFindMatchForSearchString: searchString)
  231. }
  232. func typeSelectHelper(_ typeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String) {
  233. self.botaDelegate?.tableView?(self, typeSelectHelper: typeSelectHelper, updateSearchString: searchString)
  234. }
  235. /*
  236. - (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper {
  237. if (typeSelectHelper != newTypeSelectHelper) {
  238. if ([typeSelectHelper delegate] == self)
  239. [typeSelectHelper setDelegate:nil];
  240. [typeSelectHelper release];
  241. typeSelectHelper = [newTypeSelectHelper retain];
  242. [typeSelectHelper setDelegate:self];
  243. }
  244. }
  245. - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent *)dragEvent offset:(NSPointPointer)dragImageOffset{
  246. return [super dragImageForRowsWithIndexes:dragRows tableColumns:[[self tableColumns] subarrayWithRange:NSMakeRange(0, 1)] event:dragEvent offset:dragImageOffset];
  247. }
  248. #pragma mark SKTypeSelectHelper datasource protocol
  249. - (NSArray *)typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)aTypeSelectHelper {
  250. if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelperSelectionStrings:)])
  251. return [[self delegate] tableView:self typeSelectHelperSelectionStrings:aTypeSelectHelper];
  252. return nil;
  253. }
  254. - (NSUInteger)typeSelectHelperCurrentlySelectedIndex:(SKTypeSelectHelper *)aTypeSelectHelper {
  255. return [[self selectedRowIndexes] lastIndex];
  256. }
  257. - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper selectItemAtIndex:(NSUInteger)itemIndex {
  258. [self selectRowIndexes:[NSIndexSet indexSetWithIndex:itemIndex] byExtendingSelection:NO];
  259. [self scrollRowToVisible:itemIndex];
  260. }
  261. - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString {
  262. if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelper:didFailToFindMatchForSearchString:)])
  263. [[self delegate] tableView:self typeSelectHelper:aTypeSelectHelper didFailToFindMatchForSearchString:searchString];
  264. }
  265. - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper updateSearchString:(NSString *)searchString {
  266. if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelper:updateSearchString:)])
  267. [[self delegate] tableView:self typeSelectHelper:aTypeSelectHelper updateSearchString:searchString];
  268. }
  269. */
  270. }
  271. // MARK: - Private Methods
  272. extension KMBotaTableView {
  273. private func _rebuildTrackingAreas() {
  274. if self.kmTrackingAreas == nil {
  275. return
  276. }
  277. let context = self.botaDelegate?.tableView?(self, imageContextForRow: 0)
  278. if context == nil {
  279. return
  280. }
  281. self._removeTrackingAreas()
  282. if self.window != nil {
  283. let visibleRect = self.visibleRect
  284. let rowRange = self.rows(in: visibleRect)
  285. var row: UInt = 0
  286. for i in rowRange.location ..< NSMaxRange(rowRange) {
  287. self._addTrackingAreaForRow(Int(i))
  288. }
  289. }
  290. }
  291. private func _removeTrackingAreas() {
  292. if (self.kmTrackingAreas == nil) {
  293. return
  294. }
  295. for area in self.kmTrackingAreas! {
  296. self.removeTrackingArea(area as! NSTrackingArea)
  297. }
  298. self.kmTrackingAreas?.removeAllObjects()
  299. }
  300. private func _addTrackingAreaForRow(_ row: Int) {
  301. if (self.kmTrackingAreas == nil) {
  302. return
  303. }
  304. let userInfo = ["row" : row]
  305. let area = NSTrackingArea(rect: self.rect(ofRow: row), options: [.mouseEnteredAndExited, .activeInActiveApp], owner: self, userInfo: userInfo)
  306. self.addTrackingArea(area)
  307. self.kmTrackingAreas?.add(area)
  308. }
  309. /*
  310. */
  311. }
  312. extension KMBotaTableView: NSMenuItemValidation {
  313. func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  314. if menuItem.action == #selector(delete) {
  315. return self.canDelete()
  316. } else if menuItem.action == KMSelectorCopy {
  317. return self.canCopy()
  318. } else if menuItem.action == #selector(paste) {
  319. return self.canPaste()
  320. } else if menuItem.action == #selector(selectAll) {
  321. return self.allowsMultipleSelection
  322. } else if menuItem.action == #selector(deselectAll) {
  323. return self.allowsEmptySelection
  324. }
  325. // else if ([[SKTableView superclass] instancesRespondToSelector:@selector(validateMenuItem:)])
  326. // return [super validateMenuItem:menuItem];
  327. return true
  328. }
  329. }