KMSnapshotWindowController.swift 31 KB

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