KMLeftSideViewController.swift 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665
  1. //
  2. // KMLeftSideViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lxy on 2022/10/10.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. // MARK: - KMBotaAnnotationHeaderView
  10. class KMBotaAnnotationHeaderView: NSView {
  11. private lazy var titleLabel_: NSTextField = {
  12. let view = NSTextField(labelWithString: "")
  13. view.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-bold")
  14. view.textColor = KMNColorTools.colorText_2()
  15. return view
  16. }()
  17. private lazy var searchButton_: ComponentButton = {
  18. let view = ComponentButton()
  19. let prop = ComponentButtonProperty()
  20. prop.type = .text_gray
  21. prop.size = .xxs
  22. prop.onlyIcon = true
  23. prop.icon = NSImage(named: "KMImageNameOutlineSearch")
  24. view.properties = prop
  25. return view
  26. }()
  27. private lazy var sortButton_: ComponentButton = {
  28. let view = ComponentButton()
  29. let prop = ComponentButtonProperty()
  30. prop.type = .text_gray
  31. prop.size = .xxs
  32. prop.onlyIcon = true
  33. prop.icon = NSImage(named: "KMImageNameBotaSearch")
  34. view.properties = prop
  35. return view
  36. }()
  37. private lazy var moreButton_: ComponentButton = {
  38. let view = ComponentButton()
  39. let prop = ComponentButtonProperty()
  40. prop.type = .text_gray
  41. prop.size = .xxs
  42. prop.onlyIcon = true
  43. prop.icon = NSImage(named: "KMImageNameOutlineMore")
  44. view.properties = prop
  45. return view
  46. }()
  47. private lazy var bottomLine_: NSView = {
  48. let view = NSView()
  49. view.wantsLayer = true
  50. return view
  51. }()
  52. var titleLabel: NSTextField {
  53. get {
  54. return titleLabel_
  55. }
  56. }
  57. var searchButton: ComponentButton {
  58. get {
  59. return searchButton_
  60. }
  61. }
  62. var sortButton: ComponentButton {
  63. get {
  64. return sortButton_
  65. }
  66. }
  67. var moreButton: ComponentButton {
  68. get {
  69. return moreButton_
  70. }
  71. }
  72. var bottomLine: NSView {
  73. get {
  74. return bottomLine_
  75. }
  76. }
  77. var itemClick: KMCommonClickBlock?
  78. convenience init() {
  79. self.init(frame: .init(x: 0, y: 0, width: 300, height: 40))
  80. initSubviews()
  81. }
  82. override func awakeFromNib() {
  83. super.awakeFromNib()
  84. initSubviews()
  85. }
  86. func initSubviews() {
  87. let margin: CGFloat = 12
  88. addSubview(titleLabel_)
  89. titleLabel_.km_add_leading_constraint(constant: margin)
  90. titleLabel_.km_add_centerY_constraint()
  91. let vspace: CGFloat = 8
  92. let wh: CGFloat = 24
  93. addSubview(moreButton_)
  94. moreButton_.km_add_size_constraint(size: .init(width: wh, height: wh))
  95. moreButton_.km_add_trailing_constraint(constant: -margin)
  96. moreButton_.km_add_centerY_constraint()
  97. moreButton_.setTarget(self, action: #selector(moreAction))
  98. addSubview(sortButton_)
  99. sortButton_.km_add_size_constraint(size: .init(width: wh, height: wh))
  100. sortButton_.km_add_trailing_constraint(equalTo: moreButton_, attribute: .leading, constant: -vspace)
  101. sortButton_.km_add_centerY_constraint()
  102. sortButton_.setTarget(self, action: #selector(sortAction))
  103. addSubview(searchButton_)
  104. searchButton_.km_add_size_constraint(size: .init(width: wh, height: wh))
  105. searchButton_.km_add_trailing_constraint(equalTo: sortButton_, attribute: .leading, constant: -vspace)
  106. searchButton_.km_add_centerY_constraint()
  107. searchButton_.setTarget(self, action: #selector(searchAction))
  108. addSubview(bottomLine_)
  109. bottomLine_.km_add_leading_constraint()
  110. bottomLine_.km_add_trailing_constraint()
  111. bottomLine_.km_add_bottom_constraint()
  112. bottomLine_.km_add_height_constraint(constant: 1)
  113. }
  114. @objc func searchAction() {
  115. itemClick?(1)
  116. }
  117. @objc func sortAction() {
  118. itemClick?(2)
  119. }
  120. @objc func moreAction() {
  121. itemClick?(3)
  122. }
  123. }
  124. // MARK: - KMLeftSideViewControllerDelegate
  125. @objc protocol KMLeftSideViewControllerDelegate {
  126. @objc optional func controlStateChange(_ obj: KMLeftSideViewController,show:Bool)
  127. @objc optional func enterEditMode(_ obj: KMLeftSideViewController, _ pages: [Int])
  128. @objc optional func searchAction(searchString:String, isCase:Bool)
  129. @objc optional func controller(controller: KMLeftSideViewController, itemClick item: Any?, itemKey: KMItemKey, params: Any?)
  130. @objc optional func controller(controller: KMLeftSideViewController, rotateType: KMRotateType)
  131. @objc optional func controller(controller: KMLeftSideViewController, listViewSelectionDidChange object: Any?, info: [String : Any]?)
  132. @objc optional func listViewThumPageIndeDidChange(controller: KMLeftSideViewController, pageIndexs: IndexSet)
  133. }
  134. extension KMLeftSideViewController.Key {
  135. static let disableTableToolTipsKey = "SKDisableTableToolTips"
  136. }
  137. class KMLeftSideViewController: KMSideViewController {
  138. @IBOutlet var searchViewController: KMBotaSearchViewController!
  139. @IBOutlet weak var toolButtonBox: NSBox!
  140. @IBOutlet weak var toolButtonBoxLayoutConstraint: NSLayoutConstraint!
  141. @IBOutlet var noteOutlineView: KMNoteOutlineView!
  142. var bookmarkViewC: KMBookMarkViewController?
  143. var noteFilterButtonHoverView = KMHoverView()
  144. var noteFilterSelected = false
  145. var stopRepeatLoad: Bool = false
  146. var dataUpdating = false
  147. @IBOutlet weak var leftListView: NSView!
  148. @IBOutlet weak var toolHeaderBox: NSBox!
  149. var noteFilterMenu:NSMenu?
  150. var isFirst = false //打开阅读页初始化
  151. var isShowPanel : Bool = false
  152. weak var delegate: KMLeftSideViewControllerDelegate?
  153. let noteColumnId = NSUserInterfaceItemIdentifier(rawValue: "note")
  154. let authorColumnId = NSUserInterfaceItemIdentifier(rawValue: "author")
  155. struct Key {}
  156. var mwcFlags: MwcFlags = MwcFlags()
  157. let scalingIncrement: Float = 0.1
  158. var filterButtonLayer: NSView?
  159. // 注释
  160. var noteTypeDict: [String : Any] = [:]
  161. private var _noteSortType: KMNoteSortType = .none
  162. var noteSortType: KMNoteSortType {
  163. get {
  164. return self._noteSortType
  165. }
  166. set {
  167. self._noteSortType = newValue
  168. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "KMAnnotationSortTypeKeyNotification"), object: self)
  169. }
  170. }
  171. var isAscendSort = false {
  172. didSet {
  173. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "KMAnnotationSortTypeKeyNotification"), object: self)
  174. }
  175. }
  176. var headerView: KMNBotaAnnotationHeaderView = {
  177. let view = KMNBotaAnnotationHeaderView()
  178. return view
  179. }()
  180. var isRenameNoteOutline = false
  181. var currentItem: Any?
  182. // 所有注释
  183. var allAnnotations: [CPDFAnnotation] = []
  184. // 注释搜索模式标记
  185. var noteSearchMode = false
  186. // 注释搜索数组
  187. var noteSearchArray: [KMBotaAnnotationBaseModel] = []
  188. // 注释搜索 忽略大小写标识
  189. var caseInsensitiveNoteSearch = false
  190. // 注释列表数据源
  191. var annoListModel: KMAnnotationListModel?
  192. var editNoteTextField: NSTextField?
  193. weak var editNote: CPDFAnnotation?
  194. var leftMargin: CGFloat = 0
  195. var dragIn = false
  196. private var deletePages_ = Set<CPDFPage>()
  197. let userFbHanddler = KMUserFeekbackHanddler()
  198. let noteReplyHanddler = KMNoteReplyHanddler()
  199. var searchGroupView: ComponentGroup?
  200. var searchGroupTarget: ComponentButton?
  201. var moreGroupView: ComponentGroup?
  202. var rightGroupView: ComponentGroup?
  203. var emptyView: ComponentEmpty = {
  204. let view = ComponentEmpty()
  205. view.properties = ComponentEmptyProperty(emptyType: .noAnnotation, state: .normal, image: NSImage(named: "KMImageNameEmptyAnnotate"), text: KMLocalizedString("No Annotations"), subText: "")
  206. return view
  207. }()
  208. private lazy var headerSearchView_: KMNBotaHeaderSearchView? = {
  209. let view = KMNBotaHeaderSearchView.createFromNib()
  210. return view
  211. }()
  212. var headerSearchView: KMNBotaHeaderSearchView? {
  213. get {
  214. return headerSearchView_
  215. }
  216. }
  217. deinit {
  218. KMPrint("KMLeftSideViewController deinit.")
  219. NotificationCenter.default.removeObserver(self)
  220. DistributedNotificationCenter.default().removeObserver(self)
  221. }
  222. override var nibName: NSNib.Name? {
  223. return "LeftSideView"
  224. }
  225. override func viewDidLoad() {
  226. super.viewDidLoad()
  227. noteReplyHanddler.viewC = self
  228. }
  229. override func loadView() {
  230. super.loadView()
  231. searchViewController.loadView()
  232. note_initSubViews()
  233. note_initDefalutValue()
  234. displayEmptyViewAnimating(false)
  235. displayNoteViewAnimating(false)
  236. hideHeaderSearch()
  237. toolHeaderBox.borderWidth = 0
  238. toolHeaderBox.contentView = headerView
  239. headerView.titleLabel.textColor = KMNColorTools.colorText_2()
  240. headerView.itemClick = { [weak self] idx, _ in
  241. if idx == 1 {
  242. self?.showHeaderSearch()
  243. } else if idx == 2 {
  244. self?.showFilterController()
  245. } else if idx == 3 {
  246. self?.showMoreGroupView()
  247. }
  248. }
  249. headerSearchView?.itemClick = { [weak self] idx, params in
  250. if idx == 1 { // 显示搜索限制条件
  251. guard let button = params.first as? ComponentButton else {
  252. return
  253. }
  254. self?.showSearchGroupView(sender: button)
  255. } else if idx == 2 { // 关闭搜索
  256. self?.hideHeaderSearch()
  257. self?.noteSearchMode = false
  258. self?.reloadAnnotation()
  259. }
  260. }
  261. headerSearchView?.valueDidChange = { [weak self] sender, info in
  262. let value = info?[.newKey] as? String ?? ""
  263. self?.noteSearchMode = !value.isEmpty
  264. self?.updateNoteFilterPredicate(searchString: value)
  265. }
  266. }
  267. override func updateUILanguage() {
  268. super.updateUILanguage()
  269. KMMainThreadExecute {
  270. self.headerView.titleLabel.stringValue = KMLocalizedString("Annotation")
  271. }
  272. }
  273. override func updateUIThemeColor() {
  274. super.updateUIThemeColor()
  275. KMMainThreadExecute {
  276. self.headerView.searchButton.properties.icon = NSImage(named: "KMImageNameOutlineSearch")
  277. self.headerView.searchButton.reloadData()
  278. self.headerView.sortButton.properties.icon = NSImage(named: "KMImagenameBotaAnnotationFilter")
  279. self.headerView.sortButton.reloadData()
  280. self.headerView.moreButton.properties.icon = NSImage(named: "KMImageNameOutlineMore")
  281. self.headerView.moreButton.reloadData()
  282. self.headerView.bottomLine.layer?.backgroundColor = KMNColorTools.colorBorder_divider().cgColor
  283. }
  284. }
  285. func showHeaderSearch() {
  286. toolHeaderBox.contentView = headerSearchView
  287. headerView.searchButton.properties.state = .normal
  288. headerView.searchButton.reloadData()
  289. }
  290. func hideHeaderSearch() {
  291. toolHeaderBox.contentView = headerView
  292. }
  293. func updateMarkState(model:KMBotaAnnotationModel){ // Markup
  294. let anno = model.anno
  295. guard let state = self.noteReplyHanddler.fetchAnnoState(anno), state == .marked else {
  296. self.noteReplyHanddler.markAnnotation(anno)
  297. self.noteOutlineView.reloadItem(model)
  298. return
  299. }
  300. self.noteReplyHanddler.unMarkAnnotation(anno)
  301. self.noteOutlineView.reloadItem(model)
  302. }
  303. func updateReplaState(model:KMBotaAnnotationModel,state:CPDFAnnotationState){
  304. let anno = model.anno
  305. noteReplyHanddler.updateAnnoState(anno: anno, state: state)
  306. self.noteOutlineView.reloadData()
  307. }
  308. func updateExpand (model: KMBotaAnnotationModel, isExpand: Bool, item: Any) {
  309. // 将折叠状态记录到模型
  310. model.foldType = isExpand ? .unfold : .fold
  311. model.footerModel?.isExpand = isExpand
  312. model.isExpand = isExpand
  313. self.noteOutlineView.reloadData()
  314. DispatchQueue.main.async {
  315. let row = self.noteOutlineView.row(forItem: item)
  316. self.noteOutlineView.scrollRowToVisible(row)
  317. }
  318. }
  319. // MARK: - Filter
  320. func showFilterController() {
  321. noteFilterAction(nil)
  322. }
  323. // MARK: - Group View
  324. func showMoreGroupView() {
  325. let groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  326. var expand_MenuString = KMLocalizedString("Collapse All")
  327. for secM in self.annoListModel?.datas ?? [] {
  328. if secM.isExpand == false {
  329. expand_MenuString = KMLocalizedString("Expand All")
  330. break
  331. }
  332. }
  333. let hasAnno = self.allAnnotations.count >= 1
  334. let disable = !hasAnno
  335. let expand_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(isDisabled:disable, text: expand_MenuString,
  336. identifier: BOTAMenuIdentifier_Annotation_Expand)
  337. let sort_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Sort"),
  338. identifier: BOTAMenuIdentifier_Annotation_Sort)
  339. var subMenuItemArr: [ComponentMenuitemProperty] = []
  340. let pageProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(isDisabled:disable,text: KMLocalizedString("By Page"),identifier: BOTAMenuIdentifier_Annotation_SortPage)
  341. let timeAscProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(isDisabled:disable,text: KMLocalizedString("By Time - Ascending"),identifier: BOTAMenuIdentifier_Annotation_SortAscTime)
  342. let timeDecProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(
  343. isDisabled:disable,text: KMLocalizedString("By Time - Descending"),identifier: BOTAMenuIdentifier_Annotation_SortDesTime)
  344. subMenuItemArr.append(pageProperty)
  345. subMenuItemArr.append(timeAscProperty)
  346. subMenuItemArr.append(timeDecProperty)
  347. sort_Menuitem.subPropertys = subMenuItemArr
  348. let improt_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Import Annotations"),
  349. identifier: BOTAMenuIdentifier_Annotation_Improt)
  350. let exprot_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(isDisabled:disable,text: KMLocalizedString("Export Annotations"),
  351. identifier: BOTAMenuIdentifier_Annotation_Export)
  352. let rem_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(isDisabled:disable,text: KMLocalizedString("Remove All Annotations"),
  353. identifier: BOTAMenuIdentifier_Annotation_RemoveAll)
  354. let delete_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(isDisabled:disable,text: KMLocalizedString("Delete All Reply"),
  355. identifier: BOTAMenuIdentifier_Annotation_DeleteRep)
  356. var menuItemArr: [ComponentMenuitemProperty] = []
  357. menuItemArr.append(expand_Menuitem)
  358. menuItemArr.append(sort_Menuitem)
  359. menuItemArr.append(improt_Menuitem)
  360. menuItemArr.append(exprot_Menuitem)
  361. menuItemArr.append(rem_Menuitem)
  362. menuItemArr.append(delete_Menuitem)
  363. moreGroupView = groupView
  364. groupView?.groupDelegate = self
  365. groupView?.frame = CGRectMake(310, 0, 200, 36.0 * CGFloat(menuItemArr.count))
  366. groupView?.updateGroupInfo(menuItemArr)
  367. var point = NSPoint(x: CGRectGetMaxX(headerView.moreButton.frame), y: CGRectGetMidY(headerView.moreButton.frame))
  368. point = headerView.convert(point, to: nil)
  369. point.y -= (groupView?.frame.size.height ?? 0)/2
  370. groupView?.showWithPoint(point, relativeTo: headerView.moreButton)
  371. }
  372. func showRightGropView(raw: Int,event:NSEvent,view:NSView?,item:Any) {
  373. let groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  374. currentItem = item
  375. var menuItemArr: [ComponentMenuitemProperty] = []
  376. let rowIndexes = self.selectedObjcNotes()
  377. var viewHeight = 0.0
  378. if(rowIndexes.count == 1) {
  379. let dataItem = rowIndexes.first
  380. if let data = dataItem as? KMBotaAnnotationModel {
  381. if let an = data.anno as? CPDFAnnotation {
  382. if an.isKind(of: CPDFFreeTextAnnotation.self) == true ||
  383. an.isKind(of: CPDFTextAnnotation.self) == true {
  384. } else {
  385. let note_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Edit"),
  386. identifier: BOTAMenuIdentifier_Annotation_EditNote,representedObject: [data])
  387. menuItemArr.append(note_Menuitem)
  388. viewHeight += 36.0
  389. }
  390. let reply_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Reply"),
  391. identifier: BOTAMenuIdentifier_Annotation_AddRep,representedObject: [data])
  392. menuItemArr.append(reply_Menuitem)
  393. viewHeight += 36.0
  394. let state = self.noteReplyHanddler.fetchAnnoState(an) ?? .unMarked
  395. var markString = ""
  396. if state == .unMarked {
  397. markString = KMLocalizedString("Marked")
  398. } else {
  399. markString = KMLocalizedString("Unmarked")
  400. }
  401. let mark_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: markString,
  402. identifier: BOTAMenuIdentifier_Annotation_AddMark,representedObject: [data])
  403. menuItemArr.append(mark_Menuitem)
  404. viewHeight += 36.0
  405. let state_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Status"),
  406. identifier: BOTAMenuIdentifier_Annotation_RepState)
  407. var subMenuItemArr: [ComponentMenuitemProperty] = []
  408. let property0: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("None"),identifier: BOTAMenuIdentifier_Annotation_RepStateNone,representedObject: [data])
  409. let property1: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Accepted"),identifier: BOTAMenuIdentifier_Annotation_RepStateAccepted,representedObject: [data])
  410. let property2: ComponentMenuitemProperty = ComponentMenuitemProperty(
  411. text: KMLocalizedString("Rejected"),identifier: BOTAMenuIdentifier_Annotation_RepStateRejected,representedObject: [data])
  412. let property3: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Cancelled"),identifier: BOTAMenuIdentifier_Annotation_RepStateCancelled,representedObject: [data])
  413. let property4: ComponentMenuitemProperty = ComponentMenuitemProperty(
  414. text: KMLocalizedString("Completed"),identifier: BOTAMenuIdentifier_Annotation_RepStateCompleted,representedObject: [data])
  415. subMenuItemArr.append(property0)
  416. subMenuItemArr.append(property1)
  417. subMenuItemArr.append(property2)
  418. subMenuItemArr.append(property3)
  419. subMenuItemArr.append(property4)
  420. let reviewState = noteReplyHanddler.fetchReviewState(an) ?? .none
  421. if reviewState == .none {
  422. property0.righticon = NSImage(named: "KMNImageNameMenuSelect")
  423. } else if reviewState == .completed {
  424. property4.righticon = NSImage(named: "KMNImageNameMenuSelect")
  425. } else if reviewState == .canceled {
  426. property3.righticon = NSImage(named: "KMNImageNameMenuSelect")
  427. } else if reviewState == .accepted {
  428. property1.righticon = NSImage(named: "KMNImageNameMenuSelect")
  429. } else if reviewState == .rejected {
  430. property2.righticon = NSImage(named: "KMNImageNameMenuSelect")
  431. }
  432. state_Menuitem.subPropertys = subMenuItemArr
  433. menuItemArr.append(state_Menuitem)
  434. viewHeight += 36.0
  435. menuItemArr.append(ComponentMenuitemProperty.divider())
  436. viewHeight += 8.0
  437. var showString = ""
  438. if data.isExpand {
  439. showString = KMLocalizedString("Collapse")
  440. } else {
  441. showString = KMLocalizedString("Expand")
  442. }
  443. let show_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: showString,
  444. identifier: PDFViewMenuIdentifier_Normal_ShowPopUI,representedObject: [data])
  445. menuItemArr.append(show_Menuitem)
  446. viewHeight += 36.0
  447. menuItemArr.append(ComponentMenuitemProperty.divider())
  448. viewHeight += 8.0
  449. if an.isKind(of: CPDFFreeTextAnnotation.self) == true ||
  450. an.isKind(of: CPDFTextAnnotation.self) == true ||
  451. an.isKind(of: CPDFMarkupAnnotation.self) == true {
  452. let copy_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Copy Text"),
  453. identifier: PDFViewMenuIdentifier_Normal_Copy,representedObject: [data])
  454. copy_Menuitem.keyEquivalent = "⌘ C"
  455. menuItemArr.append(copy_Menuitem)
  456. viewHeight += 36.0
  457. }
  458. let delete_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Delete"),
  459. identifier: PDFViewMenuIdentifier_Normal_Delete,representedObject: [data])
  460. delete_Menuitem.keyEquivalent = "⌘ " + "⌫"
  461. menuItemArr.append(delete_Menuitem)
  462. viewHeight += 36.0
  463. }
  464. } else if let replyModel = dataItem as? KMBotaAnnotationReplyModel {
  465. let note_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Edit"),
  466. identifier: BOTAMenuIdentifier_Annotation_EditNote,representedObject: [replyModel])
  467. menuItemArr.append(note_Menuitem)
  468. viewHeight += 36.0
  469. let showString = KMLocalizedString("Collapse")
  470. let show_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: showString,
  471. identifier: PDFViewMenuIdentifier_Normal_ShowPopUI,representedObject: [replyModel])
  472. menuItemArr.append(show_Menuitem)
  473. viewHeight += 36.0
  474. menuItemArr.append(ComponentMenuitemProperty.divider())
  475. viewHeight += 8.0
  476. let copy_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Copy Text"),
  477. identifier: PDFViewMenuIdentifier_Normal_CopyText,representedObject: [replyModel])
  478. copy_Menuitem.keyEquivalent = "⌘ C"
  479. menuItemArr.append(copy_Menuitem)
  480. viewHeight += 36.0
  481. let delete_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Delete"),
  482. identifier: BOTAMenuIdentifier_Annotation_DeleteSignRep,representedObject: [replyModel])
  483. delete_Menuitem.keyEquivalent = "⌘ " + "⌫"
  484. menuItemArr.append(delete_Menuitem)
  485. viewHeight += 36.0
  486. }
  487. } else if rowIndexes.count > 1 {
  488. let delete_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(text: KMLocalizedString("Delete"),
  489. identifier: BOTAMenuIdentifier_Annotation_DeleteMuteRep,representedObject: rowIndexes)
  490. delete_Menuitem.keyEquivalent = "⌘ " + "⌫"
  491. menuItemArr.append(delete_Menuitem)
  492. viewHeight += 36.0
  493. }
  494. rightGroupView = groupView
  495. groupView?.groupDelegate = self
  496. groupView?.frame = CGRectMake(310, 0, 200, viewHeight)
  497. groupView?.updateGroupInfo(menuItemArr)
  498. var point = event.locationInWindow
  499. point.y = point.y - (groupView?.frame.size.height ?? 0)
  500. groupView?.showWithPoint(point, relativeTo: nil)
  501. }
  502. func showSearchGroupView(sender: ComponentButton) {
  503. var viewHeight: CGFloat = 8
  504. var menuItemArr: [ComponentMenuitemProperty] = []
  505. let titles = ["Whole Words","Case Sensitive"]
  506. for i in titles {
  507. let menuI = ComponentMenuitemProperty(text: KMLocalizedString(i))
  508. menuItemArr.append(menuI)
  509. viewHeight += 36
  510. }
  511. if let info = menuItemArr.first {
  512. if KMDataManager.ud_bool(forKey: KMNSearchKey.wholeWords.botaSearch) {
  513. info.righticon = NSImage(named: "KMNImageNameMenuSelect")
  514. }
  515. }
  516. if let info = menuItemArr.last {
  517. if KMDataManager.ud_bool(forKey: KMNSearchKey.caseSensitive.botaSearch) {
  518. info.righticon = NSImage(named: "KMNImageNameMenuSelect")
  519. }
  520. }
  521. let groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  522. searchGroupView = groupView
  523. groupView?.groupDelegate = self
  524. groupView?.frame = CGRectMake(310, 0, 200, viewHeight)
  525. groupView?.updateGroupInfo(menuItemArr)
  526. var point = sender.convert(sender.frame.origin, to: nil)
  527. point.y -= viewHeight
  528. groupView?.showWithPoint(point, relativeTo: sender)
  529. searchGroupTarget = sender
  530. }
  531. func displayNoteViewAnimating(_ animate: Bool) {
  532. searchViewController.contentView = nil
  533. if let data = noteOutlineView.enclosingScrollView {
  534. replaceSideView(data, animate: animate)
  535. }
  536. var frame = noteOutlineView.enclosingScrollView?.frame ?? .zero
  537. frame.origin.y = 0
  538. frame.size.height = noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0
  539. noteOutlineView.enclosingScrollView?.frame = frame
  540. reloadAnnotation()
  541. }
  542. func displayEmptyViewAnimating(_ animate: Bool) {
  543. leftListView.addSubview(emptyView)
  544. emptyView.km_add_top_constraint(constant: 232)
  545. emptyView.km_add_bottom_constraint()
  546. emptyView.km_add_leading_constraint()
  547. emptyView.km_add_trailing_constraint()
  548. emptyView.isHidden = true
  549. }
  550. }
  551. //MARK: Cache
  552. extension KMLeftSideViewController {
  553. func clearNotification() {}
  554. }
  555. // MARK: - Private Methods
  556. extension KMLeftSideViewController {
  557. func updateThumbnail(at index: Int) {
  558. var indexs = IndexSet()
  559. indexs.insert(index)
  560. self.delegate?.listViewThumPageIndeDidChange?(controller: self, pageIndexs: indexs)
  561. }
  562. }
  563. // MARK: - NSOutlineViewDelegate, NSOutlineViewDataSource
  564. extension KMLeftSideViewController: NSOutlineViewDelegate, NSOutlineViewDataSource {
  565. func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
  566. if outlineView.isEqual(to: noteOutlineView) {
  567. if self.noteSearchMode {
  568. var cnt = 0
  569. for model in noteSearchArray {
  570. cnt += 2
  571. guard let data = model as? KMBotaAnnotationModel else {
  572. continue
  573. }
  574. if data.isExpand == false {
  575. continue
  576. }
  577. for replyI in data.replyAnnos {
  578. cnt += 1
  579. }
  580. }
  581. return cnt
  582. }
  583. var cnt = 0
  584. for sectionM in annoListModel?.datas ?? [] {
  585. if sectionM.items.count > 0 {
  586. cnt += 1
  587. if sectionM.isExpand == false {
  588. continue
  589. }
  590. for item in sectionM.items {
  591. cnt += 1
  592. if let annoItem = item as? KMBotaAnnotationModel {
  593. if annoItem.isExpand == false {
  594. continue
  595. }
  596. for replyItem in annoItem.replyAnnos {
  597. cnt += 1
  598. }
  599. }
  600. }
  601. }
  602. }
  603. self.noteOutlineView.usesAlternatingRowBackgroundColors = false
  604. let hasAnno = self.allAnnotations.count >= 1
  605. if(!hasAnno) {
  606. headerView.sortButton.properties.isDisabled = true
  607. headerView.searchButton.properties.isDisabled = true
  608. } else {
  609. headerView.sortButton.properties.isDisabled = false
  610. headerView.searchButton.properties.isDisabled = false
  611. }
  612. headerView.sortButton.reloadData()
  613. headerView.searchButton.reloadData()
  614. headerView.moreButton.reloadData()
  615. if cnt < 1 {
  616. self.showNoteEmptyView()
  617. } else {
  618. self.hideNoteEmptyView()
  619. }
  620. return cnt
  621. }
  622. return 0
  623. }
  624. func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
  625. if outlineView.isEqual(to: self.noteOutlineView) {
  626. if self.noteSearchMode {
  627. var idx = 0
  628. var models: [KMBotaAnnotationBaseModel] = []
  629. for model in self.noteSearchArray {
  630. models.append(model)
  631. if let data = (model as? KMBotaAnnotationModel)?.footerModel {
  632. models.append(data)
  633. }
  634. }
  635. for model in models {
  636. idx += 1
  637. if index + 1 == idx {
  638. return model
  639. }
  640. guard let data = model as? KMBotaAnnotationModel else {
  641. continue
  642. }
  643. if data.isExpand == false {
  644. continue
  645. }
  646. for replyI in data.replyAnnos {
  647. idx += 1
  648. if index + 1 == idx {
  649. return replyI
  650. }
  651. }
  652. }
  653. return 0
  654. }
  655. var idx = 0
  656. var flagSectionM: KMBotaAnnotationSectionModel?
  657. var flagItem: KMBotaAnnotationBaseModel?
  658. var replyIten: KMBotaAnnotationReplyModel?
  659. var isSection = false
  660. var isReply = false
  661. for sectionM in self.annoListModel?.datas ?? [] {
  662. idx += 1
  663. if index + 1 == idx {
  664. flagSectionM = sectionM
  665. isSection = true
  666. break
  667. }
  668. if sectionM.isExpand == false {
  669. continue
  670. }
  671. for item in sectionM.items {
  672. idx += 1
  673. if index + 1 == idx {
  674. flagSectionM = sectionM
  675. flagItem = item
  676. break
  677. }
  678. guard let annoItem = item as? KMBotaAnnotationModel else {
  679. continue
  680. }
  681. if annoItem.isExpand == false {
  682. continue
  683. }
  684. for replyI in annoItem.replyAnnos {
  685. idx += 1
  686. if index + 1 == idx {
  687. flagSectionM = sectionM
  688. flagItem = item
  689. replyIten = replyI
  690. isReply = true
  691. break
  692. }
  693. }
  694. }
  695. }
  696. if isSection {
  697. return flagSectionM
  698. }
  699. if isReply {
  700. return replyIten
  701. }
  702. return flagItem
  703. }
  704. return item as Any
  705. }
  706. func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
  707. if outlineView.isEqual(to: noteOutlineView) {
  708. if let data = item as? KMBotaAnnotationSectionModel {
  709. var cell = outlineView.makeView(withIdentifier: KMAnnotationSectionCellView.km_identifier, owner: nil) as? KMAnnotationSectionCellView
  710. if cell == nil {
  711. cell = KMAnnotationSectionCellView.createFromNib()
  712. }
  713. cell?.isExpand = data.isExpand
  714. if self.noteSortType == .page {
  715. let pageIndex = data.items.first?.anno?.page.pageIndex() ?? 0
  716. cell?.titleLabel.stringValue = KMLocalizedString("Page") + " \(pageIndex + 1)"
  717. } else {
  718. if let date = data.items.first?.anno?.modificationDate() {
  719. let string = KMTools.timeString(timeDate: date, formatString: "yyyy-MM-dd")
  720. cell?.titleLabel.stringValue = string
  721. } else {
  722. cell?.titleLabel.stringValue = ""
  723. }
  724. }
  725. cell?.countLabel.stringValue = "\(data.itemCount / 2)"
  726. cell?.itemClick = {[weak self] idx, _ in
  727. if idx == 1 { // 收取 & 展开
  728. data.isExpand = !data.isExpand
  729. self?.noteOutlineView.reloadData()
  730. }
  731. }
  732. return cell
  733. }
  734. if let data = item as? KMBotaAnnotationFooterModel {
  735. var cell = outlineView.makeView(withIdentifier: KMNoteFooterCellView.km_identifier, owner: self) as? KMNoteFooterCellView
  736. if cell == nil {
  737. cell = KMNoteFooterCellView.createFromNib()
  738. }
  739. cell?.model = data
  740. let state = noteReplyHanddler.fetchReviewState(data.anno) ?? .none
  741. if state == .none {
  742. cell?.operationIv.image = NSImage(named: "KMNImageNameBotaNoteStateNone")
  743. } else if state == .completed {
  744. cell?.operationIv.image = NSImage(named: "KMNImageNameBotaNoteStateCompleted")
  745. } else if state == .canceled {
  746. cell?.operationIv.image = NSImage(named: "KMNImageNameBotaNoteStateCancelled")
  747. } else if state == .accepted {
  748. cell?.operationIv.image = NSImage(named: "KMNImageNameBotaNoteStateAccepted")
  749. } else if state == .rejected {
  750. cell?.operationIv.image = NSImage(named: "KMNImageNameBotaNoteStateRejected")
  751. }
  752. cell?.operationBox.toolTip = KMPDFAnnotationStateGetString(state: state)
  753. if let con = data.replyModel?.replyAnno?.contents, con.isEmpty == false {
  754. cell?.inputTextF.stringValue = con
  755. DispatchQueue.main.async {
  756. self.view.window?.makeFirstResponder(cell?.inputTextF)
  757. }
  758. } else if let con = data.editAnnoModel?.anno?.contents, con.isEmpty == false {
  759. cell?.inputTextF.stringValue = con
  760. DispatchQueue.main.async {
  761. self.view.window?.makeFirstResponder(cell?.inputTextF)
  762. }
  763. } else {
  764. if let cont = data.inputContent {
  765. cell?.inputTextF.stringValue = cont
  766. } else {
  767. cell?.inputTextF.stringValue = ""
  768. }
  769. }
  770. cell?.updateUI(expand: data.isExpand, animated: data.animated)
  771. data.animated = false
  772. if data.isFirstResp {
  773. DispatchQueue.main.async {
  774. self.view.window?.makeFirstResponder(cell?.inputTextF)
  775. }
  776. data.isFirstResp = false
  777. }
  778. cell?.inputDidChanged = { value, _ in
  779. data.inputContent = value as? String ?? ""
  780. }
  781. cell?.itemClick = { [weak self] idx, param in
  782. if idx == 1 { // comment
  783. data.isExpand = !data.isExpand
  784. data.animated = true
  785. data.isFirstResp = true
  786. self?.noteOutlineView.reloadItem(data)
  787. } else if idx == 2 { //
  788. self?.noteReplyHanddler.showStatePopView(sender: param.first as! NSView, anno: data.anno)
  789. } else if idx == 3 { // reply
  790. let content = param.first as? String ?? ""
  791. if content.isEmpty {
  792. return
  793. }
  794. if let con = data.replyModel?.replyAnno?.contents, con.isEmpty == false { // 编辑
  795. let model = data.replyModel
  796. model?.replyAnno?.contents = content
  797. model?.replyAnno?.setUserName(KMPreference.shared.author)
  798. model?.replyAnno?.setModificationDate(Date())
  799. // 置空编辑状态
  800. data.replyModel = nil
  801. data.inputContent = nil
  802. self?.noteOutlineView.reloadData()
  803. if let row = self?.noteOutlineView.row(forItem: data) {
  804. self?.noteOutlineView.scrollRowToVisible(row)
  805. }
  806. return
  807. }
  808. if let con = data.editAnnoModel?.anno?.contents, con.isEmpty == false { // 编辑
  809. let model = data.editAnnoModel
  810. model?.anno?.contents = content
  811. model?.anno?.setUserName(KMPreference.shared.author)
  812. // 置空编辑状态
  813. data.replyModel = nil
  814. data.inputContent = nil
  815. data.editAnnoModel = nil
  816. self?.noteOutlineView.reloadData()
  817. if let row = self?.noteOutlineView.row(forItem: data) {
  818. self?.noteOutlineView.scrollRowToVisible(row)
  819. }
  820. return
  821. }
  822. if let replyAnno = self?.noteReplyHanddler.createReplyAnnotation(data.anno, content: content, userName: KMPreference.shared.author) {
  823. let model = KMBotaAnnotationReplyModel()
  824. model.anno = data.anno
  825. model.replyAnno = replyAnno
  826. model.annoModel = data.annoModel
  827. data.annoModel?.replyAnnos.append(model)
  828. data.inputContent = nil
  829. self?.noteOutlineView.reloadData()
  830. if let row = self?.noteOutlineView.row(forItem: data) {
  831. self?.noteOutlineView.scrollRowToVisible(row)
  832. }
  833. }
  834. } else if idx == 4 { // cancel
  835. data.isExpand = false
  836. data.replyModel = nil
  837. data.inputContent = nil
  838. self?.noteOutlineView.reloadItem(data)
  839. }
  840. }
  841. return cell
  842. }
  843. if let data = item as? KMBotaAnnotationReplyModel {
  844. var cell = outlineView.makeView(withIdentifier: KMNoteReplyCellView.km_identifier, owner: self) as? KMNoteReplyCellView
  845. if cell == nil {
  846. cell = KMNoteReplyCellView.createFromNib()
  847. }
  848. cell?.model = data
  849. cell?.titleLabel.stringValue = data.replyAnno?.userName() ?? ""
  850. if let data = data.replyAnno?.modificationDate() {
  851. cell?.timeLabel.stringValue = KMTools.timeString(timeDate: data)
  852. } else {
  853. cell?.timeLabel.stringValue = ""
  854. }
  855. cell?.contentLabel.stringValue = data.replyAnno?.contents ?? ""
  856. cell?.itemClick = { [weak self] idx, params in
  857. if idx == 1 { // 更多
  858. self?.noteReplyHanddler.showReplyMorePopView(sender: params.first as! NSView, replyModel: data)
  859. }
  860. }
  861. return cell
  862. }
  863. if let data = item as? KMBotaAnnotationModel {
  864. let note = (item as? KMBotaAnnotationModel)?.anno
  865. var cell = outlineView.makeView(withIdentifier: KMNoteTableViewCell.km_identifier, owner: self) as? KMNoteTableViewCell
  866. if cell == nil {
  867. cell = KMNoteTableViewCell.createFromNib()
  868. }
  869. cell?.cellNote = note
  870. cell?.model = data
  871. let state = self.noteReplyHanddler.fetchAnnoState(note) ?? .unMarked
  872. cell?.state = state
  873. cell?.itemClick = { [weak self] idx , _ in
  874. if idx == 1 { // Markup
  875. self?.updateMarkState(model: data)
  876. }
  877. }
  878. cell?.isUnFoldNote = { [weak self] cellNote, isUnfold in
  879. self?.updateExpand(model: data, isExpand: isUnfold, item: item)
  880. }
  881. return cell
  882. }
  883. }
  884. return nil
  885. }
  886. func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
  887. if outlineView.isEqual(self.noteOutlineView) {
  888. if let model = item as? KMBotaAnnotationModel {
  889. if model.isExpand == false {
  890. return model.foldH
  891. }
  892. if let anno = model.anno {
  893. return KMBOTAAnnotationTool.fetchCellHeight(annotation: anno, maxSize: CGSize(width: 260+40 - 16, height: 1000))
  894. }
  895. }
  896. return 40
  897. }
  898. return outlineView.rowHeight
  899. }
  900. func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
  901. if outlineView.isEqual(to: self.noteOutlineView) {
  902. return false
  903. }
  904. return false
  905. }
  906. func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
  907. if outlineView.isEqual(self.noteOutlineView) {
  908. let itemView = KMBotaTableRowView()
  909. if let data = item as? KMBotaAnnotationBaseModel {
  910. itemView.isSelected = data.isSelected
  911. }
  912. let rowIndexes = self.noteOutlineView.selectedRowIndexes
  913. itemView.rightMouseCallback = { [weak self] (view, event) in
  914. if let data = item as? KMBotaAnnotationFooterModel {
  915. } else {
  916. if !KMOCToolClass.arrayContains(array: self?.selectedObjcNotes(), annotation: item) ||
  917. self?.selectedObjcNotes().count == 1 {
  918. let index = self?.noteOutlineView.row(forItem: item)
  919. self?.noteOutlineView.selectRowIndexes(IndexSet(integer: IndexSet.Element(index ?? 0)), byExtendingSelection: false)
  920. }
  921. let row = outlineView.row(forItem: item)
  922. if outlineView.rowView(atRow: row, makeIfNecessary: false) != nil {
  923. let rowView = outlineView.rowView(atRow: row, makeIfNecessary: false)
  924. self?.showRightGropView(raw: row, event: event, view: rowView,item: item)
  925. }
  926. }
  927. }
  928. itemView.selectCallback = { theView in
  929. let isSelected = theView.isSelected
  930. if theView.numberOfColumns > 0 {
  931. let cellView = theView.view(atColumn: 0)
  932. if let data = cellView as? KMAnnotationSectionCellView {
  933. return
  934. }
  935. var model: KMBotaAnnotationModel?
  936. var indexs = IndexSet()
  937. if let data = cellView as? KMNoteFooterCellView {
  938. model = data.model?.annoModel
  939. model?.isSelected = isSelected
  940. self.view.window?.makeFirstResponder(data.inputTextF)
  941. }
  942. if let data = cellView as? KMNoteReplyCellView {
  943. model = data.model?.annoModel
  944. model?.isSelected = isSelected
  945. }
  946. if let data = cellView as? KMNoteTableViewCell {
  947. model = data.model
  948. model?.isSelected = isSelected
  949. }
  950. if let data = model {
  951. let row = self.noteOutlineView.row(forItem: data)
  952. indexs.insert(row)
  953. var i = 1
  954. for item in model?.replyAnnos ?? [] {
  955. indexs.insert(row+i)
  956. i += 1
  957. }
  958. indexs.insert(row+i)
  959. }
  960. }
  961. }
  962. return itemView;
  963. }
  964. return nil
  965. }
  966. func outlineView(_ outlineView: NSOutlineView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet) -> IndexSet {
  967. if outlineView.isEqual(to: self.noteOutlineView) {
  968. for i in proposedSelectionIndexes {
  969. let item = self.noteOutlineView.item(atRow: i)
  970. if let data = item as? KMBotaAnnotationFooterModel {
  971. return IndexSet()
  972. }
  973. }
  974. }
  975. return proposedSelectionIndexes
  976. }
  977. func outlineViewSelectionDidChange(_ notification: Notification) {
  978. let selectedNotes = self.selectedNotes()
  979. if selectedNotes.count == 1 {
  980. let annotation = selectedNotes.last!
  981. self.listView?.go(to: annotation.bounds, on: annotation.page, animated: true)
  982. if self.hideNotes() == false {
  983. self.listView?.updateActiveAnnotations([annotation])
  984. self.listView?.setNeedsDisplayAnnotationViewForVisiblePages()
  985. if annotation is CPDFPolygonAnnotation || annotation is CPDFPolylineAnnotation {
  986. self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: annotation)
  987. } else if let anno = annotation as? CPDFLineAnnotation {
  988. if anno.isMeasure {
  989. self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: annotation)
  990. }
  991. }
  992. }
  993. } else if(selectedNotes.count > 1){
  994. let lastSelectedNote = selectedNotes.last!
  995. let firstSelectedNote = selectedNotes.first!
  996. self.listView?.go(to: lastSelectedNote.bounds, on: lastSelectedNote.page, animated: true)
  997. if self.hideNotes() == false {
  998. if lastSelectedNote.page == firstSelectedNote.page {//同页的话支持多选选中
  999. var activeAnnotations:[CPDFAnnotation] = listView?.activeAnnotations as! [CPDFAnnotation]
  1000. activeAnnotations.append(lastSelectedNote)
  1001. self.listView?.updateActiveAnnotations(activeAnnotations)
  1002. } else {
  1003. self.listView?.updateActiveAnnotations([lastSelectedNote])
  1004. let rowIndexes = self.noteOutlineView.selectedRowIndexes
  1005. for i in 0 ..< rowIndexes.count - 1 {
  1006. noteOutlineView.deselectRow(i)
  1007. }
  1008. }
  1009. self.listView?.setNeedsDisplayAnnotationViewForVisiblePages()
  1010. if lastSelectedNote is CPDFPolygonAnnotation || lastSelectedNote is CPDFPolylineAnnotation {
  1011. self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: lastSelectedNote)
  1012. } else if let anno = lastSelectedNote as? CPDFLineAnnotation {
  1013. if anno.isMeasure {
  1014. self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: lastSelectedNote)
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
  1021. var dragOp = NSDragOperation(rawValue: 0)
  1022. if outlineView.isEqual(to: self.noteOutlineView) {
  1023. let pboard = info.draggingPasteboard
  1024. if pboard.canReadObject(forClasses: [NSColor.self], options: [:]) && index == NSOutlineViewDropOnItemIndex {
  1025. if let note = item as? CPDFAnnotation, note.type != nil {
  1026. dragOp = .every
  1027. }
  1028. }
  1029. }
  1030. return dragOp
  1031. }
  1032. func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
  1033. if outlineView.isEqual(to: noteOutlineView) {
  1034. let pboard = info.draggingPasteboard
  1035. if pboard.canReadObject(forClasses: [NSColor.self], options: [:]) {
  1036. let isShift = NSEvent.modifierFlags.contains(.shift)
  1037. let isAlt = NSEvent.modifierFlags.contains(.option)
  1038. guard let note = item as? CPDFAnnotation else {
  1039. NSSound.beep()
  1040. return false
  1041. }
  1042. if let color = NSColor(from: pboard) {
  1043. note.setColor(color, alternate: isAlt, updateDefaults: isShift)
  1044. return true
  1045. }
  1046. return false
  1047. }
  1048. }
  1049. return false
  1050. }
  1051. func outlineView(_ outlineView: NSOutlineView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, byItem item: Any?) {
  1052. if outlineView.isEqual(to: noteOutlineView) {
  1053. var note: CPDFAnnotation?
  1054. if let data = item as? CPDFAnnotation {
  1055. note = data
  1056. } else if let data = item as? KMBotaAnnotationModel {
  1057. note = data.anno
  1058. }
  1059. if let data = note?.type, data.isEmpty == false {
  1060. if tableColumn?.identifier.rawValue == self.noteColumnId.rawValue {
  1061. let string1 = (object as? String) ?? ""
  1062. let string2 = note?.string() ?? ""
  1063. if string1 != string2 {
  1064. note?.setString(string1)
  1065. }
  1066. } else if tableColumn?.identifier.rawValue == self.authorColumnId.rawValue {
  1067. let string1 = (object as? String) ?? ""
  1068. let string2 = note?.userName() ?? ""
  1069. if string1 != string2 {
  1070. note?.setUserName(string1)
  1071. }
  1072. }
  1073. }
  1074. }
  1075. }
  1076. func outlineView(_ outlineView: NSOutlineView, dataCellFor tableColumn: NSTableColumn?, item: Any) -> NSCell? {
  1077. if noteOutlineView.isEqual(to: outlineView) {
  1078. if tableColumn == nil {
  1079. if let anno = item as? CPDFAnnotation, anno.type == nil {
  1080. return outlineView.tableColumn(withIdentifier: self.noteColumnId)?.dataCell(forRow: outlineView.row(forItem: item)) as? NSCell
  1081. }
  1082. }
  1083. }
  1084. return tableColumn?.dataCell(forRow: outlineView.row(forItem: item)) as? NSCell
  1085. }
  1086. func outlineView(_ outlineView: NSOutlineView, shouldEdit tableColumn: NSTableColumn?, item: Any) -> Bool {
  1087. if outlineView.isEqual(to: noteOutlineView) {
  1088. if (tableColumn == nil) {
  1089. if self.hideNotes() == false {
  1090. if let anno = item as? CPDFAnnotation, anno.isNote() {
  1091. self.listView?.scrollAnnotationToVisible(anno)
  1092. self.listView?.updateActiveAnnotations([anno])
  1093. }
  1094. }
  1095. return false
  1096. } else if tableColumn?.identifier.rawValue == self.noteColumnId.rawValue || tableColumn?.identifier.rawValue == self.authorColumnId.rawValue {
  1097. return true
  1098. }
  1099. }
  1100. return false
  1101. }
  1102. func outlineView(_ outlineView: NSOutlineView, toolTipFor cell: NSCell, rect: NSRectPointer, tableColumn: NSTableColumn?, item: Any, mouseLocation: NSPoint) -> String {
  1103. if outlineView.isEqual(to: self.noteOutlineView) {
  1104. if tableColumn == nil || tableColumn?.identifier.rawValue == self.noteColumnId.rawValue {
  1105. return (item as? CPDFAnnotation)?.string() ?? ""
  1106. }
  1107. }
  1108. return ""
  1109. }
  1110. func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) {
  1111. self.dragIn = true
  1112. }
  1113. func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) {
  1114. self.dragIn = false
  1115. }
  1116. func noteItems(_ items: NSArray) -> NSArray {
  1117. let noteItems = NSMutableArray()
  1118. for item in items {
  1119. guard let anno = (item as? KMBotaAnnotationModel)?.anno else {
  1120. continue
  1121. }
  1122. if anno.type == nil {
  1123. }
  1124. if noteItems.contains(anno) == false {
  1125. noteItems.add(anno)
  1126. }
  1127. }
  1128. return noteItems
  1129. }
  1130. }
  1131. // MARK: - KMCustomOutlineViewDelegate, KMCustomOutlineViewDataSource
  1132. extension KMLeftSideViewController: KMCustomOutlineViewDelegate, KMCustomOutlineViewDataSource {
  1133. func outlineView(_ anOutlineView: NSOutlineView, canDeleteItems items: [Any]) -> Bool {
  1134. if anOutlineView.isEqual(to: noteOutlineView) {
  1135. return self.hideNotes() == false && items.count > 0
  1136. }
  1137. return false
  1138. }
  1139. func outlineView(_ anOutlineView: NSOutlineView, deleteItems items: [Any]) {
  1140. if anOutlineView.isEqual(to: noteOutlineView) {
  1141. if (items.isEmpty) {
  1142. return
  1143. }
  1144. self.dataUpdating = true
  1145. for item in self.noteItems(items as NSArray) {
  1146. guard let anno = item as? CPDFAnnotation else {
  1147. continue
  1148. }
  1149. if let data = anno as? CPDFFreeTextAnnotation {
  1150. self.listView?.commitEditAnnotationFreeText(data)
  1151. }
  1152. self.listView?.remove(anno)
  1153. }
  1154. for item in items {
  1155. if let data = item as? KMBotaAnnotationModel {
  1156. data.sectionModel?.items.removeObject(data)
  1157. if let footerModel = data.footerModel {
  1158. data.sectionModel?.items.removeObject(footerModel)
  1159. }
  1160. }
  1161. }
  1162. self.dataUpdating = false
  1163. self.listView?.undoManager?.setActionName(KMLocalizedString("Remove Note", comment: "Undo action name"))
  1164. self.note_refrshUIIfNeed()
  1165. }
  1166. }
  1167. func outlineView(_ anOutlineView: NSOutlineView, canCopyItems items: [Any]) -> Bool {
  1168. if anOutlineView.isEqual(to: noteOutlineView) {
  1169. if (items.count == 1) {
  1170. }
  1171. return items.count > 0
  1172. }
  1173. return false
  1174. }
  1175. func outlineView(_ anOutlineView: NSOutlineView, copyItems items: [Any]) {
  1176. if anOutlineView.isEqual(to: self.noteOutlineView) && items.isEmpty == false {
  1177. }
  1178. }
  1179. func outlineView(_ anOutlineView: NSOutlineView, typeSelectHelperSelectionStrings aTypeSelectHelper: SKTypeSelectHelper) -> NSArray {
  1180. if noteOutlineView.isEqual(to: anOutlineView) {
  1181. let count = self.noteOutlineView.numberOfRows
  1182. let texts = NSMutableArray(capacity: count)
  1183. for i in 0 ..< count {
  1184. let item = self.noteOutlineView.item(atRow: i)
  1185. let string = (item as? CPDFAnnotation)?.string() ?? ""
  1186. texts.add(string)
  1187. }
  1188. return texts
  1189. }
  1190. return []
  1191. }
  1192. }
  1193. // MARK: - Other
  1194. extension KMLeftSideViewController {
  1195. func fileNameWithSelectedPages(_ itemIndexes: IndexSet) -> String {
  1196. var pagesName = ""
  1197. if (itemIndexes.count > 1) {
  1198. pagesName.append(" pages")
  1199. } else {
  1200. pagesName.append(" page")
  1201. }
  1202. let docmentName = self.pdfDocument()?.documentURL.deletingPathExtension().lastPathComponent ?? ""
  1203. let tFileName = String(format: "%@ %@", pagesName,KMTools.parseIndexSet(indexSet: itemIndexes))
  1204. return String(format: "%@%@", docmentName,tFileName)
  1205. }
  1206. func draggedFileName(for page: CPDFPage) -> String {
  1207. let pageIndex = "\(page.pageIndex() + 1)"
  1208. var fileName = ""
  1209. if let doc = self.view.window?.windowController?.document as? NSDocument {
  1210. fileName = doc.fileURL?.deletingPathExtension().lastPathComponent ?? (MainBundle.nameSpaceName ?? "")
  1211. }
  1212. return "\(fileName)-Page \(pageIndex)"
  1213. }
  1214. func updateTableFont() {
  1215. }
  1216. }
  1217. // MARK: - ComponentGroupDelegate
  1218. extension KMLeftSideViewController: ComponentGroupDelegate {
  1219. func componentGroupDidDismiss(group: ComponentGroup?) {
  1220. if group == searchGroupView {
  1221. searchGroupTarget?.properties.state = .normal
  1222. searchGroupTarget?.reloadData()
  1223. searchGroupTarget = nil
  1224. } else if group == moreGroupView {
  1225. headerView.moreButton.properties.state = .normal
  1226. headerView.moreButton.reloadData()
  1227. }
  1228. }
  1229. func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) {
  1230. if group == searchGroupView {
  1231. guard let menuI = menuItemProperty else {
  1232. return
  1233. }
  1234. let idx = group?.menuItemArr.firstIndex(of: menuI)
  1235. if idx == 0 {
  1236. let key = KMNSearchKey.wholeWords.botaSearch
  1237. let value = KMDataManager.ud_bool(forKey: key)
  1238. KMDataManager.ud_set(!value, forKey: key)
  1239. } else if idx == 1 {
  1240. let key = KMNSearchKey.caseSensitive.botaSearch
  1241. let value = KMDataManager.ud_bool(forKey: key)
  1242. KMDataManager.ud_set(!value, forKey: key)
  1243. }
  1244. } else {
  1245. if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_Expand {
  1246. var isExpandAllItem = true
  1247. for secM in self.annoListModel?.datas ?? [] {
  1248. if secM.isExpand == false {
  1249. isExpandAllItem = false
  1250. break
  1251. }
  1252. }
  1253. if isExpandAllItem == true {
  1254. note_foldAllComments(nil)
  1255. } else {
  1256. note_expandAllComments(nil)
  1257. }
  1258. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_SortPage {
  1259. self.noteSortType = .page
  1260. if let data = self.listView?.document?.documentURL.path {
  1261. KMDataManager.udExtension_set(self.noteSortType.rawValue, forKey: Self.Key.noteSortTypeKey + data)
  1262. }
  1263. if self.noteSearchMode {
  1264. self.reloadNoteForSearchMode()
  1265. } else {
  1266. self.reloadAnnotation()
  1267. }
  1268. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_SortAscTime {
  1269. self.noteSortType = .time
  1270. self.isAscendSort = true
  1271. if let data = self.listView?.document?.documentURL.path {
  1272. KMDataManager.udExtension_set(self.noteSortType.rawValue, forKey: Self.Key.noteSortTypeKey + data)
  1273. }
  1274. if self.noteSearchMode {
  1275. self.reloadNoteForSearchMode()
  1276. } else {
  1277. self.reloadAnnotation()
  1278. }
  1279. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_SortDesTime {
  1280. self.noteSortType = .time
  1281. self.isAscendSort = false
  1282. if let data = self.listView?.document?.documentURL.path {
  1283. KMDataManager.udExtension_set(self.noteSortType.rawValue, forKey: Self.Key.noteSortTypeKey + data)
  1284. }
  1285. if self.noteSearchMode {
  1286. self.reloadNoteForSearchMode()
  1287. } else {
  1288. self.reloadAnnotation()
  1289. }
  1290. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_Improt {
  1291. importNotes(nil)
  1292. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_Export {
  1293. exportNotes(nil)
  1294. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_RemoveAll {
  1295. removeAllAnnotations(nil)
  1296. } else if menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_DeleteRep {
  1297. removeAllReplyAnnotations(nil)
  1298. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_EditNote) {
  1299. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1300. if models.first != nil {
  1301. let model = models.first
  1302. if model != nil {
  1303. updateExpand(model: model!, isExpand: true, item: currentItem)
  1304. noteReplyHanddler.editAnnotation(annotationModel: model!)
  1305. DispatchQueue.main.async {
  1306. let row = self.noteOutlineView.row(forItem: model?.footerModel)
  1307. self.noteOutlineView.scrollRowToVisible(row)
  1308. }
  1309. self.noteOutlineView.reloadData()
  1310. }
  1311. }
  1312. } else if let anModels = menuItemProperty?.representedObject as? [KMBotaAnnotationReplyModel] {
  1313. if anModels.first != nil {
  1314. let model = anModels.first
  1315. noteReplyHanddler.editReplyAnnotation(replyModel: model)
  1316. DispatchQueue.main.async {
  1317. let row = self.noteOutlineView.row(forItem: model?.annoModel?.footerModel)
  1318. self.noteOutlineView.scrollRowToVisible(row)
  1319. }
  1320. self.noteOutlineView.reloadData()
  1321. }
  1322. }
  1323. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_AddRep) {
  1324. if let anModels = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1325. if anModels.first != nil {
  1326. if let model = anModels.first {
  1327. self.updateExpand(model: model, isExpand: true, item: currentItem)
  1328. if let footMode = model.footerModel {
  1329. let row = self.noteOutlineView.row(forItem: footMode)
  1330. let rowView = self.noteOutlineView.rowView(atRow: row, makeIfNecessary: true)
  1331. if(row != nil) {
  1332. for subview in rowView!.subviews {
  1333. if let cell = subview as? KMNoteFooterCellView {
  1334. cell.inputTextF.becomeFirstResponder()
  1335. }
  1336. }
  1337. }
  1338. }
  1339. }
  1340. }
  1341. }
  1342. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_AddMark) {
  1343. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1344. if models.first != nil {
  1345. updateMarkState(model: models.first!)
  1346. }
  1347. }
  1348. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_RepStateNone) {
  1349. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1350. if models.first != nil {
  1351. updateReplaState(model:models.first! , state:.none)
  1352. }
  1353. }
  1354. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_RepStateAccepted) {
  1355. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1356. if models.first != nil {
  1357. updateReplaState(model:models.first! , state:.accepted)
  1358. }
  1359. }
  1360. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_RepStateRejected) {
  1361. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1362. if models.first != nil {
  1363. updateReplaState(model:models.first! , state:.rejected)
  1364. }
  1365. }
  1366. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_RepStateCancelled) {
  1367. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1368. if models.first != nil {
  1369. updateReplaState(model:models.first! , state:.canceled)
  1370. }
  1371. }
  1372. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_RepStateCompleted) {
  1373. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1374. if models.first != nil {
  1375. updateReplaState(model:models.first! , state:.completed)
  1376. }
  1377. }
  1378. } else if (menuItemProperty?.identifier == PDFViewMenuIdentifier_Normal_ShowPopUI) {
  1379. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1380. if models.first != nil {
  1381. let model = models.first
  1382. updateExpand(model: model!, isExpand: !(model?.isExpand == true), item: currentItem)
  1383. }
  1384. } else if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationReplyModel] {
  1385. if models.first != nil {
  1386. let model = models.first
  1387. let anModel = model?.annoModel
  1388. if anModel != nil {
  1389. updateExpand(model: anModel!, isExpand: false, item: currentItem)
  1390. }
  1391. }
  1392. }
  1393. } else if (menuItemProperty?.identifier == PDFViewMenuIdentifier_Normal_Copy) {
  1394. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1395. if models.first != nil {
  1396. let model = models.first
  1397. let an = model?.anno
  1398. var copyText:String = ""
  1399. if an?.isKind(of: CPDFMarkupAnnotation.self) == true {
  1400. if let markupAn = an as? CPDFMarkupAnnotation {
  1401. copyText = markupAn.markupContent()
  1402. }
  1403. } else if an?.isKind(of: CPDFFreeTextAnnotation.self) == true ||
  1404. an?.isKind(of: CPDFTextAnnotation.self) == true {
  1405. copyText = an?.contents ?? ""
  1406. }
  1407. let pboard = NSPasteboard.general
  1408. if copyText.isEmpty == false {
  1409. pboard.clearContents()
  1410. pboard.writeObjects([copyText as NSPasteboardWriting])
  1411. }
  1412. }
  1413. }
  1414. } else if (menuItemProperty?.identifier == PDFViewMenuIdentifier_Normal_Delete) {
  1415. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationModel] {
  1416. if models.first != nil {
  1417. let model = models.first
  1418. self.outlineView(self.noteOutlineView, deleteItems:[model!])
  1419. }
  1420. }
  1421. } else if (menuItemProperty?.identifier == PDFViewMenuIdentifier_Normal_CopyText) {
  1422. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationReplyModel] {
  1423. if models.first != nil {
  1424. let model = models.first
  1425. var copyText:String = ""
  1426. copyText = model?.replyAnno?.contents ?? ""
  1427. let pboard = NSPasteboard.general
  1428. if copyText.isEmpty == false {
  1429. pboard.clearContents()
  1430. pboard.writeObjects([copyText as NSPasteboardWriting])
  1431. }
  1432. }
  1433. }
  1434. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_DeleteMuteRep) {
  1435. if let models = menuItemProperty?.representedObject as? [Any] {
  1436. for i in 0 ..< models.count {
  1437. let model = models[i]
  1438. if let replyModel = model as? KMBotaAnnotationReplyModel {
  1439. noteReplyHanddler.removeReplyAnnotation(replyModel.replyAnno)
  1440. replyModel.annoModel?.replyAnnos.removeObject(replyModel)
  1441. } else if let anModel = model as? KMBotaAnnotationModel {
  1442. self.outlineView(self.noteOutlineView, deleteItems:[anModel])
  1443. }
  1444. }
  1445. self.noteOutlineView.reloadData()
  1446. }
  1447. } else if (menuItemProperty?.identifier == BOTAMenuIdentifier_Annotation_DeleteSignRep) {
  1448. if let models = menuItemProperty?.representedObject as? [KMBotaAnnotationReplyModel] {
  1449. if models.first != nil {
  1450. let model = models.first
  1451. if model != nil {
  1452. noteReplyHanddler.removeReplyAnnotation(model?.replyAnno)
  1453. model?.annoModel?.replyAnnos.removeObject(model!)
  1454. }
  1455. self.noteOutlineView.reloadData()
  1456. }
  1457. }
  1458. }
  1459. }
  1460. }
  1461. }