KMBotaTableView.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. //
  2. // KMTableView.swift
  3. // PDF Master
  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. /*
  181. - (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper {
  182. if (typeSelectHelper != newTypeSelectHelper) {
  183. if ([typeSelectHelper delegate] == self)
  184. [typeSelectHelper setDelegate:nil];
  185. [typeSelectHelper release];
  186. typeSelectHelper = [newTypeSelectHelper retain];
  187. [typeSelectHelper setDelegate:self];
  188. }
  189. }
  190. - (NSFont *)font {
  191. for (NSTableColumn *tc in [self tableColumns]) {
  192. NSCell *cell = [tc dataCell];
  193. if ([cell type] == NSTextCellType)
  194. return [cell font];
  195. }
  196. return nil;
  197. }
  198. - (void)setFont:(NSFont *)font {
  199. for (NSTableColumn *tc in [self tableColumns]) {
  200. NSCell *cell = [tc dataCell];
  201. if ([cell type] == NSTextCellType)
  202. [cell setFont:font];
  203. }
  204. [self setRowHeight:[font defaultViewLineHeight]];
  205. [self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];
  206. }
  207. - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent *)dragEvent offset:(NSPointPointer)dragImageOffset{
  208. return [super dragImageForRowsWithIndexes:dragRows tableColumns:[[self tableColumns] subarrayWithRange:NSMakeRange(0, 1)] event:dragEvent offset:dragImageOffset];
  209. }
  210. #pragma mark SKTypeSelectHelper datasource protocol
  211. - (NSArray *)typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)aTypeSelectHelper {
  212. if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelperSelectionStrings:)])
  213. return [[self delegate] tableView:self typeSelectHelperSelectionStrings:aTypeSelectHelper];
  214. return nil;
  215. }
  216. - (NSUInteger)typeSelectHelperCurrentlySelectedIndex:(SKTypeSelectHelper *)aTypeSelectHelper {
  217. return [[self selectedRowIndexes] lastIndex];
  218. }
  219. - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper selectItemAtIndex:(NSUInteger)itemIndex {
  220. [self selectRowIndexes:[NSIndexSet indexSetWithIndex:itemIndex] byExtendingSelection:NO];
  221. [self scrollRowToVisible:itemIndex];
  222. }
  223. - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString {
  224. if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelper:didFailToFindMatchForSearchString:)])
  225. [[self delegate] tableView:self typeSelectHelper:aTypeSelectHelper didFailToFindMatchForSearchString:searchString];
  226. }
  227. - (void)typeSelectHelper:(SKTypeSelectHelper *)aTypeSelectHelper updateSearchString:(NSString *)searchString {
  228. if ([[self delegate] respondsToSelector:@selector(tableView:typeSelectHelper:updateSearchString:)])
  229. [[self delegate] tableView:self typeSelectHelper:aTypeSelectHelper updateSearchString:searchString];
  230. }
  231. */
  232. }
  233. // MARK: - Private Methods
  234. extension KMBotaTableView {
  235. private func _rebuildTrackingAreas() {
  236. if self.kmTrackingAreas == nil {
  237. return
  238. }
  239. let context = self.botaDelegate?.tableView?(self, imageContextForRow: 0)
  240. if context == nil {
  241. return
  242. }
  243. self._removeTrackingAreas()
  244. if self.window != nil {
  245. let visibleRect = self.visibleRect
  246. let rowRange = self.rows(in: visibleRect)
  247. var row: UInt = 0
  248. for i in rowRange.location ..< NSMaxRange(rowRange) {
  249. self._addTrackingAreaForRow(Int(i))
  250. }
  251. }
  252. }
  253. private func _removeTrackingAreas() {
  254. if (self.kmTrackingAreas == nil) {
  255. return
  256. }
  257. for area in self.kmTrackingAreas! {
  258. self.removeTrackingArea(area as! NSTrackingArea)
  259. }
  260. self.kmTrackingAreas?.removeAllObjects()
  261. }
  262. private func _addTrackingAreaForRow(_ row: Int) {
  263. if (self.kmTrackingAreas == nil) {
  264. return
  265. }
  266. let userInfo = ["row" : row]
  267. let area = NSTrackingArea(rect: self.rect(ofRow: row), options: [.mouseEnteredAndExited, .activeInActiveApp], owner: self, userInfo: userInfo)
  268. self.addTrackingArea(area)
  269. self.kmTrackingAreas?.add(area)
  270. }
  271. /*
  272. */
  273. }
  274. extension KMBotaTableView: NSMenuItemValidation {
  275. func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  276. if menuItem.action == #selector(delete) {
  277. return self.canDelete()
  278. } else if menuItem.action == KMSelectorCopy {
  279. return self.canCopy()
  280. } else if menuItem.action == #selector(paste) {
  281. return self.canPaste()
  282. } else if menuItem.action == #selector(selectAll) {
  283. return self.allowsMultipleSelection
  284. } else if menuItem.action == #selector(deselectAll) {
  285. return self.allowsEmptySelection
  286. }
  287. // else if ([[SKTableView superclass] instancesRespondToSelector:@selector(validateMenuItem:)])
  288. // return [super validateMenuItem:menuItem];
  289. return true
  290. }
  291. }