KMMainViewController+Action.swift 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  1. //
  2. // KMMainViewController+Action.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by wanjun on 2022/12/15.
  6. //
  7. import Foundation
  8. extension KMMainViewController {
  9. func search(searchString: String, isCase: Bool, display: Bool = true, needShowAll: Bool = false) {
  10. }
  11. func removeSignatures(signatures:[CPDFSignature]) {
  12. for signature in signatures {
  13. self.listView.document.removeSignature(signature)
  14. }
  15. for i in 0..<self.listView.document.pageCount {
  16. guard let page = self.listView.document.page(at: i) else {
  17. continue
  18. }
  19. let annotations : [CPDFAnnotation] = page.annotations
  20. for j in 0..<annotations.count {
  21. let annotation = annotations[j]
  22. if annotation is CPDFSignatureWidgetAnnotation {
  23. (annotation as! CPDFSignatureWidgetAnnotation).updateAppearanceStream()
  24. }
  25. }
  26. }
  27. self.listView.setNeedsDisplayForVisiblePages()
  28. let tSignatures : [CPDFSignature] = self.listView.document.signatures()
  29. var mSignatures : [CPDFSignature] = []
  30. for sign in tSignatures {
  31. if sign.signers.count > 0 {
  32. mSignatures.append(sign)
  33. }
  34. }
  35. }
  36. func numberOfChars(_ str: String) -> (num: Int, indexN: Int) {
  37. var number = 0
  38. var indexN = 0
  39. guard str.count > 0 else {return (0, 0)}
  40. for i in 0...str.count - 1 {
  41. let c: unichar = (str as NSString).character(at: i)
  42. if (c >= 0x4E00) {
  43. number += 2
  44. }else {
  45. number += 1
  46. }
  47. if number > 56{
  48. indexN = i
  49. number = 100
  50. break
  51. }
  52. }
  53. return (number, indexN)
  54. }
  55. func fontSizes()->NSArray {
  56. return ["6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "24", "36", "48", "72", "96", "144", "288"]
  57. }
  58. func handleRightMouseDown(theEvent: NSEvent) -> Bool {
  59. if interactionMode == .presentation {
  60. listView.goToPreviousPage(nil)
  61. return true
  62. }
  63. return false
  64. }
  65. func useNativeFullScreen() -> Bool {
  66. var isFull = false
  67. let sel = NSSelectorFromString("toggleFullscreen:")
  68. if NSWindow.instancesRespond(to: sel) && UserDefaults.standard.bool(forKey: "SKUseLegacyFullScreenKey"){
  69. isFull = true
  70. }
  71. return isFull
  72. }
  73. func forceSubwindowsOnTop(_ flag: Bool) {
  74. }
  75. //MARK: menuItem Action
  76. @objc func menuItemEditingClick_FontColor(sender: NSMenuItem) {
  77. let color = listView.editingSelectionFontColor()
  78. let panel = NSColorPanel.shared
  79. panel.setTarget(self)
  80. panel.setAction(#selector(fontColorChangeAction))
  81. panel.orderFront(nil)
  82. panel.showsAlpha = false
  83. panel.color = color ?? NSColor.black
  84. }
  85. @objc func fontColorChangeAction(sender: NSColorPanel) {
  86. self.listView.setEditingSelectionFontColor(sender.color)
  87. }
  88. @objc func menuItemEditingClick_FontSize(sender: NSMenuItem) {
  89. let fontSize = self.fontSizes().object(at: sender.tag)
  90. self.listView.setEditingSelectionFontSize(CGFloat(Int(fontSize as! String)!))
  91. }
  92. @objc func addImageText(sender: NSMenuItem) {
  93. let event = NSApp.currentEvent
  94. let clickLocation = event?.locationInWindow
  95. var point = self.listView.convert(clickLocation!, from: NSApp.mainWindow?.contentView)
  96. var point2 = self.listView.convert(point, to: self.listView.currentPage())
  97. point2 = CGPoint(x: self.listView.bounds.width - point2.x, y: self.listView.bounds.height - point2.y)
  98. point = point2
  99. if sender.tag == 0 {
  100. KMPrint("添加文字")
  101. } else if sender.tag == 1 {
  102. } else if sender.tag == 2 {
  103. KMPrint("粘贴")
  104. }
  105. }
  106. @objc func menuItemEditingClick_CropImage(sender: NSMenuItem) {
  107. if self.listView.cropAreas != nil && self.listView.selectImageAreas != nil{
  108. self.listView.cropEditImageArea(self.listView.selectImageAreas, withBounds: self.listView.cropAreas.cropRect)
  109. }
  110. }
  111. @objc func menuItemAnnotationClick_toolModel(sender: NSMenuItem) {
  112. }
  113. @objc func changeAnnotationMode_itemAction(sender : NSMenuItem) {
  114. }
  115. @objc func menuItemAnnotationClick_add(sender : NSMenuItem) {
  116. var annotationType : CAnnotationType = .unkown
  117. switch sender.tag {
  118. case 0:
  119. annotationType = .highlight
  120. case 1:
  121. annotationType = .underline
  122. case 2:
  123. if sender.title == NSLocalizedString("Squiggly", comment: "") {
  124. annotationType = .squiggly
  125. } else {
  126. annotationType = .strikeOut
  127. }
  128. case 3:
  129. annotationType = .freeText
  130. case 4:
  131. annotationType = .anchored
  132. case 5:
  133. annotationType = .square
  134. case 6:
  135. annotationType = .circle
  136. case 7:
  137. annotationType = .line
  138. case 8:
  139. annotationType = .link
  140. case 9:
  141. return
  142. case 10:
  143. annotationType = .unkown
  144. default:
  145. break
  146. }
  147. if (annotationType != .link) {
  148. self.listView.addAnnotation(with: annotationType, selection: self.listView.currentSelection, page: self.listView.currentSelection.page, bounds: self.listView.currentSelection.bounds)
  149. self.listView.currentSelection = nil;
  150. return
  151. }
  152. // link
  153. let selection = self.listView.currentSelection
  154. DispatchQueue.main.async {
  155. Task { @MainActor in
  156. let annotation = self.listView.addAnnotation(with: annotationType, selection: selection, page: selection?.page, bounds: selection!.bounds)
  157. self.listView.currentSelection = nil;
  158. if (annotation != nil) {
  159. self.listView.updateActiveAnnotations([annotation!])
  160. }
  161. }
  162. }
  163. }
  164. @objc func menuItemAnnotationClick_addStype(sender: NSMenuItem) {
  165. Task { @MainActor in
  166. let idx = sender.tag
  167. if idx == 10 {
  168. if IAPProductsManager.default().isAvailableAllFunction() == false {
  169. KMPurchaseCompareWindowController.sharedInstance().showWindow(nil)
  170. return
  171. }
  172. }
  173. var point = mouseRightMenuEvent?.locationInWindow
  174. if (point == nil) {
  175. point = NSZeroPoint
  176. }
  177. let currentPoint: NSPoint = self.listView.convert(point!, from: self.listView.superview)
  178. let currentPage = self.listView.page(for: currentPoint, nearest: true)
  179. var pagePoint = self.listView.convert(currentPoint, to: currentPage)
  180. var annotation: CPDFAnnotation?
  181. if viewManager.isPDFReadMode {
  182. if (sender.tag == 0 || sender.tag == 7 || sender.tag == 8 || sender.tag == 9) { // Ink & Link & stamp & sign
  183. self.listView.toolMode = .CNoteToolMode
  184. }
  185. switch sender.tag {
  186. case 0:
  187. self.listView.annotationType = CAnnotationType.ink
  188. case 1:
  189. let defaultSize = self.listView.defaultSize(with: .freeText, in: currentPage)
  190. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  191. annotation = self.listView.addAnnotation(with: .freeText, selection: nil, page: currentPage, bounds: bounds)
  192. if ((annotation) != nil) {
  193. self.listView.updateActiveAnnotations([annotation!])
  194. self.listView.edit(annotation)
  195. }
  196. case 2:
  197. let defaultSize = self.listView.defaultSize(with: .anchored, in: currentPage)
  198. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  199. annotation = self.listView.addAnnotation(with: .anchored, selection: nil, page: currentPage, bounds: bounds)
  200. self.listView.edit(annotation)
  201. case 3:
  202. let defaultSize = self.listView.defaultSize(with: .square, in: currentPage)
  203. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  204. annotation = self.listView.addAnnotation(with: .square, selection: nil, page: currentPage, bounds: bounds)
  205. case 4:
  206. let defaultSize = self.listView.defaultSize(with: .circle, in: currentPage)
  207. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  208. annotation = self.listView.addAnnotation(with: .circle, selection: nil, page: currentPage, bounds: bounds)
  209. case 5:
  210. let defaultSize = self.listView.defaultSize(with: .arrow, in: currentPage)
  211. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  212. annotation = self.listView.addAnnotation(with: .arrow, selection: nil, page: currentPage, bounds: bounds)
  213. case 6:
  214. let defaultSize = self.listView.defaultSize(with: .line, in: currentPage)
  215. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  216. annotation = self.listView.addAnnotation(with: .line, selection: nil, page: currentPage, bounds: bounds)
  217. case 7:
  218. self.listView.annotationType = CAnnotationType.link
  219. toggleOpenRightSide()
  220. case 8:
  221. self.listView.annotationType = CAnnotationType.stamp
  222. toggleOpenRightSide()
  223. case 9:
  224. self.listView.annotationType = CAnnotationType.signSignature
  225. toggleOpenRightSide()
  226. default:
  227. break
  228. }
  229. } else {
  230. if (sender.tag == 7 || sender.tag == 8 || sender.tag == 9) { // Ink & Link & stamp & sign
  231. self.listView.toolMode = .CNoteToolMode
  232. }
  233. switch sender.tag {
  234. case 0:
  235. self.listView.toolMode = .CNoteToolMode
  236. self.listView.annotationType = CAnnotationType.ink
  237. case 1:
  238. let defaultSize = self.listView.defaultSize(with: .freeText, in: currentPage)
  239. if (pagePoint.x - defaultSize.width > 0){
  240. pagePoint.x -= defaultSize.width;
  241. }else{
  242. pagePoint.x = 0;
  243. }
  244. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  245. annotation = self.listView.addAnnotation(with: .freeText, selection: nil, page: currentPage, bounds: bounds)
  246. if ((annotation) != nil) {
  247. self.listView.edit(annotation)
  248. }
  249. case 2:
  250. let defaultSize = self.listView.defaultSize(with: .anchored, in: currentPage)
  251. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  252. annotation = self.listView.addAnnotation(with: .anchored, selection: nil, page: currentPage, bounds: bounds)
  253. self.listView.edit(annotation)
  254. case 3:
  255. let defaultSize = self.listView.defaultSize(with: .square, in: currentPage)
  256. if (pagePoint.x - defaultSize.width > 0){
  257. pagePoint.x -= defaultSize.width;
  258. }else{
  259. pagePoint.x = 0;
  260. }
  261. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  262. annotation = self.listView.addAnnotation(with: .square, selection: nil, page: currentPage, bounds: bounds)
  263. case 4:
  264. let defaultSize = self.listView.defaultSize(with: .circle, in: currentPage)
  265. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  266. annotation = self.listView.addAnnotation(with: .circle, selection: nil, page: currentPage, bounds: bounds)
  267. case 5:
  268. let defaultSize = self.listView.defaultSize(with: .arrow, in: currentPage)
  269. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  270. annotation = self.listView.addAnnotation(with: .arrow, selection: nil, page: currentPage, bounds: bounds)
  271. case 6:
  272. let defaultSize = self.listView.defaultSize(with: .line, in: currentPage)
  273. let bounds = CPDFListViewRectFromCenterAndSize(CPDFListViewIntegralPoint(pagePoint), defaultSize)
  274. annotation = self.listView.addAnnotation(with: .line, selection: nil, page: currentPage, bounds: bounds)
  275. case 7:
  276. self.listView.annotationType = CAnnotationType.link
  277. toggleOpenRightSide()
  278. case 8:
  279. self.listView.annotationType = CAnnotationType.stamp
  280. toggleOpenRightSide()
  281. case 9:
  282. self.listView.annotationType = CAnnotationType.signSignature
  283. toggleOpenRightSide()
  284. case 10:
  285. self.addImgAnnotationToView(center: pagePoint)
  286. default:
  287. break
  288. }
  289. if annotation != nil{
  290. self.listView.activeAnnotations.removeAllObjects()
  291. var newAnnonations : [CPDFAnnotation] = []
  292. newAnnonations.append(annotation!)
  293. self.listView.updateActiveAnnotations(newAnnonations)
  294. }
  295. }
  296. }
  297. }
  298. func addImgAnnotationToView(center: NSPoint) {
  299. let accessoryCtr = KMImageAccessoryController()
  300. let openPanel = NSOpenPanel()
  301. openPanel.allowedFileTypes = KMImageAccessoryController.supportedImageTypes()
  302. openPanel.allowsMultipleSelection = false
  303. openPanel.accessoryView = accessoryCtr.view
  304. openPanel.isAccessoryViewDisclosed = true
  305. openPanel.beginSheetModal(for: self.view.window!) { result in
  306. if result == .OK {
  307. let fileURL = openPanel.urls.first
  308. let filePath = fileURL!.path
  309. if filePath.pathExtension == "pdf" {
  310. let pdf = CPDFDocument(url: fileURL)
  311. if pdf!.isEncrypted {
  312. NSSound.beep()
  313. return
  314. }
  315. }
  316. let img = NSImage(contentsOfFile: filePath)
  317. let isRemoveBGColor = accessoryCtr.selectedButton.state == .on
  318. self.listView.addImageAnnotation(img, center: center, isRemoveBGColor: isRemoveBGColor)
  319. }
  320. }
  321. }
  322. // MARK: - Measure
  323. @objc func menuItemActionMeasureProperty(sender: NSMenuItem) {
  324. guard let anno = sender.representedObject as? CPDFAnnotation else {
  325. return
  326. }
  327. if distanceMeasureInfoWindowController?.window?.isVisible == true {
  328. distanceMeasureInfoWindowController?.hideFloatingWindow()
  329. } else if perimeterMeasureInfoWindowController?.window?.isVisible == true {
  330. perimeterMeasureInfoWindowController?.hideFloatingWindow()
  331. } else if areaMeasureInfoWindowController?.window?.isVisible == true {
  332. areaMeasureInfoWindowController?.hideFloatingWindow()
  333. }
  334. self.listView.updateActiveAnnotations([anno])
  335. self.pdfListViewChangeatioActiveAnnotations(self.listView, forActiveAnnotations: [anno], isRightMenu: false)
  336. self.listView.setNeedsDisplayForVisiblePages()
  337. }
  338. @objc func menuItemActionMeasureEditNote(sender: NSMenuItem) {
  339. guard let anno = sender.representedObject as? CPDFAnnotation else {
  340. return
  341. }
  342. self.listView.edit(anno)
  343. }
  344. @objc func menuItemActionMeasureSetting(sender: NSMenuItem) {
  345. guard let anno = sender.representedObject as? CPDFAnnotation else {
  346. return
  347. }
  348. self.listView.updateActiveAnnotations([anno])
  349. self.listView.setNeedsDisplayForVisiblePages()
  350. if let data = anno as? CPDFLineAnnotation, data.isMeasure {
  351. self.showMeasureDistanceSettingWindow(measureInfo: data.measureInfo)
  352. } else if let data = anno as? CPDFPolylineAnnotation {
  353. self.showMeasurePerimeterSettingWindow(measureInfo: data.measureInfo)
  354. } else if let data = anno as? CPDFPolygonAnnotation {
  355. self.showMeasureAreaSettingWindow(measureInfo: data.measureInfo)
  356. }
  357. }
  358. @objc func menuItemActionMeasureDelete(sender: NSMenuItem) {
  359. guard let anno = sender.representedObject as? CPDFAnnotation else {
  360. return
  361. }
  362. self.listView.remove(anno)
  363. }
  364. func showMeasureDistanceSettingWindow(measureInfo: CPDFDistanceMeasureInfo?, hideInfoWindow: Bool = true) {
  365. guard let mInfo = measureInfo else {
  366. return
  367. }
  368. let winC = CDistanceSettingWindowController(distanceMeasureInfo: mInfo)
  369. if hideInfoWindow {
  370. self.distanceMeasureInfoWindowController?.hideFloatingWindow()
  371. }
  372. winC.delegate = self
  373. winC.startModal("")
  374. }
  375. func showMeasurePerimeterSettingWindow(measureInfo: CPDFPerimeterMeasureInfo?, hideInfoWindow: Bool = true) {
  376. guard let mInfo = measureInfo else {
  377. return
  378. }
  379. let winC = CDistanceSettingWindowController(perimeterMeasureInfo: mInfo)
  380. if hideInfoWindow {
  381. self.perimeterMeasureInfoWindowController?.hideFloatingWindow()
  382. }
  383. winC.delegate = self
  384. winC.startModal("")
  385. }
  386. func showMeasureAreaSettingWindow(measureInfo: CPDFAreaMeasureInfo?, hideInfoWindow: Bool = true) {
  387. guard let mInfo = measureInfo else {
  388. return
  389. }
  390. let winC = CAreaSettingWindowController(measureInfo: mInfo)
  391. if hideInfoWindow {
  392. self.areaMeasureInfoWindowController?.hideFloatingWindow()
  393. }
  394. winC.delegate = self
  395. winC.startModal("")
  396. }
  397. // MARK: - 幻灯片
  398. func fadeInFullScreenWindow(with backgroundColor: NSColor, level: Int) {
  399. let view: NSView = self.view.window!.firstResponder as! NSView
  400. if view.isDescendant(of: pdfSplitView){
  401. self.view.window?.makeFirstResponder(nil)
  402. }
  403. self.mainWindow = self.view.window
  404. let fullScreenWindow = KMFullScreenWindow(screen: (self.mainWindow?.screen ?? NSScreen.main)!, bgColor: backgroundColor, level: NSWindow.Level.popUpMenu.rawValue, isMain: true)
  405. fullScreenWindow.interactionParent = self.view.window
  406. self.mainWindow?.delegate = nil
  407. fullScreenWindow.fadeInBlocking()
  408. self.browserWindowController?.window = fullScreenWindow
  409. fullScreenWindow.makeKey()
  410. let sel = NSSelectorFromString("setAnimationBehavior:")
  411. if self.mainWindow?.responds(to: sel) ?? false{
  412. self.mainWindow?.animationBehavior = .none
  413. }
  414. self.mainWindow?.orderOut(nil)
  415. if self.mainWindow?.responds(to: sel) ?? false{
  416. self.mainWindow?.animationBehavior = .default
  417. }
  418. fullScreenWindow.level = NSWindow.Level(rawValue: level)
  419. fullScreenWindow.orderFront(nil)
  420. }
  421. func fadeInFullScreenView(_ view: NSView, inset: CGFloat) {
  422. guard let fullScreenWindow = self.browserWindowController?.window as? KMFullScreenWindow else {
  423. return
  424. }
  425. let fadeWindow = KMFullScreenWindow(screen: fullScreenWindow.screen!, bgColor: fullScreenWindow.backgroundColor, level: fullScreenWindow.level.rawValue, isMain: false)
  426. fadeWindow.order(.above, relativeTo: fullScreenWindow.windowNumber)
  427. view.frame = NSInsetRect(fullScreenWindow.contentView?.bounds ?? .zero, inset, 0)
  428. fullScreenWindow.contentView?.addSubview(view)
  429. self.listView.layoutDocumentView()
  430. self.listView.requiresDisplay()
  431. fullScreenWindow.makeFirstResponder(self.listView)
  432. fullScreenWindow.recalculateKeyViewLoop()
  433. fullScreenWindow.delegate = self.browserWindowController
  434. fullScreenWindow.display()
  435. fadeWindow.fadeOut()
  436. }
  437. @IBAction func doZoomToAutoSelection(sender:NSMenuItem) {
  438. let rect = listView.currentSelectionRect()
  439. let page = listView.currentPage()
  440. if NSIsEmptyRect(rect) == false && page != nil {
  441. let isLegacy = NSScroller.responds(to: NSSelectorFromString("preferredScrollerStyle")) == false || NSScroller.preferredScrollerStyle == .legacy
  442. var bounds = listView.bounds
  443. var scale = 1.0
  444. if isLegacy {
  445. bounds.size.width -= NSScroller.scrollerWidth(for: .regular, scrollerStyle: listView.documentView().scrollerStyle)
  446. bounds.size.height -= NSScroller.scrollerWidth(for: .regular, scrollerStyle: listView.documentView().scrollerStyle)
  447. }
  448. if NSWidth(bounds) * NSHeight(rect) > NSWidth(rect) * NSHeight(bounds) {
  449. scale = NSHeight(bounds) / NSHeight(rect)
  450. } else {
  451. scale = NSWidth(bounds) / NSWidth(rect)
  452. }
  453. listView.setScaleFactor(scale, animated: false)
  454. let scrollView = listView.scroll()
  455. if isLegacy && scrollView?.hasHorizontalScroller == false || scrollView?.hasVerticalScroller == false {
  456. if ((scrollView?.hasVerticalScroller) != nil) {
  457. bounds.size.width -= NSScroller.scrollerWidth(for: .regular, scrollerStyle: listView.documentView().scrollerStyle)
  458. }
  459. if ((scrollView?.hasHorizontalScroller) != nil) {
  460. bounds.size.height -= NSScroller.scrollerWidth(for: .regular, scrollerStyle: listView.documentView().scrollerStyle)
  461. }
  462. if NSWidth(bounds) * NSHeight(rect) > NSWidth(rect) * NSHeight(bounds) {
  463. scale = NSHeight(bounds) / NSHeight(rect)
  464. } else {
  465. scale = NSWidth(bounds) / NSWidth(rect)
  466. }
  467. listView.setScaleFactor(scale, animated: false)
  468. }
  469. DispatchQueue.main.asyncAfter(deadline: .now() + 0.03) { [self] in
  470. let pagePoint = CGPoint(x: rect.origin.x, y: (rect.origin.y + rect.size.height))
  471. listView.go(toTargetPoint: pagePoint, on: page, at: .top)
  472. };
  473. }
  474. // 执行右键操作后,需要取消框选区域
  475. if self.listView.toolMode == .CSelectToolMode {
  476. objc_sync_enter(self)
  477. self.listView.selectionRect = NSZeroRect
  478. self.listView.selectionPageIndex = UInt(NSNotFound)
  479. objc_sync_exit(self)
  480. }
  481. }
  482. //MARK: - action
  483. // 开启左边栏
  484. @objc func openLeftPane() -> Void {
  485. self.model.leftPanelOpen = true
  486. applyLeftSideWidth(self.model.panelWidth+functionWidth, rightSideWidth: self.model.lastRightPanWidth)
  487. }
  488. // 关闭左边栏
  489. @objc func closeLeftPane() -> Void {
  490. self.model.leftPanelOpen = false
  491. applyLeftSideWidth(functionWidth, rightSideWidth: self.model.lastRightPanWidth)
  492. }
  493. func rename(_ sender: NSNotification) -> Void {
  494. if (self.view.window == nil || self.view.window!.isVisible == false) {
  495. return
  496. }
  497. let tabController = sender.object as? CTTabController
  498. if tabController?.title == self.document?.documentURL.deletingPathExtension().lastPathComponent {
  499. if let doc = self.myDocument, doc.isDocumentEdited {
  500. Task {
  501. let resp = await KMAlertTool.runModel(message: NSLocalizedString("File Updated", comment: ""), buttons: [NSLocalizedString("Save", comment: ""), NSLocalizedString("Cancel", comment: "")])
  502. if resp != .alertFirstButtonReturn { // 取消
  503. return
  504. }
  505. doc.updateChangeCount(.changeCleared)
  506. self.document?.write(to: doc.fileURL)
  507. Task { @MainActor in
  508. self._renameForSavePanel(tabController)
  509. }
  510. }
  511. return
  512. }
  513. self._renameForSavePanel(tabController)
  514. }
  515. }
  516. func savePdfAlertView() {
  517. if AutoSaveManager.manager.isSaving || AutoSaveManager.manager.isSaveNoti{
  518. return
  519. }
  520. AutoSaveManager.manager.isSaveNoti = true
  521. var num = 0
  522. if self.listView.document != nil{
  523. num = Int(self.listView.document.pageCount)
  524. }
  525. if Thread.current.isMainThread {
  526. self.beginProgressSheet(withMessage: NSLocalizedString("Saving PDF", comment: "") + "...", maxValue: UInt(num))
  527. } else {
  528. DispatchQueue.main.async {
  529. self.beginProgressSheet(withMessage: NSLocalizedString("Saving PDF", comment: "") + "...", maxValue: UInt(num))
  530. }
  531. }
  532. }
  533. func savePdfFinishAlertView() {
  534. if !AutoSaveManager.manager.isSaveNoti{
  535. return
  536. }
  537. AutoSaveManager.manager.isSaveNoti = false
  538. if Thread.current.isMainThread {
  539. self.dismissProgressSheet()
  540. } else {
  541. DispatchQueue.main.async {
  542. self.dismissProgressSheet()
  543. }
  544. }
  545. }
  546. private func _renameForSavePanel(_ tabC: CTTabController?) {
  547. let outputSavePanel = NSSavePanel()
  548. outputSavePanel.title = NSLocalizedString("Rename", comment: "")
  549. outputSavePanel.allowedFileTypes = ["pdf"]
  550. outputSavePanel.nameFieldStringValue = (self.document?.documentURL.lastPathComponent)!
  551. outputSavePanel.directoryURL = self.document?.documentURL.deletingLastPathComponent()
  552. let resp = outputSavePanel.runModal()
  553. if resp == .OK {
  554. let pdfDocument = CPDFDocument(url: self.document?.documentURL)
  555. let fileURL = pdfDocument?.documentURL
  556. let fileManager = FileManager.default
  557. let newFileURL = fileURL!.deletingLastPathComponent().appendingPathComponent(outputSavePanel.url!.lastPathComponent)
  558. var result = true
  559. do {
  560. try fileManager.moveItem(at: fileURL!, to: newFileURL)
  561. } catch {
  562. result = false
  563. KMPrint("Error renaming file! Threw: \(error.localizedDescription)")
  564. }
  565. if (result) {
  566. tabC?.title = outputSavePanel.url!.lastPathComponent
  567. if let newPdfDocument = CPDFDocument(url: newFileURL) {
  568. self.model.isSaveKeyChain = false
  569. newPdfDocument.unlock(withPassword: self.document?.password)
  570. if (newPdfDocument.pageCount > 0) {
  571. self.setDocument = newPdfDocument
  572. }
  573. }
  574. }
  575. } else {
  576. outputSavePanel.close()
  577. }
  578. }
  579. func showInFinder(_ sender: Any) -> Void {
  580. if sender is NSNotification {
  581. let tabController = (sender as! NSNotification).object as? CTTabController
  582. let path = self.document?.documentURL.deletingPathExtension().lastPathComponent
  583. if tabController?.title == path {
  584. if let file = self.myDocument?.fileURL {
  585. if FileManager.default.fileExists(atPath: file.path) {
  586. NSWorkspace.shared.activateFileViewerSelecting([file])
  587. }
  588. }
  589. }
  590. } else {
  591. guard let url = self.myDocument?.fileURL else { return }
  592. let file: URL = url
  593. if FileManager.default.fileExists(atPath: file.path) {
  594. NSWorkspace.shared.activateFileViewerSelecting([file])
  595. }
  596. }
  597. }
  598. func closeTab(_ sender: NSNotification) -> Void {
  599. }
  600. @IBAction func toggleSplitPDF(_ sender: Any) {
  601. }
  602. // MARK: - 图片注释
  603. @IBAction func imageAnnotation(_ sender: Any) {
  604. FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_Tools", withProperties: ["SubTbr_Btn": "Btn_SubTbr_Tools_Image"])
  605. guard IAPProductsManager.default().isAvailableAllFunction() else {
  606. KMPurchaseCompareWindowController.sharedInstance().showWindow(nil)
  607. return
  608. }
  609. let accessoryCtr = KMImageAccessoryController()
  610. let openPanel = NSOpenPanel()
  611. openPanel.allowedFileTypes = KMImageAccessoryController.supportedImageTypes()
  612. openPanel.allowsMultipleSelection = false
  613. openPanel.accessoryView = accessoryCtr.view
  614. openPanel.canSelectHiddenExtension = true
  615. openPanel.beginSheetModal(for: NSApp.mainWindow!) { [self] (result) in
  616. if result == .OK {
  617. guard let url = openPanel.url else {
  618. return
  619. }
  620. let filePath = url.path
  621. if filePath.pathExtension.lowercased() == "pdf" {
  622. if let pdf = PDFDocument(url: url), pdf.isEncrypted {
  623. NSSound.beep()
  624. return
  625. }
  626. }
  627. guard let image = NSImage(contentsOfFile: url.path) else {
  628. let alert = NSAlert()
  629. alert.alertStyle = .critical
  630. alert.messageText = String(format: NSLocalizedString("The file \"%@\" could not be opened.", comment: ""), url.lastPathComponent)
  631. alert.informativeText = NSLocalizedString("It may be damaged or use a file format that PDF Reader Pro doesn’t recognize.", comment: "")
  632. alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
  633. alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in
  634. if response == .alertFirstButtonReturn {
  635. // Handle cancel button clicked
  636. }
  637. }
  638. return
  639. }
  640. let isDamageImage: Bool = self.isDamageImage(image, imagePath: url.path)
  641. if isDamageImage {
  642. let alert = NSAlert()
  643. alert.alertStyle = .critical
  644. alert.messageText = String(format: NSLocalizedString("The file \"%@\" could not be opened.", comment: ""), url.lastPathComponent)
  645. alert.informativeText = NSLocalizedString("It may be damaged or use a file format that PDF Reader Pro doesn’t recognize.", comment: "")
  646. alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
  647. alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in
  648. if response == .alertFirstButtonReturn {
  649. // Handle cancel button clicked
  650. }
  651. }
  652. return
  653. }
  654. let isRemoveBGColor = accessoryCtr.selectedButton.state == .on
  655. listView.addAnnotation(with: image, isRemoveBGColor: isRemoveBGColor)
  656. if (self.listView.activeAnnotation != nil) && (self.listView.activeAnnotation.type == "Image") {
  657. }
  658. }
  659. }
  660. }
  661. @IBAction func tableAnnotation(_ sender: Any) {
  662. FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_Tools", withProperties: ["SubTbr_Btn": "Btn_SubTbr_Tools_Table"])
  663. guard IAPProductsManager.default().isAvailableAllFunction() else {
  664. let winC = KMPurchaseCompareWindowController.sharedInstance()
  665. winC?.kEventName = "Reading_Table_BuyNow"
  666. winC?.showWindow(nil)
  667. return
  668. }
  669. listView.addAnnotationWithTable()
  670. toggleOpenRightSide()
  671. }
  672. func isDamageImage(_ image: NSImage, imagePath path: String) -> Bool {
  673. let addImageAnnotation = (NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last! as NSString).appendingPathComponent(Bundle.main.bundleIdentifier!)
  674. if !FileManager.default.fileExists(atPath: addImageAnnotation) {
  675. try? FileManager.default.createDirectory(atPath: addImageAnnotation, withIntermediateDirectories: false, attributes: nil)
  676. }
  677. if let data = image.tiffRepresentation,
  678. let imageRep = NSBitmapImageRep(data: data) {
  679. imageRep.size = image.size
  680. var imageData: Data?
  681. if path.lowercased() == "png" {
  682. imageData = imageRep.representation(using: .png, properties: [:])
  683. } else {
  684. imageData = imageRep.representation(using: .jpeg, properties: [:])
  685. }
  686. if let imageData = imageData {
  687. let rPath = (addImageAnnotation as NSString).appendingPathComponent((self.tagString() as NSString).appendingPathExtension("png")!)
  688. if !((try? imageData.write(to: URL(fileURLWithPath: rPath), options: .atomicWrite)) != nil) {
  689. return true
  690. } else {
  691. return false
  692. }
  693. }
  694. }
  695. return false
  696. }
  697. func tagString() -> String {
  698. let dateFormatter = DateFormatter()
  699. dateFormatter.dateFormat = "yyMMddHHmmss"
  700. return "\(dateFormatter.string(from: Date()))\(Int.random(in: 0..<10000))"
  701. }
  702. }
  703. extension KMMainViewController {
  704. func changeModelAction(mode: CToolMode) {
  705. self.listView.toolMode = mode
  706. if mode == .CEditPDFToolMode {
  707. }
  708. }
  709. func aiTranslationPDFFileAction() {
  710. self._aiTranslationPDFFileAction()
  711. }
  712. private func _aiTranslationPDFFileAction() {
  713. let isExceedsLimit = self.isPDFPageCountExceedsLimit(filePath: (self.document?.documentURL.path)!)
  714. if KMTools.isFileGreaterThan10MB(atPath: (self.document?.documentURL.path)!) {
  715. let alert = NSAlert()
  716. alert.alertStyle = .critical
  717. alert.messageText = NSLocalizedString("The uploaded file size cannot exceed 10MB", comment: "")
  718. alert.runModal()
  719. return
  720. } else if isExceedsLimit {
  721. let alert = NSAlert()
  722. alert.alertStyle = .critical
  723. alert.messageText = NSLocalizedString("Documents cannot exceed 30 pages", comment: "")
  724. alert.runModal()
  725. return
  726. }
  727. let alert = NSAlert()
  728. alert.messageText = NSLocalizedString("Processing times may be longer for larger documents. Thank you for your patience.", comment: "")
  729. alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
  730. alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
  731. alert.beginSheetModal(for: view.window!) { [weak self] result in
  732. if (result == .alertFirstButtonReturn) {
  733. } else if result == .alertSecondButtonReturn {
  734. return
  735. }
  736. }
  737. }
  738. func showBatchWindow(type: KMBatchOperationType, filepaths: [String]?) {
  739. let batchWindowController = KMBatchOperateWindowController.sharedWindowController
  740. var array: [KMBatchOperateFile] = []
  741. for fpath in filepaths ?? [] {
  742. let batchOperateFile = KMBatchOperateFile(filePath: fpath, type: type)
  743. array.append(batchOperateFile)
  744. }
  745. batchWindowController.switchToOperateType(type, files: array)
  746. batchWindowController.window?.makeKeyAndOrderFront("")
  747. }
  748. func openNewWindowAlertWindow() {
  749. var needShowChooseWindow = false
  750. //#if VERSION_FREE
  751. if (!IAPProductsManager.default().isAvailableAllFunction()) {
  752. needShowChooseWindow = true
  753. }
  754. //#endif
  755. if needShowChooseWindow {
  756. let preferenceNoteShow = UserDefaults.standard.bool(forKey: KMTabbingHintShowFlag)
  757. if preferenceNoteShow {
  758. } else {
  759. if !KMDataManager.default.isTabbingWin{
  760. KMDataManager.default.isTabbingWin = true
  761. let tabbingWin: KMTabbingHintWindowController = KMTabbingHintWindowController()
  762. tabbingWin.selectCallBack = {[weak self] continueOrNot in
  763. KMDataManager.default.isTabbingWin = false
  764. if continueOrNot {
  765. self?.reopenDocument(forPaths: [])
  766. } else {
  767. }
  768. }
  769. self.km_beginSheet(windowC: tabbingWin)
  770. }
  771. }
  772. }else{
  773. handleTabbingLogic()
  774. }
  775. }
  776. func reopenDocument(forPaths paths: [String]) -> Void {
  777. let browser = KMBrowser.init() as KMBrowser
  778. browser.windowController = KMBrowserWindowController.init(browser: browser)
  779. browser.addHomeTabContents()
  780. browser.windowController.showWindow(self)
  781. }
  782. func handleTabbingLogic() {
  783. self.browserWindowController?.browser?.selectTabContents(at: 0, userGesture: true)
  784. }
  785. }
  786. //MARK: LeftSideViewController
  787. extension KMMainViewController {
  788. func leftSideViewCancelSelect() {
  789. if self.listView.isEditing() == true {
  790. if self.listView.editingAreas() != nil &&
  791. self.listView.editingAreas().count != 0 {
  792. let areas = self.listView.editingAreas().first
  793. if areas is CPDFEditTextArea {
  794. self.listView.clearEditingSelectCharItem()
  795. self.listView.updateEditing([])
  796. KMPrint("取消选中")
  797. }
  798. }
  799. }
  800. }
  801. }
  802. extension KMMainViewController {
  803. func documentAllowsEdit() -> Bool {
  804. if (self.listView.document.allowsCopying == false || self.listView.document.allowsPrinting == false) {
  805. let alert = NSAlert()
  806. alert.alertStyle = .critical
  807. alert.messageText = NSLocalizedString("This is a secured document. Editing is not permitted.", comment: "")
  808. alert.runModal()
  809. return false
  810. } else {
  811. return true
  812. }
  813. }
  814. func changeFont(_ sender: NSFontManager) {
  815. KMPrint("changeFont ...")
  816. if ((self.listView.activeAnnotation?.isKind(of: CPDFFreeTextAnnotation.self)) != nil) {
  817. let annotation: CPDFFreeTextAnnotation = self.listView.activeAnnotation as! CPDFFreeTextAnnotation
  818. var font = NSFont(name: annotation.fontName() ?? "Helvetica", size: (annotation.fontSize()) )
  819. font = sender.convert(font!)
  820. annotation.fontSize = font?.pointSize ?? 12
  821. self.listView.commitEditAnnotationFreeText(annotation)
  822. self.listView.setNeedsDisplay(annotation)
  823. }
  824. }
  825. func currentSetup() -> [String: Any] {
  826. var setup: [String: Any] = [:]
  827. var point = NSZeroPoint
  828. if listView == nil {
  829. return setup
  830. }
  831. let pageIndex = listView.currentPageIndexAndPoint(&point, rotated: nil)
  832. setup[kWindowFrameKey] = NSStringFromRect(mainWindow?.frame ?? NSZeroRect)
  833. setup[KMMainModel.Key.kLeftSidePaneWidth] = self.model.lastLeftPanWidth
  834. setup[KMMainModel.Key.kRightSidePaneWidth] = self.model.lastRightPanWidth
  835. setup[KMMainModel.Key.pageIndex] = pageIndex
  836. return setup
  837. }
  838. }