OSDN Git Service

srt: change format string from "utf8" to "utf-8"
[handbrake-jp/handbrake-jp-git.git] / macosx / HBPreviewController.mm
1 /* $Id: HBPreviewController.mm,v 1.11 2005/08/01 15:10:44 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #import "HBPreviewController.h"
8 #import "Controller.h"
9
10 @implementation QTMovieView ( HBQTkitExt )
11 - (void) mouseMoved:(NSEvent *)theEvent
12 {
13     [super mouseMoved:theEvent];
14 }
15 @end
16
17 @interface PreviewController (Private)
18
19 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize;
20 - (void)resizeSheetForViewSize: (NSSize)viewSize;
21 - (void)setViewSize: (NSSize)viewSize;
22 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize;
23
24 @end
25
26 @implementation PreviewController
27
28 - (id)init
29 {
30         if (self = [super initWithWindowNibName:@"PicturePreview"])
31         {
32         // NSWindowController likes to lazily load its window. However since
33         // this controller tries to set all sorts of outlets before the window
34         // is displayed, we need it to load immediately. The correct way to do
35         // this, according to the documentation, is simply to invoke the window
36         // getter once.
37         //
38         // If/when we switch a lot of this stuff to bindings, this can probably
39         // go away.
40         [self window];
41         
42                 fPicturePreviews = [[NSMutableDictionary dictionaryWithCapacity: HB_NUM_HBLIB_PICTURES] retain];
43         /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
44         int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
45         fPreviewLibhb = hb_init(loggingLevel, 0);
46         
47         }
48         return self;
49 }
50
51
52
53 //------------------------------------------------------------------------------------
54 // Displays and brings the picture window to the front
55 //------------------------------------------------------------------------------------
56 - (IBAction) showPreviewWindow: (id)sender
57 {
58     [self showWindow:sender];
59     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"PreviewWindowIsOpen"];
60     
61     /* lets set the preview window to accept mouse moved events */
62     [fPreviewWindow setAcceptsMouseMovedEvents:YES];
63     hudTimerSeconds = 0;
64     [self pictureSliderChanged:nil];
65     [self startReceivingLibhbNotifications];
66 }
67
68 - (void)setHBController: (HBController *)controller
69 {
70     fHBController = controller;
71 }
72
73 - (void)awakeFromNib
74 {
75     [fPreviewWindow setDelegate:self];
76     if( ![[self window] setFrameUsingName:@"Preview"] )
77         [[self window] center];
78     [self setWindowFrameAutosaveName:@"Preview"];
79     [[self window] setExcludedFromWindowsMenu:YES];
80     
81     /* lets set the preview window to accept mouse moved events */
82     [fPreviewWindow setAcceptsMouseMovedEvents:YES];
83     //[self pictureSliderChanged:nil];
84     [self startReceivingLibhbNotifications];
85     
86     isFullScreen = NO;
87     hudTimerSeconds = 0;
88     /* we set the progress indicator to not use threaded animation
89      * as it causes a conflict with the qtmovieview's controllerbar
90     */
91     [fMovieCreationProgressIndicator setUsesThreadedAnimation:NO];
92     
93     /* Setup our layers for core animation */
94     [fPictureViewArea setWantsLayer:YES];
95     [fPictureView setWantsLayer:YES];
96
97     [fCancelPreviewMovieButton setWantsLayer:YES];
98     [fMovieCreationProgressIndicator setWantsLayer:YES];
99
100     [fPictureControlBox setWantsLayer:YES];
101     [fEncodingControlBox setWantsLayer:YES];
102         [fMovieView setWantsLayer:YES];
103         [fMovieView setHidden:YES];
104     [fMovieView setDelegate:self];
105
106     /* Since the xib has everything off center for easy acess
107      * we align our views and windows here we an align to anything
108      * since it will actually change later upon source load, but
109      * for convenience we will use the fPictureViewArea
110      */
111      
112      /* Align the still preview image view to the picture box */
113      [fPictureView setFrameSize:[fPictureViewArea frame].size];
114      [fMovieView setFrameSize:[fPictureViewArea frame].size];
115      //[fPreviewWindow setFrameSize:[fPictureViewArea frame].size];
116     
117     
118 }
119 - (BOOL)acceptsMouseMovedEvents
120 {
121     return YES;
122 }
123
124 - (void)windowWillClose:(NSNotification *)aNotification
125 {
126     /* Upon Closing the picture window, we make sure we clean up any
127      * preview movie that might be playing
128      */
129     hb_stop( fPreviewLibhb );
130     isEncoding = NO;
131     // Show the picture view
132     [fPictureView setHidden:NO];
133     [fMovieView pause:nil];
134     [fMovieView setHidden:YES];
135         [fMovieView setMovie:nil];
136
137     isFullScreen = NO;
138     hudTimerSeconds = 0;
139     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"PreviewWindowIsOpen"];
140 }
141
142 - (BOOL)windowShouldClose:(id)fPictureWindow
143 {
144      return YES;
145 }
146
147 - (void) dealloc
148 {
149     hb_stop(fPreviewLibhb);
150     if (fPreviewMoviePath)
151     {
152         [[NSFileManager defaultManager] removeFileAtPath:fPreviewMoviePath handler:nil];
153         [fPreviewMoviePath release];
154     }    
155     
156     [fLibhbTimer invalidate];
157     [fLibhbTimer release];
158     
159     [fHudTimer invalidate];
160     [fHudTimer release];
161     
162     [fPicturePreviews release];
163     [fFullScreenWindow release];
164
165     [super dealloc];
166 }
167
168 - (void) SetHandle: (hb_handle_t *) handle
169 {
170     fHandle = handle;
171     
172
173     
174     /* we set the preview length popup in seconds */
175     [fPreviewMovieLengthPopUp removeAllItems];
176     [fPreviewMovieLengthPopUp addItemWithTitle: @"5"];
177     [fPreviewMovieLengthPopUp addItemWithTitle: @"10"];
178     [fPreviewMovieLengthPopUp addItemWithTitle: @"15"];
179     [fPreviewMovieLengthPopUp addItemWithTitle: @"20"];
180     [fPreviewMovieLengthPopUp addItemWithTitle: @"25"];
181     [fPreviewMovieLengthPopUp addItemWithTitle: @"30"];
182     [fPreviewMovieLengthPopUp addItemWithTitle: @"35"];
183     [fPreviewMovieLengthPopUp addItemWithTitle: @"40"];
184     [fPreviewMovieLengthPopUp addItemWithTitle: @"45"];
185     [fPreviewMovieLengthPopUp addItemWithTitle: @"50"];
186     [fPreviewMovieLengthPopUp addItemWithTitle: @"55"];
187     [fPreviewMovieLengthPopUp addItemWithTitle: @"60"];
188     
189     /* adjust the preview slider length */
190     /* We use our advance pref to determine how many previews we scanned */
191     int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
192     [fPictureSlider setMaxValue: hb_num_previews - 1.0];
193     [fPictureSlider setNumberOfTickMarks: hb_num_previews];
194     
195     if ([[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"])
196     {
197         [fPreviewMovieLengthPopUp selectItemWithTitle:[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"]];
198     }
199     else
200     {
201         /* currently hard set default to 10 seconds */
202         [fPreviewMovieLengthPopUp selectItemAtIndex: 1];
203     }
204 }
205
206 - (void) SetTitle: (hb_title_t *) title
207 {
208     hb_job_t * job = title->job;
209     
210     fTitle = title;
211     fPicture = 0;
212     MaxOutputWidth = title->width - job->crop[2] - job->crop[3];
213     MaxOutputHeight = title->height - job->crop[0] - job->crop[1];
214     [self SettingsChanged: nil];
215 }
216
217
218
219 // Adjusts the window to draw the current picture (fPicture) adjusting its size as
220 // necessary to display as much of the picture as possible.
221 - (void) displayPreview
222 {
223     hb_job_t * job = fTitle->job;
224     /* lets make sure that the still picture view is not hidden and that 
225      * the movie preview is 
226      */
227     [fMovieView pause:nil];
228     [fMovieView setHidden:YES];
229         [fMovieView setMovie:nil];
230     [fMovieCreationProgressIndicator stopAnimation: nil];
231     [fMovieCreationProgressIndicator setHidden: YES];
232     
233     [fPictureView setHidden:NO];
234     
235     //[fHBController writeToActivityLog: "displayPreview called"];
236     
237     NSImage *fPreviewImage = [self imageForPicture: fPicture];
238     NSSize imageScaledSize = [fPreviewImage size];
239     [fPictureView setImage: fPreviewImage];
240     
241     NSSize displaySize = NSMakeSize( ( CGFloat )fTitle->width, ( CGFloat )fTitle->height );
242     NSString *sizeInfoString;
243     /* Set the picture size display fields below the Preview Picture*/
244     if( fTitle->job->anamorphic.mode == 1 ) // Original PAR Implementation
245     {
246         output_width = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
247         output_height = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
248         display_width = output_width * fTitle->job->anamorphic.par_width / fTitle->job->anamorphic.par_height;
249         sizeInfoString = [NSString stringWithFormat:
250                           @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Strict",
251                           fTitle->width, fTitle->height, output_width, output_height, display_width, output_height];
252         
253         displaySize.width = display_width;
254         displaySize.height = fTitle->height;
255         imageScaledSize.width = display_width;
256         imageScaledSize.height = output_height;   
257     }
258     else if (fTitle->job->anamorphic.mode == 2) // Loose Anamorphic
259     {
260         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
261         display_width = output_width * output_par_width / output_par_height;
262         sizeInfoString = [NSString stringWithFormat:
263                           @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Loose",
264                           fTitle->width, fTitle->height, output_width, output_height, display_width, output_height];
265         
266         displaySize.width = display_width;
267         displaySize.height = fTitle->height;
268         imageScaledSize.width = display_width;
269         imageScaledSize.height = output_height;
270     }
271     else if (fTitle->job->anamorphic.mode == 3) // Custom Anamorphic
272     {
273         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
274         display_width = output_width * output_par_width / output_par_height;
275         sizeInfoString = [NSString stringWithFormat:
276                           @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Custom",
277                           fTitle->width, fTitle->height, output_width, output_height, fTitle->job->anamorphic.dar_width, fTitle->job->anamorphic.dar_height];
278         
279         displaySize.width = fTitle->job->anamorphic.dar_width + fTitle->job->crop[2] + fTitle->job->crop[3];
280         displaySize.height = fTitle->job->anamorphic.dar_height + fTitle->job->crop[0] + fTitle->job->crop[1];
281         imageScaledSize.width = (int)fTitle->job->anamorphic.dar_width;
282         imageScaledSize.height = (int)fTitle->job->height;   
283     } 
284     else // No Anamorphic
285     {
286         sizeInfoString = [NSString stringWithFormat:
287                           @"Source: %dx%d, Output: %dx%d", fTitle->width, fTitle->height,
288                           fTitle->job->width, fTitle->job->height];
289         
290         displaySize.width = fTitle->width;
291         displaySize.height = fTitle->height;
292         imageScaledSize.width = fTitle->job->width;
293         imageScaledSize.height = fTitle->job->height;
294     }
295     
296     NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
297     
298     /* Initially set our preview image here */
299     /*
300     if (scaleToScreen == YES)
301     {
302         viewSize.width = viewSize.width - (viewSize.width - imageScaledSize.width);
303         viewSize.height = viewSize.height - (viewSize.height - imageScaledSize.height);
304         [fPreviewImage setSize: viewSize];
305         //[fPictureView setFrameSize: viewSize];
306     }
307     
308     else
309     {
310         [fPreviewImage setSize: imageScaledSize];
311         [fPictureView setFrameSize: imageScaledSize];
312     }
313     [fPictureView setImage: fPreviewImage];
314     // center it vertically and horizontally
315     NSPoint origin = [fPictureViewArea frame].origin;
316     origin.y += ([fPictureViewArea frame].size.height -
317                  [fPictureView frame].size.height) / 2.0;
318     
319     origin.x += ([fPictureViewArea frame].size.width -
320                  [fPictureView frame].size.width) / 2.0;
321     [fPictureView setFrameOrigin:origin]; 
322     */
323     /* we also need to take into account scaling to full screen to activate switching the view size */
324     if( [self viewNeedsToResizeToSize:viewSize])
325     {
326         if (fTitle->job->anamorphic.mode != 2 || (fTitle->job->anamorphic.mode == 2 && fTitle->width == fTitle->job->width))
327         {
328             [self resizeSheetForViewSize:viewSize];
329             //[self setViewSize:viewSize];
330             
331         }
332     }   
333     
334     viewSize.width = viewSize.width - (viewSize.width - imageScaledSize.width);
335     viewSize.height = viewSize.height - (viewSize.height - imageScaledSize.height);
336     [self setViewSize:viewSize];
337     
338     /* special case for scaleToScreen */
339     if (scaleToScreen == YES)
340     {
341         [fPreviewImage setSize: viewSize];
342         [fPictureView setImage: fPreviewImage];
343     }
344     
345     NSString *scaleString;
346     
347     if( imageScaledSize.height > [fPictureView frame].size.height)
348     {
349         CGFloat scale = ( ( CGFloat )[fPictureView frame].size.width) / ( ( CGFloat )imageScaledSize.width);        
350         scaleString = [NSString stringWithFormat:
351                        NSLocalizedString( @" (Scaled to %.0f%% actual size)",
352                                          @"String shown when a preview is scaled" ), scale * 100.0];
353     }
354     else
355     {
356         scaleString = @"";
357     }
358     /* Set the info fields in the hud controller */
359     [fInfoField setStringValue: [NSString stringWithFormat:
360                                  @"%@", sizeInfoString]];
361     
362     [fscaleInfoField setStringValue: [NSString stringWithFormat:
363                                       @"%@", scaleString]];
364     /* Set the info field in the window title bar */
365     [[self window] setTitle:[NSString stringWithFormat: @"Preview - %@ %@",sizeInfoString, scaleString]];
366 }
367
368 - (IBAction) previewDurationPopUpChanged: (id) sender
369 {
370     
371     [[NSUserDefaults standardUserDefaults] setObject:[fPreviewMovieLengthPopUp titleOfSelectedItem] forKey:@"PreviewLength"];
372     
373 }    
374     
375 - (IBAction) SettingsChanged: (id) sender
376 {
377          // Purge the existing picture previews so they get recreated the next time
378         // they are needed.
379         [self purgeImageCache];
380         [self pictureSliderChanged:nil];
381 }
382
383 - (IBAction) pictureSliderChanged: (id) sender
384 {
385     // Show the picture view
386     [fPictureView setHidden:NO];
387     [fMovieView pause:nil];
388     [fMovieView setHidden:YES];
389         [fMovieView setMovie:nil];
390     [fEncodingControlBox setHidden: YES];
391     
392     int newPicture = [fPictureSlider intValue];
393     if (newPicture != fPicture)
394     {
395         fPicture = newPicture;
396     }
397     [self displayPreview];
398     
399 }
400
401 - (IBAction)showPreviewPanel: (id)sender forTitle: (hb_title_t *)title
402 {
403     if ([fPreviewWindow isVisible])
404     {
405         [fPreviewWindow close];
406     }
407     else
408     {
409         [self showWindow:sender];
410         [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"PreviewWindowIsOpen"];
411         [fPreviewWindow setAcceptsMouseMovedEvents:YES];
412         isFullScreen = NO;
413         scaleToScreen = NO;
414         [self pictureSliderChanged:nil];
415     }
416     
417 }
418
419 - (NSString*) pictureSizeInfoString
420 {
421     return [fInfoField stringValue];
422 }
423
424 - (IBAction)showPictureSettings:(id)sender
425 {
426     [fHBController showPicturePanel:self];
427 }
428
429 #pragma mark Hud Control Overlay
430 - (void) mouseMoved:(NSEvent *)theEvent
431 {
432     [super mouseMoved:theEvent];
433     NSPoint mouseLoc = [theEvent locationInWindow];
434     
435     /* Test for mouse location to show/hide hud controls */
436     if( isEncoding == NO ) {
437         if( NSPointInRect( mouseLoc, [fPictureControlBox frame] ) )
438         {
439             [[fPictureControlBox animator] setHidden: NO];
440             [self stopHudTimer];
441         }
442                 else if( NSPointInRect( mouseLoc, [fPictureViewArea frame] ) )
443         {
444             [[fPictureControlBox animator] setHidden: NO];
445             [self startHudTimer];
446         }
447         else
448             [[fPictureControlBox animator] setHidden: YES];
449         }
450 }
451
452 - (void) startHudTimer
453 {
454         if( fHudTimer ) {
455                 [fHudTimer invalidate];
456                 [fHudTimer release];
457         }
458     fHudTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(hudTimerFired:) userInfo:nil repeats:YES];
459     [fHudTimer retain];
460 }
461
462 - (void) stopHudTimer
463 {
464     if( fHudTimer )
465     {
466         [fHudTimer invalidate];
467         [fHudTimer release];
468         fHudTimer = nil;
469         hudTimerSeconds = 0;
470     }
471 }
472
473 - (void) hudTimerFired: (NSTimer*)theTimer
474 {
475     hudTimerSeconds++;
476     if( hudTimerSeconds >= 10 ) {
477         [[fPictureControlBox animator] setHidden: YES];
478         [self stopHudTimer];
479     }
480 }
481
482 #pragma mark Fullscreen Mode
483
484 - (IBAction)toggleScreenMode:(id)sender
485 {
486     if (!isFullScreen)
487     {
488         [self goFullScreen:nil];
489     }
490     else
491     {
492         [self goWindowedScreen:nil];
493     }
494 }
495
496 - (IBAction)toggleScaleToScreen:(id)sender
497 {
498     if (scaleToScreen == YES)
499     {
500         scaleToScreen = NO;
501         /* make sure we are set to a still preview */
502         [self pictureSliderChanged:nil];
503         [fScaleToScreenToggleButton setTitle:@"<->"];
504     }
505     else
506     {
507         scaleToScreen = YES;
508         /* make sure we are set to a still preview */
509         [self pictureSliderChanged:nil];
510         [fScaleToScreenToggleButton setTitle:@">-<"];
511     }
512 }
513
514 - (BOOL)fullScreen
515 {
516     return isFullScreen;
517 }
518
519 - (IBAction)goFullScreen:(id)sender 
520
521     // Get the screen information. 
522     NSScreen* mainScreen = [fPreviewWindow screen];
523     NSDictionary* screenInfo = [mainScreen deviceDescription]; 
524     NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"]; 
525     // Capture the screen. 
526     CGDirectDisplayID displayID = (CGDirectDisplayID)[screenID longValue]; 
527     CGDisplayErr err = CGDisplayCapture(displayID); 
528     
529     if (err == CGDisplayNoErr) 
530     { 
531         
532         /* make sure we are set to a still preview and not scaled to screen */
533         scaleToScreen = NO;
534         [self pictureSliderChanged:nil];
535         
536         // Create the full-screen window. 
537         //NSRect winRect = [mainScreen frame];
538         //fPictureViewArea
539         NSRect winRect = [fPictureViewArea frame];
540           
541         fFullScreenWindow = [[NSWindow alloc] initWithContentRect:winRect 
542                                                         styleMask:NSBorderlessWindowMask 
543                                                           backing:NSBackingStoreBuffered 
544                                                             defer:NO 
545                                                            screen:mainScreen]; 
546         
547         // Establish the window attributes. 
548         [fFullScreenWindow setReleasedWhenClosed:NO]; 
549         [fFullScreenWindow setDisplaysWhenScreenProfileChanges:YES]; 
550         [fFullScreenWindow setDelegate:self]; 
551         
552         /* insert a view into the new window */
553         [fFullScreenWindow setContentView:fPictureViewArea]; 
554         [fPictureViewArea setNeedsDisplay:YES];
555         
556         /* Better to center the window using the screen's frame
557          * and the windows origin. Note that we should take into
558          * account the auto sizing and alignment that occurs in 
559          * setViewSize each time the preview changes.
560          * Note: by using [fFullScreenWindow screen] (instead of
561          * [NSScreen mainScreen]) in referencing the screen
562          * coordinates, the full screen window will show up on
563          * whichever display was being used in windowed mode
564          * on multi-display systems
565          */
566         
567         NSSize screenSize = [[fFullScreenWindow screen] frame].size;
568         NSSize windowSize = [fFullScreenWindow frame].size;
569         NSPoint windowOrigin = [fFullScreenWindow frame].origin;
570         
571         /* Adjust our origin y (vertical) based on the screen height */
572         windowOrigin.y += (screenSize.height - windowSize.height) / 2.0;
573         windowOrigin.x += (screenSize.width - windowSize.width) / 2.0;
574         
575         [fFullScreenWindow setFrameOrigin:windowOrigin];
576         
577         /* lets kill the timer for now */
578         [self stopReceivingLibhbNotifications];
579         
580         /* We need to retain the fPreviewWindow */
581         [fPreviewWindow retain];
582         
583         [self setWindow:fFullScreenWindow];
584         
585         // The window has to be above the level of the shield window.
586         int32_t shieldLevel = CGShieldingWindowLevel(); 
587         
588         [fFullScreenWindow setLevel:shieldLevel]; 
589         
590         // Show the window. 
591         [fFullScreenWindow makeKeyAndOrderFront:self];
592         
593         
594         /* Change the name of fFullScreenToggleButton appropriately */
595         [fFullScreenToggleButton setTitle: @"Windowed"];
596         
597         /* Lets fire the timer back up for the hud controls, etc. */
598         [self startReceivingLibhbNotifications];
599         
600         isFullScreen = YES;
601         [fScaleToScreenToggleButton setHidden:NO];
602         
603         /* make sure we are set to a still preview */
604         [self pictureSliderChanged:nil];
605         
606         [fFullScreenWindow setAcceptsMouseMovedEvents:YES];
607         
608         
609         hudTimerSeconds = 0;
610         [self startHudTimer];
611     } 
612
613
614 // Title-less windows normally don't receive key presses, override this
615 - (BOOL)canBecomeKeyWindow
616 {
617     return YES;
618 }
619
620 // Title-less windows normally can't become main which means that another
621 // non-fullscreen window will have the "active" titlebar in expose. Bad, fix it.
622 - (BOOL)canBecomeMainWindow
623 {
624     return YES;
625 }
626
627
628 - (IBAction)goWindowedScreen:(id)sender
629 {
630     
631     /* Get the screen info to release the display but don't actually do
632      * it until the windowed screen is setup.
633      */
634     scaleToScreen = NO;
635     [self pictureSliderChanged:nil];
636     [fScaleToScreenToggleButton setTitle:@"<->"];
637         
638     NSScreen* mainScreen = [NSScreen mainScreen]; 
639     NSDictionary* screenInfo = [mainScreen deviceDescription]; 
640     NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
641     CGDirectDisplayID displayID = (CGDirectDisplayID)[screenID longValue]; 
642     
643     [fFullScreenWindow dealloc];
644     [fFullScreenWindow release];
645     
646     
647     [fPreviewWindow setContentView:fPictureViewArea]; 
648     [fPictureViewArea setNeedsDisplay:YES];
649     [self setWindow:fPreviewWindow];
650     
651     // Show the window. 
652     [fPreviewWindow makeKeyAndOrderFront:self];
653     
654     /* Set the window back to regular level */
655     [fPreviewWindow setLevel:NSNormalWindowLevel];
656     
657     /* Set the isFullScreen flag back to NO */
658     isFullScreen = NO;
659     scaleToScreen = NO;
660     /* make sure we are set to a still preview */
661     [self pictureSliderChanged:nil];
662     [self showPreviewWindow:nil];
663     
664     /* Change the name of fFullScreenToggleButton appropriately */
665     [fFullScreenToggleButton setTitle: @"Full Screen"];
666     // [fScaleToScreenToggleButton setHidden:YES];
667     /* set the picture settings pallete back to normal level */
668     [fHBController picturePanelWindowed];
669     
670     /* Release the display now that the we are back in windowed mode */
671     CGDisplayRelease(displayID);
672     
673     [fPreviewWindow setAcceptsMouseMovedEvents:YES];
674     //[fFullScreenWindow setAcceptsMouseMovedEvents:NO];
675     
676     hudTimerSeconds = 0;
677     [self startHudTimer];
678     
679 }
680
681
682 #pragma mark Still Preview Image Processing
683
684
685 // This function converts an image created by libhb (specified via pictureIndex) into
686 // an NSImage suitable for the GUI code to use. If removeBorders is YES,
687 // makeImageForPicture crops the image generated by libhb stripping off the gray
688 // border around the content. This is the low-level method that generates the image.
689 // -imageForPicture calls this function whenever it can't find an image in its cache.
690 + (NSImage *) makeImageForPicture: (int)pictureIndex
691                 libhb:(hb_handle_t*)handle
692                 title:(hb_title_t*)title
693 {
694     static uint8_t * buffer;
695     static int bufferSize;
696
697     // Make sure we have a big enough buffer to receive the image from libhb. libhb
698     int dstWidth = title->job->width;
699     int dstHeight = title->job->height;
700         
701     int newSize;
702     newSize = dstWidth * dstHeight * 4;
703     if( bufferSize < newSize )
704     {
705         bufferSize = newSize;
706         buffer     = (uint8_t *) realloc( buffer, bufferSize );
707     }
708
709     hb_get_preview( handle, title, pictureIndex, buffer );
710
711     // Create an NSBitmapImageRep and copy the libhb image into it, converting it from
712     // libhb's format to one suitable for NSImage. Along the way, we'll strip off the
713     // border around libhb's image.
714         
715     // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
716     // Alpha is ignored.
717         
718     NSBitmapFormat bitmapFormat = (NSBitmapFormat)NSAlphaFirstBitmapFormat;
719     NSBitmapImageRep * imgrep = [[[NSBitmapImageRep alloc]
720             initWithBitmapDataPlanes:nil
721             pixelsWide:dstWidth
722             pixelsHigh:dstHeight
723             bitsPerSample:8
724             samplesPerPixel:3   // ignore alpha
725             hasAlpha:NO
726             isPlanar:NO
727             colorSpaceName:NSCalibratedRGBColorSpace
728             bitmapFormat:bitmapFormat
729             bytesPerRow:dstWidth * 4
730             bitsPerPixel:32] autorelease];
731
732     UInt32 * src = (UInt32 *)buffer;
733     UInt32 * dst = (UInt32 *)[imgrep bitmapData];
734     for (int r = 0; r < dstHeight; r++)
735     {
736         for (int c = 0; c < dstWidth; c++)
737 #if TARGET_RT_LITTLE_ENDIAN
738             *dst++ = Endian32_Swap(*src++);
739 #else
740             *dst++ = *src++;
741 #endif
742     }
743
744     NSImage * img = [[[NSImage alloc] initWithSize: NSMakeSize(dstWidth, dstHeight)] autorelease];
745     [img addRepresentation:imgrep];
746
747     return img;
748 }
749
750 // Returns the preview image for the specified index, retrieving it from its internal
751 // cache or by calling makeImageForPicture if it is not cached. Generally, you should
752 // use imageForPicture so that images are cached. Calling makeImageForPicture will
753 // always generate a new copy of the image.
754 - (NSImage *) imageForPicture: (int) pictureIndex
755 {
756     // The preview for the specified index may not currently exist, so this method
757     // generates it if necessary.
758     NSString * key = [NSString stringWithFormat:@"%d", pictureIndex];
759     NSImage * theImage = [fPicturePreviews objectForKey:key];
760     if (!theImage)
761     {
762         theImage = [PreviewController makeImageForPicture:pictureIndex libhb:fHandle title:fTitle];
763         [fPicturePreviews setObject:theImage forKey:key];
764     }
765     return theImage;
766 }
767
768 // Purges all images from the cache. The next call to imageForPicture will cause a new
769 // image to be generated.
770 - (void) purgeImageCache
771 {
772     [fPicturePreviews removeAllObjects];
773 }
774
775  
776
777 #pragma mark Movie Preview
778 - (IBAction) createMoviePreview: (id) sender
779 {
780     
781     
782     /* Lets make sure the still picture previews are showing in case
783      * there is currently a movie showing */
784     [self pictureSliderChanged:nil];
785     
786     /* Rip or Cancel ? */
787     hb_state_t s;
788     hb_get_state2( fPreviewLibhb, &s );
789     
790     if(sender == fCancelPreviewMovieButton && (s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED))
791         {
792         hb_stop( fPreviewLibhb );
793         [fPictureView setHidden:NO];
794         [fMovieView pause:nil];
795         [fMovieView setHidden:YES];
796                 [fMovieView setMovie:nil];
797         [fPictureSlider setHidden:NO];
798         isEncoding = NO;
799         
800         return;
801     }
802     
803     
804     /* we use controller.mm's prepareJobForPreview to go ahead and set all of our settings
805      * however, we want to use a temporary destination field of course
806      * so that we do not put our temp preview in the users chosen
807      * directory */
808     
809     hb_job_t * job = fTitle->job;
810     
811     /* We run our current setting through prepeareJob in Controller.mm
812      * just as if it were a regular encode */
813     
814     [fHBController prepareJobForPreview];
815     
816     /* Destination file. We set this to our preview directory
817      * changing the extension appropriately.*/
818     if (fTitle->job->mux == HB_MUX_MP4) // MP4 file
819     {
820         /* we use .m4v for our mp4 files so that ac3 and chapters in mp4 will play properly */
821         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.m4v";
822     }
823     else if (fTitle->job->mux == HB_MUX_MKV) // MKV file
824     {
825         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.mkv";
826     }
827     else if (fTitle->job->mux == HB_MUX_AVI) // AVI file
828     {
829         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.avi";
830     }
831     else if (fTitle->job->mux == HB_MUX_OGM) // OGM file
832     {
833         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.ogm";
834     }
835     
836     fPreviewMoviePath = [[fPreviewMoviePath stringByExpandingTildeInPath]retain];
837     
838     /* See if there is an existing preview file, if so, delete it */
839     if( ![[NSFileManager defaultManager] fileExistsAtPath:fPreviewMoviePath] )
840     {
841         [[NSFileManager defaultManager] removeFileAtPath:fPreviewMoviePath
842                                                  handler:nil];
843     }
844     
845     /* We now direct our preview encode to fPreviewMoviePath */
846     fTitle->job->file = [fPreviewMoviePath UTF8String];
847     
848     /* We use our advance pref to determine how many previews to scan */
849     int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
850     job->start_at_preview = fPicture + 1;
851     job->seek_points = hb_num_previews;
852     
853     /* we use the preview duration popup to get the specified
854      * number of seconds for the preview encode.
855      */
856     
857     job->pts_to_stop = [[fPreviewMovieLengthPopUp titleOfSelectedItem] intValue] * 90000LL;
858     
859     /* lets go ahead and send it off to libhb
860      * Note: unlike a full encode, we only send 1 pass regardless if the final encode calls for 2 passes.
861      * this should suffice for a fairly accurate short preview and cuts our preview generation time in half.
862      * However we also need to take into account the indepth scan for subtitles.
863      */
864     /*
865      * If scanning we need to do some extra setup of the job.
866      */
867     if( job->indepth_scan == 1 )
868     {
869         char *x264opts_tmp;
870         
871         /*
872          * When subtitle scan is enabled do a fast pre-scan job
873          * which will determine which subtitles to enable, if any.
874          */
875         job->pass = -1;
876         x264opts_tmp = job->x264opts;
877         
878         job->x264opts = NULL;
879         job->indepth_scan = 1;  
880         /*
881          * Add the pre-scan job
882          */
883         hb_add( fPreviewLibhb, job );
884         job->x264opts = x264opts_tmp;
885     }                  
886     /* Go ahead and perform the actual encoding preview scan */
887     job->indepth_scan = 0;
888     job->pass = 0;
889     hb_add( fPreviewLibhb, job );
890     
891     [fEncodingControlBox setHidden: NO];
892     [fPictureControlBox setHidden: YES];
893     
894     [fMovieCreationProgressIndicator setHidden: NO];
895     [fPreviewMovieStatusField setHidden: NO];
896     
897     isEncoding = YES;
898
899     /* Let fPreviewLibhb do the job */
900     hb_start( fPreviewLibhb );
901         
902 }
903
904 - (void) startReceivingLibhbNotifications
905 {
906     if (!fLibhbTimer)
907     {
908         fLibhbTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(libhbTimerFired:) userInfo:nil repeats:YES];
909         [fLibhbTimer retain];
910     }
911 }
912
913 - (void) stopReceivingLibhbNotifications
914 {
915     if (fLibhbTimer)
916     {
917         [fLibhbTimer invalidate];
918         [fLibhbTimer release];
919         fLibhbTimer = nil;
920     }
921 }
922 - (void) libhbTimerFired: (NSTimer*)theTimer
923 {
924     hb_state_t s;
925     hb_get_state( fPreviewLibhb, &s );
926     [self libhbStateChanged: s];
927     
928 }
929
930 - (void) libhbStateChanged: (hb_state_t &)state
931 {
932     switch( state.state )
933     {
934         case HB_STATE_IDLE:
935         case HB_STATE_SCANNING:
936         case HB_STATE_SCANDONE:
937             break;
938             
939         case HB_STATE_WORKING:
940         {
941 #define p state.param.working
942             
943             NSMutableString * string;
944                         /* Update text field */
945                         string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding preview:  %.2f %%", @"" ), 100.0 * p.progress];
946             
947                         if( p.seconds > -1 )
948             {
949                 [string appendFormat:
950                  NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
951                  p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
952             }
953             [fPreviewMovieStatusField setStringValue: string];
954             
955             [fMovieCreationProgressIndicator setIndeterminate: NO];
956             /* Update slider */
957                         [fMovieCreationProgressIndicator setDoubleValue: 100.0 * p.progress];
958             
959             [fCreatePreviewMovieButton setTitle: @"Cancel Preview"];
960             
961             break;
962             
963         }
964 #undef p
965             
966 #define p state.param.muxing            
967         case HB_STATE_MUXING:
968         {
969             // Update fMovieCreationProgressIndicator
970             [fMovieCreationProgressIndicator setIndeterminate: YES];
971             [fMovieCreationProgressIndicator startAnimation: nil];
972             [fPreviewMovieStatusField setStringValue: [NSString stringWithFormat:
973                                          NSLocalizedString( @"Muxing Preview ...", @"" )]];
974             break;
975         }
976 #undef p                        
977         case HB_STATE_PAUSED:
978             [fMovieCreationProgressIndicator stopAnimation: nil];
979             break;
980                         
981         case HB_STATE_WORKDONE:
982         {
983             // Delete all remaining jobs since libhb doesn't do this on its own.
984             hb_job_t * job;
985             while( ( job = hb_job(fPreviewLibhb, 0) ) )
986                 hb_rem( fHandle, job );
987             
988             [fPreviewMovieStatusField setStringValue: @""];
989             [fPreviewMovieStatusField setHidden: YES];
990             
991             [fMovieCreationProgressIndicator stopAnimation: nil];
992             [fMovieCreationProgressIndicator setHidden: YES];
993             [fEncodingControlBox setHidden: YES];
994             isEncoding = NO;
995             /* we make sure the picture slider and preview match */
996             [self pictureSliderChanged:nil];
997
998             // Show the movie view
999             [self showMoviePreview:fPreviewMoviePath];
1000             [fCreatePreviewMovieButton setTitle: @"Live Preview"];
1001
1002             break;
1003         }
1004     }
1005 }
1006
1007 - (IBAction) showMoviePreview: (NSString *) path
1008 {
1009     /* Since the gray background for the still images is part of
1010      * fPictureView, lets leave the picture view visible and postion
1011      * the fMovieView over the image portion of fPictureView so
1012      * we retain the gray cropping border  we have already established
1013      * with the still previews
1014      */
1015
1016     /* Load the new movie into fMovieView */
1017     if (path) {
1018                 QTMovie * aMovie;
1019                 NSError  *outError;
1020                 NSURL *movieUrl = [NSURL fileURLWithPath:path];
1021                 NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
1022                                                                                  movieUrl, QTMovieURLAttribute,
1023                                                                                  [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute,
1024                                                                                  [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute",
1025                                                                                  [NSNumber numberWithBool:NO], @"QTMovieOpenAsyncRequiredAttribute",                                                            
1026                                                                                  [NSNumber numberWithBool:NO], @"QTMovieOpenAsyncOKAttribute",
1027                                                                                  QTMovieApertureModeClean, QTMovieApertureModeAttribute,
1028                                                                                  nil];
1029
1030         aMovie = [[[QTMovie alloc] initWithAttributes:movieAttributes error:&outError] autorelease];
1031
1032                 if (!aMovie) {
1033                         NSLog(@"Unable to open movie");
1034                 }
1035         else {
1036             NSRect movieBounds;
1037             /* we get some size information from the preview movie */
1038             NSSize movieSize= [[aMovie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
1039             movieBounds = [fMovieView movieBounds];
1040             movieBounds.size.height = movieSize.height;
1041             
1042             if ([fMovieView isControllerVisible]) {
1043                 CGFloat controllerBarHeight = [fMovieView controllerBarHeight];
1044                 if ( controllerBarHeight != 0 ) //Check if QTKit return a real value or not.
1045                     movieBounds.size.height += controllerBarHeight;
1046                 else
1047                     movieBounds.size.height += 15;
1048             }
1049             
1050             movieBounds.size.width = movieSize.width;
1051             
1052             /* We need to find out if the preview movie needs to be scaled down so
1053              * that it doesn't overflow our available viewing container (just like for image
1054              * in -displayPreview) for HD sources, etc. [fPictureViewArea frame].size.height*/
1055             if( (movieBounds.size.height) > [fPictureViewArea frame].size.height || scaleToScreen == YES )
1056             {
1057                 /* The preview movie would be larger than the available viewing area
1058                  * in the preview movie, so we go ahead and scale it down to the same size
1059                  * as the still preview  or we readjust our window to allow for the added height if need be
1060                  */
1061                 NSSize displaySize = NSMakeSize( ( CGFloat ) movieBounds.size.width, ( CGFloat ) movieBounds.size.height );
1062                 NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
1063                 if( [self viewNeedsToResizeToSize:viewSize] ) {
1064                     [self resizeSheetForViewSize:viewSize];
1065                     [self setViewSize:viewSize];
1066                 }
1067                 [fMovieView setFrameSize:viewSize];
1068             }
1069             else
1070             {
1071                 /* Since the preview movie is smaller than the available viewing area
1072                  * we can go ahead and use the preview movies native size */
1073                 [fMovieView setFrameSize:movieBounds.size];
1074             }
1075             
1076             //lets reposition the movie if need be
1077             
1078             NSPoint origin = [fPictureViewArea frame].origin;
1079             origin.x += trunc( ( [fPictureViewArea frame].size.width -
1080                                 [fMovieView frame].size.width ) / 2.0 );
1081             /* We need to detect whether or not we are currently less than the available height.*/
1082             if( movieBounds.size.height < [fPictureView frame].size.height )
1083             {
1084                 /* If we are, we are adding 15 to the height to allow for the controller bar so
1085                  * we need to subtract half of that for the origin.y to get the controller bar
1086                  * below the movie to it lines up vertically with where our still preview was
1087                  */
1088                 origin.y += trunc( ( ( [fPictureViewArea frame].size.height -
1089                                       [fMovieView frame].size.height ) / 2.0 ) - 7.5 );
1090             }
1091             else
1092             {
1093                 /* if we are >= to the height of the picture view area, the controller bar
1094                  * gets taken care of with picture resizing, so we do not want to offset the height
1095                  */
1096                 origin.y += trunc( ( [fPictureViewArea frame].size.height -
1097                                     [fMovieView frame].size.height ) / 2.0 );
1098             }
1099             [fMovieView setFrameOrigin:origin];
1100             [fMovieView setMovie:aMovie];
1101             [fMovieView setHidden:NO];
1102             // to actually play the movie
1103             [fMovieView play:aMovie];
1104         }
1105     }
1106     isEncoding = NO;
1107 }
1108
1109 @end
1110
1111 @implementation PreviewController (Private)
1112
1113 //
1114 // -[PictureController(Private) optimalViewSizeForImageSize:]
1115 //
1116 // Given the size of the preview image to be shown, returns the best possible
1117 // size for the view.
1118 //
1119 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize
1120 {
1121     // The min size is 320x240
1122     CGFloat minWidth = 480.0;
1123     CGFloat minHeight = 360.0;
1124
1125     NSSize screenSize = [[NSScreen mainScreen] frame].size;
1126     NSSize sheetSize = [[self window] frame].size;
1127     NSSize viewAreaSize = [fPictureViewArea frame].size;
1128     CGFloat paddingX = sheetSize.width - viewAreaSize.width;
1129     CGFloat paddingY = sheetSize.height - viewAreaSize.height;
1130     CGFloat maxWidth;
1131     CGFloat maxHeight;
1132     
1133     if (isFullScreen)
1134     {
1135         /* We are in full screen mode so lets use the full screen if we need to */
1136         maxWidth =  screenSize.width - paddingX;
1137         maxHeight = screenSize.height - paddingY;
1138     }
1139     else
1140     {
1141         // The max size of the view is when the sheet is taking up 85% of the screen.
1142         maxWidth = (0.85 * screenSize.width) - paddingX;
1143         maxHeight = (0.85 * screenSize.height) - paddingY;
1144     }
1145     
1146     NSSize resultSize = imageSize;
1147     
1148     // Its better to have a view that's too small than a view that's too big, so
1149     // apply the maximum constraints last.
1150     if( resultSize.width < minWidth )
1151     {
1152         resultSize.height *= (minWidth / resultSize.width);
1153         resultSize.width = minWidth;
1154     }
1155     if( resultSize.height < minHeight )
1156     {
1157         resultSize.width *= (minHeight / resultSize.height);
1158         resultSize.height = minHeight;
1159     }
1160     if( resultSize.width > maxWidth )
1161     {
1162         resultSize.height *= (maxWidth / resultSize.width);
1163         resultSize.width = maxWidth;
1164     }
1165     if( resultSize.height > maxHeight )
1166     {
1167         resultSize.width *= (maxHeight / resultSize.height);
1168         resultSize.height = maxHeight;
1169     }
1170     
1171     if (scaleToScreen == YES)
1172     {
1173         CGFloat screenAspect;
1174         CGFloat viewAreaAspect; 
1175         //note, a mbp 15" at 1440 x 900 is a 1.6 ar
1176         screenAspect = screenSize.width / screenSize.height;
1177         
1178         // Note, a standard dvd will use 720 x 480 which is a 1.5
1179         viewAreaAspect = viewAreaSize.width / viewAreaSize.height;
1180         
1181         if (screenAspect < viewAreaAspect)
1182         {
1183             resultSize.width = screenSize.width;
1184             resultSize.height = (screenSize.width / viewAreaAspect);
1185         }
1186         else
1187         {
1188             resultSize.height = screenSize.height;
1189             resultSize.width = resultSize.height * viewAreaAspect;
1190         }
1191         
1192     }
1193
1194       return resultSize;
1195
1196     
1197 }
1198
1199 //
1200 // -[PictureController(Private) resizePanelForViewSize:animate:]
1201 //
1202 // Resizes the entire window to accomodate a view of a particular size.
1203 //
1204 - (void)resizeSheetForViewSize: (NSSize)viewSize
1205 {
1206     // Figure out the deltas for the new frame area
1207     NSSize currentSize = [fPictureViewArea frame].size;
1208     CGFloat deltaX = viewSize.width - currentSize.width;
1209     CGFloat deltaY = viewSize.height - currentSize.height;
1210     
1211     // Now resize the whole panel by those same deltas, but don't exceed the min
1212     NSRect frame = [[self window] frame];
1213     NSSize maxSize = [[self window] maxSize];
1214     NSSize minSize = [[self window] minSize];
1215     frame.size.width += deltaX;
1216     frame.size.height += deltaY;
1217     if( frame.size.width < minSize.width )
1218     {
1219         frame.size.width = minSize.width;
1220     }
1221     
1222     if( frame.size.height < minSize.height )
1223     {
1224         frame.size.height = minSize.height;
1225     }
1226     
1227     
1228     // But now the sheet is off-center, so also shift the origin to center it and
1229     // keep the top aligned.
1230     if( frame.size.width != [[self window] frame].size.width )
1231         frame.origin.x -= (deltaX / 2.0);
1232     
1233     if (isFullScreen)
1234     {
1235         if( frame.size.height != [[self window] frame].size.height )
1236         {
1237             frame.origin.y -= (deltaY / 2.0);
1238         }
1239         else
1240         {
1241             if( frame.size.height != [[self window] frame].size.height )
1242                 frame.origin.y -= deltaY;
1243         }
1244         
1245         [[self window] setFrame:frame display:YES animate:NO];
1246     }
1247     else
1248     {
1249         /* Since upon launch we can open up the preview window if it was open
1250          * the last time we quit (and at the size it was) we want to make
1251          * sure that upon resize we do not have the window off the screen
1252          * So check the origin against the screen origin and adjust if
1253          * necessary.
1254          */
1255         NSSize screenSize = [[[self window] screen] frame].size;
1256         NSPoint screenOrigin = [[[self window] screen] frame].origin;
1257         /* our origin is off the screen to the left*/
1258         if (frame.origin.x < screenOrigin.x)
1259         {
1260             /* so shift our origin to the right */
1261             frame.origin.x = screenOrigin.x;
1262         }
1263         else if ((frame.origin.x + frame.size.width) > (screenOrigin.x + screenSize.width))
1264         {
1265             /* the right side of the preview is off the screen, so shift to the left */
1266             frame.origin.x = (screenOrigin.x + screenSize.width) - frame.size.width;
1267         }
1268         
1269         [[self window] setFrame:frame display:YES animate:YES];
1270     }
1271     
1272 }
1273
1274 //
1275 // -[PictureController(Private) setViewSize:]
1276 //
1277 // Changes the view's size and centers it vertically inside of its area.
1278 // Assumes resizeSheetForViewSize: has already been called.
1279 //
1280 - (void)setViewSize: (NSSize)viewSize
1281 {   
1282     /* special case for scaleToScreen */
1283     if (scaleToScreen == YES)
1284     {
1285         /* for scaleToScreen, we expand the fPictureView to fit the entire screen */
1286         NSSize areaSize = [fPictureViewArea frame].size;
1287         CGFloat viewSizeAspect = viewSize.width / viewSize.height;
1288         if (viewSizeAspect > 1.0) // we are wider than taller, so expand the width to fill the area and scale the height
1289         {
1290             viewSize.width = areaSize.width;
1291             viewSize.height = viewSize.width / viewSizeAspect;
1292         }
1293         else
1294         {
1295             viewSize.height = areaSize.height;
1296             viewSize.width = viewSize.height * viewSizeAspect;
1297         }
1298         
1299     }
1300
1301     [fPictureView setFrameSize:viewSize];
1302
1303     // center it vertically and horizontally
1304     NSPoint origin = [fPictureViewArea frame].origin;
1305     origin.y += ([fPictureViewArea frame].size.height -
1306                  [fPictureView frame].size.height) / 2.0;
1307     
1308     origin.x += ([fPictureViewArea frame].size.width -
1309                  [fPictureView frame].size.width) / 2.0; 
1310
1311     origin.x = floor( origin.x );
1312     origin.y = floor( origin.y );
1313     
1314     [fPictureView setFrameOrigin:origin];
1315 }
1316
1317
1318 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize
1319 {
1320     NSSize viewSize = [fPictureViewArea frame].size;
1321     return (newSize.width != viewSize.width || newSize.height != viewSize.height);
1322 }
1323
1324 @end