KMSnapshotWindowController.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. //
  2. // KMSnapshotWindowController.swift
  3. // PDF Master
  4. //
  5. // Created by tangchao on 2023/12/12.
  6. //
  7. import Cocoa
  8. @objc protocol KMSnapshotWindowControllerDelegate: NSObjectProtocol {
  9. @objc optional func snapshotControllerDidFinishSetup(_ controller: KMSnapshotWindowController)
  10. @objc optional func snapshotControllerWillClose(_ controller: KMSnapshotWindowController)
  11. @objc optional func snapshotControllerDidChange(_ controller: KMSnapshotWindowController)
  12. @objc optional func snapshotController(_ controller: KMSnapshotWindowController, miniaturizedRect isMiniaturize: Bool) -> NSRect
  13. }
  14. class KMSnapshotWindowController: NSWindowController {
  15. @IBOutlet var pdfView: KMSnapshotPDFView!
  16. var hasWindow = false
  17. var forceOnTop = false
  18. weak var delegate: KMSnapshotWindowControllerDelegate?
  19. var string: String = ""
  20. var pageLabel: String = "" {
  21. didSet {
  22. self.synchronizeWindowTitleWithDocumentName()
  23. }
  24. }
  25. var animating = false
  26. var thumbnail: NSImage?
  27. var windowImage: NSImage?
  28. /*
  29. extern NSString *SKSnapshotCurrentSetupKey;
  30. @property (nonatomic, readonly) NSUInteger pageIndex;
  31. @property (nonatomic, readonly) BOOL hasWindow;
  32. @property (nonatomic, readonly) NSDictionary *pageAndWindow;
  33. @property (nonatomic, readonly) NSDictionary *currentSetup;
  34. @property (nonatomic, readonly) NSAttributedString *thumbnailAttachment, *thumbnail512Attachment, *thumbnail256Attachment, *thumbnail128Attachment, *thumbnail64Attachment, *thumbnail32Attachment;
  35. - (void)setPdfDocument:(PDFDocument *)pdfDocument goToPageNumber:(NSInteger)pageNum rect:(NSRect)rect scaleFactor:(CGFloat)factor autoFits:(BOOL)autoFits;
  36. - (void)setPdfDocument:(PDFDocument *)pdfDocument setup:(NSDictionary *)setup;
  37. - (BOOL)isPageVisible:(PDFPage *)page;
  38. - (void)redisplay;
  39. - (NSImage *)thumbnailWithSize:(CGFloat)size;
  40. - (NSAttributedString *)thumbnailAttachmentWithSize:(CGFloat)size;
  41. - (void)miniaturize;
  42. - (void)deminiaturize;
  43. - (void)handleDocumentDidUnlockNotification:(NSNotification *)notification;
  44. - (void)handlePDFViewFrameChangedNotification:(NSNotification *)notification;
  45. - (void)handleViewChangedNotification:(NSNotification *)notification;
  46. - (void)handleDidAddRemoveAnnotationNotification:(NSNotification *)notification;
  47. - (void)handleDidMoveAnnotationNotification:(NSNotification *)notification;
  48. - (void)setNeedsDisplayInRect:(NSRect)rect ofPage:(PDFPage *)page;
  49. - (void)setNeedsDisplayForAnnotation:(PDFAnnotation *)annotation onPage:(PDFPage *)page;
  50. @end
  51. */
  52. /*
  53. #define EM_DASH_CHARACTER (unichar)0x2014
  54. NSString *SKSnapshotCurrentSetupKey = @"currentSetup";
  55. static char SKSnaphotWindowDefaultsObservationContext;
  56. */
  57. private let SMALL_DELAY = 0.1
  58. private let RESIZE_TIME_FACTOR = 0.6
  59. private let PAGE_KEY = "page"
  60. private let RECT_KEY = "rect"
  61. private let SCALEFACTOR_KEY = "scaleFactor"
  62. private let AUTOFITS_KEY = "autoFits"
  63. private let WINDOWFRAME_KEY = "windowFrame"
  64. private let HASWINDOW_KEY = "hasWindow"
  65. private let PAGELABEL_KEY = "pageLabel"
  66. private let PAGEANDWINDOW_KEY = "pageAndWindow"
  67. private let SKSnapshotWindowFrameAutosaveName = "SKSnapshotWindow"
  68. private let SKSnapshotViewChangedNotification = "SKSnapshotViewChangedNotification"
  69. private let SKSnapshotPageCellLabelKey = "label"
  70. private let SKSnapshotPageCellHasWindowKey = "hasWindow"
  71. deinit {
  72. KMPrint("KMSnapshotWindowController deinit.")
  73. NotificationCenter.default.removeObserver(self)
  74. }
  75. override var windowNibName: NSNib.Name? {
  76. return "SnapshotWindow"
  77. }
  78. override func windowDidLoad() {
  79. super.windowDidLoad()
  80. // if ([NSWindow instancesRespondToSelector:@selector(setTabbingMode:)])
  81. // [[self window] setTabbingMode:NSWindowTabbingModeDisallowed];
  82. // if ([NSWindow instancesRespondToSelector:@selector(toggleFullScreen:)])
  83. // [[self window] setCollectionBehavior:[[self window] collectionBehavior] | NSWindowCollectionBehaviorFullScreenAuxiliary];
  84. // if ([[self window] respondsToSelector:@selector(contentLayoutRect)]) {
  85. // [[self window] setStyleMask:[[self window] styleMask] | NSFullSizeContentViewWindowMask];
  86. // [pdfView setFrame:[[self window] contentLayoutRect]];
  87. // }
  88. if let data = self.window?.responds(to: NSSelectorFromString("contentLayoutRect")), data {
  89. self.window?.styleMask.insert(.fullSizeContentView)
  90. self.pdfView.frame = self.window?.contentLayoutRect ?? NSMakeRect(0, 0, 400, 400)
  91. }
  92. self._updateWindowLevel()
  93. // [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeys:[NSArray arrayWithObjects:SKSnapshotsOnTopKey, SKShouldAntiAliasKey, SKGreekingThresholdKey, SKBackgroundColorKey, SKPageBackgroundColorKey, nil] context:&SKSnaphotWindowDefaultsObservationContext];
  94. // the window is initialially exposed. The windowDidExpose notification is useless, it has nothing to do with showing the window
  95. self.hasWindow = true
  96. }
  97. /*
  98. + (NSSet *)keyPathsForValuesAffectingPageAndWindow {
  99. return [NSSet setWithObjects:PAGELABEL_KEY, HASWINDOW_KEY, nil];
  100. }
  101. // these should never be reached, but just to be sure
  102. - (void)windowDidDeminiaturize:(NSNotification *)notification {
  103. [self updateWindowLevel];
  104. [self setHasWindow:YES];
  105. }
  106. - (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName {
  107. return [NSString stringWithFormat:@"%@ %C %@", displayName, EM_DASH_CHARACTER, [NSString stringWithFormat:NSLocalizedString(@"Page %@", @""), [self pageLabel]]];
  108. }
  109. - (void)setNeedsDisplayInRect:(NSRect)rect ofPage:(PDFPage *)page {
  110. NSRect aRect = [pdfView convertRect:rect fromPage:page];
  111. CGFloat scale = [pdfView scaleFactor];
  112. CGFloat maxX = ceil(NSMaxX(aRect) + scale);
  113. CGFloat maxY = ceil(NSMaxY(aRect) + scale);
  114. CGFloat minX = floor(NSMinX(aRect) - scale);
  115. CGFloat minY = floor(NSMinY(aRect) - scale);
  116. aRect = NSIntersectionRect([pdfView bounds], NSMakeRect(minX, minY, maxX - minX, maxY - minY));
  117. if (NSIsEmptyRect(aRect) == NO)
  118. [pdfView setNeedsDisplayInRect:aRect];
  119. }
  120. - (void)setNeedsDisplayForAnnotation:(PDFAnnotation *)annotation onPage:(PDFPage *)page {
  121. [self setNeedsDisplayInRect:[annotation displayRect] ofPage:page];
  122. }
  123. - (void)redisplay {
  124. [pdfView requiresDisplay];
  125. }
  126. */
  127. @objc func handlePageChangedNotification(_ notification: NSNotification?) {
  128. // [self setPageLabel:[[pdfView currentPage] displayLabel]];
  129. let label = "\(self.pdfView.currentPage().pageIndex()+1)"
  130. self.pageLabel = label
  131. self.handlePDFViewFrameChangedNotification(nil)
  132. }
  133. @objc func handleDocumentDidUnlockNotification(_ notification: NSNotification) {
  134. let label = "\(self.pdfView.currentPage().pageIndex()+1)"
  135. self.pageLabel = label
  136. self.handlePDFViewFrameChangedNotification(nil)
  137. }
  138. @objc func handlePDFViewFrameChangedNotification(_ notification: NSNotification?) {
  139. if let _ = self.delegate?.snapshotControllerDidChange?(self) {
  140. let note = NSNotification(name: NSNotification.Name(rawValue: SKSnapshotViewChangedNotification), object: self)
  141. NotificationQueue.default.enqueue(note as Notification, postingStyle: .whenIdle, coalesceMask: .onName, forModes: nil)
  142. }
  143. }
  144. @objc func handleViewChangedNotification(_ notification: NSNotification) {
  145. self.updateString()
  146. self.delegate?.snapshotControllerDidChange?(self)
  147. }
  148. @objc func handleDidAddRemoveAnnotationNotification(_ notification: NSNotification) {
  149. // PDFAnnotation *annotation = [[notification userInfo] objectForKey:SKPDFViewAnnotationKey];
  150. // PDFPage *page = [[notification userInfo] objectForKey:SKPDFViewPageKey];
  151. guard let annotation = notification.userInfo?["annotation"] as? CPDFAnnotation else {
  152. return
  153. }
  154. guard let page = notification.userInfo?["page"] as? CPDFPage else {
  155. return
  156. }
  157. // if ([[page document] isEqual:[pdfView document]] && [self isPageVisible:page])
  158. // [self setNeedsDisplayForAnnotation:annotation onPage:page];
  159. }
  160. @objc func notifiyDidFinishSetup() {
  161. self.delegate?.snapshotControllerDidFinishSetup?(self)
  162. }
  163. /*
  164. - (void)handleDidMoveAnnotationNotification:(NSNotification *)notification {
  165. PDFAnnotation *annotation = [[notification userInfo] objectForKey:SKPDFViewAnnotationKey];
  166. PDFPage *oldPage = [[notification userInfo] objectForKey:SKPDFViewOldPageKey];
  167. PDFPage *newPage = [[notification userInfo] objectForKey:SKPDFViewNewPageKey];
  168. if ([[newPage document] isEqual:[pdfView document]]) {
  169. if ([self isPageVisible:oldPage])
  170. [self setNeedsDisplayForAnnotation:annotation onPage:oldPage];
  171. if ([self isPageVisible:newPage])
  172. [self setNeedsDisplayForAnnotation:annotation onPage:newPage];
  173. }
  174. }
  175. - (void)setPdfDocument:(PDFDocument *)pdfDocument setup:(NSDictionary *)setup {
  176. [self setPdfDocument:pdfDocument
  177. goToPageNumber:[[setup objectForKey:PAGE_KEY] unsignedIntegerValue]
  178. rect:NSRectFromString([setup objectForKey:RECT_KEY])
  179. scaleFactor:[[setup objectForKey:SCALEFACTOR_KEY] doubleValue]
  180. autoFits:[[setup objectForKey:AUTOFITS_KEY] boolValue]];
  181. [self setHasWindow:[[setup objectForKey:HASWINDOW_KEY] boolValue]];
  182. if ([setup objectForKey:WINDOWFRAME_KEY])
  183. [[self window] setFrame:NSRectFromString([setup objectForKey:WINDOWFRAME_KEY]) display:NO];
  184. }
  185. - (BOOL)isPageVisible:(PDFPage *)page {
  186. return [[page document] isEqual:[pdfView document]] && NSLocationInRange([page pageIndex], [pdfView displayedPageIndexRange]);
  187. }
  188. #pragma mark Acessors
  189. - (NSRect)bounds {
  190. NSView *clipView = [[[pdfView documentView] enclosingScrollView] contentView];
  191. return [pdfView convertRect:[pdfView convertRect:[clipView bounds] fromView:clipView] toPage:[pdfView currentPage]];
  192. }
  193. - (NSUInteger)pageIndex {
  194. return [[pdfView currentPage] pageIndex];
  195. }
  196. */
  197. func pageAndWindow() -> NSDictionary {
  198. return [SKSnapshotPageCellLabelKey : self.pageLabel, SKSnapshotPageCellHasWindowKey : NSNumber(booleanLiteral: self.hasWindow)]
  199. }
  200. func setForceOnTop(_ flag: Bool) {
  201. self.forceOnTop = flag
  202. if let data = self.window?.isVisible, data {
  203. self._updateWindowLevel()
  204. }
  205. }
  206. /*
  207. - (NSDictionary *)currentSetup {
  208. NSView *clipView = [[[pdfView documentView] enclosingScrollView] contentView];
  209. NSRect rect = [pdfView convertRect:[pdfView convertRect:[clipView bounds] fromView:clipView] toPage:[pdfView currentPage]];
  210. BOOL autoFits = [pdfView autoFits];
  211. return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:[self pageIndex]], PAGE_KEY, NSStringFromRect(rect), RECT_KEY, [NSNumber numberWithDouble:[pdfView scaleFactor]], SCALEFACTOR_KEY, [NSNumber numberWithBool:autoFits], AUTOFITS_KEY, [NSNumber numberWithBool:[[self window] isVisible]], HASWINDOW_KEY, NSStringFromRect([[self window] frame]), WINDOWFRAME_KEY, nil];
  212. }
  213. #pragma mark Actions
  214. - (IBAction)doGoToNextPage:(id)sender {
  215. [pdfView goToNextPage:sender];
  216. }
  217. - (IBAction)doGoToPreviousPage:(id)sender {
  218. [pdfView goToPreviousPage:sender];
  219. }
  220. - (IBAction)doGoToFirstPage:(id)sender {
  221. [pdfView goToFirstPage:sender];
  222. }
  223. - (IBAction)doGoToLastPage:(id)sender {
  224. [pdfView goToLastPage:sender];
  225. }
  226. - (IBAction)doGoBack:(id)sender {
  227. [pdfView goBack:sender];
  228. }
  229. - (IBAction)doGoForward:(id)sender {
  230. [pdfView goForward:sender];
  231. }
  232. - (IBAction)doZoomIn:(id)sender {
  233. [pdfView zoomIn:sender];
  234. }
  235. - (IBAction)doZoomOut:(id)sender {
  236. [pdfView zoomOut:sender];
  237. }
  238. - (IBAction)doZoomToPhysicalSize:(id)sender {
  239. [pdfView setPhysicalScaleFactor:1.0];
  240. }
  241. - (IBAction)doZoomToActualSize:(id)sender {
  242. [pdfView setScaleFactor:1.0];
  243. }
  244. - (IBAction)toggleAutoScale:(id)sender {
  245. [pdfView setAutoFits:[pdfView autoFits] == NO];
  246. }
  247. - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
  248. SEL action = [menuItem action];
  249. if (action == @selector(doGoToNextPage:)) {
  250. return [pdfView canGoToNextPage];
  251. } else if (action == @selector(doGoToPreviousPage:)) {
  252. return [pdfView canGoToPreviousPage];
  253. } else if (action == @selector(doGoToFirstPage:)) {
  254. return [pdfView canGoToFirstPage];
  255. } else if (action == @selector(doGoToLastPage:)) {
  256. return [pdfView canGoToLastPage];
  257. } else if (action == @selector(doGoBack:)) {
  258. return [pdfView canGoBack];
  259. } else if (action == @selector(doGoForward:)) {
  260. return [pdfView canGoForward];
  261. } else if (action == @selector(doZoomIn:)) {
  262. return [pdfView canZoomIn];
  263. } else if (action == @selector(doZoomOut:)) {
  264. return [pdfView canZoomOut];
  265. } else if (action == @selector(doZoomToActualSize:)) {
  266. return fabs([pdfView scaleFactor] - 1.0 ) > 0.01;
  267. } else if (action == @selector(doZoomToPhysicalSize:)) {
  268. return fabs([pdfView physicalScaleFactor] - 1.0 ) > 0.01;
  269. } else if (action == @selector(toggleAutoScale:)) {
  270. [menuItem setState:[pdfView autoFits] ? NSOnState : NSOffState];
  271. return YES;
  272. }
  273. return YES;
  274. }
  275. */
  276. // MARK: - Thumbnails
  277. func thumbnailWithSize(_ size: CGFloat) -> NSImage? {
  278. let clipView = self.pdfView.documentView().contentView
  279. var bounds = self.pdfView.convert(clipView.bounds, from: clipView)
  280. if (CGRectEqualToRect(CGRectZero, bounds) || CGRectIsNull(bounds)) {
  281. return nil
  282. }
  283. let imageRep = self.pdfView.bitmapImageRepForCachingDisplay(in: bounds)
  284. var transform: NSAffineTransform?
  285. var thumbnailSize = bounds.size
  286. var shadowBlurRadius: CGFloat = 0.0
  287. var shadowOffset: CGFloat = 0.0
  288. var image: NSImage?
  289. if let data = imageRep {
  290. self.pdfView.cacheDisplay(in: bounds, to: data)
  291. }
  292. bounds.origin = NSZeroPoint
  293. if (size > 0.0) {
  294. shadowBlurRadius = round(size / 32.0)
  295. shadowOffset = -ceil(shadowBlurRadius * 0.75)
  296. if (NSHeight(bounds) > NSWidth(bounds)) {
  297. thumbnailSize = NSMakeSize(round((size - 2.0 * shadowBlurRadius) * NSWidth(bounds) / NSHeight(bounds) + 2.0 * shadowBlurRadius), size);
  298. } else {
  299. thumbnailSize = NSMakeSize(size, round((size - 2.0 * shadowBlurRadius) * NSHeight(bounds) / NSWidth(bounds) + 2.0 * shadowBlurRadius));
  300. }
  301. transform = NSAffineTransform()
  302. transform?.translateX(by: shadowBlurRadius, yBy: shadowBlurRadius - shadowOffset)
  303. transform?.scaleX(by: (thumbnailSize.width - 2.0 * shadowBlurRadius) / NSWidth(bounds), yBy: (thumbnailSize.height - 2.0 * shadowBlurRadius) / NSHeight(bounds))
  304. }
  305. if (CGSizeEqualToSize(CGSizeZero, thumbnailSize)) {
  306. return nil
  307. }
  308. image = NSImage(size: thumbnailSize)
  309. if (CGSizeEqualToSize(CGSizeZero, image!.size)) {
  310. return nil
  311. }
  312. image?.lockFocus()
  313. NSGraphicsContext.current?.imageInterpolation = .high
  314. transform?.concat()
  315. NSGraphicsContext.saveGraphicsState()
  316. // [[PDFView defaultPageBackgroundColor] set];
  317. if (shadowBlurRadius > 0.0) {
  318. // [NSShadow setShadowWithColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.5] blurRadius:shadowBlurRadius yOffset:shadowOffset];
  319. }
  320. __NSRectFill(bounds)
  321. NSGraphicsContext.current?.imageInterpolation = .default
  322. NSGraphicsContext.restoreGraphicsState()
  323. imageRep?.draw(in: bounds)
  324. image?.unlockFocus()
  325. return image
  326. }
  327. /*
  328. - (NSAttributedString *)thumbnailAttachmentWithSize:(CGFloat)size {
  329. NSImage *image = [self thumbnailWithSize:size];
  330. NSFileWrapper *wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:[image TIFFRepresentation]];
  331. NSString *filename = [NSString stringWithFormat:@"snapshot_page_%lu.tiff",(unsigned long)( [self pageIndex] + 1)];
  332. [wrapper setFilename:filename];
  333. [wrapper setPreferredFilename:filename];
  334. NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper];
  335. [wrapper release];
  336. NSAttributedString *attrString = [NSAttributedString attributedStringWithAttachment:attachment];
  337. [attachment release];
  338. return attrString;
  339. }
  340. - (NSAttributedString *)thumbnailAttachment {
  341. return [self thumbnailAttachmentWithSize:0.0];
  342. }
  343. - (NSAttributedString *)thumbnail512Attachment {
  344. return [self thumbnailAttachmentWithSize:512.0];
  345. }
  346. - (NSAttributedString *)thumbnail256Attachment {
  347. return [self thumbnailAttachmentWithSize:256.0];
  348. }
  349. - (NSAttributedString *)thumbnail128Attachment {
  350. return [self thumbnailAttachmentWithSize:128.0];
  351. }
  352. - (NSAttributedString *)thumbnail64Attachment {
  353. return [self thumbnailAttachmentWithSize:64.0];
  354. }
  355. - (NSAttributedString *)thumbnail32Attachment {
  356. return [self thumbnailAttachmentWithSize:32.0];
  357. }
  358. */
  359. // MARK: - Miniaturize / Deminiaturize
  360. func miniaturizedRectForDockingRect(_ dockRect: NSRect) -> NSRect {
  361. let clipView = self.pdfView.documentView().contentView
  362. let sourceRect = clipView.convert(clipView.bounds, to: nil)
  363. var targetRect: NSRect = .zero
  364. let windowSize = self.window?.frame.size ?? .zero
  365. let thumbSize = self.thumbnail?.size ?? .zero
  366. var thumbRatio: CGFloat = 0
  367. if thumbSize.width > 0 {
  368. thumbRatio = thumbSize.height / thumbSize.width
  369. }
  370. var dockRatio: CGFloat = 0
  371. if NSWidth(dockRect) > 0 {
  372. dockRatio = NSHeight(dockRect) / NSWidth(dockRect)
  373. }
  374. var scaleFactor: CGFloat = 0
  375. var shadowRadius = round(fmax(thumbSize.width, thumbSize.height) / 32.0)
  376. var shadowOffset = ceil(0.75 * shadowRadius)
  377. if (thumbRatio > dockRatio) {
  378. targetRect = NSInsetRect(dockRect, 0.5 * NSWidth(dockRect) * (1.0 - dockRatio / thumbRatio), 0.0)
  379. scaleFactor = NSHeight(targetRect) / thumbSize.height
  380. } else {
  381. targetRect = NSInsetRect(dockRect, 0.0, 0.5 * NSHeight(dockRect) * (1.0 - thumbRatio / dockRatio))
  382. scaleFactor = NSWidth(targetRect) / thumbSize.width
  383. }
  384. shadowRadius *= scaleFactor
  385. shadowOffset *= scaleFactor
  386. targetRect = NSOffsetRect(NSInsetRect(targetRect, shadowRadius, shadowRadius), 0.0, shadowOffset)
  387. if thumbRatio > dockRatio {
  388. if NSHeight(sourceRect) != 0 {
  389. scaleFactor = NSHeight(targetRect) / NSHeight(sourceRect)
  390. }
  391. } else {
  392. if NSWidth(sourceRect) != 0 {
  393. scaleFactor = NSWidth(targetRect) / NSWidth(sourceRect)
  394. }
  395. }
  396. return NSMakeRect(NSMinX(targetRect) - scaleFactor * NSMinX(sourceRect), NSMinY(targetRect) - scaleFactor * NSMinY(sourceRect), scaleFactor * windowSize.width, scaleFactor * windowSize.height);
  397. }
  398. func miniaturizeWindowFromRect(_ startRect: NSRect, toRect endRect: NSRect) {
  399. if (self.windowImage == nil) {
  400. self.windowImage = (self.window as? KMSnapshotWindow)?.windowImage
  401. }
  402. let miniaturizeWindow = KMAnimatedBorderlessWindow(contentRect: startRect)
  403. miniaturizeWindow.level = .floating
  404. miniaturizeWindow.backgroundImage = self.windowImage
  405. miniaturizeWindow.orderFront(nil)
  406. self.animating = true
  407. NSAnimationContext.runAnimationGroup { context in
  408. context.duration = RESIZE_TIME_FACTOR * miniaturizeWindow.animationResizeTime(endRect)
  409. miniaturizeWindow.animator().setFrame(endRect, display: true)
  410. } completionHandler: {
  411. if self.hasWindow {
  412. if let data = self.window?.responds(to: NSSelectorFromString("setAnimationBehavior:")), data {
  413. self.window?.animationBehavior = .none
  414. }
  415. self.window?.orderFront(nil)
  416. self._updateWindowLevel()
  417. if let data = self.window?.responds(to: NSSelectorFromString("setAnimationBehavior:")), data {
  418. self.window?.animationBehavior = .default
  419. }
  420. }
  421. miniaturizeWindow.orderOut(nil)
  422. self.animating = false
  423. }
  424. }
  425. func miniaturize() {
  426. if (self.animating) {
  427. return
  428. }
  429. if let dockRect = self.delegate?.snapshotController?(self, miniaturizedRect: true) {
  430. let startRect = self.window?.frame ?? .zero
  431. let endRect = self.miniaturizedRectForDockingRect(dockRect)
  432. self.miniaturizeWindowFromRect(startRect, toRect: endRect)
  433. if let data = self.window?.responds(to: NSSelectorFromString("setAnimationBehavior:")), data {
  434. self.window?.animationBehavior = .none
  435. }
  436. }
  437. self.window?.orderOut(nil)
  438. if let data = self.window?.responds(to: NSSelectorFromString("setAnimationBehavior:")), data {
  439. self.window?.animationBehavior = .default
  440. }
  441. self.hasWindow = false
  442. }
  443. func deminiaturize() {
  444. if (self.animating) {
  445. return
  446. }
  447. if let dockRect = self.delegate?.snapshotController?(self, miniaturizedRect: false) {
  448. let endRect = self.window?.frame ?? .zero
  449. let startRect = self.miniaturizedRectForDockingRect(dockRect)
  450. self.miniaturizeWindowFromRect(startRect, toRect: endRect)
  451. // SKDESTROY(windowImage);
  452. } else {
  453. self.showWindow(self)
  454. }
  455. self.hasWindow = true
  456. }
  457. /*
  458. #pragma mark KVO
  459. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  460. if (context == &SKSnaphotWindowDefaultsObservationContext) {
  461. NSString *key = [keyPath substringFromIndex:7];
  462. if ([key isEqualToString:SKSnapshotsOnTopKey]) {
  463. if ([[self window] isVisible])
  464. [self updateWindowLevel];
  465. } else if ([key isEqualToString:SKShouldAntiAliasKey]) {
  466. [pdfView setShouldAntiAlias:[[NSUserDefaults standardUserDefaults] boolForKey:SKShouldAntiAliasKey]];
  467. [pdfView applyDefaultInterpolationQuality];
  468. } else if ([key isEqualToString:SKGreekingThresholdKey]) {
  469. [pdfView setGreekingThreshold:[[NSUserDefaults standardUserDefaults] floatForKey:SKGreekingThresholdKey]];
  470. } else if ([key isEqualToString:SKBackgroundColorKey]) {
  471. [pdfView setBackgroundColor:[[NSUserDefaults standardUserDefaults] colorForKey:SKBackgroundColorKey]];
  472. } else if ([key isEqualToString:SKPageBackgroundColorKey]) {
  473. [pdfView applyDefaultPageBackgroundColor];
  474. }
  475. } else {
  476. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  477. }
  478. }
  479. */
  480. func updateString() {
  481. var mutableString = ""
  482. let clipView = self.pdfView.documentView().contentView
  483. let rect = clipView.convert(clipView.visibleRect, to: self.pdfView)
  484. for page in self.pdfView.displayedPages() {
  485. guard let _page = page as? CPDFPage else {
  486. continue
  487. }
  488. let frame = self.pdfView.convert(rect, to: _page)
  489. let sel = _page.selection(for: frame)
  490. if let data = sel?.hasCharacters(), data {
  491. if mutableString.isEmpty == false {
  492. mutableString.append("\n")
  493. }
  494. mutableString.append(sel?.string() ?? "")
  495. }
  496. }
  497. self.string = mutableString
  498. }
  499. @objc func goToRect(_ rectValue: NSValue) {
  500. self.pdfView.go(to: rectValue.rectValue, on: self.pdfView.currentPage())
  501. self.pdfView.resetHistory()
  502. self.updateString()
  503. self.window?.makeFirstResponder(self.pdfView)
  504. self.handlePageChangedNotification(nil)
  505. //
  506. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePageChangedNotification:)
  507. // name:PDFViewPageChangedNotification object:pdfView];
  508. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDocumentDidUnlockNotification:)
  509. // name:PDFDocumentDidUnlockNotification object:[pdfView document]];
  510. //
  511. // NSView *clipView = [[[pdfView documentView] enclosingScrollView] contentView];
  512. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePDFViewFrameChangedNotification:)
  513. // name:NSViewFrameDidChangeNotification object:clipView];
  514. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePDFViewFrameChangedNotification:)
  515. // name:NSViewBoundsDidChangeNotification object:clipView];
  516. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleViewChangedNotification:)
  517. // name:SKSnapshotViewChangedNotification object:self];
  518. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidAddRemoveAnnotationNotification:)
  519. // name:SKPDFViewDidAddAnnotationNotification object:nil];
  520. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidAddRemoveAnnotationNotification:)
  521. // name:SKPDFViewDidRemoveAnnotationNotification object:nil];
  522. // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidMoveAnnotationNotification:)
  523. // name:SKPDFViewDidMoveAnnotationNotification object:nil];
  524. self.perform(#selector(notifiyDidFinishSetup), with: nil, afterDelay: SMALL_DELAY)
  525. //
  526. if self.hasWindow {
  527. self.showWindow(nil)
  528. }
  529. }
  530. func setPdfDocument(_ pdfDocument: CPDFDocument, goToPageNumber pageNum: Int, rect: NSRect, scaleFactor factor: CGFloat, autoFits: Bool) {
  531. let window = self.window
  532. self.pdfView.scaleFactor = factor
  533. self.pdfView.autoScales = false
  534. self.pdfView.displaysPageBreaks = false
  535. self.pdfView.displayBox = .cropBox
  536. // [pdfView setShouldAntiAlias:[[NSUserDefaults standardUserDefaults] floatForKey:]];
  537. self.pdfView.setShouldAntiAlias(UserDefaults.standard.bool(forKey: SKShouldAntiAliasKey))
  538. self.pdfView.setGreekingThreshold(UserDefaults.standard.float(forKey: SKGreekingThresholdKey).cgFloat)
  539. self.pdfView.backgroundColor = UserDefaults.standard.color(forKey: SKBackgroundColorKey)
  540. // [pdfView applyDefaultPageBackgroundColor];
  541. // [pdfView applyDefaultInterpolationQuality];
  542. self.pdfView.document = pdfDocument
  543. // [self setWindowFrameAutosaveNameOrCascade:SKSnapshotWindowFrameAutosaveName];
  544. if let controlView = self.pdfView.scalePopUpButton {
  545. var controlFrame: NSRect = .zero
  546. var frame: NSRect = .zero
  547. NSDivideRect(self.pdfView.frame, &controlFrame, &frame, NSHeight(controlView.frame), .minY)
  548. // NSDivideRect([pdfView frame], &controlFrame, &frame, NSHeight([controlView frame]), NSMinYEdge);
  549. controlFrame.size.width = NSWidth(controlView.frame)
  550. controlView.frame = controlFrame
  551. controlView.autoresizingMask = [.maxXMargin, .maxYMargin]
  552. window?.contentView?.addSubview(controlView)
  553. self.pdfView.frame = frame
  554. window?.backgroundColor = NSColor(calibratedWhite: 0.97, alpha: 1)
  555. let page = pdfDocument.page(at: UInt(pageNum))
  556. frame = self.pdfView.convert(rect, from: page)
  557. var view: NSView?
  558. frame = self.pdfView.convert(frame, to: view)
  559. frame.size.height += NSHeight(controlFrame)
  560. // frame = [NSWindow frameRectForContentRect:frame styleMask:[window styleMask] & ~NSWindowStyleMaskFullSizeContentView];
  561. var styleMask = window?.styleMask
  562. styleMask?.remove(.fullSizeContentView)
  563. frame = NSWindow.frameRect(forContentRect: frame, styleMask: styleMask!)
  564. frame.origin.x = NSMinX(window!.frame)
  565. frame.origin.y = NSMaxY(window!.frame) - NSHeight(frame)
  566. self.window?.setFrame(frame, display: false, animate: false)
  567. }
  568. self.pdfView.go(toPageIndex: pageNum, animated: false)
  569. if (autoFits) {
  570. self.pdfView.autoFits = autoFits
  571. }
  572. // Delayed to allow PDFView to finish its bookkeeping
  573. // fixes bug of apparently ignoring the point but getting the page right.
  574. self.perform(#selector(goToRect), with: NSValue(rect: rect), afterDelay: SMALL_DELAY)
  575. }
  576. }
  577. // MARK: - Private Methods
  578. extension KMSnapshotWindowController {
  579. private func _updateWindowLevel() {
  580. let onTop = self.forceOnTop || UserDefaults.standard.bool(forKey: SKSnapshotsOnTopKey)
  581. self.window?.level = onTop ? .floating : .normal
  582. self.window?.hidesOnDeactivate = onTop
  583. }
  584. }
  585. extension KMSnapshotWindowController: NSWindowDelegate {
  586. func windowWillClose(_ notification: Notification) {
  587. // @try { [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeys:[NSArray arrayWithObjects:SKSnapshotsOnTopKey, SKShouldAntiAliasKey, SKGreekingThresholdKey, SKBackgroundColorKey, SKPageBackgroundColorKey, nil]]; }
  588. // @catch (id e) {}
  589. self.delegate?.snapshotControllerWillClose?(self)
  590. self.delegate = nil
  591. // Yosemite and El Capitan have a retain cycle when we leave the PDFView with a document
  592. // if (RUNNING_AFTER(10_9) && RUNNING_BEFORE(10_12))
  593. self.pdfView.document = nil
  594. }
  595. func windowDidMiniaturize(_ notification: Notification) {
  596. self.window?.orderOut(nil)
  597. self.hasWindow = false
  598. }
  599. }