// // KMNoteReplyHanddler.swift // PDF Reader Pro // // Created by User-Tangchao on 2024/9/19. // import Cocoa // 注释回复处理类 func KMPDFAnnotationStateGetString(state: CPDFAnnotationState) -> String? { if state == .none { return NSLocalizedString("None", comment: "") } else if state == .unMarked { return NSLocalizedString("Unmarked", comment: "") } else if state == .marked { return NSLocalizedString("Marked", comment: "") } else if state == .accepted { return NSLocalizedString("Accepted", comment: "") } else if state == .rejected { return NSLocalizedString("Rejected", comment: "") } else if state == .canceled { return NSLocalizedString("Cancelled", comment: "") } else if state == .completed { return NSLocalizedString("Completed", comment: "") } else if state == .error { return NSLocalizedString("Error", comment: "") } return nil } func KMPDFAnnotationStateGetIcon(state: CPDFAnnotationState) -> String? { if state == .none { return "KMImageNameBotaNoteStateNone" } else if state == .unMarked { return "KMImageNameBotaNoteMarkup" } else if state == .marked { return "KMImageNameBotaNoteMarkSelected" } else if state == .accepted { return "KMImageNameBotaNoteStateAccepted" } else if state == .rejected { return "KMImageNameBotaNoteStateRejected" } else if state == .canceled { return "KMImageNameBotaNoteStateCancelled" } else if state == .completed { return "KMImageNameBotaNoteStateCompleted" } else if state == .error { } return nil } class KMNoteReplyPopController: KMHomePopViewController { override func updateUI() { customBox.fillColor = background var widthMax: Float = 0; let subViews: [NSView] = self.customBox.contentView!.subviews for subView in subViews { subView.removeFromSuperview() } for string in self.dataArr ?? [] { if !(string as AnyObject).isEqual(to: KMHorizontalLine) { let width = self.cellContentAdaptiveWidth(string) if widthMax < width { widthMax = width } } } var formTopFloat: Float = 4.0 // for i in (0.. Void in if !isDisabled { if isSelected { // 选中没有 hover 效果 return } if mouseEntered { mouseBox.fillColor = self.enterFillColor } else { mouseBox.fillColor = NSColor.clear } } } box.downCallback = {(downEntered, mouseBox, event) -> Void in if !isDisabled { if downEntered { mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.sel") boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.sel") if let callback = self.downCallback { callback(true, stringValue) } } else { mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.norm") boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.def") } } } if isDisabled { box.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.dis") boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.dis") } else if (isSelected) { if KMAppearance.isDarkMode() { box.fillColor = NSColor(hex: "#227AFF").withAlphaComponent(0.3) } else { box.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.sel") } // boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.sel") } let idx = self.dataArr?.index(of: stringValue) ?? 0 self.viewWillShow?(box, idx) } } class KMNoteReplyHanddler: NSObject { weak var viewC: KMLeftSideViewController? private weak var popover_: NSPopover? func showStatePopView(sender: NSView, anno: CPDFAnnotation?) { if let _ = self.popover_ { return } // let datas = [NSLocalizedString("Accepted", comment: ""), NSLocalizedString("Rejected", comment: ""), NSLocalizedString("Cancelled", comment: ""), NSLocalizedString("Completed", comment: ""), NSLocalizedString("None", comment: "")] let vc = KMNoteReplyPopController(nibName: "KMHomePopViewController", bundle: nil) _ = vc.initWithPopViewDataArr(datas) vc.background = KMAppearance.Layout.bgColor() vc.textColor = KMAppearance.Layout.h0Color() vc.enterFillColor = KMAppearance.Interactive.s0Color() let state = self.fetchReviewState(anno) ?? .none let stateStr = KMPDFAnnotationStateGetString(state: state) ?? NSLocalizedString("None", comment: "") vc.selectedItems = [stateStr] vc.downCallback = { [weak self] result, data in self?.popover_?.close() if data == NSLocalizedString("Accepted", comment: "") { self?.updateAnnoState(anno: anno, state: .accepted) } else if data == NSLocalizedString("Rejected", comment: "") { self?.updateAnnoState(anno: anno, state: .rejected) } else if data == NSLocalizedString("Cancelled", comment: "") { self?.updateAnnoState(anno: anno, state: .canceled) } else if data == NSLocalizedString("Completed", comment: "") { self?.updateAnnoState(anno: anno, state: .completed) } else if data == NSLocalizedString("None", comment: "") { self?.updateAnnoState(anno: anno, state: .none) } FMTrackEventManager.defaultManager.trackEvent(event: "LeftSidebar", withProperties: ["LeftSidebar_Btn": "Btn_LeftSidebar_AnnotationMark"]) self?.viewC?.noteOutlineView.reloadData() } let popover = NSPopover() popover.contentViewController = vc popover.animates = true popover.behavior = .semitransient popover.setValue(true, forKey: "shouldHideAnchor") popover.delegate = self popover.show(relativeTo: sender.bounds, of: sender, preferredEdge: .maxY) self.popover_ = popover } func showReplyMorePopView(sender: NSView, replyModel: KMBotaAnnotationReplyModel?) { if let _ = self.popover_ { return } // let datas = [NSLocalizedString("Edit", comment: ""), NSLocalizedString("Delete", comment: "")] let vc = KMHomePopViewController(nibName: "KMHomePopViewController", bundle: nil) _ = vc.initWithPopViewDataArr(datas) vc.background = KMAppearance.Layout.bgColor() vc.textColor = KMAppearance.Layout.h0Color() vc.enterFillColor = KMAppearance.Interactive.s0Color() vc.downCallback = { [weak self] result, data in self?.popover_?.close() if data == NSLocalizedString("Edit", comment: "") { self?.editReplyAnnotation(replyModel: replyModel) DispatchQueue.main.async { if let row = self?.viewC?.noteOutlineView.row(forItem: replyModel?.annoModel?.footerModel) { self?.viewC?.noteOutlineView.scrollRowToVisible(row) } } } else if data == NSLocalizedString("Delete", comment: "") { if let model = replyModel { self?.removeReplyAnnotation(model.replyAnno) model.annoModel?.replyAnnos.removeObject(model) } } // self?.viewC?.reloadAnnotation() self?.viewC?.noteOutlineView.reloadData() } let popover = NSPopover() popover.contentViewController = vc popover.animates = true popover.behavior = .semitransient popover.setValue(true, forKey: "shouldHideAnchor") popover.delegate = self popover.show(relativeTo: sender.bounds, of: sender, preferredEdge: .maxY) self.popover_ = popover } func markAnnotation(_ anno: CPDFAnnotation?) { guard let replyA = self.fetchMarkAnnotation(anno) else { anno?.createReplyStateAnnotation(.marked) if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } return } if replyA.getAnnotState() == .unMarked { replyA.setAnnotState(.marked) if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } } } func unMarkAnnotation(_ anno: CPDFAnnotation?) { guard let replyA = self.fetchMarkAnnotation(anno) else { return } if replyA.getAnnotState() == .marked { replyA.setAnnotState(.unMarked) if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } } } func updateAnnoState(anno: CPDFAnnotation?, state: CPDFAnnotationState) { guard let theAnno = self.fetchReviewAnnotation(anno) else { anno?.createReplyStateAnnotation(state) if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } return } theAnno.setAnnotState(state) if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } } func createReplyAnnotation(_ anno: CPDFAnnotation?, content: String?, userName: String?) -> CPDFAnnotation? { guard let theAnno = anno else { return nil } let a = theAnno.createReply() a?.contents = content ?? "" a?.setUserName(userName ?? "") a?.bounds = .zero if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } return a } func editReplyAnnotation(replyModel: KMBotaAnnotationReplyModel?) { guard let model = replyModel else { return } model.annoModel?.footerModel?.isExpand = true model.annoModel?.footerModel?.replyModel = model } func removeReplyAnnotation(_ anno: CPDFAnnotation?) { guard let theAnno = anno else { return } theAnno.page.removeAnnotation(theAnno) if let data = self.viewC?.view.window { KMTools.setDocumentEditedState(window: data) } } func fetchReviewState(_ anno: CPDFAnnotation?) -> CPDFAnnotationState? { guard let theAnno = self.fetchReviewAnnotation(anno) else { return nil } return theAnno.getAnnotState() } func fetchAnnoState(_ anno: CPDFAnnotation?) -> CPDFAnnotationState? { guard let replyA = self.fetchMarkAnnotation(anno) else { return nil } return replyA.getAnnotState() } func fetchReplyAnnotations(_ anno: CPDFAnnotation?) -> [CPDFAnnotation]? { guard let theAnno = anno else { return nil } var annos: [CPDFAnnotation] = [] for a in theAnno.replyAnnotations ?? [] { if a.replyAnnotationType == .reply { annos.append(a) } } return annos } func fetchReviewAnnotation(_ anno: CPDFAnnotation?) -> CPDFAnnotation? { guard let theAnno = anno else { return nil } for a in theAnno.replyAnnotations ?? [] { if a.replyAnnotationType == .review { return a } } return nil } func fetchMarkAnnotation(_ anno: CPDFAnnotation?) -> CPDFAnnotation? { guard let theAnno = anno else { return nil } for replyA in theAnno.replyAnnotations ?? [] { if replyA.replyAnnotationType == .mark { return replyA } } return nil } } extension KMNoteReplyHanddler: NSPopoverDelegate { func popoverWillClose(_ notification: Notification) { if let data = self.popover_?.isEqual(to: notification.object), data { self.popover_ = nil } } }