// // KMNoteTableViewCell.swift // PDF Reader Pro // // Created by tangchao on 2023/11/17. // import Cocoa import KMComponentLibrary class KMNoteTableViewCell: NSTableCellView { @IBOutlet var topView: NSView! @IBOutlet var typeImageView: NSImageView! @IBOutlet var autherLabel: NSTextField! @IBOutlet var timeLabel: NSTextField! @IBOutlet var pageLabel: NSTextField! @IBOutlet var contentView: NSView! @IBOutlet var foldButton: NSButton! @IBOutlet var annotationContentLabel: NSTextField! @IBOutlet var noteContentBox: NSBox! @IBOutlet var noteContentLabel: NSTextField! @IBOutlet var noteImageView: NSImageView! @IBOutlet var markupButton: KMCoverButton! @IBOutlet var topViewLayoutConstraint: NSLayoutConstraint! @IBOutlet var autherLayoutConstraint: NSLayoutConstraint! @IBOutlet var typeImageViewLayoutConstraint: NSLayoutConstraint! @IBOutlet var contentBoxLayoutConstraint: NSLayoutConstraint! @IBOutlet var maltlineLabelLayoutConstraint: NSLayoutConstraint! @IBOutlet var imageViewHeightConstraint: NSLayoutConstraint! @IBOutlet var pageLabelWidthConstraint: NSLayoutConstraint! @IBOutlet var noteContentHeightConstraint: NSLayoutConstraint! @IBOutlet var contentViewTopConstraint: NSLayoutConstraint! @IBOutlet var pageLabelTopConstraint: NSLayoutConstraint! @IBOutlet var timeWidthConstraint: NSLayoutConstraint! @IBOutlet var annotationContentWidthConstraint: NSLayoutConstraint! var state: CPDFAnnotationState = .unMarked { didSet { if self.state == .unMarked { self.markupButton.image = NSImage(named: "KMNImageNameBotaNoteMarkup") } else { self.markupButton.image = NSImage(named: "KMNImageNameBotaNoteMarkSelected") } } } var isFold = false { didSet { if (self.isFold) { self.maltlineLabelLayoutConstraint.constant = 18.0 self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") self.noteContentBox.isHidden = true self.imageViewHeightConstraint.constant = 18.0 + 8 } else { let labelheight = self._heightForNoteStringValue(self.annotationContentLabel.stringValue, maxW: NSWidth(self.annotationContentLabel.bounds)) self.maltlineLabelLayoutConstraint.constant = labelheight + 18.0 self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) self.noteContentBox.isHidden = false let noteLabelHeight = self._heightForNoteStringValue(self.noteContentLabel.stringValue, maxW: NSWidth(self.noteContentLabel.bounds)) self.noteContentHeightConstraint.constant = noteLabelHeight self.imageViewHeightConstraint.constant = self.maltlineLabelLayoutConstraint.constant + self.noteContentHeightConstraint.constant + 25 } self.contentBoxLayoutConstraint.constant = self.noteContentBox.isHidden ? -(self.noteContentBox.bounds.size.height+8.0) : 8.0 } } weak var cellNote: CPDFAnnotation? var isUnFoldNote: ((_ cellNote: CPDFAnnotation?, _ isUnfold: Bool)->Void)? var renameActionCallback: ((_ count: String)->Void)? var itemClick: KMCommonClickBlock? var model: KMBotaAnnotationModel? { didSet { self.updateView(self.model) } } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Drawing code here. } deinit { NotificationCenter.default.removeObserver(self) } override func awakeFromNib() { super.awakeFromNib() self.wantsLayer = true self.layer?.backgroundColor = .clear self.layer?.cornerRadius = 0.0 self.autherLabel.wantsLayer = true self.autherLabel.layer?.backgroundColor = KMAppearance.Else.textTagColor().cgColor self.autherLabel.layer?.cornerRadius = 1.0 self.autherLabel.textColor = KMAppearance.Layout.h0Color() self.timeLabel.lineBreakMode = .byTruncatingTail self.timeLabel.cell?.truncatesLastVisibleLine = true self.pageLabel.textColor = KMAppearance.Layout.h2Color() self.noteContentLabel.isEditable = false self.noteContentBox.borderWidth = 1.0 self.noteContentBox.borderColor = KMAppearance.Layout.b15Color() self.noteContentBox.cornerRadius = 1.0 self.noteContentBox.fillColor = KMAppearance.Layout.l1Color() self.annotationContentLabel.cell?.truncatesLastVisibleLine = true self.annotationContentLabel.isEditable = false self.markupButton.toolTip = KMLocalizedString("Add or Remove Marked") self.markupButton.target = self self.markupButton.action = #selector(markupAction) self.markupButton.coverAction = { [weak self] btn, act in let state = self?.state ?? .unMarked if state == .unMarked { btn.image = NSImage(named: "KMNImageNameBotaNoteMarkup") } else { btn.image = NSImage(named: "KMNImageNameBotaNoteMarkSelected") } } NotificationCenter.default.addObserver(self, selector: #selector(_contextDidEndEdit), name: NSControl.textDidEndEditingNotification, object: self.annotationContentLabel) self.isFold = true NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: APPAppearanceChangedNotificationName, object: nil) updateUI() } @objc func updateUI() { self.timeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/3") self.noteContentLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") self.annotationContentLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") self.timeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-xs-regular") self.noteContentLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular") self.annotationContentLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular") } func contentViewHidden(_ hidden: Bool) { self.contentViewTopConstraint.constant = hidden ? -(NSHeight(self.topView.bounds)) : 8.0 self.pageLabelTopConstraint.constant = -(NSHeight(self.topView.bounds)-4) } @IBAction func foldButtonAction(_ sender: NSButton) { let anno = self.model?.anno if anno is CPDFStampAnnotation || anno is CPDFInkAnnotation || anno is CPDFSignatureAnnotation || anno is CPDFTextAnnotation { let expand = model?.isExpand ?? false if expand { self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") } else { self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) } if (self.isUnFoldNote != nil) { self.isUnFoldNote!(self.cellNote, !expand) } } else { self.isFold = !self.isFold if (self.isUnFoldNote != nil) { self.isUnFoldNote!(self.cellNote, !self.isFold) } } } override func layout() { super.layout() let showAuthor = self.model?.showAuthor ?? false let authorW = showAuthor ? self.autherLabel.frame.size.width : 0 let showTime = self.model?.showTime ?? false if showTime { let x: CGFloat = 16 + 10 + authorW let width = NSWidth(self.topView.frame) self.timeWidthConstraint.constant = width - x } let foldW: CGFloat = 16 let contentW = NSWidth(self.contentView.frame) self.annotationContentWidthConstraint.constant = contentW-foldW } @objc func markupAction() { self.itemClick?(1) } } // MARK: - Private Methods extension KMNoteTableViewCell { @objc private func _contextDidEndEdit(_ notification: NSNotification) { let textField = notification.object as? NSTextField if self.noteContentLabel.isEqual(to: textField) { if (self.renameActionCallback != nil) { self.renameActionCallback!(textField!.stringValue) } } } private func _equalToImage(_ image: NSImage?) -> Bool { guard let _image = image else { return false } let imageData1 = _image.tiffRepresentation let imageData2 = NSImage(named: "KMImageNameBtnTriRightNor")?.tiffRepresentation return imageData1 == imageData2 } private func _heightForNoteStringValue(_ text: String, maxW: CGFloat) -> CGFloat { let maxSize = NSMakeSize(maxW, CGFloat(MAXFLOAT)) let opts: NSString.DrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading] let style = NSMutableParagraphStyle() style.lineBreakMode = .byWordWrapping let attributes: [NSAttributedString.Key : Any] = [.font : NSFont.systemFont(ofSize: 14), .paragraphStyle : style] let rect = text.boundingRect(with: maxSize, options: opts, attributes: attributes) return rect.size.height } private func updateView(_ model: KMBotaAnnotationModel?) { let note = model?.anno let noteColor = note?.color let noteType = KMPDFAnnotationNoteType(note) var noteString = "" if let data = note { noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: data) } let pageString = "\((note?.page?.pageIndex() ?? 0) + 1)" var dateString = "" if let date = note?.modificationDate() { dateString = KMTools.timeString(timeDate: date) } let authorString = note?.userName() ?? "" let noteTextString = note?.string() ?? "" let showTime = model?.showTime ?? true if showTime { self.timeLabel.stringValue = dateString self.timeLabel.lineBreakMode = .byTruncatingTail self.timeLabel.cell?.truncatesLastVisibleLine = true self.timeLabel.isHidden = false self.timeLabel.toolTip = dateString } else { self.timeLabel.isHidden = true } let showPage = model?.showPage ?? true if showPage { self.pageLabel.stringValue = pageString let labelsize = self.pageLabel.stringValue.boundingRect(with: NSMakeSize(CGFloat(MAXFLOAT),NSHeight(self.pageLabel.bounds)), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font : NSFont.systemFont(ofSize: 12)]) self.pageLabelWidthConstraint.constant = labelsize.size.width + 5 } else { self.pageLabel.isHidden = true } let showAuthor = model?.showAuthor ?? true if showAuthor { self.autherLabel.stringValue = authorString self.autherLabel.isHidden = false self.autherLabel.sizeToFit() } else { self.autherLabel.isHidden = true } let imageView = KMNoteTypeImageView() self.typeImageView.image = imageView.noteTypeImage(withType: noteType, color: noteColor ?? .red) self.typeImageView.isHidden = false self.noteContentBox.isHidden = true self.noteImageView.isHidden = true self.foldButton.isHidden = true self.annotationContentLabel.isHidden = false self.noteContentLabel.stringValue = noteString self.contentView.isHidden = false self.contentViewHidden(false) // 默认折叠 let isFold = model?.isFold() ?? true if let data = note { if data.isKind(of: CPDFMarkupAnnotation.self) { let markup = data as! CPDFMarkupAnnotation var contentString = KMBOTAAnnotationTool.fetchText(text: markup.markupContent()) contentString = contentString.replacingOccurrences(of: "\r", with: "") contentString = contentString.replacingOccurrences(of: "\n", with: "") self.noteContentLabel.stringValue = contentString self.foldButton.isHidden = false // } let attributeStr = NSMutableAttributedString(string: noteString) if (markup.markupType() == .highlight) { attributeStr.addAttribute(.backgroundColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length)) } else if (markup.markupType() == .strikeOut) { attributeStr.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeStr.length)) attributeStr.addAttribute(.strikethroughColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length)) } else if (markup.markupType() == .underline) { attributeStr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeStr.length)) attributeStr.addAttribute(.underlineColor, value: noteColor as Any, range: NSMakeRange(0, attributeStr.length)) } self.annotationContentLabel.attributedStringValue = attributeStr self.isFold = !(model?.isExpand ?? false) self.contentViewHidden(false) } else if data.isKind(of: CPDFLineAnnotation.self) || noteType == SKNSquareString || noteType == SKNCircleString { // || noteType == SKNInkString noteString = data.contents ?? "" if let anno = data as? CPDFLineAnnotation, anno.isMeasure { noteString = anno.string() ?? "" } self.annotationContentLabel.stringValue = noteString self.isFold = true let expand = model?.isExpand ?? false if expand { self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) } else { self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") } self.foldButton.isHidden = false self.contentViewHidden(false) } else if data.isKind(of: CPDFStampAnnotation.self) { if data.isKind(of: CSelfSignAnnotation.self) { let newAnnotation = note as! CSelfSignAnnotation let type = newAnnotation.annotationType var returnString = "" if (type == .signFalse) { returnString = KMLocalizedString("X", comment: nil) } else if (type == .signature) { returnString = KMLocalizedString("Check mark", comment: nil) } else if (type == .signCircle) { returnString = KMLocalizedString("Circle", comment: nil) } else if (type == .signLine) { returnString = KMLocalizedString("Line", comment: nil) } else if (type == .signDot) { returnString = KMLocalizedString("Dot", comment: nil) } else if (type == .signText) { returnString = KMLocalizedString("Text", comment: nil) } self.annotationContentLabel.stringValue = returnString self.isFold = !(model?.isExpand ?? false) } else { self.annotationContentLabel.isHidden = true self.isFold = true let expand = model?.isExpand ?? false if expand { self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) } else { self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") } self.noteImageView.isHidden = false let anno = note as! CPDFStampAnnotation self.noteImageView.image = anno.stampImage() self.foldButton.isHidden = false self.contentViewHidden(false) } } else if let anno = data as? CPDFSignatureAnnotation { self.annotationContentLabel.isHidden = true self.noteImageView.isHidden = false self.noteImageView.image = anno.signImage self.foldButton.isHidden = false let expand = model?.isExpand ?? false if expand { self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) } else { self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") } self.contentViewHidden(false) } else if data.isKind(of: CPDFTextAnnotation.self) { self.foldButton.isHidden = false var noteTilte = noteString var noteContent = noteTextString if noteTilte == noteContent { let textArray = noteTilte.components(separatedBy: " ") if textArray.count == 2 { noteTilte = textArray.first ?? "" noteContent = textArray.last ?? "" } } if noteTilte.isEmpty { self.annotationContentLabel.stringValue = noteContent }else{ self.annotationContentLabel.stringValue = noteTilte self.noteContentLabel.stringValue = noteContent } self.isFold = true let expand = model?.isExpand ?? false if expand { self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) } else { self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") } if (noteString.isEmpty == false || noteContent.isEmpty == false) { self.contentView.isHidden = false } else { self.contentView.isHidden = true self.contentViewHidden(true) } if (noteTextString.isEmpty == false) { self.foldButton.isHidden = false }else{ self.foldButton.isHidden = true } } else if data is CPDFRedactAnnotation { noteString = KMLocalizedString("Redact") self.annotationContentLabel.stringValue = noteString } else if data is CPDFInkAnnotation { self.foldButton.isHidden = false self.isFold = true let expand = model?.isExpand ?? false if expand { self.foldButton.image = NSImage(named: KMImageNameUXIconBtnTriDownNor) } else { self.foldButton.image = NSImage(named: "KMImageNameBtnTriRightNor") } noteString = data.contents ?? "" self.annotationContentLabel.stringValue = noteString self.contentViewHidden(false) } else if data is CPDFFreeTextAnnotation { self.annotationContentLabel.stringValue = noteString self.imageViewHeightConstraint.constant = self.contentView.frame.size.height self.foldButton.isHidden = false self.isFold = !(model?.isExpand ?? false) self.contentViewHidden(false) } else { self.annotationContentLabel.stringValue = noteString self.isFold = !(model?.isExpand ?? false) self.foldButton.isHidden = false self.contentViewHidden(false) } } self.autherLayoutConstraint.constant = self.autherLabel.isHidden ? -(self.autherLabel.bounds.size.width) + 10.0 : 10.0 self.typeImageViewLayoutConstraint.constant = self.typeImageView.isHidden ? -(self.typeImageView.bounds.size.width) : 0.0 self.contentBoxLayoutConstraint.constant = self.noteContentBox.isHidden ? -(self.noteContentBox.bounds.size.height+8.0) : 8.0 self.needsLayout = true } }