// // KMNoteTableViewCell.swift // PDF Reader Pro // // Created by tangchao on 2023/11/17. // import Cocoa import KMComponentLibrary class KMNoteTableViewCell: NSTableCellView, NibLoadable { @IBOutlet var topView: NSView! @IBOutlet var typeImageView: NSImageView! @IBOutlet var autherLabel: NSTextField! @IBOutlet var timeLabel: NSTextField! @IBOutlet var contentView: NSView! @IBOutlet var foldButton: NSButton! @IBOutlet var annotationContentLabel: NSTextField! @IBOutlet var noteImageView: NSImageView! @IBOutlet var markupButton: KMCoverButton! @IBOutlet var autherLayoutConstraint: NSLayoutConstraint! @IBOutlet var maltlineLabelLayoutConstraint: NSLayoutConstraint! // @IBOutlet var contentViewTopConstraint: NSLayoutConstraint! @IBOutlet var timeWidthConstraint: NSLayoutConstraint! @IBOutlet var annotationContentWidthConstraint: NSLayoutConstraint! @IBOutlet weak var contentHeightConstraint: 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 } else { let labelheight = self._heightForNoteStringValue(self.annotationContentLabel.stringValue, maxW: NSWidth(self.annotationContentLabel.bounds)) self.maltlineLabelLayoutConstraint.constant = labelheight + 18.0 } self.updateFoldButtonImage(isFold: !self.isFold) } } weak var cellNote: CPDFAnnotation? var isUnFoldNote: ((_ cellNote: CPDFAnnotation?, _ isUnfold: Bool)->Void)? var renameActionCallback: ((_ count: String)->Void)? var itemClick: KMCommonClickBlock? var model: KMBotaAnnotationModel? { didSet { self.reloadData(self.model) 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.setup() } func setup() { self.wantsLayer = true self.layer?.backgroundColor = .clear self.layer?.cornerRadius = 0.0 self.annotationContentLabel.cell?.truncatesLastVisibleLine = true self.annotationContentLabel.isEditable = false self.annotationContentLabel.isSelectable = 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") } } self.isFold = true updateUI() self.addNotification() } @objc func updateUI() { self.autherLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1") self.autherLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") self.timeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/3") self.timeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-xs-regular") self.annotationContentLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") self.annotationContentLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-regular") } func addNotification() { NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: APPAppearanceChangedNotificationName, object: nil) } func reloadData(_ model: KMBotaAnnotationModel?) { guard let model = model else { return } guard let annotation = model.anno else { return } self.contentHeightConstraint.constant = 36 + 8 + 8 + 28 } func contentViewHidden(_ hidden: Bool) { } } //MARK: Action extension KMNoteTableViewCell { @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 self.isFold = !self.isFold if (self.isUnFoldNote != nil) { self.isUnFoldNote!(self.cellNote, !expand) } } else { self.isFold = !self.isFold if (self.isUnFoldNote != nil) { self.isUnFoldNote!(self.cellNote, !self.isFold) } } } @objc func markupAction() { self.itemClick?(1) } } // MARK: - Private Methods extension KMNoteTableViewCell { 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 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.noteImageView.isHidden = true self.foldButton.isHidden = true self.annotationContentLabel.isHidden = false 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.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.contentViewHidden(false) } else if data.isKind(of: CPDFLineAnnotation.self) || noteType == SKNSquareString || noteType == SKNCircleString { // || noteType == SKNInkString self.annotationContentLabel.stringValue = noteString 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 } else { self.annotationContentLabel.isHidden = true 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 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 } 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.annotationContentLabel.stringValue = noteString self.contentViewHidden(false) } else if data is CPDFFreeTextAnnotation { self.annotationContentLabel.stringValue = noteString self.foldButton.isHidden = false self.contentViewHidden(false) } else { self.annotationContentLabel.stringValue = noteString self.foldButton.isHidden = false self.contentViewHidden(false) } } let expand = model?.isExpand ?? false self.isFold = !expand self.autherLayoutConstraint.constant = self.autherLabel.isHidden ? -(self.autherLabel.bounds.size.width) + 10.0 : 10.0 self.needsLayout = true } func updateFoldButtonImage(isFold: Bool) { if isFold { self.foldButton.image = NSImage(named: "KMImageNameBotaNoteExpand") } else { self.foldButton.image = NSImage(named: "KMImageNameBotaNoteNoExpand") } } }