KMBotaTableView.swift 13 KB

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