KMNoteReplyHanddler.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. //
  2. // KMNoteReplyHanddler.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by User-Tangchao on 2024/9/19.
  6. //
  7. import Cocoa
  8. // 注释回复处理类
  9. func KMPDFAnnotationStateGetString(state: CPDFAnnotationState) -> String? {
  10. if state == .none {
  11. return KMLocalizedString("None")
  12. } else if state == .unMarked {
  13. return KMLocalizedString("Unmarked")
  14. } else if state == .marked {
  15. return KMLocalizedString("Marked")
  16. } else if state == .accepted {
  17. return KMLocalizedString("Accepted")
  18. } else if state == .rejected {
  19. return KMLocalizedString("Rejected")
  20. } else if state == .canceled {
  21. return KMLocalizedString("Cancelled")
  22. } else if state == .completed {
  23. return KMLocalizedString("Completed")
  24. } else if state == .error {
  25. return KMLocalizedString("Error")
  26. }
  27. return nil
  28. }
  29. func KMPDFAnnotationStateGetIcon(state: CPDFAnnotationState) -> String? {
  30. if state == .none {
  31. return "KMNImageNameBotaNoteStateNone"
  32. } else if state == .unMarked {
  33. return "KMNImageNameBotaNoteMarkup"
  34. } else if state == .marked {
  35. return "KMNImageNameBotaNoteMarkSelected"
  36. } else if state == .accepted {
  37. return "KMNImageNameBotaNoteStateAccepted"
  38. } else if state == .rejected {
  39. return "KMNImageNameBotaNoteStateRejected"
  40. } else if state == .canceled {
  41. return "KMNImageNameBotaNoteStateCancelled"
  42. } else if state == .completed {
  43. return "KMNImageNameBotaNoteStateCompleted"
  44. } else if state == .error {
  45. }
  46. return nil
  47. }
  48. class KMNoteReplyPopController: KMHomePopViewController {
  49. override func updateUI() {
  50. customBox.fillColor = background
  51. var widthMax: Float = 0;
  52. let subViews: [NSView] = self.customBox.contentView!.subviews
  53. for subView in subViews {
  54. subView.removeFromSuperview()
  55. }
  56. for string in self.dataArr ?? [] {
  57. if !(string as AnyObject).isEqual(to: KMHorizontalLine) {
  58. let width = self.cellContentAdaptiveWidth(string)
  59. if widthMax < width {
  60. widthMax = width
  61. }
  62. }
  63. }
  64. var formTopFloat: Float = 4.0
  65. for string in dataArr?.reversed() ?? [] {
  66. if (string as AnyObject).isEqual(to: KMHorizontalLine) {
  67. self.createHonrizontalLineWithFrame(CGRect(x: 12.0, y: CGFloat(formTopFloat), width: CGFloat(widthMax)+23, height: 11))
  68. formTopFloat += 11
  69. } else {
  70. popCellViewDownString = string
  71. createPopViewCellLabelWithFrame(formTopFloat, stringValue: string)
  72. formTopFloat += 32;
  73. }
  74. }
  75. customBoxWidthLayoutConstraint.constant = CGFloat(widthMax+47+60)
  76. customBoxHeightLayoutConstraint.constant = CGFloat(formTopFloat+4.0)
  77. }
  78. override func createPopViewCellLabelWithFrame(_ mas_top: Float, stringValue: String) {
  79. var isDisabled = false
  80. if disItems.contains(stringValue) {
  81. isDisabled = true
  82. }
  83. var isSelected = false
  84. if (isDisabled == false && self.selectedItems.contains(stringValue)) {
  85. isSelected = true
  86. }
  87. let box: KMBox = KMBox(frame: NSZeroRect)
  88. box.boxType = .custom
  89. box.borderWidth = 0.0
  90. box.contentViewMargins = NSMakeSize(0, 0)
  91. box.cornerRadius = 4.0
  92. customBox.contentView?.addSubview(box)
  93. box.mas_makeConstraints { (make) in
  94. make?.leading.equalTo()(12.0)
  95. make?.trailing.equalTo()(-12)
  96. make?.height.equalTo()(32.0)
  97. make?.top.equalTo()(customBox.mas_top)?.offset()(CGFloat(mas_top))
  98. }
  99. let iv = NSImageView()
  100. box.addSubview(iv)
  101. iv.mas_makeConstraints { make in
  102. make?.leading.equalTo()(4)
  103. make?.size.equalTo()(NSSize(width: 20, height: 20))
  104. make?.centerY.equalTo()(0.0)
  105. }
  106. if stringValue == KMLocalizedString("Accepted") {
  107. iv.image = NSImage(named: "KMNImageNameBotaNoteStateAccepted")
  108. } else if stringValue == KMLocalizedString("Rejected") {
  109. iv.image = NSImage(named: "KMNImageNameBotaNoteStateRejected")
  110. } else if stringValue == KMLocalizedString("Cancelled") {
  111. iv.image = NSImage(named: "KMNImageNameBotaNoteStateCancelled")
  112. } else if stringValue == KMLocalizedString("Completed") {
  113. iv.image = NSImage(named: "KMNImageNameBotaNoteStateCompleted")
  114. } else if stringValue == KMLocalizedString("None") {
  115. iv.image = NSImage(named: "KMNImageNameBotaNoteStateNone")
  116. }
  117. let boxLabel: NSTextField = NSTextField.init()
  118. boxLabel.isEditable = false
  119. boxLabel.isBordered = false
  120. boxLabel.stringValue = stringValue
  121. boxLabel.font = NSFont.systemFont(ofSize: 14.0)
  122. boxLabel.translatesAutoresizingMaskIntoConstraints = false
  123. boxLabel.backgroundColor = NSColor.clear
  124. boxLabel.textColor = textColor//NSColor.km_init(hex: "#252629")
  125. box.contentView?.addSubview(boxLabel)
  126. boxLabel.mas_makeConstraints { (make) in
  127. make?.centerY.equalTo()(0.0)
  128. make?.leading.equalTo()(28.0)
  129. }
  130. box.moveCallback = {(mouseEntered: Bool, mouseBox: KMBox) -> Void in
  131. if !isDisabled {
  132. if isSelected { // 选中没有 hover 效果
  133. return
  134. }
  135. if mouseEntered {
  136. mouseBox.fillColor = self.enterFillColor
  137. } else {
  138. mouseBox.fillColor = NSColor.clear
  139. }
  140. }
  141. }
  142. box.downCallback = {(downEntered, mouseBox, event) -> Void in
  143. if !isDisabled {
  144. if downEntered {
  145. if let callback = self.downCallback {
  146. callback(true, stringValue)
  147. }
  148. } else {
  149. }
  150. }
  151. }
  152. let idx = self.dataArr?.index(of: stringValue) ?? 0
  153. self.viewWillShow?(box, idx)
  154. }
  155. }
  156. class KMNoteReplyHanddler: NSObject {
  157. weak var viewC: KMLeftSideViewController?
  158. private weak var popover_: NSPopover?
  159. func showStatePopView(sender: NSView, anno: CPDFAnnotation?) {
  160. if let _ = self.popover_ {
  161. return
  162. }
  163. let datas = [KMLocalizedString("Accepted"),
  164. KMLocalizedString("Rejected"),
  165. KMLocalizedString("Cancelled"),
  166. KMLocalizedString("Completed", comment: ""),
  167. KMLocalizedString("None")]
  168. let vc = KMNoteReplyPopController(nibName: "KMHomePopViewController", bundle: nil)
  169. _ = vc.initWithPopViewDataArr(datas)
  170. vc.background = KMAppearance.Layout.bgColor()
  171. vc.textColor = KMAppearance.Layout.h0Color()
  172. vc.enterFillColor = KMAppearance.Interactive.s0Color()
  173. let state = self.fetchReviewState(anno) ?? .none
  174. let stateStr = KMPDFAnnotationStateGetString(state: state) ?? KMLocalizedString("None")
  175. vc.selectedItems = [stateStr]
  176. vc.downCallback = { [weak self] result, data in
  177. self?.popover_?.close()
  178. if data == KMLocalizedString("Accepted") {
  179. self?.updateAnnoState(anno: anno, state: .accepted)
  180. } else if data == KMLocalizedString("Rejected") {
  181. self?.updateAnnoState(anno: anno, state: .rejected)
  182. } else if data == KMLocalizedString("Cancelled") {
  183. self?.updateAnnoState(anno: anno, state: .canceled)
  184. } else if data == KMLocalizedString("Completed") {
  185. self?.updateAnnoState(anno: anno, state: .completed)
  186. } else if data == KMLocalizedString("None") {
  187. self?.updateAnnoState(anno: anno, state: .none)
  188. }
  189. self?.viewC?.noteOutlineView.reloadData()
  190. }
  191. let popover = NSPopover()
  192. popover.contentViewController = vc
  193. popover.animates = true
  194. popover.behavior = .semitransient
  195. popover.setValue(true, forKey: "shouldHideAnchor")
  196. popover.delegate = self
  197. popover.show(relativeTo: sender.bounds, of: sender, preferredEdge: .maxY)
  198. self.popover_ = popover
  199. }
  200. func showReplyMorePopView(sender: NSView, replyModel: KMBotaAnnotationReplyModel?) {
  201. if let _ = self.popover_ {
  202. return
  203. }
  204. //
  205. let datas = [KMLocalizedString("Edit"), KMLocalizedString("Delete")]
  206. let vc = KMHomePopViewController(nibName: "KMHomePopViewController", bundle: nil)
  207. _ = vc.initWithPopViewDataArr(datas)
  208. vc.background = KMAppearance.Layout.bgColor()
  209. vc.textColor = KMAppearance.Layout.h0Color()
  210. vc.enterFillColor = KMAppearance.Interactive.s0Color()
  211. vc.downCallback = { [weak self] result, data in
  212. self?.popover_?.close()
  213. if data == KMLocalizedString("Edit") {
  214. self?.editReplyAnnotation(replyModel: replyModel)
  215. DispatchQueue.main.async {
  216. }
  217. } else if data == KMLocalizedString("Delete") {
  218. if let model = replyModel {
  219. self?.removeReplyAnnotation(model.replyAnno)
  220. model.annoModel?.replyAnnos.removeObject(model)
  221. }
  222. }
  223. }
  224. let popover = NSPopover()
  225. popover.contentViewController = vc
  226. popover.animates = true
  227. popover.behavior = .semitransient
  228. popover.setValue(true, forKey: "shouldHideAnchor")
  229. popover.delegate = self
  230. popover.show(relativeTo: sender.bounds, of: sender, preferredEdge: .maxY)
  231. self.popover_ = popover
  232. }
  233. func markAnnotation(_ anno: CPDFAnnotation?) {
  234. guard let replyA = self.fetchMarkAnnotation(anno) else {
  235. anno?.createReplyStateAnnotation(.marked)
  236. return
  237. }
  238. if replyA.getAnnotState() == .unMarked {
  239. replyA.setAnnotState(.marked)
  240. }
  241. }
  242. func unMarkAnnotation(_ anno: CPDFAnnotation?) {
  243. guard let replyA = self.fetchMarkAnnotation(anno) else {
  244. return
  245. }
  246. if replyA.getAnnotState() == .marked {
  247. replyA.setAnnotState(.unMarked)
  248. }
  249. }
  250. func updateAnnoState(anno: CPDFAnnotation?, state: CPDFAnnotationState) {
  251. guard let theAnno = self.fetchReviewAnnotation(anno) else {
  252. anno?.createReplyStateAnnotation(state)
  253. return
  254. }
  255. theAnno.setAnnotState(state)
  256. }
  257. func createReplyAnnotation(_ anno: CPDFAnnotation?, content: String?, userName: String?) -> CPDFAnnotation? {
  258. guard let theAnno = anno else {
  259. return nil
  260. }
  261. let a = theAnno.createReply()
  262. a?.contents = content ?? ""
  263. a?.setUserName(userName ?? "")
  264. a?.bounds = .zero
  265. return a
  266. }
  267. func editReplyAnnotation(replyModel: KMBotaAnnotationReplyModel?) {
  268. guard let model = replyModel else {
  269. return
  270. }
  271. model.annoModel?.footerModel?.isExpand = true
  272. model.annoModel?.footerModel?.replyModel = model
  273. }
  274. func editAnnotation(annotationModel: KMBotaAnnotationModel?) {
  275. guard let model = annotationModel else {
  276. return
  277. }
  278. model.footerModel?.isExpand = true
  279. model.footerModel?.editAnnoModel = annotationModel
  280. }
  281. func removeReplyAnnotation(_ anno: CPDFAnnotation?) {
  282. guard let theAnno = anno else {
  283. return
  284. }
  285. theAnno.page.removeAnnotation(theAnno)
  286. }
  287. func fetchReviewState(_ anno: CPDFAnnotation?) -> CPDFAnnotationState? {
  288. guard let theAnno = self.fetchReviewAnnotation(anno) else {
  289. return nil
  290. }
  291. return theAnno.getAnnotState()
  292. }
  293. func fetchAnnoState(_ anno: CPDFAnnotation?) -> CPDFAnnotationState? {
  294. guard let replyA = self.fetchMarkAnnotation(anno) else {
  295. return nil
  296. }
  297. return replyA.getAnnotState()
  298. }
  299. func fetchReplyAnnotations(_ anno: CPDFAnnotation?) -> [CPDFAnnotation]? {
  300. guard let theAnno = anno else {
  301. return nil
  302. }
  303. var annos: [CPDFAnnotation] = []
  304. for a in theAnno.replyAnnotations ?? [] {
  305. if a.replyAnnotationType == .reply {
  306. annos.append(a)
  307. }
  308. }
  309. return annos
  310. }
  311. func fetchReviewAnnotation(_ anno: CPDFAnnotation?) -> CPDFAnnotation? {
  312. guard let theAnno = anno else {
  313. return nil
  314. }
  315. for a in theAnno.replyAnnotations ?? [] {
  316. if a.replyAnnotationType == .review {
  317. return a
  318. }
  319. }
  320. return nil
  321. }
  322. func fetchMarkAnnotation(_ anno: CPDFAnnotation?) -> CPDFAnnotation? {
  323. guard let theAnno = anno else {
  324. return nil
  325. }
  326. for replyA in theAnno.replyAnnotations ?? [] {
  327. if replyA.replyAnnotationType == .mark {
  328. return replyA
  329. }
  330. }
  331. return nil
  332. }
  333. }
  334. extension KMNoteReplyHanddler: NSPopoverDelegate {
  335. func popoverWillClose(_ notification: Notification) {
  336. if let data = self.popover_?.isEqual(to: notification.object), data {
  337. self.popover_ = nil
  338. }
  339. }
  340. }