OSDN Git Service

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