KMBotaTableView.swift 15 KB

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