KMLeftSideViewController+Note.swift 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553
  1. //
  2. // KMLeftSideViewController+Note.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/12/23.
  6. //
  7. import Foundation
  8. extension KMLeftSideViewController.Key {
  9. static let noteAscendSortKey = "KMLeftSideViewAscendSortBoolKey"
  10. static let noteSortTypeKey = "KMLeftSideViewNoteSortTypeKey"
  11. static let noteTableColumn = "KMNoteOutlineViewTableColumnKey"
  12. static let noteFilterPage = "kKMNoteFilterAnnotationPageKey"
  13. static let noteFilterTime = "kKMNoteFilterAnnotationTimeKey"
  14. static let noteFilterAuther = "kKMNoteFilterAnnotationAutherKey"
  15. }
  16. // MARK: - Action
  17. extension KMLeftSideViewController {
  18. func note_initSubViews() {
  19. self.noteSearchField.backgroundColor = KMAppearance.Layout.l_1Color()
  20. self.noteSearchField.wantsLayer = true
  21. self.noteSearchField.layer?.backgroundColor = KMAppearance.Layout.l_1Color().cgColor
  22. self.noteSearchField.layer?.borderWidth = 1.0
  23. self.noteMoreButton.target = self
  24. self.noteMoreButton.tag = 304
  25. self.noteMoreButton.action = #selector(leftSideViewMoreButtonAction)
  26. self.moreButtonLayer = KMButtonLayer()
  27. self.noteMoreButton.layer?.addSublayer(self.moreButtonLayer!)
  28. self.moreButtonLayer?.frame = NSMakeRect(0, 0, NSWidth(self.noteMoreButton.bounds), NSHeight(self.noteMoreButton.bounds))
  29. self.noteFilterButton.target = self
  30. self.noteFilterButton.action = #selector(noteFilterAction)
  31. self.filterButtonLayer = NSView()
  32. self.noteFilterButton.addSubview(self.filterButtonLayer!)
  33. self.filterButtonLayer?.frame = NSMakeRect(14, 2, 8, 8)
  34. self.noteFilterButton.superview?.addSubview(self.noteFilterButtonHoverView, positioned: .below, relativeTo: self.noteFilterButton)
  35. self.noteFilterButtonHoverView.frame = self.noteFilterButton.frame
  36. self.noteFilterButtonHoverView.autoresizingMask = [.minXMargin,.height]
  37. self.noteFilterButtonHoverView.wantsLayer = true
  38. self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor.clear.cgColor
  39. self.noteFilterButtonHoverView.layer?.cornerRadius = 6
  40. self.noteFilterButtonHoverView.hoverAction = { [weak self] sender, act in
  41. let selected = self?.noteFilterSelected ?? false
  42. if selected {
  43. return
  44. }
  45. if act == .enter {
  46. if KMAppearance.isDarkMode() {
  47. sender.layer?.backgroundColor = NSColor(white: 1, alpha: 0.05).cgColor
  48. } else {
  49. sender.layer?.backgroundColor = NSColor(white: 0, alpha: 0.05).cgColor
  50. }
  51. } else if act == .exit {
  52. sender.layer?.backgroundColor = .clear
  53. }
  54. }
  55. self.noteDoneButton.action = #selector(leftSideViewDoneButtonAction)
  56. self.noteDoneButton.target = self
  57. self.noteDoneButton.tag = 311
  58. self.noteDoneButton.isHidden = true
  59. self.noteSearchField.delegate = self
  60. self.noteSearchField.isHidden = true
  61. self.noteSearchField.endEditCallBack = { [weak self] isEndEdit in
  62. // if (isEndEdit) {
  63. // self.noteSearchField.isHidden = true
  64. // self.noteSearchButton.isHidden = false
  65. // self.noteTitleLabel.isHidden = false
  66. // }
  67. }
  68. self.sortTypeBox.downCallback = { [unowned self] downEntered, mouseBox, _ in
  69. if (downEntered) {
  70. let menu = NSMenu()
  71. let timeItem = menu.addItem(title: KMLocalizedString("Time", nil), action: #selector(sortTypeAction), target: self)
  72. timeItem?.representedObject = self
  73. timeItem?.tag = 0
  74. let pageItem = menu.addItem(title: KMLocalizedString("Page", nil), action: #selector(sortTypeAction), target: self)
  75. pageItem?.representedObject = self
  76. timeItem?.tag = 1
  77. if (self.noteSortType == .time) {
  78. timeItem?.state = .on
  79. pageItem?.state = .off
  80. } else if (self.noteSortType == .page) {
  81. timeItem?.state = .off
  82. pageItem?.state = .on
  83. }
  84. menu.popUp(positioning: nil, at: NSMakePoint(-10, 0), in: self.sortTypeBox)
  85. }
  86. }
  87. self.noteOutlineView.delegate = self
  88. self.noteOutlineView.dataSource = self
  89. self.noteOutlineView.botaDelegate = self
  90. self.noteOutlineView.botaDataSource = self
  91. self.noteOutlineView.noteDelegate = self
  92. self.noteOutlineView.menu = NSMenu()
  93. self.noteOutlineView.menu?.delegate = self
  94. self.noteOutlineView.typeSelectHelper = SKTypeSelectHelper(matchOption: .SKSubstringMatch)
  95. self.noteOutlineView.enclosingScrollView?.scrollerStyle = .legacy
  96. self.noteOutlineView.enclosingScrollView?.autohidesScrollers = true
  97. self.noteOutlineView.registerForDraggedTypes(NSColor.readableTypes(for: NSPasteboard(name: .drag)))
  98. self.noteOutlineView.target = self
  99. self.noteOutlineView.doubleAction = #selector(selectSelectedNote)
  100. }
  101. func note_initDefalutValue() {
  102. self.noteView.wantsLayer = true
  103. self.noteView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor
  104. let sud = UserDefaults.standard
  105. if let dict = sud.dictionary(forKey: Self.Key.noteTableColumn) {
  106. self.noteTypeDict = dict
  107. } else {
  108. self.noteTypeDict = [Self.Key.noteFilterPage : false,
  109. Self.Key.noteFilterTime : false,
  110. Self.Key.noteFilterAuther : false]
  111. sud.sync_setValue(self.noteTypeDict, forKey: Self.Key.noteTableColumn)
  112. }
  113. self.caseInsensitiveNoteSearch = sud.bool(forKey: SKCaseInsensitiveNoteSearchKey)
  114. self.isAscendSort = KMDataManager.ud_bool(forKey: Self.Key.noteAscendSortKey)
  115. self.noteTitleLabel.stringValue = KMLocalizedString("Notes", nil);
  116. self.noteTitleLabel.textColor = KMAppearance.Layout.h0Color()
  117. self.noteSearchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor
  118. self.noteMoreButton.wantsLayer = true
  119. self.moreButtonLayer?.layerType = .none
  120. self.moreButtonLayer?.isHidden = true
  121. self.noteFilterButton.toolTip = KMLocalizedString("Sort", nil)
  122. self.noteFilterButton.wantsLayer = true
  123. self.filterButtonLayer?.isHidden = true
  124. self.filterButtonLayer?.wantsLayer = true
  125. self.filterButtonLayer?.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor
  126. self.filterButtonLayer?.layer?.cornerRadius = 4.0
  127. self.noteFilterSelected = false
  128. if (self.isAscendSort) {
  129. self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankReverse)
  130. self.noteSortButton.toolTip = KMLocalizedString("ascending sort", nil)
  131. } else {
  132. self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankPositive)
  133. self.noteSortButton.toolTip = KMLocalizedString("descending sort", nil)
  134. }
  135. self.noteSearchButton.toolTip = KMLocalizedString("Search", nil)
  136. self.noteDoneButton.title = KMLocalizedString("Done", nil)
  137. self.noteDoneButton.toolTip = KMLocalizedString("Done", nil)
  138. self.noteDoneButton.setTitleColor(KMAppearance.Layout.w0Color())
  139. self.noteDoneButton.wantsLayer = true
  140. self.noteDoneButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor
  141. self.noteDoneButton.layer?.cornerRadius = 4.0
  142. // self.noteHeaderView.wantsLayer = true
  143. // self.noteHeaderView.layer?.backgroundColor = KMAppearance.Else.textTagColor().cgColor
  144. // self.noteHeaderView.layer?.cornerRadius = 1.0
  145. let sortType = KMDataManager.ud_integer(forKey: Self.Key.noteSortTypeKey)
  146. if (sortType == 1) {
  147. self.noteSortType = KMNoteSortType(rawValue: sortType) ?? .none
  148. if (self.noteSortType == .time) {
  149. self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil)
  150. self.sortTypeBox.toolTip = KMLocalizedString("Time", nil)
  151. } else if (self.noteSortType == .page) {
  152. self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil)
  153. self.sortTypeBox.toolTip = KMLocalizedString("Page", nil)
  154. }
  155. } else {
  156. self.noteSortType = .page
  157. self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil)
  158. }
  159. self.sortTypeLabel.textColor = KMAppearance.Layout.h1Color()
  160. self.noteOutlineView.backgroundColor = KMAppearance.Layout.l0Color()
  161. self.noteOutlineView.autoresizesOutlineColumn = false
  162. self.noteOutlineView.indentationPerLevel = 0
  163. }
  164. func annoListIsShowPage() -> Bool {
  165. return !(self.noteTypeDict[Self.Key.noteFilterPage] as? Bool ?? false)
  166. }
  167. func annoListIsShowTime() -> Bool {
  168. return !(self.noteTypeDict[Self.Key.noteFilterTime] as? Bool ?? false)
  169. }
  170. func annoListIsShowAnther() -> Bool {
  171. return !(self.noteTypeDict[Self.Key.noteFilterAuther] as? Bool ?? false)
  172. }
  173. }
  174. // MARK: - Menu
  175. extension KMLeftSideViewController {
  176. func annoListMenu(_ menu: NSMenu) {
  177. var item: NSMenuItem?
  178. var items: NSArray?
  179. var rowIndexes = self.noteOutlineView.selectedRowIndexes
  180. let row = self.noteOutlineView.clickedRow
  181. if row == -1 {
  182. // _ = self._addExportPDFMenu(menu)
  183. _ = self._addDeleteAllAnnoItem(menu)
  184. return
  185. }
  186. if rowIndexes.contains(row) == false {
  187. rowIndexes = IndexSet(integer: row)
  188. }
  189. items = self.noteOutlineView.itemsAtRowIndexes(rowIndexes) as NSArray
  190. guard let model = self.fetchAnnoModel(for: row) else {
  191. return
  192. }
  193. let isFold = model.isFold()
  194. item = menu.addItem(title: KMLocalizedString("Expand", nil), action: #selector(unfoldNoteAction), target: self)
  195. item?.state = isFold ? .off : .on
  196. item?.representedObject = items
  197. item = menu.addItem(title: KMLocalizedString("Collapse", nil), action: #selector(foldNoteAction), target: self)
  198. item?.state = isFold ? .on : .off
  199. item?.representedObject = items
  200. menu.addItem(.separator())
  201. let hideNotes = self.hideNotes()
  202. // var theItems: [KMBotaAnnotationModel] = []
  203. // for data in items ?? [] {
  204. // if let annoModel = data as? KMBotaAnnotationModel {
  205. // theItems.append(annoModel)
  206. // }
  207. // }
  208. if hideNotes == false && (items?.count ?? 0) == 1 {
  209. // let annotation = self.noteItems(items!).lastObject as? CPDFAnnotation
  210. // if let data = annotation?.isEditable(), data {
  211. // if annotation?.type == nil {
  212. // let isNote = annotation?.isNote() ?? false
  213. // if isNote {
  214. // // [NSLocalizedString(@"Edit", @"Menu item title") stringByAppendingEllipsis]
  215. // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editNoteTextFromTable), target: self)
  216. // item?.representedObject = annotation
  217. // }
  218. // } else if let data = self.noteOutlineView.tableColumn(withIdentifier: NSUserInterfaceItemIdentifier("note"))?.isHidden, data {
  219. // // [NSLocalizedString(@"Edit", @"Menu item title") stringByAppendingEllipsis]
  220. // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editThisAnnotation), target: self)
  221. // item?.representedObject = annotation
  222. // } else {
  223. // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editNoteFromTable), target: self)
  224. // item?.representedObject = annotation
  225. // item = menu.addItem(title: KMLocalizedString("Edit", "Menu item title"), action: #selector(editThisAnnotation), target: self)
  226. // item?.representedObject = annotation
  227. // item?.keyEquivalentModifierMask = [.option]
  228. // item?.isAlternate = true
  229. // }
  230. // }
  231. }
  232. if menu.numberOfItems > 0 {
  233. // _ = self._addExportPDFMenu(menu)
  234. // menu.addItem(.separator())
  235. if self.outlineView(self.noteOutlineView, canDeleteItems: items as? [Any] ?? []) {
  236. item = menu.addItem(title: KMLocalizedString("Delete", "Menu item title"), action: #selector(deleteNotes), target: self)
  237. item?.representedObject = items
  238. }
  239. _ = self._addDeleteAllAnnoItem(menu)
  240. }
  241. }
  242. private func _addExportPDFMenu(_ menu: NSMenu) -> NSMenu {
  243. var item = menu.addItem(title: NSLocalizedString("Export Annotations…", tableName: "", comment: ""), action: nil, target: self)
  244. let subMenu = NSMenu()
  245. item?.submenu = subMenu
  246. item = subMenu.addItem(title: NSLocalizedString("PDF", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  247. item?.tag = 0
  248. item = subMenu.addItem(title: NSLocalizedString("PDF Bundle", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  249. item?.tag = 1
  250. item = subMenu.addItem(title: NSLocalizedString("PDF Reader Pro Edition Notes", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  251. item?.tag = 2
  252. item = subMenu.addItem(title: NSLocalizedString("Notes as Text", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  253. item?.tag = 3
  254. item = subMenu.addItem(title: NSLocalizedString("Notes as RTF", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  255. item?.tag = 4
  256. item = subMenu.addItem(title: NSLocalizedString("Notes as RTFD", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  257. item?.tag = 5
  258. item = subMenu.addItem(title: NSLocalizedString("Notes as FDF", tableName: "", comment: ""), action: #selector(exportAnnotationNotes), target: self)
  259. item?.tag = 6
  260. return menu
  261. }
  262. private func _addDeleteAllAnnoItem(_ menu: NSMenu) -> NSMenuItem? {
  263. return menu.addItem(title: NSLocalizedString("Remove All Annotations", tableName: "", comment: ""), action: #selector(removeAllAnnotations), target: self)
  264. }
  265. private func _addDeleteAllReplyAnnoItem(_ menu: NSMenu) -> NSMenuItem? {
  266. return menu.addItem(title: NSLocalizedString("Delete All Reply", tableName: "", comment: ""), action: #selector(removeAllReplyAnnotations), target: self)
  267. }
  268. func annoListMoreMenu(_ view: NSButton) {
  269. let menu = NSMenu()
  270. let object = KMPopupMenuObject()
  271. object.menuTag = 1001
  272. menu.delegate = object
  273. object.enterControllerCallback = { [weak self] isEnter in
  274. if (isEnter) {
  275. self?.moreButtonLayer?.isHidden = false
  276. } else {
  277. self?.moreButtonLayer?.isHidden = true
  278. }
  279. }
  280. let expandAllItem = menu.addItem(title: KMLocalizedString("Expand All", nil), action: #selector(note_expandAllComments), target: self)
  281. expandAllItem?.representedObject = self.noteOutlineView
  282. let foldAllItem = menu.addItem(title: KMLocalizedString("Collapse All", nil), action: #selector(note_foldAllComments), target: self)
  283. foldAllItem?.representedObject = self.noteOutlineView
  284. let type = self.annoListModel?.foldType ?? .none
  285. // expandAllItem?.state = type == .unfold ? .on : .off
  286. expandAllItem?.state = .on
  287. for secM in self.annoListModel?.datas ?? [] {
  288. if secM.isExpand == false {
  289. expandAllItem?.state = .off
  290. break
  291. }
  292. }
  293. // foldAllItem?.state = type == .fold ? .on : .off
  294. foldAllItem?.state = .on
  295. for secM in self.annoListModel?.datas ?? [] {
  296. if secM.isExpand {
  297. foldAllItem?.state = .off
  298. break
  299. }
  300. }
  301. let showItem = menu.addItem(title: KMLocalizedString("Show Note", nil), action: nil, target: self)
  302. let subMenu = NSMenu()
  303. let pageItem = subMenu.addItem(title: KMLocalizedString("Page", nil), action: #selector(noteShowNoteAction), target: self)
  304. pageItem?.state = self.annoListIsShowPage() ? .on : .off
  305. pageItem?.representedObject = self.noteOutlineView
  306. pageItem?.tag = 101
  307. let timeItem = subMenu.addItem(title: KMLocalizedString("Time", nil) , action: #selector(noteShowNoteAction), target: self)
  308. timeItem?.state = self.annoListIsShowTime() ? .on : .off
  309. timeItem?.representedObject = self.noteOutlineView
  310. timeItem?.tag = 102
  311. let authorItem = subMenu.addItem(title: KMLocalizedString("Author", nil) , action: #selector(noteShowNoteAction), target: self)
  312. authorItem?.state = self.annoListIsShowAnther() ? .on : .off
  313. authorItem?.representedObject = self.noteOutlineView
  314. authorItem?.tag = 103
  315. showItem?.submenu = subMenu
  316. menu.addItem(.separator())
  317. // _ = self._addExportPDFMenu(menu)
  318. // menu.addItem(.separator())
  319. _ = self._addDeleteAllAnnoItem(menu)
  320. _ = self._addDeleteAllReplyAnnoItem(menu)
  321. menu.addItem(.separator())
  322. let importItem = NSMenuItem(title: NSLocalizedString("Import Annotations", comment: ""), action: #selector(importNotes), keyEquivalent: "")
  323. importItem.target = self
  324. menu.addItem(importItem)
  325. let exportItem = NSMenuItem(title: NSLocalizedString("Export Annotations", comment: ""), action: #selector(exportNotes), keyEquivalent: "")
  326. exportItem.target = self
  327. menu.addItem(exportItem)
  328. if let data = NSApp.currentEvent {
  329. NSMenu.popUpContextMenu(menu, with: data, for: view, with: nil)
  330. }
  331. }
  332. func annoListValidateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  333. let action = menuItem.action
  334. if (action == #selector(note_expandAllComments) ||
  335. action == #selector(note_foldAllComments) ||
  336. action == #selector(exportAnnotationNotes) ||
  337. action == #selector(removeAllAnnotations)) {
  338. let cnt = self.annoListModel?.datas.count ?? 0
  339. return cnt > 0
  340. } else if (action == #selector(unfoldNoteAction) ||
  341. action == #selector(foldNoteAction)) {
  342. let row = self.noteOutlineView.clickedRow
  343. let foldNote = self.fetchNote(for: row)
  344. // SKNPDFAnnotationNote
  345. // if foldNote is CPDFMarkupAnnotation || foldNote is CPDFTextAnnotation {
  346. return true
  347. // } else {
  348. // return false
  349. // }
  350. } else if (action == #selector(editNoteFromTable)) {
  351. let row = self.noteOutlineView.clickedRow
  352. let foldNote = self.fetchNote(for: row)
  353. // if (@available(macOS 10.13, *)) {
  354. // if ([foldNote.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeSignature]) {
  355. // return NO;
  356. // }
  357. // }
  358. if foldNote is CPDFStampAnnotation || foldNote is KMAnnotationStamp || foldNote is CPDFListStampAnnotation {
  359. return false
  360. } else {
  361. return true
  362. }
  363. }
  364. return true
  365. }
  366. @IBAction func note_expandAllComments(_ sender: AnyObject?) {
  367. guard let model = self.annoListModel else {
  368. return
  369. }
  370. // if (model.foldType == .unfold) { // 已全部展开
  371. // return
  372. // }
  373. // model.foldType = .unfold
  374. for secM in self.annoListModel?.datas ?? [] {
  375. // for model in secM.items {
  376. // if let data = model as? KMBotaAnnotationFooterModel {
  377. // data.isExpand = true
  378. // }
  379. // }
  380. secM.isExpand = true
  381. }
  382. self.noteOutlineView.reloadData()
  383. }
  384. @IBAction func note_foldAllComments(_ sender: AnyObject?) {
  385. guard let model = self.annoListModel else {
  386. return
  387. }
  388. // if (model.foldType == .fold) {
  389. // return
  390. // }
  391. // model.foldType = .fold
  392. for secM in self.annoListModel?.datas ?? [] {
  393. // for model in secM.items {
  394. // if let data = model as? KMBotaAnnotationFooterModel {
  395. // data.isExpand = false
  396. // }
  397. // }
  398. secM.isExpand = false
  399. }
  400. self.noteOutlineView.reloadData()
  401. }
  402. @IBAction func noteShowNoteAction(_ sender: AnyObject?) {
  403. let item = sender as? NSMenuItem
  404. let tag = item?.tag ?? 0
  405. if (tag == 100) {
  406. } else if (tag == 101) {
  407. let isPage = !self.annoListIsShowPage()
  408. self.noteTypeDict[Self.Key.noteFilterPage] = !isPage
  409. } else if (tag == 102) {
  410. let isTime = !self.annoListIsShowTime()
  411. self.noteTypeDict[Self.Key.noteFilterTime] = !isTime
  412. } else if (tag == 103) {
  413. let isAuther = !self.annoListIsShowAnther()
  414. self.noteTypeDict[Self.Key.noteFilterAuther] = !isAuther
  415. }
  416. UserDefaults.standard.sync_setValue(self.noteTypeDict, forKey: Self.Key.noteTableColumn)
  417. // 更新数据
  418. var models: [KMBotaAnnotationModel] = []
  419. if self.noteSearchMode {
  420. // models = self.noteSearchArray
  421. for model in self.noteSearchArray {
  422. guard let data = model as? KMBotaAnnotationModel else {
  423. continue
  424. }
  425. models.append(data)
  426. }
  427. } else {
  428. let selModels = self.annoListModel?.datas ?? []
  429. for selModel in selModels {
  430. for item in selModel.items {
  431. if let data = item as? KMBotaAnnotationModel {
  432. models.append(data)
  433. }
  434. }
  435. }
  436. }
  437. for model in models {
  438. model.showPage = self.annoListIsShowPage()
  439. model.showTime = self.annoListIsShowTime()
  440. model.showAuthor = self.annoListIsShowAnther()
  441. }
  442. let selectRow = self.noteOutlineView.selectedRow
  443. self.noteOutlineView.reloadData()
  444. self.noteOutlineView.selectRowIndexes(IndexSet(integer: selectRow), byExtendingSelection: false)
  445. }
  446. @objc func importNotes(_ sender: NSMenuItem) {
  447. let panel = NSOpenPanel()
  448. panel.allowedFileTypes = ["xfdf"]
  449. panel.allowsMultipleSelection = false
  450. panel.beginSheetModal(for: self.view.window!) { resp in
  451. if resp != .OK {
  452. return
  453. }
  454. if let result = self.pdfDocument()?.importAnnotation(fromXFDFPath: panel.url?.path), result {
  455. self.mainViewController?.convertNotesUsingPDFDocument(self.pdfDocument()!, callback: { [weak self] in
  456. self?.reloadAnnotation()
  457. self?.listView?.setNeedsDisplayForVisiblePages()
  458. })
  459. }
  460. }
  461. }
  462. @objc func exportNotes(_ sender: NSMenuItem?) {
  463. guard let cnt = self.listView?.notes?.count, cnt > 0 else {
  464. NSSound.beep()
  465. return
  466. }
  467. let fileName = "\(self.pdfDocument()?.documentURL.deletingPathExtension().lastPathComponent ?? "")" + "_xfdf"
  468. let panel = NSSavePanel()
  469. panel.directoryURL = self.pdfDocument()?.documentURL.deletingLastPathComponent()
  470. panel.allowedFileTypes = ["xfdf"]
  471. panel.nameFieldStringValue = fileName
  472. panel.beginSheetModal(for: self.view.window!) { resp in
  473. if resp != .OK {
  474. return
  475. }
  476. let filePath = panel.url?.path
  477. if let success = self.pdfDocument()?.exportAnnotation(toXFDFPath: filePath), success {
  478. NSWorkspace.shared.selectFile(filePath, inFileViewerRootedAtPath: "")
  479. } else {
  480. Task {
  481. _ = await KMAlertTool.runModel(message: NSLocalizedString("Export Failure!", comment: ""), buttons: ["OK"])
  482. }
  483. }
  484. }
  485. }
  486. @objc func exportAnnotationNotes(_ sender: AnyObject?) {
  487. let doc = self.view.window?.windowController?.document as? NSDocument
  488. doc?.saveTo(sender)
  489. }
  490. // 展开
  491. @objc func unfoldNoteAction(_ sender: NSMenuItem) {
  492. if sender.state == .on {
  493. return
  494. }
  495. let row = self.noteOutlineView.clickedRow
  496. guard let model = self.fetchAnnoModel(for: row) else {
  497. return
  498. }
  499. model.foldType = .unfold
  500. model.isExpand = true
  501. let viewS = self.noteOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true)
  502. (viewS as? KMNoteTableViewCell)?.isFold = false
  503. model.footerModel?.isExpand = true
  504. // self.noteOutlineView.reloadItem(model.footerModel)
  505. self.noteOutlineView.reloadData()
  506. }
  507. @objc func foldNoteAction(_ sender: NSMenuItem) {
  508. if sender.state == .on {
  509. return
  510. }
  511. let row = self.noteOutlineView.clickedRow
  512. guard let model = self.fetchAnnoModel(for: row) else {
  513. return
  514. }
  515. model.foldType = .fold
  516. model.isExpand = false
  517. let viewS = self.noteOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true)
  518. (viewS as? KMNoteTableViewCell)?.isFold = true
  519. model.footerModel?.isExpand = false
  520. // self.noteOutlineView.reloadItem(model.footerModel)
  521. self.noteOutlineView.reloadData()
  522. }
  523. @objc func deleteNotes(_ sender: NSMenuItem) {
  524. self.outlineView(self.noteOutlineView, deleteItems: sender.representedObject as? [Any] ?? [])
  525. }
  526. @objc func removeAllAnnotations(_ sender: AnyObject?) {
  527. guard let doc = self.pdfDocument() else {
  528. return
  529. }
  530. Task {
  531. let response = await KMAlertTool.runModel(message: KMLocalizedString("This will permanently remove all annotations. Are you sure to continue?", nil), buttons: [KMLocalizedString("Yes", nil), KMLocalizedString("No", nil)])
  532. if response == .alertFirstButtonReturn {
  533. // var annos: [CPDFAnnotation] = []
  534. // for i in 0 ..< doc.pageCount {
  535. // let page = self.pdfDocument()?.page(at: i)
  536. // for anno in page?.annotations ?? [] {
  537. // if anno is CPDFTextWidgetAnnotation || anno is CPDFButtonWidgetAnnotation || anno is CPDFChoiceWidgetAnnotation {
  538. // continue
  539. // }
  540. // // if ([annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeSignature]) {
  541. // // continue;
  542. // // }
  543. // if anno is CPDFLinkAnnotation {
  544. // continue
  545. // }
  546. // annos.append(anno)
  547. // }
  548. // }
  549. self.dataUpdating = true
  550. // for anno in annos {
  551. // self.listView?.remove(anno)
  552. // }
  553. for model in self.annoListModel?.datas ?? [] {
  554. for item in model.items {
  555. if let anno = item.anno {
  556. self.listView?.remove(anno)
  557. }
  558. }
  559. }
  560. self.annoListModel?.datas.removeAll()
  561. self.dataUpdating = false
  562. self.note_refrshUIIfNeed()
  563. }
  564. }
  565. }
  566. @objc func removeAllReplyAnnotations(_ sender: NSMenuItem?) {
  567. Task {
  568. let response = await KMAlertTool.runModel(message: KMLocalizedString("Are you sure to delete all comment replies?", nil), buttons: [KMLocalizedString("Yes", nil), KMLocalizedString("No", nil)])
  569. if response == .alertFirstButtonReturn {
  570. self.dataUpdating = true
  571. var hasAnno = false
  572. for model in self.annoListModel?.datas ?? [] {
  573. for item in model.items {
  574. // if let anno = item.anno {
  575. // self.listView?.remove(anno)
  576. // }
  577. guard let annoM = item as? KMBotaAnnotationModel else {
  578. continue
  579. }
  580. for replyM in annoM.replyAnnos {
  581. // self.listView?.remove(replyM.replyAnno)
  582. replyM.replyAnno?.page.removeAnnotation(replyM.replyAnno)
  583. hasAnno = true
  584. }
  585. annoM.replyAnnos.removeAll()
  586. }
  587. }
  588. self.dataUpdating = false
  589. self.note_refrshUIIfNeed()
  590. if let data = self.view.window, hasAnno {
  591. KMTools.setDocumentEditedState(window: data)
  592. }
  593. }
  594. }
  595. }
  596. @objc func editNoteTextFromTable(_ sender: NSMenuItem) {
  597. // PDFAnnotation *annotation = [sender representedObject];
  598. guard let annotation = sender.representedObject as? CPDFAnnotation else {
  599. return
  600. }
  601. self.listView?.scrollAnnotationToVisible(annotation)
  602. // self.listView.activeAnnotation = annotation
  603. // [self showNote:annotation];
  604. // SKNoteWindowController *noteController = (SKNoteWindowController *)[self windowControllerForNote:annotation];
  605. // [[noteController window] makeFirstResponder:[noteController textView]];
  606. // [[noteController textView] selectAll:nil];
  607. }
  608. @objc func editThisAnnotation(_ sender: AnyObject?) {
  609. guard let annotation = (sender as? NSMenuItem)?.representedObject as? CPDFAnnotation else {
  610. NSSound.beep()
  611. return
  612. }
  613. self.listView?.edit(annotation)
  614. }
  615. @objc func editNoteFromTable(_ sender: AnyObject?) {
  616. guard let annotation = (sender as? NSMenuItem)?.representedObject as? CPDFAnnotation else {
  617. NSSound.beep()
  618. return
  619. }
  620. let model = fetchAnnoModel(for: annotation)
  621. let row = self.noteOutlineView.row(forItem: model)
  622. self.noteOutlineView.km_safe_selectRowIndexes(.init(integer: row), byExtendingSelection: false)
  623. let noteIndex = self.noteOutlineView.column(withIdentifier: .init("note"))
  624. if (noteIndex >= 0) {
  625. self.noteOutlineView.scrollColumnToVisible(noteIndex)
  626. //
  627. self.isRenameNoteOutline = true
  628. // self.renamePDFOutline = [rightSideController.noteOutlineView itemAtRow:rightSideController.noteOutlineView.clickedRow];
  629. let viewS = self.noteOutlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as? KMNoteTableViewCell
  630. viewS!.isFold = false
  631. let targrtTextField = viewS?.noteContentLabel
  632. self.editNoteTextField = targrtTextField
  633. self.editNote = annotation
  634. targrtTextField?.delegate = self
  635. targrtTextField?.isEditable = true
  636. targrtTextField?.becomeFirstResponder()
  637. }
  638. }
  639. @IBAction func noteSortAction(_ sender: AnyObject?) {
  640. if (self.isAscendSort) {
  641. self.isAscendSort = false
  642. self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankPositive)
  643. self.noteSortButton.toolTip = KMLocalizedString("descending sort", nil)
  644. } else {
  645. self.isAscendSort = true
  646. self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankReverse)
  647. self.noteSortButton.toolTip = KMLocalizedString("ascending sort", nil)
  648. }
  649. KMDataManager.ud_set(self.isAscendSort, forKey: Self.Key.noteAscendSortKey)
  650. if self.noteSearchMode {
  651. self.reloadNoteForSearchMode()
  652. } else {
  653. self.reloadNoteForSortMode()
  654. }
  655. }
  656. @IBAction func noteSearchAction(_ sender: NSButton) {
  657. self.noteSearchField.isHidden = false
  658. self.noteTitleLabel.isHidden = true
  659. self.noteSearchButton.isHidden = true
  660. self.noteDoneButton.isHidden = false
  661. self.noteFilterButton.isHidden = true
  662. self.noteMoreButton.isHidden = true
  663. self.noteSearchField.becomeFirstResponder()
  664. }
  665. @IBAction func noteFilterAction(_ sender: AnyObject?) {
  666. let button = sender as? NSButton
  667. if self.noteFilterSelected == false {
  668. if KMAppearance.isDarkMode() {
  669. self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(white: 1, alpha: 0.05).cgColor
  670. } else {
  671. self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(white: 0, alpha: 0.05).cgColor
  672. }
  673. }
  674. let menu = NSMenu()
  675. let filterViewController = KMNoteOutlineFilterViewController()
  676. filterViewController.listView = self.listView
  677. filterViewController.view.layer?.backgroundColor = .clear
  678. filterViewController.hoverView = self.noteFilterButtonHoverView
  679. var states: [CPDFAnnotationState] = []
  680. for anno in self.allAnnotations {
  681. if let reviewS = self.noteReplyHanddler.fetchReviewState(anno) {
  682. if states.contains(reviewS) == false {
  683. states.append(reviewS)
  684. }
  685. } else {
  686. if states.contains(.none) == false {
  687. states.append(.none)
  688. }
  689. }
  690. if let markS = self.noteReplyHanddler.fetchAnnoState(anno) {
  691. if states.contains(markS) == false {
  692. states.append(markS)
  693. }
  694. } else {
  695. if states.contains(.unMarked) == false {
  696. states.append(.unMarked)
  697. }
  698. }
  699. }
  700. filterViewController.updateStates(states: states)
  701. filterViewController.setNotesArray(self.allAnnotations as NSArray)
  702. filterViewController.applyFilterCallback = { [weak self] typeArr, colorArr, authorArr, isEmpty in
  703. menu.cancelTracking()
  704. if (isEmpty) {
  705. self?.filterButtonLayer?.isHidden = true
  706. self?.noteFilterButtonHoverView.layer?.backgroundColor = .clear
  707. self?.noteFilterSelected = false
  708. } else {
  709. self?.filterButtonLayer?.isHidden = false
  710. self?.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(hex: "#227AFF").withAlphaComponent(0.3).cgColor
  711. self?.noteFilterSelected = true
  712. }
  713. self?.reloadAnnotation()
  714. }
  715. filterViewController.cancelCallback = { isCancel in
  716. if (isCancel) {
  717. menu.cancelTracking()
  718. }
  719. }
  720. let item = menu.addItem(withTitle: "", action: nil, keyEquivalent: "")
  721. item.target = self
  722. item.representedObject = filterViewController
  723. item.view = filterViewController.view
  724. menu.popUp(positioning: nil, at: NSMakePoint(-130, 30), in: button)
  725. // let win = KMNoteOutlineFilterViewController_window(contentRect: .zero, styleMask: .borderless, backing: .buffered, defer: false)
  726. // win.center()
  727. // win.contentViewController = filterViewController
  728. // win.orderFront(nil)
  729. }
  730. func fetchNote(for index: Int) -> CPDFAnnotation? {
  731. return self.fetchAnnoModel(for: index)?.anno
  732. }
  733. func fetchAnnoModel(for index: Int) -> KMBotaAnnotationModel? {
  734. if self.noteSearchMode { // 搜索模式
  735. return self.noteSearchArray.safe_element(for: index) as? KMBotaAnnotationModel
  736. } else { // 常规模式(非搜索)
  737. // let model = self.annoListModel?.datas.safe_element(for: index)
  738. let model = self.noteOutlineView.item(atRow: index)
  739. if let data = model as? KMBotaAnnotationSectionModel {
  740. return nil
  741. }
  742. if let data = model as? KMBotaAnnotationModel {
  743. return data
  744. }
  745. if let data = model as? KMBotaAnnotationFooterModel {
  746. // return data.annoModel
  747. }
  748. if let data = model as? KMBotaAnnotationReplyModel {
  749. // return data.annoModel
  750. }
  751. // return self.annoListModel?.datas.safe_element(for: index) as? KMBotaAnnotationModel
  752. return nil
  753. }
  754. }
  755. func fetchAnnoModel(for anno: CPDFAnnotation) -> KMBotaAnnotationModel? {
  756. if self.noteSearchMode { // 搜索模式
  757. for model in self.noteSearchArray {
  758. guard let data = model as? KMBotaAnnotationModel else {
  759. continue
  760. }
  761. if anno.isEqual(to: data.anno) {
  762. return data
  763. }
  764. }
  765. } else { // 常规模式(非搜索)
  766. for model in self.annoListModel?.datas ?? [] {
  767. for item in model.items {
  768. if let data = item as? KMBotaAnnotationModel {
  769. if anno.isEqual(to: data.anno) {
  770. return data
  771. }
  772. }
  773. }
  774. }
  775. }
  776. return nil
  777. }
  778. @IBAction @objc func sortTypeAction(_ sender: NSMenuItem) {
  779. let item = sender
  780. let tag = item.tag
  781. if (item.state == .on) {
  782. item.state = .off
  783. } else {
  784. item.state = .on
  785. }
  786. if (tag == 0) {
  787. self.noteSortType = .page
  788. self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil)
  789. self.sortTypeBox.toolTip = KMLocalizedString("Page", nil)
  790. } else if (tag == 1) {
  791. self.noteSortType = .time
  792. self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil)
  793. self.sortTypeBox.toolTip = KMLocalizedString("Time", nil)
  794. }
  795. KMDataManager.ud_set(self.noteSortType.rawValue, forKey: Self.Key.noteSortTypeKey)
  796. if self.noteSearchMode {
  797. self.reloadNoteForSearchMode()
  798. } else {
  799. self.reloadAnnotation()
  800. }
  801. }
  802. func showNoteEmptyView() {
  803. let view = self.noteOutlineView.enclosingScrollView?.documentView
  804. let viewFrame = view?.frame ?? .zero
  805. let emptyVcSize = self.leftSideEmptyVC.emptyAnnotationView.frame.size
  806. self.leftSideEmptyVC.emptyAnnotationView.frame = NSMakeRect((viewFrame.size.width-emptyVcSize.width)/2.0,(viewFrame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height)
  807. self.leftSideEmptyVC.emptyAnnotationView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin]
  808. self.noteOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptyAnnotationView)
  809. self.leftSideEmptyVC.exportAnnotationBtn.isEnabled = false
  810. self.leftSideEmptyVC.deleteAnnotationBtn.isEnabled = false
  811. if (self.leftView.segmentedControl.selectedSegment == KMSelectedSegmentType.annotation.rawValue) {
  812. self.noteHeaderView.isHidden = true
  813. self.toolButtonBoxLayoutConstraint.constant = 40.0
  814. }
  815. }
  816. func hideNoteEmptyView() {
  817. self.leftSideEmptyVC.emptyAnnotationView.removeFromSuperview()
  818. self.leftSideEmptyVC.exportAnnotationBtn.isEnabled = true
  819. self.leftSideEmptyVC.deleteAnnotationBtn.isEnabled = true
  820. if (self.leftView.segmentedControl.selectedSegment == 3) {
  821. self.noteHeaderView.isHidden = false
  822. self.toolButtonBoxLayoutConstraint.constant = 64.0
  823. }
  824. }
  825. }
  826. // MARK: - Note
  827. extension KMLeftSideViewController {
  828. public func refreshUIForAddAnnotation(annos: [CPDFAnnotation]?, page: CPDFPage?) {
  829. let need = self._annoList_needRefreshUI(annos: annos)
  830. if need == false {
  831. return
  832. }
  833. self.updateThumbnail(at: Int(page?.pageIndex() ?? 0))
  834. self.note_reloadDataIfNeed()
  835. }
  836. public func refreshUIForAnnoAttributeDidChange(_ anno: CPDFAnnotation?, attributes: [String : Any]?) {
  837. self.updateThumbnail(at: Int(anno?.page?.pageIndex() ?? 0))
  838. let need = self._annoList_needRefreshUI(annos: anno != nil ? [anno!] : [])
  839. if need == false {
  840. return
  841. }
  842. if let data = anno {
  843. self.note_reloadDataForAnnoIfNeed(anno: data)
  844. }
  845. }
  846. public func annoList_refreshUIForDeleteAnnotations(annos: [CPDFAnnotation]?, page: CPDFPage?) {
  847. self.updateThumbnail(at: Int(page?.pageIndex() ?? 0))
  848. if self.type.methodType != .Annotation {
  849. return
  850. }
  851. let need = self._annoList_needRefreshUI(annos: annos)
  852. if need == false {
  853. return
  854. }
  855. for anno in annos ?? [] {
  856. if let model = self.fetchAnnoModel(for: anno) {
  857. self.noteSearchArray.removeObject(model)
  858. // self.annoListModel?.datas.removeObject(model)
  859. model.sectionModel?.items.removeObject(model)
  860. if let footer = model.footerModel {
  861. model.sectionModel?.items.removeObject(footer)
  862. }
  863. if self.allAnnotations.contains(anno) {
  864. self.allAnnotations.removeObject(anno)
  865. }
  866. }
  867. }
  868. if self.dataUpdating == false {
  869. self.note_refrshUIIfNeed()
  870. }
  871. }
  872. func note_refrshUIIfNeed() {
  873. if self.type.methodType != .Annotation {
  874. return
  875. }
  876. Task { @MainActor in
  877. self.noteOutlineView.reloadData()
  878. }
  879. }
  880. func note_reloadDataIfNeed() {
  881. if self.type.methodType != .Annotation {
  882. return
  883. }
  884. self.reloadAnnotation()
  885. }
  886. func note_reloadDataForAnnoIfNeed(anno: CPDFAnnotation) {
  887. if self.type.methodType != .Annotation {
  888. return
  889. }
  890. if anno is CPDFLineAnnotation || anno is CPDFSquareAnnotation || anno is CPDFCircleAnnotation || anno is CPDFInkAnnotation {
  891. // 形状注释 + Ink 需要显示框住的内容【刷新】
  892. for item in self.annoListModel?.datas ?? [] {
  893. for itemM in item.items {
  894. if anno.isEqual(to: itemM.anno) {
  895. self.noteOutlineView.reloadItem(itemM)
  896. break
  897. }
  898. }
  899. }
  900. } else {
  901. for item in self.annoListModel?.datas ?? [] {
  902. for itemM in item.items {
  903. if anno.isEqual(to: itemM.anno) {
  904. self.noteOutlineView.reloadItem(itemM)
  905. break
  906. }
  907. }
  908. }
  909. }
  910. }
  911. func reloadAnnotation() {
  912. if self.listView != nil {
  913. let filterKey = self.pdfDocument()?.documentURL.path ?? ""
  914. let typeArr: [String] = KMBotaTools.noteFilterAnnoTypes(key: filterKey)
  915. let colorArr: [Any] = KMBotaTools.noteFilterColors(key: filterKey)
  916. let authorArr: [Any] = KMBotaTools.noteFilterAuthors(key: filterKey)
  917. var stateArr: [NSNumber] = KMBotaTools.noteFilterStates(key: filterKey)
  918. if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 && stateArr.count == 0 {
  919. // self.filtrateButton.image = NSImage(named: "KMImageNameAnnotationsFiltrate")
  920. self.filterButtonLayer?.isHidden = true
  921. self.noteFilterButtonHoverView.layer?.backgroundColor = .clear
  922. self.noteFilterSelected = false
  923. } else {
  924. // self.filtrateButton.image = NSImage(named: "icon_annotation_screening_select")self.filterButtonLayer?.isHidden = true
  925. self.filterButtonLayer?.isHidden = false
  926. self.noteFilterButtonHoverView.layer?.backgroundColor = NSColor(hex: "#227AFF").withAlphaComponent(0.3).cgColor
  927. self.noteFilterSelected = true
  928. }
  929. var hasMark = false
  930. var hasReview = false
  931. for data in stateArr {
  932. let state = data.intValue
  933. if state == CPDFAnnotationState.marked.rawValue || state == CPDFAnnotationState.unMarked.rawValue {
  934. hasMark = true
  935. }
  936. if state == CPDFAnnotationState.none.rawValue || state == CPDFAnnotationState.accepted.rawValue || state == CPDFAnnotationState.rejected.rawValue || state == CPDFAnnotationState.canceled.rawValue || state == CPDFAnnotationState.completed.rawValue {
  937. hasReview = true
  938. }
  939. }
  940. if hasMark == false {
  941. stateArr.append(NSNumber(value: CPDFAnnotationState.marked.rawValue))
  942. stateArr.append(NSNumber(value: CPDFAnnotationState.unMarked.rawValue))
  943. }
  944. if hasReview == false {
  945. stateArr.append(NSNumber(value: CPDFAnnotationState.none.rawValue))
  946. stateArr.append(NSNumber(value: CPDFAnnotationState.accepted.rawValue))
  947. stateArr.append(NSNumber(value: CPDFAnnotationState.rejected.rawValue))
  948. stateArr.append(NSNumber(value: CPDFAnnotationState.canceled.rawValue))
  949. stateArr.append(NSNumber(value: CPDFAnnotationState.completed.rawValue))
  950. }
  951. var annotationArray: [CPDFAnnotation] = []
  952. var allAnnotation: [CPDFAnnotation] = []
  953. for i in 0 ..< self.pageCount() {
  954. let page = self.pdfDocument()?.page(at: UInt(i))
  955. var annos: [CPDFAnnotation] = []
  956. // 处理过滤
  957. let types = ["Highlight","Underline","Strikeout","Squiggly","Freehand","FreeText","Note","Square","Circle","Line","Stamp","Arrow","Image","Redact","Sign"/*, "table"*/,"Polyline","Polygon"]
  958. if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 && stateArr.count == 0 {
  959. annos = KMOCToolClass.filterAnnotation(annotations: page?.annotations ?? [],types: types) as? [CPDFAnnotation] ?? []
  960. annotationArray += annos
  961. } else {
  962. var filterAnnos: [CPDFAnnotation] = page?.annotations ?? []
  963. let allAnnos = KMOCToolClass.filterAnnotation(annotations: filterAnnos,types: types) as? [CPDFAnnotation] ?? []
  964. annotationArray += allAnnos
  965. if typeArr.count > 0 {
  966. var theTypes = typeArr
  967. if typeArr.contains(CPDFAnnotation.kType.measureArrow) && typeArr.contains(CPDFAnnotation.kType.arrow) == false {
  968. theTypes.append(CPDFAnnotation.kType.arrow)
  969. }
  970. filterAnnos = (KMOCToolClass.filterAnnotation(annotations: filterAnnos, types: theTypes) as? [CPDFAnnotation]) ?? []
  971. }
  972. if (colorArr.count > 0) {
  973. filterAnnos = (KMOCToolClass.filterAnnotation(annotations: filterAnnos,colors: colorArr) as? [CPDFAnnotation]) ?? []
  974. }
  975. if (authorArr.count > 0) {
  976. filterAnnos = (KMOCToolClass.filterAnnotation(annotations: filterAnnos,authors: authorArr) as? [CPDFAnnotation]) ?? []
  977. }
  978. if typeArr.contains(CPDFAnnotation.kType.measureArrow) {
  979. if typeArr.contains(CPDFAnnotation.kType.arrow) == false {
  980. for anno in filterAnnos {
  981. if let data = anno as? CPDFLineAnnotation, data.type == CPDFAnnotation.kType.arrow && data.isMeasure == false {
  982. filterAnnos.removeObject(anno)
  983. }
  984. }
  985. }
  986. } else {
  987. for anno in filterAnnos {
  988. if let data = anno as? CPDFLineAnnotation, data.isMeasure {
  989. filterAnnos.removeObject(anno)
  990. }
  991. }
  992. }
  993. if stateArr.count > 0 {
  994. for anno in filterAnnos {
  995. let markState = self.noteReplyHanddler.fetchAnnoState(anno) ?? .unMarked
  996. let reviewState = self.noteReplyHanddler.fetchReviewState(anno) ?? .none
  997. if stateArr.contains(NSNumber(value: markState.rawValue)) == false || stateArr.contains(NSNumber(value: reviewState.rawValue)) == false {
  998. filterAnnos.removeObject(anno)
  999. }
  1000. }
  1001. }
  1002. annos = filterAnnos
  1003. }
  1004. //添加签名注释
  1005. for annotation in page?.annotations ?? [] {
  1006. if annotation.isKind(of: CPDFSignatureAnnotation.self) {
  1007. annos.append(annotation)
  1008. annotationArray.append(annotation)
  1009. }
  1010. }
  1011. for annotation in annos {
  1012. if annotation.isKind(of: KMTableAnnotation.self) {
  1013. annos.removeObject(annotation)
  1014. if annotationArray.contains(annotation) {
  1015. annotationArray.removeObject(annotation)
  1016. }
  1017. } else if annotation.annotationShouldDisplay() == false {
  1018. annos.removeObject(annotation)
  1019. if annotationArray.contains(annotation) {
  1020. annotationArray.removeObject(annotation)
  1021. }
  1022. } else if annotation.isKind(of: CPDFLinkAnnotation.self) {
  1023. annos.removeObject(annotation)
  1024. if annotationArray.contains(annotation) {
  1025. annotationArray.removeObject(annotation)
  1026. }
  1027. } else if annotation.isForm() {
  1028. annos.removeObject(annotation)
  1029. if annotationArray.contains(annotation) {
  1030. annotationArray.removeObject(annotation)
  1031. }
  1032. }
  1033. }
  1034. // 添加刷选后的注释
  1035. allAnnotation += annos
  1036. //添加所有annotation 用于筛选
  1037. // annotationArray += (page?.annotations ?? [])
  1038. // annotationArray += annos
  1039. }
  1040. // 处理排序
  1041. if self.noteSortType == .page {
  1042. /// 排序(升序)
  1043. if self.isAscendSort {
  1044. allAnnotation.sort {
  1045. let idx0 = $0.page?.pageIndex() ?? 0
  1046. let idx1 = $1.page?.pageIndex() ?? 0
  1047. return idx0 <= idx1
  1048. }
  1049. } else {
  1050. allAnnotation.sort {
  1051. let idx0 = $0.page?.pageIndex() ?? 0
  1052. let idx1 = $1.page?.pageIndex() ?? 0
  1053. return idx0 > idx1
  1054. }
  1055. }
  1056. } else if self.noteSortType == .time {
  1057. /// 排序(升序)
  1058. if self.isAscendSort {
  1059. allAnnotation.sort {
  1060. if $0.modificationDate() == nil {
  1061. return false
  1062. }
  1063. if $1.modificationDate() == nil {
  1064. return false
  1065. }
  1066. return $0.modificationDate() <= $1.modificationDate()
  1067. }
  1068. } else {
  1069. allAnnotation.sort {
  1070. if $0.modificationDate() == nil {
  1071. return false
  1072. }
  1073. if $1.modificationDate() == nil {
  1074. return false
  1075. }
  1076. return $0.modificationDate() > $1.modificationDate()
  1077. }
  1078. }
  1079. }
  1080. // 数据模型化
  1081. let model = KMAnnotationListModel()
  1082. var datas: [KMBotaAnnotationModel] = []
  1083. var prePageIdx: Int = NSNotFound
  1084. var preDate: Date?
  1085. var secM: KMBotaAnnotationSectionModel?
  1086. for anno in allAnnotation {
  1087. let item = KMBotaAnnotationModel()
  1088. item.anno = anno
  1089. item.showPage = self.annoListIsShowPage()
  1090. item.showTime = self.annoListIsShowTime()
  1091. item.showAuthor = self.annoListIsShowAnther()
  1092. // datas.append(item)
  1093. if self.noteSortType == .page {
  1094. let pageIdx = Int(anno.pageIndex())
  1095. if pageIdx != prePageIdx { // 不是同一个页面
  1096. secM = KMBotaAnnotationSectionModel()
  1097. model.datas.append(secM!)
  1098. }
  1099. secM?.items.append(item)
  1100. prePageIdx = Int(anno.pageIndex())
  1101. } else { // time
  1102. let date = anno.modificationDate()
  1103. if let same = date?.isSameDay(other: preDate), same == false { // 不是同一天
  1104. secM = KMBotaAnnotationSectionModel()
  1105. model.datas.append(secM!)
  1106. }
  1107. secM?.items.append(item)
  1108. preDate = date
  1109. }
  1110. item.sectionModel = secM
  1111. let replyAnnos = self.noteReplyHanddler.fetchReplyAnnotations(anno) ?? []
  1112. for replyAnno in replyAnnos {
  1113. let replyM = KMBotaAnnotationReplyModel()
  1114. replyM.anno = anno
  1115. replyM.replyAnno = replyAnno
  1116. // secM?.items.append(replyM)
  1117. replyM.annoModel = item
  1118. item.replyAnnos.append(replyM)
  1119. }
  1120. let footerI = KMBotaAnnotationFooterModel()
  1121. footerI.anno = anno
  1122. secM?.items.append(footerI)
  1123. item.footerModel = footerI
  1124. footerI.annoModel = item
  1125. }
  1126. // model.datas = datas
  1127. self.annoListModel = model
  1128. // 转换对象,用于数据显示
  1129. self.allAnnotations = annotationArray
  1130. self.noteFilterButton.isEnabled = self.allAnnotations.count >= 1
  1131. }
  1132. self.note_refrshUIIfNeed()
  1133. }
  1134. func reloadNoteForSearchMode() {
  1135. if self.noteSearchMode == false {
  1136. return
  1137. }
  1138. // 处理排序
  1139. if self.noteSortType == .page {
  1140. if self.isAscendSort { /// 排序(升序)
  1141. self.noteSearchArray.sort {
  1142. let idx0 = $0.anno?.page?.pageIndex() ?? 0
  1143. let idx1 = $1.anno?.page?.pageIndex() ?? 0
  1144. return idx0 <= idx1
  1145. }
  1146. } else {
  1147. self.noteSearchArray.sort {
  1148. let idx0 = $0.anno?.page?.pageIndex() ?? 0
  1149. let idx1 = $1.anno?.page?.pageIndex() ?? 0
  1150. return idx0 > idx1
  1151. }
  1152. }
  1153. } else if self.noteSortType == .time {
  1154. if self.isAscendSort { /// 排序(升序)
  1155. self.noteSearchArray.sort {
  1156. if $0.anno?.modificationDate() == nil {
  1157. return false
  1158. }
  1159. if $1.anno?.modificationDate() == nil {
  1160. return false
  1161. }
  1162. return $0.anno!.modificationDate() <= $1.anno!.modificationDate()
  1163. }
  1164. } else {
  1165. self.noteSearchArray.sort {
  1166. if $0.anno?.modificationDate() == nil {
  1167. return false
  1168. }
  1169. if $1.anno?.modificationDate() == nil {
  1170. return false
  1171. }
  1172. return $0.anno!.modificationDate() > $1.anno!.modificationDate()
  1173. }
  1174. }
  1175. }
  1176. self.note_refrshUIIfNeed()
  1177. }
  1178. func reloadNoteForSortMode() {
  1179. // 处理排序
  1180. var models: [KMBotaAnnotationModel] = []
  1181. for selM in self.annoListModel?.datas ?? [] {
  1182. for model in selM.items {
  1183. guard let annoModel = model as? KMBotaAnnotationModel else {
  1184. continue
  1185. }
  1186. models.append(annoModel)
  1187. }
  1188. }
  1189. if self.noteSortType == .page {
  1190. if self.isAscendSort { /// 排序(升序)
  1191. models.sort {
  1192. let idx0 = $0.anno?.page?.pageIndex() ?? 0
  1193. let idx1 = $1.anno?.page?.pageIndex() ?? 0
  1194. return idx0 <= idx1
  1195. }
  1196. } else {
  1197. models.sort {
  1198. let idx0 = $0.anno?.page?.pageIndex() ?? 0
  1199. let idx1 = $1.anno?.page?.pageIndex() ?? 0
  1200. return idx0 > idx1
  1201. }
  1202. }
  1203. } else if self.noteSortType == .time {
  1204. if self.isAscendSort { /// 排序(升序)
  1205. models.sort {
  1206. if $0.anno?.modificationDate() == nil {
  1207. return false
  1208. }
  1209. if $1.anno?.modificationDate() == nil {
  1210. return false
  1211. }
  1212. return $0.anno!.modificationDate() <= $1.anno!.modificationDate()
  1213. }
  1214. } else {
  1215. models.sort {
  1216. if $0.anno?.modificationDate() == nil {
  1217. return false
  1218. }
  1219. if $1.anno?.modificationDate() == nil {
  1220. return false
  1221. }
  1222. return $0.anno!.modificationDate() > $1.anno!.modificationDate()
  1223. }
  1224. }
  1225. }
  1226. // 数据模型\化
  1227. let listModel = KMAnnotationListModel()
  1228. var prePageIdx: Int = NSNotFound
  1229. var preDate: Date?
  1230. var secM: KMBotaAnnotationSectionModel?
  1231. for itemM in models {
  1232. guard let anno = itemM.anno else {
  1233. continue
  1234. }
  1235. if self.noteSortType == .page {
  1236. let pageIdx = Int(anno.pageIndex())
  1237. if pageIdx != prePageIdx { // 不是同一个页面
  1238. secM = KMBotaAnnotationSectionModel()
  1239. listModel.datas.append(secM!)
  1240. secM?.isExpand = itemM.sectionModel?.isExpand ?? true
  1241. }
  1242. secM?.items.append(itemM)
  1243. prePageIdx = Int(anno.pageIndex())
  1244. } else { // time
  1245. let date = anno.modificationDate()
  1246. if let same = date?.isSameDay(other: preDate), same == false { // 不是同一天
  1247. secM = KMBotaAnnotationSectionModel()
  1248. listModel.datas.append(secM!)
  1249. secM?.isExpand = itemM.sectionModel?.isExpand ?? true
  1250. }
  1251. secM?.items.append(itemM)
  1252. preDate = date
  1253. }
  1254. var tmpSelM = itemM.sectionModel
  1255. itemM.sectionModel = secM
  1256. let footerI = KMBotaAnnotationFooterModel()
  1257. footerI.anno = anno
  1258. secM?.items.append(footerI)
  1259. footerI.isExpand = itemM.footerModel?.isExpand ?? false
  1260. itemM.footerModel = footerI
  1261. footerI.annoModel = itemM
  1262. }
  1263. self.annoListModel = listModel
  1264. self.note_refrshUIIfNeed()
  1265. }
  1266. // 搜索 Action
  1267. func updateNoteFilterPredicate() {
  1268. var stringValue = self.noteSearchField.stringValue
  1269. // 清空数据
  1270. self.noteSearchArray.removeAll()
  1271. if stringValue.isEmpty {
  1272. for model in self.annoListModel?.datas ?? [] {
  1273. for item in model.items {
  1274. guard let _ = item.anno else {
  1275. continue
  1276. }
  1277. guard let data = item as? KMBotaAnnotationModel else {
  1278. continue
  1279. }
  1280. self.noteSearchArray.append(item)
  1281. }
  1282. }
  1283. } else {
  1284. // 忽略大小写
  1285. let caseInsensite = self.caseInsensitiveNoteSearch
  1286. if caseInsensite {
  1287. stringValue = stringValue.lowercased()
  1288. }
  1289. for model in self.annoListModel?.datas ?? [] {
  1290. for item in model.items {
  1291. guard let note = item.anno else {
  1292. continue
  1293. }
  1294. var noteString = ""
  1295. if let anno = note as? CPDFMarkupAnnotation {
  1296. noteString = anno.markupContent()
  1297. } else {
  1298. noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: note)
  1299. }
  1300. if caseInsensite {
  1301. noteString = noteString.lowercased()
  1302. }
  1303. guard let data = item as? KMBotaAnnotationModel else {
  1304. continue
  1305. }
  1306. if noteString.contains(stringValue) {
  1307. self.noteSearchArray.append(item)
  1308. }
  1309. }
  1310. }
  1311. }
  1312. self.note_refrshUIIfNeed()
  1313. }
  1314. @objc func selectSelectedNote(_ sender: AnyObject?) {
  1315. if self.hideNotes() == false {
  1316. let selectedNotes = self.selectedNotes()
  1317. if selectedNotes.count == 1 {
  1318. let annotation = selectedNotes.last!
  1319. self.listView?.go(to: annotation.bounds, on: annotation.page, animated: true)
  1320. // [pdfView scrollAnnotationToVisible:annotation];
  1321. // [pdfView setActiveAnnotation:annotation];
  1322. self.listView?.updateActiveAnnotations([annotation])
  1323. self.listView?.setNeedsDisplayAnnotationViewForVisiblePages()
  1324. if annotation is CPDFPolygonAnnotation || annotation is CPDFPolylineAnnotation {
  1325. self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: annotation)
  1326. } else if let anno = annotation as? CPDFLineAnnotation {
  1327. if anno.isMeasure {
  1328. self.listView?.pdfListViewDelegate.pdfListViewAnnotationMeasureInfoChange?(self.listView, with: annotation)
  1329. }
  1330. }
  1331. // }
  1332. }
  1333. // NSInteger column = [sender clickedColumn];
  1334. // if (column != -1) {
  1335. // NSString *colID = [[[sender tableColumns] objectAtIndex:column] identifier];
  1336. //
  1337. // if ([colID isEqualToString:@"color"]){
  1338. // for (PDFAnnotation *annotation in self.pdfView.activeAnnotations) {
  1339. // if (![annotation isKindOfClass:[PDFAnnotationChoiceWidget class]] &&
  1340. // ![annotation isKindOfClass:[PDFAnnotationButtonWidget class]] &&
  1341. // ![annotation isKindOfClass:[PDFAnnotationTextWidget class]]) {
  1342. // [[NSColorPanel sharedColorPanel] orderFront:nil];
  1343. // break;
  1344. // }
  1345. //
  1346. // }
  1347. // }
  1348. // }
  1349. }
  1350. }
  1351. func selectedNotes() -> [CPDFAnnotation] {
  1352. var selectedNotes: [CPDFAnnotation] = []
  1353. let rowIndexes = self.noteOutlineView.selectedRowIndexes
  1354. for row in rowIndexes {
  1355. let item = self.noteOutlineView.item(atRow: row)
  1356. if item is KMBotaAnnotationModel {
  1357. if let anno = (item as! KMBotaAnnotationModel).anno {
  1358. // if anno.type == nil {
  1359. // item = [(SKNoteText *)item note];
  1360. // }
  1361. if selectedNotes.contains(anno) == false {
  1362. selectedNotes.append(anno)
  1363. }
  1364. }
  1365. }
  1366. }
  1367. return selectedNotes
  1368. }
  1369. func clearAnnotationFilterData() {
  1370. if let _key = self.pdfDocument()?.documentURL?.path {
  1371. let userDefaults = UserDefaults.standard
  1372. let typeData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false)
  1373. userDefaults.set(typeData, forKey: NoteFilterVC.filterSelectTypeKey + _key)
  1374. let colorData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false)
  1375. userDefaults.set(colorData, forKey: NoteFilterVC.filterSelectColorKey + _key)
  1376. let authorData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false)
  1377. userDefaults.set(authorData, forKey: NoteFilterVC.filterSelectAuthorKey + _key)
  1378. userDefaults.synchronize()
  1379. }
  1380. }
  1381. private func _annoList_needRefreshUI(annos: [CPDFAnnotation]?) -> Bool {
  1382. guard let data = annos else {
  1383. return false
  1384. }
  1385. var need = false
  1386. for anno in data {
  1387. // if anno.isKind(of: KMTableAnnotation.self) == false {
  1388. // return true
  1389. // }
  1390. if anno.isForm() {
  1391. continue
  1392. }
  1393. if anno is CPDFLinkAnnotation {
  1394. continue
  1395. }
  1396. need = true
  1397. }
  1398. return need
  1399. }
  1400. }