KMNoteTableViewCell.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. //
  2. // KMNoteTableViewCell.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/11/17.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. class KMNoteTableViewCell: NSTableCellView {
  10. @IBOutlet var topView: NSView!
  11. @IBOutlet var typeImageView: NSImageView!
  12. @IBOutlet var autherLabel: NSTextField!
  13. @IBOutlet var timeLabel: NSTextField!
  14. @IBOutlet var pageLabel: NSTextField!
  15. @IBOutlet var contentView: NSView!
  16. @IBOutlet var foldButton: NSButton!
  17. @IBOutlet var annotationContentLabel: NSTextField!
  18. @IBOutlet var noteContentBox: NSBox!
  19. @IBOutlet var noteContentLabel: NSTextField!
  20. @IBOutlet var noteImageView: NSImageView!
  21. @IBOutlet var markupButton: KMCoverButton!
  22. @IBOutlet var topViewLayoutConstraint: NSLayoutConstraint!
  23. @IBOutlet var autherLayoutConstraint: NSLayoutConstraint!
  24. @IBOutlet var typeImageViewLayoutConstraint: NSLayoutConstraint!
  25. @IBOutlet var contentBoxLayoutConstraint: NSLayoutConstraint!
  26. @IBOutlet var maltlineLabelLayoutConstraint: NSLayoutConstraint!
  27. @IBOutlet var imageViewHeightConstraint: NSLayoutConstraint!
  28. @IBOutlet var pageLabelWidthConstraint: NSLayoutConstraint!
  29. @IBOutlet var noteContentHeightConstraint: NSLayoutConstraint!
  30. @IBOutlet var contentViewTopConstraint: NSLayoutConstraint!
  31. @IBOutlet var pageLabelTopConstraint: NSLayoutConstraint!
  32. @IBOutlet var timeWidthConstraint: NSLayoutConstraint!
  33. @IBOutlet var annotationContentWidthConstraint: NSLayoutConstraint!
  34. var state: CPDFAnnotationState = .unMarked {
  35. didSet {
  36. if self.state == .unMarked {
  37. self.markupButton.image = NSImage(named: "KMNImageNameBotaNoteMarkup")
  38. } else {
  39. self.markupButton.image = NSImage(named: "KMNImageNameBotaNoteMarkSelected")
  40. }
  41. }
  42. }
  43. var isFold = false {
  44. didSet {
  45. if (self.isFold) {
  46. self.maltlineLabelLayoutConstraint.constant = 18.0
  47. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  48. self.noteContentBox.isHidden = true
  49. self.imageViewHeightConstraint.constant = 18.0 + 8
  50. } else {
  51. let labelheight = self._heightForNoteStringValue(self.annotationContentLabel.stringValue, maxW: NSWidth(self.annotationContentLabel.bounds))
  52. self.maltlineLabelLayoutConstraint.constant = labelheight + 18.0
  53. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  54. self.noteContentBox.isHidden = false
  55. let noteLabelHeight = self._heightForNoteStringValue(self.noteContentLabel.stringValue, maxW: NSWidth(self.noteContentLabel.bounds))
  56. self.noteContentHeightConstraint.constant = noteLabelHeight
  57. self.imageViewHeightConstraint.constant = self.maltlineLabelLayoutConstraint.constant + self.noteContentHeightConstraint.constant + 25
  58. }
  59. self.contentBoxLayoutConstraint.constant = self.noteContentBox.isHidden ? -(self.noteContentBox.bounds.size.height+8.0) : 8.0
  60. }
  61. }
  62. weak var cellNote: CPDFAnnotation?
  63. var isUnFoldNote: ((_ cellNote: CPDFAnnotation?, _ isUnfold: Bool)->Void)?
  64. var renameActionCallback: ((_ count: String)->Void)?
  65. var itemClick: KMCommonClickBlock?
  66. var model: KMBotaAnnotationModel? {
  67. didSet {
  68. self.updateView(self.model)
  69. }
  70. }
  71. override func draw(_ dirtyRect: NSRect) {
  72. super.draw(dirtyRect)
  73. // Drawing code here.
  74. }
  75. deinit {
  76. NotificationCenter.default.removeObserver(self)
  77. }
  78. override func awakeFromNib() {
  79. super.awakeFromNib()
  80. self.wantsLayer = true
  81. self.layer?.backgroundColor = .clear
  82. self.layer?.cornerRadius = 0.0
  83. self.autherLabel.wantsLayer = true
  84. self.autherLabel.layer?.backgroundColor = KMAppearance.Else.textTagColor().cgColor
  85. self.autherLabel.layer?.cornerRadius = 1.0
  86. self.autherLabel.textColor = KMAppearance.Layout.h0Color()
  87. self.timeLabel.lineBreakMode = .byTruncatingTail
  88. self.timeLabel.cell?.truncatesLastVisibleLine = true
  89. self.pageLabel.textColor = KMAppearance.Layout.h2Color()
  90. self.noteContentLabel.isEditable = false
  91. self.noteContentBox.borderWidth = 1.0
  92. self.noteContentBox.borderColor = KMAppearance.Layout.b15Color()
  93. self.noteContentBox.cornerRadius = 1.0
  94. self.noteContentBox.fillColor = KMAppearance.Layout.l1Color()
  95. self.annotationContentLabel.cell?.truncatesLastVisibleLine = true
  96. self.annotationContentLabel.isEditable = false
  97. self.markupButton.toolTip = KMLocalizedString("Add or Remove Marked")
  98. self.markupButton.target = self
  99. self.markupButton.action = #selector(markupAction)
  100. self.markupButton.coverAction = { [weak self] btn, act in
  101. let state = self?.state ?? .unMarked
  102. if state == .unMarked {
  103. btn.image = NSImage(named: "KMNImageNameBotaNoteMarkup")
  104. } else {
  105. btn.image = NSImage(named: "KMNImageNameBotaNoteMarkSelected")
  106. }
  107. }
  108. NotificationCenter.default.addObserver(self, selector: #selector(_contextDidEndEdit), name: NSControl.textDidEndEditingNotification, object: self.annotationContentLabel)
  109. self.isFold = true
  110. NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: APPAppearanceChangedNotificationName, object: nil)
  111. updateUI()
  112. }
  113. @objc func updateUI() {
  114. self.timeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/3")
  115. self.noteContentLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  116. self.annotationContentLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  117. self.timeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-xs-regular")
  118. self.noteContentLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular")
  119. self.annotationContentLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular")
  120. }
  121. func contentViewHidden(_ hidden: Bool) {
  122. self.contentViewTopConstraint.constant = hidden ? -(NSHeight(self.topView.bounds)) : 8.0
  123. self.pageLabelTopConstraint.constant = -(NSHeight(self.topView.bounds)-4)
  124. }
  125. @IBAction func foldButtonAction(_ sender: NSButton) {
  126. let anno = self.model?.anno
  127. if anno is CPDFStampAnnotation || anno is CPDFInkAnnotation || anno is CPDFSignatureAnnotation || anno is CPDFTextAnnotation {
  128. let expand = model?.isExpand ?? false
  129. if expand {
  130. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  131. } else {
  132. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  133. }
  134. if (self.isUnFoldNote != nil) {
  135. self.isUnFoldNote!(self.cellNote, !expand)
  136. }
  137. } else {
  138. self.isFold = !self.isFold
  139. if (self.isUnFoldNote != nil) {
  140. self.isUnFoldNote!(self.cellNote, !self.isFold)
  141. }
  142. }
  143. }
  144. override func layout() {
  145. super.layout()
  146. let showAuthor = self.model?.showAuthor ?? false
  147. let authorW = showAuthor ? self.autherLabel.frame.size.width : 0
  148. let showTime = self.model?.showTime ?? false
  149. if showTime {
  150. let x: CGFloat = 16 + 10 + authorW
  151. let width = NSWidth(self.topView.frame)
  152. self.timeWidthConstraint.constant = width - x
  153. }
  154. let foldW: CGFloat = 16
  155. let contentW = NSWidth(self.contentView.frame)
  156. self.annotationContentWidthConstraint.constant = contentW-foldW
  157. }
  158. @objc func markupAction() {
  159. self.itemClick?(1)
  160. }
  161. }
  162. // MARK: - Private Methods
  163. extension KMNoteTableViewCell {
  164. @objc private func _contextDidEndEdit(_ notification: NSNotification) {
  165. let textField = notification.object as? NSTextField
  166. if self.noteContentLabel.isEqual(to: textField) {
  167. if (self.renameActionCallback != nil) {
  168. self.renameActionCallback!(textField!.stringValue)
  169. }
  170. }
  171. }
  172. private func _equalToImage(_ image: NSImage?) -> Bool {
  173. guard let _image = image else {
  174. return false
  175. }
  176. let imageData1 = _image.tiffRepresentation
  177. let imageData2 = NSImage(named: "KMImageNameBtnTriRightNor")?.tiffRepresentation
  178. return imageData1 == imageData2
  179. }
  180. private func _heightForNoteStringValue(_ text: String, maxW: CGFloat) -> CGFloat {
  181. let maxSize = NSMakeSize(maxW, CGFloat(MAXFLOAT))
  182. let opts: NSString.DrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
  183. let style = NSMutableParagraphStyle()
  184. style.lineBreakMode = .byWordWrapping
  185. let attributes: [NSAttributedString.Key : Any] = [.font : NSFont.systemFont(ofSize: 14), .paragraphStyle : style]
  186. let rect = text.boundingRect(with: maxSize, options: opts, attributes: attributes)
  187. return rect.size.height
  188. }
  189. private func updateView(_ model: KMBotaAnnotationModel?) {
  190. let note = model?.anno
  191. let noteColor = note?.color
  192. let noteType = KMPDFAnnotationNoteType(note)
  193. var noteString = ""
  194. if let data = note {
  195. noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: data)
  196. }
  197. let pageString = "\((note?.page?.pageIndex() ?? 0) + 1)"
  198. var dateString = ""
  199. if let date = note?.modificationDate() {
  200. dateString = KMTools.timeString(timeDate: date)
  201. }
  202. let authorString = note?.userName() ?? ""
  203. let noteTextString = note?.string() ?? ""
  204. let showTime = model?.showTime ?? true
  205. if showTime {
  206. self.timeLabel.stringValue = dateString
  207. self.timeLabel.lineBreakMode = .byTruncatingTail
  208. self.timeLabel.cell?.truncatesLastVisibleLine = true
  209. self.timeLabel.isHidden = false
  210. self.timeLabel.toolTip = dateString
  211. } else {
  212. self.timeLabel.isHidden = true
  213. }
  214. let showPage = model?.showPage ?? true
  215. if showPage {
  216. self.pageLabel.stringValue = pageString
  217. let labelsize = self.pageLabel.stringValue.boundingRect(with: NSMakeSize(CGFloat(MAXFLOAT),NSHeight(self.pageLabel.bounds)), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font : NSFont.systemFont(ofSize: 12)])
  218. self.pageLabelWidthConstraint.constant = labelsize.size.width + 5
  219. } else {
  220. self.pageLabel.isHidden = true
  221. }
  222. let showAuthor = model?.showAuthor ?? true
  223. if showAuthor {
  224. self.autherLabel.stringValue = authorString
  225. self.autherLabel.isHidden = false
  226. self.autherLabel.sizeToFit()
  227. } else {
  228. self.autherLabel.isHidden = true
  229. }
  230. let imageView = KMNoteTypeImageView()
  231. self.typeImageView.image = imageView.noteTypeImage(withType: noteType, color: noteColor ?? .red)
  232. self.typeImageView.isHidden = false
  233. self.noteContentBox.isHidden = true
  234. self.noteImageView.isHidden = true
  235. self.foldButton.isHidden = true
  236. self.annotationContentLabel.isHidden = false
  237. self.noteContentLabel.stringValue = noteString
  238. self.contentView.isHidden = false
  239. self.contentViewHidden(false)
  240. // 默认折叠
  241. let isFold = model?.isFold() ?? true
  242. if let data = note {
  243. if data.isKind(of: CPDFMarkupAnnotation.self) {
  244. let markup = data as! CPDFMarkupAnnotation
  245. var contentString = KMBOTAAnnotationTool.fetchText(text: markup.markupContent())
  246. contentString = contentString.replacingOccurrences(of: "\r", with: "")
  247. contentString = contentString.replacingOccurrences(of: "\n", with: "")
  248. self.noteContentLabel.stringValue = contentString
  249. self.foldButton.isHidden = false
  250. // }
  251. let attributeStr = NSMutableAttributedString(string: noteString)
  252. if (markup.markupType() == .highlight) {
  253. attributeStr.addAttribute(.backgroundColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length))
  254. } else if (markup.markupType() == .strikeOut) {
  255. attributeStr.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeStr.length))
  256. attributeStr.addAttribute(.strikethroughColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length))
  257. } else if (markup.markupType() == .underline) {
  258. attributeStr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeStr.length))
  259. attributeStr.addAttribute(.underlineColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length))
  260. }
  261. self.annotationContentLabel.attributedStringValue = attributeStr
  262. self.isFold = !(model?.isExpand ?? false)
  263. self.contentViewHidden(false)
  264. } else if data.isKind(of: CPDFLineAnnotation.self) || noteType == SKNSquareString || noteType == SKNCircleString { // || noteType == SKNInkString
  265. noteString = data.contents ?? ""
  266. if let anno = data as? CPDFLineAnnotation, anno.isMeasure {
  267. noteString = anno.string() ?? ""
  268. }
  269. self.annotationContentLabel.stringValue = noteString
  270. self.isFold = true
  271. let expand = model?.isExpand ?? false
  272. if expand {
  273. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  274. } else {
  275. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  276. }
  277. self.foldButton.isHidden = false
  278. self.contentViewHidden(false)
  279. } else if data.isKind(of: CPDFStampAnnotation.self) {
  280. if data.isKind(of: CSelfSignAnnotation.self) {
  281. let newAnnotation = note as! CSelfSignAnnotation
  282. let type = newAnnotation.annotationType
  283. var returnString = ""
  284. if (type == .signFalse) {
  285. returnString = KMLocalizedString("X", comment: nil)
  286. } else if (type == .signature) {
  287. returnString = KMLocalizedString("Check mark", comment: nil)
  288. } else if (type == .signCircle) {
  289. returnString = KMLocalizedString("Circle", comment: nil)
  290. } else if (type == .signLine) {
  291. returnString = KMLocalizedString("Line", comment: nil)
  292. } else if (type == .signDot) {
  293. returnString = KMLocalizedString("Dot", comment: nil)
  294. } else if (type == .signText) {
  295. returnString = KMLocalizedString("Text", comment: nil)
  296. }
  297. self.annotationContentLabel.stringValue = returnString
  298. self.isFold = !(model?.isExpand ?? false)
  299. } else {
  300. self.annotationContentLabel.isHidden = true
  301. self.isFold = true
  302. let expand = model?.isExpand ?? false
  303. if expand {
  304. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  305. } else {
  306. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  307. }
  308. self.noteImageView.isHidden = false
  309. let anno = note as! CPDFStampAnnotation
  310. self.noteImageView.image = anno.stampImage()
  311. self.foldButton.isHidden = false
  312. self.contentViewHidden(false)
  313. }
  314. } else if let anno = data as? CPDFSignatureAnnotation {
  315. self.annotationContentLabel.isHidden = true
  316. self.noteImageView.isHidden = false
  317. self.noteImageView.image = anno.signImage
  318. self.foldButton.isHidden = false
  319. let expand = model?.isExpand ?? false
  320. if expand {
  321. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  322. } else {
  323. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  324. }
  325. self.contentViewHidden(false)
  326. } else if data.isKind(of: CPDFTextAnnotation.self) {
  327. self.foldButton.isHidden = false
  328. var noteTilte = noteString
  329. var noteContent = noteTextString
  330. if noteTilte == noteContent {
  331. let textArray = noteTilte.components(separatedBy: " ")
  332. if textArray.count == 2 {
  333. noteTilte = textArray.first ?? ""
  334. noteContent = textArray.last ?? ""
  335. }
  336. }
  337. if noteTilte.isEmpty {
  338. self.annotationContentLabel.stringValue = noteContent
  339. }else{
  340. self.annotationContentLabel.stringValue = noteTilte
  341. self.noteContentLabel.stringValue = noteContent
  342. }
  343. self.isFold = true
  344. let expand = model?.isExpand ?? false
  345. if expand {
  346. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  347. } else {
  348. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  349. }
  350. if (noteString.isEmpty == false || noteContent.isEmpty == false) {
  351. self.contentView.isHidden = false
  352. } else {
  353. self.contentView.isHidden = true
  354. self.contentViewHidden(true)
  355. }
  356. if (noteTextString.isEmpty == false) {
  357. self.foldButton.isHidden = false
  358. }else{
  359. self.foldButton.isHidden = true
  360. }
  361. } else if data is CPDFRedactAnnotation {
  362. noteString = KMLocalizedString("Redact")
  363. self.annotationContentLabel.stringValue = noteString
  364. } else if data is CPDFInkAnnotation {
  365. self.foldButton.isHidden = false
  366. self.isFold = true
  367. let expand = model?.isExpand ?? false
  368. if expand {
  369. self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor)
  370. } else {
  371. self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor")
  372. }
  373. noteString = data.contents ?? ""
  374. self.annotationContentLabel.stringValue = noteString
  375. self.contentViewHidden(false)
  376. } else if data is CPDFFreeTextAnnotation {
  377. self.annotationContentLabel.stringValue = noteString
  378. self.imageViewHeightConstraint.constant = self.contentView.frame.size.height
  379. self.foldButton.isHidden = false
  380. self.isFold = !(model?.isExpand ?? false)
  381. self.contentViewHidden(false)
  382. } else {
  383. self.annotationContentLabel.stringValue = noteString
  384. self.isFold = !(model?.isExpand ?? false)
  385. self.foldButton.isHidden = false
  386. self.contentViewHidden(false)
  387. }
  388. }
  389. self.autherLayoutConstraint.constant = self.autherLabel.isHidden ? -(self.autherLabel.bounds.size.width) + 10.0 : 10.0
  390. self.typeImageViewLayoutConstraint.constant = self.typeImageView.isHidden ? -(self.typeImageView.bounds.size.width) : 0.0
  391. self.contentBoxLayoutConstraint.constant = self.noteContentBox.isHidden ? -(self.noteContentBox.bounds.size.height+8.0) : 8.0
  392. self.needsLayout = true
  393. }
  394. }