OSDN Git Service

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