KMNoteTableViewCell.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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, NibLoadable {
  10. @IBOutlet var topView: NSView!
  11. @IBOutlet var typeImageView: NSImageView!
  12. @IBOutlet var autherLabel: NSTextField!
  13. @IBOutlet var timeLabel: NSTextField!
  14. @IBOutlet var contentView: NSView!
  15. @IBOutlet var foldButton: NSButton!
  16. @IBOutlet var annotationContentLabel: NSTextField!
  17. @IBOutlet var noteImageView: NSImageView!
  18. @IBOutlet var markupButton: KMCoverButton!
  19. @IBOutlet var autherLayoutConstraint: NSLayoutConstraint!
  20. @IBOutlet var maltlineLabelLayoutConstraint: NSLayoutConstraint!
  21. // @IBOutlet var contentViewTopConstraint: NSLayoutConstraint!
  22. @IBOutlet var timeWidthConstraint: NSLayoutConstraint!
  23. @IBOutlet var annotationContentWidthConstraint: NSLayoutConstraint!
  24. @IBOutlet weak var contentHeightConstraint: NSLayoutConstraint!
  25. var state: CPDFAnnotationState = .unMarked {
  26. didSet {
  27. if self.state == .unMarked {
  28. self.markupButton.image = NSImage(named: "KMNImageNameBotaNoteMarkup")
  29. } else {
  30. self.markupButton.image = NSImage(named: "KMNImageNameBotaNoteMarkSelected")
  31. }
  32. }
  33. }
  34. var isFold = false {
  35. didSet {
  36. if (self.isFold) {
  37. self.maltlineLabelLayoutConstraint.constant = 18.0
  38. } else {
  39. let labelheight = self._heightForNoteStringValue(self.annotationContentLabel.stringValue, maxW: NSWidth(self.annotationContentLabel.bounds))
  40. self.maltlineLabelLayoutConstraint.constant = labelheight + 18.0
  41. }
  42. self.updateFoldButtonImage(isFold: !self.isFold)
  43. }
  44. }
  45. weak var cellNote: CPDFAnnotation?
  46. var isUnFoldNote: ((_ cellNote: CPDFAnnotation?, _ isUnfold: Bool)->Void)?
  47. var renameActionCallback: ((_ count: String)->Void)?
  48. var itemClick: KMCommonClickBlock?
  49. var model: KMBotaAnnotationModel? {
  50. didSet {
  51. self.reloadData(self.model)
  52. self.updateView(self.model)
  53. }
  54. }
  55. override func draw(_ dirtyRect: NSRect) {
  56. super.draw(dirtyRect)
  57. // Drawing code here.
  58. }
  59. deinit {
  60. NotificationCenter.default.removeObserver(self)
  61. }
  62. override func awakeFromNib() {
  63. super.awakeFromNib()
  64. self.setup()
  65. }
  66. func setup() {
  67. self.wantsLayer = true
  68. self.layer?.backgroundColor = .clear
  69. self.layer?.cornerRadius = 0.0
  70. self.annotationContentLabel.cell?.truncatesLastVisibleLine = true
  71. self.annotationContentLabel.isEditable = false
  72. self.annotationContentLabel.isSelectable = false
  73. self.markupButton.toolTip = KMLocalizedString("Add or Remove Marked")
  74. self.markupButton.target = self
  75. self.markupButton.action = #selector(markupAction)
  76. self.markupButton.coverAction = { [weak self] btn, act in
  77. let state = self?.state ?? .unMarked
  78. if state == .unMarked {
  79. btn.image = NSImage(named: "KMNImageNameBotaNoteMarkup")
  80. } else {
  81. btn.image = NSImage(named: "KMNImageNameBotaNoteMarkSelected")
  82. }
  83. }
  84. self.isFold = true
  85. updateUI()
  86. self.addNotification()
  87. }
  88. @objc func updateUI() {
  89. self.autherLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1")
  90. self.autherLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  91. self.timeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/3")
  92. self.timeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-xs-regular")
  93. self.annotationContentLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  94. self.annotationContentLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular")
  95. }
  96. func addNotification() {
  97. NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: APPAppearanceChangedNotificationName, object: nil)
  98. }
  99. func reloadData(_ model: KMBotaAnnotationModel?) {
  100. guard let model = model else { return }
  101. guard let annotation = model.anno else { return }
  102. self.contentHeightConstraint.constant = 36 + 8 + 8 + 28
  103. }
  104. func contentViewHidden(_ hidden: Bool) {
  105. }
  106. }
  107. //MARK: Action
  108. extension KMNoteTableViewCell {
  109. @IBAction func foldButtonAction(_ sender: NSButton) {
  110. let anno = self.model?.anno
  111. if anno is CPDFStampAnnotation ||
  112. anno is CPDFInkAnnotation ||
  113. anno is CPDFSignatureAnnotation ||
  114. anno is CPDFTextAnnotation {
  115. let expand = model?.isExpand ?? false
  116. self.isFold = !self.isFold
  117. if (self.isUnFoldNote != nil) {
  118. self.isUnFoldNote!(self.cellNote, !expand)
  119. }
  120. } else {
  121. self.isFold = !self.isFold
  122. if (self.isUnFoldNote != nil) {
  123. self.isUnFoldNote!(self.cellNote, !self.isFold)
  124. }
  125. }
  126. }
  127. @objc func markupAction() {
  128. self.itemClick?(1)
  129. }
  130. }
  131. // MARK: - Private Methods
  132. extension KMNoteTableViewCell {
  133. private func _equalToImage(_ image: NSImage?) -> Bool {
  134. guard let _image = image else {
  135. return false
  136. }
  137. let imageData1 = _image.tiffRepresentation
  138. let imageData2 = NSImage(named: "KMImageNameBtnTriRightNor")?.tiffRepresentation
  139. return imageData1 == imageData2
  140. }
  141. private func _heightForNoteStringValue(_ text: String, maxW: CGFloat) -> CGFloat {
  142. let maxSize = NSMakeSize(maxW, CGFloat(MAXFLOAT))
  143. let opts: NSString.DrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
  144. let style = NSMutableParagraphStyle()
  145. style.lineBreakMode = .byWordWrapping
  146. let attributes: [NSAttributedString.Key : Any] = [.font : NSFont.systemFont(ofSize: 14), .paragraphStyle : style]
  147. let rect = text.boundingRect(with: maxSize, options: opts, attributes: attributes)
  148. return rect.size.height
  149. }
  150. private func updateView(_ model: KMBotaAnnotationModel?) {
  151. let note = model?.anno
  152. let noteColor = note?.color
  153. let noteType = KMPDFAnnotationNoteType(note)
  154. var noteString = ""
  155. if let data = note {
  156. noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: data)
  157. }
  158. let pageString = "\((note?.page?.pageIndex() ?? 0) + 1)"
  159. var dateString = ""
  160. if let date = note?.modificationDate() {
  161. dateString = KMTools.timeString(timeDate: date)
  162. }
  163. let authorString = note?.userName() ?? ""
  164. let noteTextString = note?.string() ?? ""
  165. let showTime = model?.showTime ?? true
  166. if showTime {
  167. self.timeLabel.stringValue = dateString
  168. self.timeLabel.lineBreakMode = .byTruncatingTail
  169. self.timeLabel.cell?.truncatesLastVisibleLine = true
  170. self.timeLabel.isHidden = false
  171. self.timeLabel.toolTip = dateString
  172. } else {
  173. self.timeLabel.isHidden = true
  174. }
  175. let showAuthor = model?.showAuthor ?? true
  176. if showAuthor {
  177. self.autherLabel.stringValue = authorString
  178. self.autherLabel.isHidden = false
  179. self.autherLabel.sizeToFit()
  180. } else {
  181. self.autherLabel.isHidden = true
  182. }
  183. let imageView = KMNoteTypeImageView()
  184. self.typeImageView.image = imageView.noteTypeImage(withType: noteType, color: noteColor ?? .red)
  185. self.typeImageView.isHidden = false
  186. self.noteImageView.isHidden = true
  187. self.foldButton.isHidden = true
  188. self.annotationContentLabel.isHidden = false
  189. self.contentView.isHidden = false
  190. self.contentViewHidden(false)
  191. // 默认折叠
  192. let isFold = model?.isFold() ?? true
  193. if let data = note {
  194. if data.isKind(of: CPDFMarkupAnnotation.self) {
  195. let markup = data as! CPDFMarkupAnnotation
  196. var contentString = KMBOTAAnnotationTool.fetchText(text: markup.markupContent())
  197. contentString = contentString.replacingOccurrences(of: "\r", with: "")
  198. contentString = contentString.replacingOccurrences(of: "\n", with: "")
  199. self.foldButton.isHidden = false
  200. let attributeStr = NSMutableAttributedString(string: noteString)
  201. if (markup.markupType() == .highlight) {
  202. attributeStr.addAttribute(.backgroundColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length))
  203. } else if (markup.markupType() == .strikeOut) {
  204. attributeStr.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeStr.length))
  205. attributeStr.addAttribute(.strikethroughColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length))
  206. } else if (markup.markupType() == .underline) {
  207. attributeStr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeStr.length))
  208. attributeStr.addAttribute(.underlineColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length))
  209. }
  210. self.annotationContentLabel.attributedStringValue = attributeStr
  211. self.contentViewHidden(false)
  212. } else if data.isKind(of: CPDFLineAnnotation.self) || noteType == SKNSquareString || noteType == SKNCircleString { // || noteType == SKNInkString
  213. self.annotationContentLabel.stringValue = noteString
  214. self.foldButton.isHidden = false
  215. self.contentViewHidden(false)
  216. } else if data.isKind(of: CPDFStampAnnotation.self) {
  217. if data.isKind(of: CSelfSignAnnotation.self) {
  218. let newAnnotation = note as! CSelfSignAnnotation
  219. let type = newAnnotation.annotationType
  220. var returnString = ""
  221. if (type == .signFalse) {
  222. returnString = KMLocalizedString("X", comment: nil)
  223. } else if (type == .signature) {
  224. returnString = KMLocalizedString("Check mark", comment: nil)
  225. } else if (type == .signCircle) {
  226. returnString = KMLocalizedString("Circle", comment: nil)
  227. } else if (type == .signLine) {
  228. returnString = KMLocalizedString("Line", comment: nil)
  229. } else if (type == .signDot) {
  230. returnString = KMLocalizedString("Dot", comment: nil)
  231. } else if (type == .signText) {
  232. returnString = KMLocalizedString("Text", comment: nil)
  233. }
  234. self.annotationContentLabel.stringValue = returnString
  235. } else {
  236. self.annotationContentLabel.isHidden = true
  237. self.noteImageView.isHidden = false
  238. let anno = note as! CPDFStampAnnotation
  239. self.noteImageView.image = anno.stampImage()
  240. self.foldButton.isHidden = false
  241. self.contentViewHidden(false)
  242. }
  243. } else if let anno = data as? CPDFSignatureAnnotation {
  244. self.annotationContentLabel.isHidden = true
  245. self.noteImageView.isHidden = false
  246. self.noteImageView.image = anno.signImage
  247. self.foldButton.isHidden = false
  248. self.contentViewHidden(false)
  249. } else if data.isKind(of: CPDFTextAnnotation.self) {
  250. self.foldButton.isHidden = false
  251. var noteTilte = noteString
  252. var noteContent = noteTextString
  253. if noteTilte == noteContent {
  254. let textArray = noteTilte.components(separatedBy: " ")
  255. if textArray.count == 2 {
  256. noteTilte = textArray.first ?? ""
  257. noteContent = textArray.last ?? ""
  258. }
  259. }
  260. if noteTilte.isEmpty {
  261. self.annotationContentLabel.stringValue = noteContent
  262. }else{
  263. self.annotationContentLabel.stringValue = noteTilte
  264. }
  265. if (noteString.isEmpty == false || noteContent.isEmpty == false) {
  266. self.contentView.isHidden = false
  267. } else {
  268. self.contentView.isHidden = true
  269. self.contentViewHidden(true)
  270. }
  271. if (noteTextString.isEmpty == false) {
  272. self.foldButton.isHidden = false
  273. }else{
  274. self.foldButton.isHidden = true
  275. }
  276. } else if data is CPDFRedactAnnotation {
  277. noteString = KMLocalizedString("Redact")
  278. self.annotationContentLabel.stringValue = noteString
  279. } else if data is CPDFInkAnnotation {
  280. self.foldButton.isHidden = false
  281. self.annotationContentLabel.stringValue = noteString
  282. self.contentViewHidden(false)
  283. } else if data is CPDFFreeTextAnnotation {
  284. self.annotationContentLabel.stringValue = noteString
  285. self.foldButton.isHidden = false
  286. self.contentViewHidden(false)
  287. } else {
  288. self.annotationContentLabel.stringValue = noteString
  289. self.foldButton.isHidden = false
  290. self.contentViewHidden(false)
  291. }
  292. }
  293. let expand = model?.isExpand ?? false
  294. self.isFold = !expand
  295. self.autherLayoutConstraint.constant = self.autherLabel.isHidden ? -(self.autherLabel.bounds.size.width) + 10.0 : 10.0
  296. self.needsLayout = true
  297. }
  298. func updateFoldButtonImage(isFold: Bool) {
  299. if isFold {
  300. self.foldButton.image = NSImage(named: "KMImageNameBotaNoteExpand")
  301. } else {
  302. self.foldButton.image = NSImage(named: "KMImageNameBotaNoteNoExpand")
  303. }
  304. }
  305. }