OSDN Git Service

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