OSDN Git Service

LinGui: make Help->Guide work on windows/mingw
[handbrake-jp/handbrake-jp-git.git] / macosx / HBPreviewController.m
1 /* $Id: HBPreviewController.mm,v 1.11 2005/08/01 15:10:44 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #import "HBPreviewController.h"
8 #import "Controller.h"
9
10 @implementation QTMovieView ( HBQTkitExt )
11 - (void) mouseMoved:(NSEvent *)theEvent
12 {
13     [super mouseMoved:theEvent];
14 }
15 @end
16
17
18
19 @interface PreviewController (Private)
20
21 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize;
22 - (void)resizeSheetForViewSize: (NSSize)viewSize;
23 - (void)setViewSize: (NSSize)viewSize;
24 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize;
25
26 @end
27
28 @implementation PreviewController
29
30 - (id)init
31 {
32         if (self = [super initWithWindowNibName:@"PicturePreview"])
33         {
34         // NSWindowController likes to lazily load its window. However since
35         // this controller tries to set all sorts of outlets before the window
36         // is displayed, we need it to load immediately. The correct way to do
37         // this, according to the documentation, is simply to invoke the window
38         // getter once.
39         //
40         // If/when we switch a lot of this stuff to bindings, this can probably
41         // go away.
42         [self window];
43         
44                 fPicturePreviews = [[NSMutableDictionary dictionaryWithCapacity: HB_NUM_HBLIB_PICTURES] retain];
45         /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
46         int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
47         fPreviewLibhb = hb_init(loggingLevel, 0);
48         
49         
50
51         }
52         return self;
53 }
54
55
56 //------------------------------------------------------------------------------------
57 // Displays and brings the picture window to the front
58 //------------------------------------------------------------------------------------
59 - (IBAction) showPreviewWindow: (id)sender
60 {
61     [self showWindow:sender];
62     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"PreviewWindowIsOpen"];
63     
64     /* lets set the preview window to accept mouse moved events */
65     [fPreviewWindow setAcceptsMouseMovedEvents:YES];
66     hudTimerSeconds = 0;
67     [self pictureSliderChanged:nil];
68     [self startReceivingLibhbNotifications];
69 }
70
71 - (void)setHBController: (HBController *)controller
72 {
73     fHBController = controller;
74 }
75
76 - (void)awakeFromNib
77 {
78     [fPreviewWindow setDelegate:self];
79     if( ![[self window] setFrameUsingName:@"Preview"] )
80         [[self window] center];
81     [self setWindowFrameAutosaveName:@"Preview"];
82     [[self window] setExcludedFromWindowsMenu:YES];
83     
84     /* lets set the preview window to accept mouse moved events */
85     [fPreviewWindow setAcceptsMouseMovedEvents:YES];
86     //[self pictureSliderChanged:nil];
87     [self startReceivingLibhbNotifications];
88     
89     hudTimerSeconds = 0;
90     /* we set the progress indicator to not use threaded animation
91      * as it causes a conflict with the qtmovieview's controllerbar
92     */
93     [fMovieCreationProgressIndicator setUsesThreadedAnimation:NO];
94     
95     /* Setup our layers for core animation */
96     [fPictureViewArea setWantsLayer:YES];
97     [fPictureView setWantsLayer:YES];
98
99     [fCancelPreviewMovieButton setWantsLayer:YES];
100     [fMovieCreationProgressIndicator setWantsLayer:YES];
101
102     [fPictureControlBox setWantsLayer:YES];
103     [fEncodingControlBox setWantsLayer:YES];
104         [fMovieView setWantsLayer:YES];
105         [fMovieView setHidden:YES];
106     [fMovieView setDelegate:self];
107
108     /* Since the xib has everything off center for easy acess
109      * we align our views and windows here we an align to anything
110      * since it will actually change later upon source load, but
111      * for convenience we will use the fPictureViewArea
112      */
113      
114      /* Align the still preview image view to the picture box */
115      [fPictureView setFrameSize:[fPictureViewArea frame].size];
116      [fMovieView setFrameSize:[fPictureViewArea frame].size];
117      //[fPreviewWindow setFrameSize:[fPictureViewArea frame].size];
118     
119     
120 }
121 - (BOOL)acceptsMouseMovedEvents
122 {
123     return YES;
124 }
125
126 - (void)windowWillClose:(NSNotification *)aNotification
127 {
128     /* Upon Closing the picture window, we make sure we clean up any
129      * preview movie that might be playing
130      */
131     hb_stop( fPreviewLibhb );
132     isEncoding = NO;
133     // Show the picture view
134     [fPictureView setHidden:NO];
135     [fMovieView pause:nil];
136     [fMovieTimer invalidate];
137     [fMovieTimer release];
138     [fMovieView setHidden:YES];
139         [fMovieView setMovie:nil];
140
141     hudTimerSeconds = 0;
142     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"PreviewWindowIsOpen"];
143 }
144
145 - (BOOL)windowShouldClose:(id)fPictureWindow
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     [fMovieTimer invalidate];
166     [fMovieTimer release];
167     
168     [fPicturePreviews release];
169     [fFullScreenWindow release];
170     
171     hb_close(&fPreviewLibhb);
172     
173     [self removeMovieCallbacks];
174     
175     [super dealloc];
176 }
177
178 - (void) SetHandle: (hb_handle_t *) handle
179 {
180     fHandle = handle;
181     
182
183     
184     /* we set the preview length popup in seconds */
185     [fPreviewMovieLengthPopUp removeAllItems];
186     [fPreviewMovieLengthPopUp addItemWithTitle: @"5"];
187     [fPreviewMovieLengthPopUp addItemWithTitle: @"10"];
188     [fPreviewMovieLengthPopUp addItemWithTitle: @"15"];
189     [fPreviewMovieLengthPopUp addItemWithTitle: @"20"];
190     [fPreviewMovieLengthPopUp addItemWithTitle: @"25"];
191     [fPreviewMovieLengthPopUp addItemWithTitle: @"30"];
192     [fPreviewMovieLengthPopUp addItemWithTitle: @"35"];
193     [fPreviewMovieLengthPopUp addItemWithTitle: @"40"];
194     [fPreviewMovieLengthPopUp addItemWithTitle: @"45"];
195     [fPreviewMovieLengthPopUp addItemWithTitle: @"50"];
196     [fPreviewMovieLengthPopUp addItemWithTitle: @"55"];
197     [fPreviewMovieLengthPopUp addItemWithTitle: @"60"];
198     
199     /* adjust the preview slider length */
200     /* We use our advance pref to determine how many previews we scanned */
201     int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
202     [fPictureSlider setMaxValue: hb_num_previews - 1.0];
203     [fPictureSlider setNumberOfTickMarks: hb_num_previews];
204     
205     if ([[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"])
206     {
207         [fPreviewMovieLengthPopUp selectItemWithTitle:[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"]];
208     }
209     else
210     {
211         /* currently hard set default to 10 seconds */
212         [fPreviewMovieLengthPopUp selectItemAtIndex: 1];
213     }
214 }
215
216 - (void) SetTitle: (hb_title_t *) title
217 {
218     hb_job_t * job = title->job;
219     
220     fTitle = title;
221     fPicture = 0;
222     MaxOutputWidth = title->width - job->crop[2] - job->crop[3];
223     MaxOutputHeight = title->height - job->crop[0] - job->crop[1];
224     
225     [self SettingsChanged: nil];
226
227 }
228
229
230
231 // Adjusts the window to draw the current picture (fPicture) adjusting its size as
232 // necessary to display as much of the picture as possible.
233 - (void) displayPreview 
234 {
235     hb_job_t * job = fTitle->job;
236     /* lets make sure that the still picture view is not hidden and that 
237      * the movie preview is 
238      */
239      aMovie = nil;
240     [fMovieView pause:nil];
241     [fMovieView setHidden:YES];
242         [fMovieView setMovie:nil];
243     [fMovieCreationProgressIndicator stopAnimation: nil];
244     [fMovieCreationProgressIndicator setHidden: YES];
245     [fMoviePlaybackControlBox setHidden: YES];
246     [self stopMovieTimer];
247     [fPictureControlBox setHidden: NO];
248     
249     [fPictureView setHidden:NO];
250     
251     NSImage *fPreviewImage = [self imageForPicture: fPicture];
252     NSSize imageScaledSize = [fPreviewImage size];
253     [fPictureView setImage: fPreviewImage];
254     
255     NSSize displaySize = NSMakeSize( ( CGFloat )fTitle->width, ( CGFloat )fTitle->height );
256     NSString *sizeInfoString;
257     /* Set the picture size display fields below the Preview Picture*/
258     if( fTitle->job->anamorphic.mode == 1 ) // Original PAR Implementation
259     {
260         output_width = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
261         output_height = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
262         display_width = output_width * fTitle->job->anamorphic.par_width / fTitle->job->anamorphic.par_height;
263         sizeInfoString = [NSString stringWithFormat:
264                           @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Strict",
265                           fTitle->width, fTitle->height, output_width, output_height, display_width, output_height];
266         
267         displaySize.width = display_width;
268         displaySize.height = fTitle->height;
269         imageScaledSize.width = display_width;
270         imageScaledSize.height = output_height;   
271     }
272     else if (fTitle->job->anamorphic.mode == 2) // Loose Anamorphic
273     {
274         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
275         display_width = output_width * output_par_width / output_par_height;
276         sizeInfoString = [NSString stringWithFormat:
277                           @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Loose",
278                           fTitle->width, fTitle->height, output_width, output_height, display_width, output_height];
279         
280         displaySize.width = display_width;
281         displaySize.height = fTitle->height;
282         imageScaledSize.width = display_width;
283         imageScaledSize.height = output_height;
284     }
285     else if (fTitle->job->anamorphic.mode == 3) // Custom Anamorphic
286     {
287         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
288         display_width = output_width * output_par_width / output_par_height;
289         sizeInfoString = [NSString stringWithFormat:
290                           @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Custom",
291                           fTitle->width, fTitle->height, output_width, output_height, fTitle->job->anamorphic.dar_width, fTitle->job->anamorphic.dar_height];
292         
293         displaySize.width = fTitle->job->anamorphic.dar_width + fTitle->job->crop[2] + fTitle->job->crop[3] ;
294         displaySize.height = fTitle->job->anamorphic.dar_height + fTitle->job->crop[0] + fTitle->job->crop[1];
295         imageScaledSize.width = (int)fTitle->job->anamorphic.dar_width;
296         imageScaledSize.height = (int)fTitle->job->height;   
297     } 
298     else // No Anamorphic
299     {
300         sizeInfoString = [NSString stringWithFormat:
301                           @"Source: %dx%d, Output: %dx%d", fTitle->width, fTitle->height,
302                           fTitle->job->width, fTitle->job->height];
303        
304         displaySize.width = fTitle->width;
305         displaySize.height = fTitle->height;
306         imageScaledSize.width = fTitle->job->width;
307         imageScaledSize.height = fTitle->job->height;
308     }
309     
310     
311     
312     NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
313     [self resizeSheetForViewSize:viewSize];
314
315     NSSize windowSize = [[self window] frame].size;    
316     
317     if (scaleToScreen == YES)
318     {
319         /* Note: this should probably become a utility function */
320         /* We are in Scale To Screen mode so, we have to get the ratio for height and width against the window
321          *size so we can scale from there.
322          */
323         CGFloat deltaWidth = imageScaledSize.width / displaySize.width;
324         CGFloat deltaHeight = imageScaledSize.height /displaySize.height;
325         NSSize windowSize = [[self window] frame].size;  
326         CGFloat pictureAspectRatio = imageScaledSize.width / imageScaledSize.height;
327         
328         /* Set our min size to the storage size */
329         NSSize minSize;
330         minSize.width = fTitle->width;
331         minSize.height = fTitle->height;
332
333         /* Set delta's based on minimum size */
334         if (imageScaledSize.width <  minSize.width)
335         {
336             deltaWidth = imageScaledSize.width / minSize.width;
337         }
338         else
339         {
340             deltaWidth = 1.0;
341         }
342         
343         if (imageScaledSize.height <  minSize.height)
344         {
345             deltaHeight =  imageScaledSize.height / minSize.height;
346         }
347         else
348         {
349             deltaHeight = 1.0;
350         }
351         
352         /* Now apply our deltas to the full screen view */
353         if (pictureAspectRatio > 1.0) // we are wider than taller, so expand the width to fill the area and scale the height
354         {
355             viewSize.width = windowSize.width * deltaWidth;
356             viewSize.height = viewSize.width / pictureAspectRatio;
357             
358         }
359         else
360         {
361             viewSize.height = windowSize.height * deltaHeight; 
362             viewSize.width = viewSize.height * pictureAspectRatio;
363         }
364         
365     }
366     else
367     {
368         viewSize.width = viewSize.width - (viewSize.width - imageScaledSize.width);
369         viewSize.height = viewSize.height - (viewSize.height - imageScaledSize.height);
370         
371         if (fTitle->width > windowSize.width || fTitle->height > windowSize.height)
372         {
373             CGFloat viewSizeAspect = viewSize.width / viewSize.height;
374             if (viewSizeAspect > 1.0) // we are wider than taller, so expand the width to fill the area and scale the height
375             {
376                 viewSize.width = viewSize.width * (windowSize.width / fTitle->width) ;
377                 viewSize.height = viewSize.width / viewSizeAspect;
378             }
379             else
380             {
381                 viewSize.height = viewSize.height * (windowSize.height / fTitle->height);
382                 viewSize.width = viewSize.height * viewSizeAspect;
383             }
384         }
385         
386     }
387     
388     [self setViewSize:viewSize];
389     
390     /* relocate our hud origins as per setViewSize */
391     NSPoint hudControlBoxOrigin = [fPictureControlBox frame].origin;
392     hudControlBoxOrigin.y = ([[self window] frame].size.height / 2) - (viewSize.height / 2);
393     hudControlBoxOrigin.x = ([[self window] frame].size.width / 2) - ([fPictureControlBox frame].size.width / 2);
394     [fPictureControlBox setFrameOrigin:hudControlBoxOrigin];
395     [fEncodingControlBox setFrameOrigin:hudControlBoxOrigin];
396     [fMoviePlaybackControlBox setFrameOrigin:hudControlBoxOrigin];
397
398
399     NSString *scaleString;
400     CGFloat scale = ( ( CGFloat )[fPictureView frame].size.width) / ( ( CGFloat )imageScaledSize.width);
401     if (scale * 100.0 != 100)
402     {
403         scaleString = [NSString stringWithFormat:
404                        NSLocalizedString( @" (%.0f%% actual size)",
405                                          @"String shown when a preview is scaled" ), scale * 100.0];
406     }
407     else
408     {
409         scaleString = @"(Actual size)";
410     }
411     
412     if (scaleToScreen == YES)
413     {
414         scaleString = [scaleString stringByAppendingString:@" Scaled To Screen"];
415     }
416     /* Set the info fields in the hud controller */
417     [fInfoField setStringValue: [NSString stringWithFormat:
418                                  @"%@", sizeInfoString]];
419     
420     [fscaleInfoField setStringValue: [NSString stringWithFormat:
421                                       @"%@", scaleString]];
422     /* Set the info field in the window title bar */
423     [[self window] setTitle:[NSString stringWithFormat: @"Preview - %@ %@",sizeInfoString, scaleString]];
424 }
425
426 - (IBAction) previewDurationPopUpChanged: (id) sender
427 {
428     
429     [[NSUserDefaults standardUserDefaults] setObject:[fPreviewMovieLengthPopUp titleOfSelectedItem] forKey:@"PreviewLength"];
430     
431 }    
432     
433 - (IBAction) SettingsChanged: (id) sender
434 {
435          // Purge the existing picture previews so they get recreated the next time
436         // they are needed.
437         [self purgeImageCache];
438         [self pictureSliderChanged:nil];
439 }
440
441 - (IBAction) pictureSliderChanged: (id) sender
442 {
443     // Show the picture view
444     [fPictureView setHidden:NO];
445     [fMovieView pause:nil];
446     [fMovieView setHidden:YES];
447         [fMovieView setMovie:nil];
448     [fEncodingControlBox setHidden: YES];
449     
450     int newPicture = [fPictureSlider intValue];
451     if (newPicture != fPicture)
452     {
453         fPicture = newPicture;
454     }
455     [self displayPreview];
456     
457 }
458
459 - (IBAction)showPreviewPanel: (id)sender forTitle: (hb_title_t *)title
460 {
461     if ([fPreviewWindow isVisible])
462     {
463         [fPreviewWindow close];
464     }
465     else
466     {
467         [self showWindow:sender];
468         [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"PreviewWindowIsOpen"];
469         [fPreviewWindow setAcceptsMouseMovedEvents:YES];
470         scaleToScreen = NO;
471         [self pictureSliderChanged:nil];
472     }
473     
474 }
475
476 - (NSString*) pictureSizeInfoString
477 {
478     return [fInfoField stringValue];
479 }
480
481 - (IBAction)showPictureSettings:(id)sender
482 {
483     [fHBController showPicturePanel:self];
484 }
485
486 #pragma mark Hud Control Overlay
487 - (void) mouseMoved:(NSEvent *)theEvent
488 {
489     [super mouseMoved:theEvent];
490     NSPoint mouseLoc = [theEvent locationInWindow];
491     
492     /* Test for mouse location to show/hide hud controls */
493     if( isEncoding == NO ) 
494     {
495         /* Since we are not encoding, verify which control hud to show
496          * or hide based on aMovie ( aMovie indicates we need movie controls )
497          */
498         NSBox           * hudBoxToShow;
499         if ( aMovie == nil ) // No movie loaded up
500         {
501             hudBoxToShow = fPictureControlBox;
502         }
503         else // We have a movie
504         {
505             hudBoxToShow = fMoviePlaybackControlBox;
506         }
507         
508         if( NSPointInRect( mouseLoc, [fPictureControlBox frame] ) )
509         {
510             [[hudBoxToShow animator] setHidden: NO];
511             [self stopHudTimer];
512         }
513                 else if( NSPointInRect( mouseLoc, [fPictureViewArea frame] ) )
514         {
515             [[hudBoxToShow animator] setHidden: NO];
516             [self startHudTimer];
517         }
518         else
519         {
520             [[hudBoxToShow animator] setHidden: YES];
521         }
522         }
523 }
524
525 - (void) startHudTimer
526 {
527         if( fHudTimer ) {
528                 [fHudTimer invalidate];
529                 [fHudTimer release];
530         }
531     fHudTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(hudTimerFired:) userInfo:nil repeats:YES];
532     [fHudTimer retain];
533 }
534
535 - (void) stopHudTimer
536 {
537     if( fHudTimer )
538     {
539         [fHudTimer invalidate];
540         [fHudTimer release];
541         fHudTimer = nil;
542         hudTimerSeconds = 0;
543     }
544 }
545
546 - (void) hudTimerFired: (NSTimer*)theTimer
547 {
548     hudTimerSeconds++;
549     if( hudTimerSeconds >= 10 ) 
550     {
551         /* Regardless which control box is active, after the timer
552          * period we want either one to fade to hidden.
553          */
554         [[fPictureControlBox animator] setHidden: YES];
555         [[fMoviePlaybackControlBox animator] setHidden: YES];
556         [self stopHudTimer];
557     }
558 }
559
560
561
562 - (IBAction)toggleScaleToScreen:(id)sender
563 {
564     if (scaleToScreen == YES)
565     {
566         scaleToScreen = NO;
567         /* make sure we are set to a still preview */
568         [self pictureSliderChanged:nil];
569         [fScaleToScreenToggleButton setTitle:@"Scale To Screen"];
570     }
571     else
572     {
573         scaleToScreen = YES;
574         /* make sure we are set to a still preview */
575         [self pictureSliderChanged:nil];
576         [fScaleToScreenToggleButton setTitle:@"Actual Scale"];
577     }
578     
579 }
580
581
582
583 // Title-less windows normally don't receive key presses, override this
584 - (BOOL)canBecomeKeyWindow
585 {
586     return YES;
587 }
588
589 // Title-less windows normally can't become main which means that another
590 // non-fullscreen window will have the "active" titlebar in expose. Bad, fix it.
591 - (BOOL)canBecomeMainWindow
592 {
593     return YES;
594 }
595
596
597 - (IBAction)goWindowedScreen:(id)sender
598 {
599     
600     /* Get the screen info to release the display but don't actually do
601      * it until the windowed screen is setup.
602      */
603     scaleToScreen = NO;
604     [self pictureSliderChanged:nil];
605     [fScaleToScreenToggleButton setTitle:@"<->"];
606         
607     NSScreen* mainScreen = [NSScreen mainScreen]; 
608     NSDictionary* screenInfo = [mainScreen deviceDescription]; 
609     NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
610     CGDirectDisplayID displayID = (CGDirectDisplayID)[screenID longValue]; 
611     
612     [fFullScreenWindow dealloc];
613     [fFullScreenWindow release];
614     
615     
616     [fPreviewWindow setContentView:fPictureViewArea]; 
617     [fPictureViewArea setNeedsDisplay:YES];
618     [self setWindow:fPreviewWindow];
619     
620     // Show the window. 
621     [fPreviewWindow makeKeyAndOrderFront:self];
622     
623     /* Set the window back to regular level */
624     [fPreviewWindow setLevel:NSNormalWindowLevel];
625     
626     /* Set the isFullScreen flag back to NO */
627     //isFullScreen = NO;
628     scaleToScreen = NO;
629     /* make sure we are set to a still preview */
630     [self pictureSliderChanged:nil];
631     [self showPreviewWindow:nil];
632     
633     /* Change the name of fFullScreenToggleButton appropriately */
634     //[fFullScreenToggleButton setTitle: @"Full Screen"];
635     // [fScaleToScreenToggleButton setHidden:YES];
636     /* set the picture settings pallete back to normal level */
637     [fHBController picturePanelWindowed];
638     
639     /* Release the display now that the we are back in windowed mode */
640     CGDisplayRelease(displayID);
641     
642     [fPreviewWindow setAcceptsMouseMovedEvents:YES];
643     //[fFullScreenWindow setAcceptsMouseMovedEvents:NO];
644     
645     hudTimerSeconds = 0;
646     [self startHudTimer];
647     
648 }
649
650
651 #pragma mark Still Preview Image Processing
652
653
654 // This function converts an image created by libhb (specified via pictureIndex) into
655 // an NSImage suitable for the GUI code to use. If removeBorders is YES,
656 // makeImageForPicture crops the image generated by libhb stripping off the gray
657 // border around the content. This is the low-level method that generates the image.
658 // -imageForPicture calls this function whenever it can't find an image in its cache.
659 + (NSImage *) makeImageForPicture: (int)pictureIndex
660                 libhb:(hb_handle_t*)handle
661                 title:(hb_title_t*)title
662 {
663     static uint8_t * buffer;
664     static int bufferSize;
665
666     // Make sure we have a big enough buffer to receive the image from libhb. libhb
667     int dstWidth = title->job->width;
668     int dstHeight = title->job->height;
669         
670     int newSize;
671     newSize = dstWidth * dstHeight * 4;
672     if( bufferSize < newSize )
673     {
674         bufferSize = newSize;
675         buffer     = (uint8_t *) realloc( buffer, bufferSize );
676     }
677
678     hb_get_preview( handle, title, pictureIndex, buffer );
679
680     // Create an NSBitmapImageRep and copy the libhb image into it, converting it from
681     // libhb's format to one suitable for NSImage. Along the way, we'll strip off the
682     // border around libhb's image.
683         
684     // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
685     // Alpha is ignored.
686         
687     NSBitmapFormat bitmapFormat = (NSBitmapFormat)NSAlphaFirstBitmapFormat;
688     NSBitmapImageRep * imgrep = [[[NSBitmapImageRep alloc]
689             initWithBitmapDataPlanes:nil
690             pixelsWide:dstWidth
691             pixelsHigh:dstHeight
692             bitsPerSample:8
693             samplesPerPixel:3   // ignore alpha
694             hasAlpha:NO
695             isPlanar:NO
696             colorSpaceName:NSCalibratedRGBColorSpace
697             bitmapFormat:bitmapFormat
698             bytesPerRow:dstWidth * 4
699             bitsPerPixel:32] autorelease];
700
701     UInt32 * src = (UInt32 *)buffer;
702     UInt32 * dst = (UInt32 *)[imgrep bitmapData];
703     int r, c;
704     for (r = 0; r < dstHeight; r++)
705     {
706         for (c = 0; c < dstWidth; c++)
707 #if TARGET_RT_LITTLE_ENDIAN
708             *dst++ = Endian32_Swap(*src++);
709 #else
710             *dst++ = *src++;
711 #endif
712     }
713
714     NSImage * img = [[[NSImage alloc] initWithSize: NSMakeSize(dstWidth, dstHeight)] autorelease];
715     [img addRepresentation:imgrep];
716
717     return img;
718 }
719
720 // Returns the preview image for the specified index, retrieving it from its internal
721 // cache or by calling makeImageForPicture if it is not cached. Generally, you should
722 // use imageForPicture so that images are cached. Calling makeImageForPicture will
723 // always generate a new copy of the image.
724 - (NSImage *) imageForPicture: (int) pictureIndex
725 {
726     // The preview for the specified index may not currently exist, so this method
727     // generates it if necessary.
728     NSString * key = [NSString stringWithFormat:@"%d", pictureIndex];
729     NSImage * theImage = [fPicturePreviews objectForKey:key];
730     if (!theImage)
731     {
732         theImage = [PreviewController makeImageForPicture:pictureIndex libhb:fHandle title:fTitle];
733         [fPicturePreviews setObject:theImage forKey:key];
734     }
735     return theImage;
736 }
737
738 // Purges all images from the cache. The next call to imageForPicture will cause a new
739 // image to be generated.
740 - (void) purgeImageCache
741 {
742     [fPicturePreviews removeAllObjects];
743 }
744
745  
746
747 #pragma mark Movie Preview
748 - (IBAction) createMoviePreview: (id) sender
749 {
750     
751     
752     /* Lets make sure the still picture previews are showing in case
753      * there is currently a movie showing */
754     [self pictureSliderChanged:nil];
755     
756     /* Rip or Cancel ? */
757     hb_state_t s;
758     hb_get_state2( fPreviewLibhb, &s );
759     
760     if(sender == fCancelPreviewMovieButton && (s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED))
761         {
762         hb_stop( fPreviewLibhb );
763         [fPictureView setHidden:NO];
764         [fMovieView pause:nil];
765         [fMovieView setHidden:YES];
766                 [fMovieView setMovie:nil];
767         [fPictureSlider setHidden:NO];
768         isEncoding = NO;
769         
770         return;
771     }
772     
773     
774     /* we use controller.mm's prepareJobForPreview to go ahead and set all of our settings
775      * however, we want to use a temporary destination field of course
776      * so that we do not put our temp preview in the users chosen
777      * directory */
778     
779     hb_job_t * job = fTitle->job;
780     
781     /* We run our current setting through prepeareJob in Controller.mm
782      * just as if it were a regular encode */
783     
784     [fHBController prepareJobForPreview];
785     
786     /* Destination file. We set this to our preview directory
787      * changing the extension appropriately.*/
788     if (fTitle->job->mux == HB_MUX_MP4) // MP4 file
789     {
790         /* we use .m4v for our mp4 files so that ac3 and chapters in mp4 will play properly */
791         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.m4v";
792     }
793     else if (fTitle->job->mux == HB_MUX_MKV) // MKV file
794     {
795         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.mkv";
796     }
797     else if (fTitle->job->mux == HB_MUX_AVI) // AVI file
798     {
799         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.avi";
800     }
801     else if (fTitle->job->mux == HB_MUX_OGM) // OGM file
802     {
803         fPreviewMoviePath = @"~/Library/Application Support/HandBrake/Previews/preview_temp.ogm";
804     }
805     
806     fPreviewMoviePath = [[fPreviewMoviePath stringByExpandingTildeInPath]retain];
807     
808     /* See if there is an existing preview file, if so, delete it */
809     if( ![[NSFileManager defaultManager] fileExistsAtPath:fPreviewMoviePath] )
810     {
811         [[NSFileManager defaultManager] removeFileAtPath:fPreviewMoviePath
812                                                  handler:nil];
813     }
814     
815     /* We now direct our preview encode to fPreviewMoviePath */
816     fTitle->job->file = [fPreviewMoviePath UTF8String];
817     
818     /* We use our advance pref to determine how many previews to scan */
819     int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
820     job->start_at_preview = fPicture + 1;
821     job->seek_points = hb_num_previews;
822     
823     /* we use the preview duration popup to get the specified
824      * number of seconds for the preview encode.
825      */
826     
827     job->pts_to_stop = [[fPreviewMovieLengthPopUp titleOfSelectedItem] intValue] * 90000LL;
828     
829     /* lets go ahead and send it off to libhb
830      * Note: unlike a full encode, we only send 1 pass regardless if the final encode calls for 2 passes.
831      * this should suffice for a fairly accurate short preview and cuts our preview generation time in half.
832      * However we also need to take into account the indepth scan for subtitles.
833      */
834     /*
835      * If scanning we need to do some extra setup of the job.
836      */
837     if( job->indepth_scan == 1 )
838     {
839         char *x264opts_tmp;
840         
841         /*
842          * When subtitle scan is enabled do a fast pre-scan job
843          * which will determine which subtitles to enable, if any.
844          */
845         job->pass = -1;
846         x264opts_tmp = job->x264opts;
847         
848         job->x264opts = NULL;
849         job->indepth_scan = 1;  
850         /*
851          * Add the pre-scan job
852          */
853         hb_add( fPreviewLibhb, job );
854         job->x264opts = x264opts_tmp;
855     }                  
856     /* Go ahead and perform the actual encoding preview scan */
857     job->indepth_scan = 0;
858     job->pass = 0;
859     hb_add( fPreviewLibhb, job );
860     
861     [fEncodingControlBox setHidden: NO];
862     [fPictureControlBox setHidden: YES];
863     
864     [fMovieCreationProgressIndicator setHidden: NO];
865     [fPreviewMovieStatusField setHidden: NO];
866     
867     isEncoding = YES;
868
869     /* Let fPreviewLibhb do the job */
870     hb_start( fPreviewLibhb );
871         
872 }
873
874 - (void) startReceivingLibhbNotifications
875 {
876     if (!fLibhbTimer)
877     {
878         fLibhbTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(libhbTimerFired:) userInfo:nil repeats:YES];
879         [fLibhbTimer retain];
880     }
881 }
882
883 - (void) stopReceivingLibhbNotifications
884 {
885     if (fLibhbTimer)
886     {
887         [fLibhbTimer invalidate];
888         [fLibhbTimer release];
889         fLibhbTimer = nil;
890     }
891 }
892 - (void) libhbTimerFired: (NSTimer*)theTimer
893 {
894     hb_state_t s;
895     hb_get_state( fPreviewLibhb, &s );
896     [self libhbStateChanged: s];
897     
898 }
899
900 - (void) libhbStateChanged: (hb_state_t)state
901 {
902     switch( state.state )
903     {
904         case HB_STATE_IDLE:
905         case HB_STATE_SCANNING:
906         case HB_STATE_SCANDONE:
907             break;
908             
909         case HB_STATE_WORKING:
910         {
911 #define p state.param.working
912             
913             NSMutableString * string;
914                         /* Update text field */
915                         string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding preview:  %.2f %%", @"" ), 100.0 * p.progress];
916             
917                         if( p.seconds > -1 )
918             {
919                 [string appendFormat:
920                  NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
921                  p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
922             }
923             [fPreviewMovieStatusField setStringValue: string];
924             
925             [fMovieCreationProgressIndicator setIndeterminate: NO];
926             /* Update slider */
927                         [fMovieCreationProgressIndicator setDoubleValue: 100.0 * p.progress];
928             
929             [fCreatePreviewMovieButton setTitle: @"Cancel Preview"];
930             
931             break;
932             
933         }
934 #undef p
935             
936 #define p state.param.muxing            
937         case HB_STATE_MUXING:
938         {
939             // Update fMovieCreationProgressIndicator
940             [fMovieCreationProgressIndicator setIndeterminate: YES];
941             [fMovieCreationProgressIndicator startAnimation: nil];
942             [fPreviewMovieStatusField setStringValue: [NSString stringWithFormat:
943                                          NSLocalizedString( @"Muxing Preview ...", @"" )]];
944             break;
945         }
946 #undef p                        
947         case HB_STATE_PAUSED:
948             [fMovieCreationProgressIndicator stopAnimation: nil];
949             break;
950                         
951         case HB_STATE_WORKDONE:
952         {
953             // Delete all remaining jobs since libhb doesn't do this on its own.
954             hb_job_t * job;
955             while( ( job = hb_job(fPreviewLibhb, 0) ) )
956                 hb_rem( fHandle, job );
957             
958             [fPreviewMovieStatusField setStringValue: @""];
959             [fPreviewMovieStatusField setHidden: YES];
960             
961             [fMovieCreationProgressIndicator stopAnimation: nil];
962             [fMovieCreationProgressIndicator setHidden: YES];
963             [fEncodingControlBox setHidden: YES];
964             [fPictureControlBox setHidden: YES];
965             isEncoding = NO;
966
967             // Show the movie view
968             [self showMoviePreview:fPreviewMoviePath];
969             [fCreatePreviewMovieButton setTitle: @"Live Preview"];
970
971             break;
972         }
973     }
974 }
975
976 - (IBAction) toggleMoviePreviewPlayPause: (id) sender
977 {
978     /* make sure a movie is even loaded up */
979     if (aMovie != nil)
980     {
981         /* For some stupid reason there is no "isPlaying" method for a QTMovie
982          * object, given that, we detect the rate to determine whether the movie
983          * is playing or not.
984          */
985         if ([aMovie rate] != 0) // we are playing 
986         {
987             [fMovieView pause:aMovie];
988             [fPlayPauseButton setTitle: @">"];
989         }
990         else // we are paused or stopped
991         {
992             [fMovieView play:aMovie];
993             [fPlayPauseButton setTitle: @"||"];   
994         }
995     }
996     
997 }
998
999 - (IBAction) moviePlaybackGoToBeginning: (id) sender
1000 {
1001     /* make sure a movie is even loaded up */
1002     if (aMovie != nil)
1003     {
1004         [fMovieView gotoBeginning:aMovie];
1005      }
1006     
1007 }
1008
1009 - (IBAction) moviePlaybackGoToEnd: (id) sender
1010 {
1011     /* make sure a movie is even loaded up */
1012     if (aMovie != nil)
1013     {
1014         [fMovieView gotoEnd:aMovie];
1015      }
1016     
1017 }
1018
1019 - (IBAction) moviePlaybackGoBackwardOneFrame: (id) sender
1020 {
1021     /* make sure a movie is even loaded up */
1022     if (aMovie != nil)
1023     {
1024         [fMovieView pause:aMovie]; // Pause the movie
1025         [fMovieView stepBackward:aMovie];
1026      }
1027     
1028 }
1029
1030 - (IBAction) moviePlaybackGoForwardOneFrame: (id) sender
1031 {
1032     /* make sure a movie is even loaded up */
1033     if (aMovie != nil)
1034     {
1035         [fMovieView pause:aMovie]; // Pause the movie
1036         [fMovieView stepForward:aMovie];
1037      }
1038     
1039 }
1040
1041
1042 - (void) startMovieTimer
1043 {
1044         if( fMovieTimer ) {
1045                 [fMovieTimer invalidate];
1046                 [fMovieTimer release];
1047         }
1048     fMovieTimer = [NSTimer scheduledTimerWithTimeInterval:0.10 target:self selector:@selector(movieTimerFired:) userInfo:nil repeats:YES];
1049     [fMovieTimer retain];
1050 }
1051
1052 - (void) stopMovieTimer
1053 {
1054     if( fMovieTimer )
1055     {
1056         [fMovieTimer invalidate];
1057         [fMovieTimer release];
1058         fMovieTimer = nil;
1059     }
1060 }
1061
1062 - (void) movieTimerFired: (NSTimer*)theTimer
1063 {
1064      if (aMovie != nil)
1065     {
1066         [self adjustPreviewScrubberForCurrentMovieTime];
1067         [fMovieInfoField setStringValue: [NSString stringWithFormat:NSLocalizedString( @"%@", @"" ),[self calculatePlaybackSMTPETimecodeForDisplay]]];    
1068     }
1069 }
1070
1071
1072
1073 - (IBAction) showMoviePreview: (NSString *) path
1074 {
1075     /* Since the gray background for the still images is part of
1076      * fPictureView, lets leave the picture view visible and postion
1077      * the fMovieView over the image portion of fPictureView so
1078      * we retain the gray cropping border  we have already established
1079      * with the still previews
1080      */
1081     
1082     /* Load the new movie into fMovieView */
1083     if (path) 
1084     {
1085                 //QTMovie * aMovie;
1086                 NSError  *outError;
1087                 NSURL *movieUrl = [NSURL fileURLWithPath:path];
1088                 NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
1089                                                                                  movieUrl, QTMovieURLAttribute,
1090                                                                                  [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute,
1091                                                                                  [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute",
1092                                                                                  [NSNumber numberWithBool:NO], @"QTMovieOpenAsyncRequiredAttribute",                                                            
1093                                                                                  [NSNumber numberWithBool:NO], @"QTMovieOpenAsyncOKAttribute",
1094                                          [NSNumber numberWithBool:YES], @"QTMovieIsSteppableAttribute",
1095                                                                                  QTMovieApertureModeClean, QTMovieApertureModeAttribute,
1096                                                                                  nil];
1097         
1098         aMovie = [[[QTMovie alloc] initWithAttributes:movieAttributes error:&outError] autorelease];
1099         
1100         
1101                 if (!aMovie) 
1102         {
1103                         NSLog(@"Unable to open movie");
1104                 }
1105         else 
1106         {
1107             NSRect movieBounds;
1108             /* we get some size information from the preview movie */
1109             NSSize movieSize= [[aMovie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
1110             movieBounds = [fMovieView movieBounds];
1111             movieBounds.size.height = movieSize.height;
1112             /* We also get our view size to use for scaling fMovieView's size */
1113             NSSize scaledMovieViewSize = [fPictureView frame].size;
1114             [fMovieView setControllerVisible:FALSE];
1115             if ([fMovieView isControllerVisible]) 
1116             {
1117                 CGFloat controllerBarHeight = [fMovieView controllerBarHeight];
1118                 if ( controllerBarHeight != 0 ) //Check if QTKit return a real value or not.
1119                 {
1120                     movieBounds.size.height += controllerBarHeight;
1121                     scaledMovieViewSize.height += controllerBarHeight;
1122                 }
1123                 else
1124                 {
1125                     movieBounds.size.height += 15;
1126                     scaledMovieViewSize.height += 15;
1127                 }
1128             }
1129             
1130             movieBounds.size.width = movieSize.width;
1131             
1132             /* we need to account for an issue where the scaledMovieViewSize > the window size */
1133             if (scaledMovieViewSize.height > [[self window] frame].size.height)
1134             {
1135                 [fHBController writeToActivityLog: "showMoviePreview: Our window is not tall enough to show the controller bar ..."];
1136             }
1137             
1138             
1139             
1140             /* Scale the fMovieView to scaledMovieViewSize */
1141             [fMovieView setFrameSize:scaledMovieViewSize];
1142             
1143             /*set our origin try using fPictureViewArea or fPictureView */
1144             NSPoint origin = [fPictureView frame].origin;
1145             origin.x += trunc( ( [fPictureView frame].size.width -
1146                                 [fMovieView frame].size.width ) / 2.0 );
1147             origin.y += trunc( ( ( [fPictureView frame].size.height -
1148                                       [fMovieView frame].size.height ) / 2.0 ) - 7.5 );
1149
1150             [fMovieView setFrameOrigin:origin];
1151             [fMovieView setMovie:aMovie];
1152             [fMovieView setHidden:NO];
1153             [fMoviePlaybackControlBox setHidden: NO];
1154             [fPictureControlBox setHidden: YES];
1155             
1156             // to actually play the movie
1157             
1158             [self initPreviewScrubberForMovie];
1159             [self startMovieTimer];
1160             /* Install amovie notifications */
1161             [aMovie setDelegate:self];
1162             [self installMovieCallbacks];
1163             [fMovieView play:aMovie];
1164
1165         }
1166     }
1167     isEncoding = NO;
1168 }
1169 #pragma mark *** Movie Playback Scrubber and time code methods ***
1170
1171 /* Since MacOSX Leopard QTKit has taken over some responsibility for assessing movie playback
1172  * information from the old QuickTime carbon api ( time code information as well as fps, etc.).
1173  * However, the QTKit devs at apple were not really big on documentation and further ...
1174  * QuickTimes ability to playback HB's largely variable framerate output makes perfectly frame
1175  * accurate information at best convoluted. Still, for the purpose of a custom hud based custom
1176  * playback scrubber slider this has so far proven to be as accurate as I have found. To say it
1177  * could use some better accuracy is not understating it enough probably.
1178  * Most of this was gleaned from this obscure Apple Mail list thread:
1179  * http://www.mailinglistarchive.com/quicktime-api@lists.apple.com/msg05642.html
1180  * Now as we currently do not show a QTKit control bar with scrubber for display sizes > container
1181  * size, this seems to facilitate playback control from the HB custom HUD controller fairly close
1182  * to the built in controller bar.
1183  * Further work needs to be done to try to get accurate frame by frame playback display if we want it.
1184  * Note that the keyboard commands for frame by frame step through etc. work as always.
1185  */ 
1186
1187 // Returns a human readable string from the currentTime of movie playback
1188 - (NSString*) calculatePlaybackSMTPETimecodeForDisplay
1189 {
1190     QTTime time = [aMovie currentTime];
1191     
1192     NSString *smtpeTimeCodeString;
1193     int days, hour, minute, second, frame;
1194     long long result;
1195     
1196     result = time.timeValue / time.timeScale; // second
1197     frame = (time.timeValue % time.timeScale) / 100;
1198     
1199     second = result % 60;
1200     
1201     result = result / 60; // minute
1202     minute = result % 60;
1203     
1204     result = result / 60; // hour
1205     hour = result % 24;  
1206     days = result;
1207     
1208     smtpeTimeCodeString = [NSString stringWithFormat:@"Time: %02d:%02d:%02d", hour, minute, second]; // hh:mm:ss
1209     return smtpeTimeCodeString;
1210     
1211 }
1212
1213
1214 // Initialize the preview scrubber min/max to appropriate values for the current movie
1215 -(void) initPreviewScrubberForMovie
1216 {
1217     if (aMovie)
1218     {
1219         
1220         QTTime duration = [aMovie duration];
1221         float result = duration.timeValue / duration.timeScale;
1222         
1223         [fMovieScrubberSlider setMinValue:0.0];
1224         [fMovieScrubberSlider setMaxValue: (float)result];
1225         [fMovieScrubberSlider setFloatValue: 0.0];
1226     }
1227 }
1228
1229
1230 -(void) adjustPreviewScrubberForCurrentMovieTime
1231 {
1232     if (aMovie)
1233     {
1234         QTTime time = [aMovie currentTime];
1235         
1236         float result = (float)time.timeValue / (float)time.timeScale;;
1237         [fMovieScrubberSlider setFloatValue:result];
1238     }
1239 }
1240
1241 - (IBAction) previewScrubberChanged: (id) sender
1242 {
1243     if (aMovie)
1244     {
1245         [fMovieView pause:aMovie]; // Pause the movie
1246         QTTime time = [aMovie currentTime];
1247         [self setTime: time.timeScale * [fMovieScrubberSlider floatValue]];
1248         [self calculatePlaybackSMTPETimecodeForDisplay];
1249     }
1250 }
1251 #pragma mark *** Movie Notifications ***
1252
1253 - (void) installMovieCallbacks
1254 {
1255
1256
1257 /*Notification for any time the movie rate changes */
1258         [[NSNotificationCenter defaultCenter] addObserver:self
1259                                                  selector:@selector(movieRateDidChange:)
1260                                                      name:@"QTMovieRateDidChangeNotification"
1261                                                    object:aMovie];
1262         /*Notification for when the movie ends */
1263         [[NSNotificationCenter defaultCenter] addObserver:self
1264                                                  selector:@selector(movieDidEnd:)
1265                                                      name:@"QTMovieDidEndNotification"
1266                                                    object:aMovie];
1267 }
1268
1269 - (void)removeMovieCallbacks
1270 {
1271     if (aMovie)
1272     {
1273         /*Notification for any time the movie rate changes */
1274         [[NSNotificationCenter defaultCenter] removeObserver:self
1275                                                         name:@"QTMovieRateDidChangeNotification"
1276                                                       object:aMovie];
1277         /*Notification for when the movie ends */
1278         [[NSNotificationCenter defaultCenter] removeObserver:self
1279                                                         name:@"QTMovieDidEndNotification"
1280                                                       object:aMovie];
1281     }
1282 }
1283
1284 - (void)movieRateDidChange:(NSNotification *)notification
1285 {
1286     if (aMovie != nil)
1287     {
1288         /* For some stupid reason there is no "isPlaying" method for a QTMovie
1289          * object, given that, we detect the rate to determine whether the movie
1290          * is playing or not.
1291          */
1292         //[self adjustPreviewScrubberForCurrentMovieTime];
1293         if ([aMovie rate] != 0) // we are playing 
1294         {
1295             [fPlayPauseButton setTitle: @"||"];
1296         }
1297         else // we are paused or stopped
1298         {
1299             [fPlayPauseButton setTitle: @">"];
1300         }
1301     }
1302 }
1303 /* This notification is not currently used. However we should keep it "just in case" as
1304  * live preview playback is enhanced.
1305  */
1306 - (void)movieDidEnd:(NSNotification *)notification
1307 {
1308
1309     //[fHBController writeToActivityLog: "Movie DidEnd Notification Received"];
1310 }
1311
1312
1313 #pragma mark *** QTTime Utilities ***
1314
1315         // convert a time value (long) to a QTTime structure
1316 -(void)timeToQTTime:(long)timeValue resultTime:(QTTime *)aQTTime
1317 {
1318         NSNumber *timeScaleObj;
1319         long timeScaleValue;
1320
1321         timeScaleObj = [aMovie attributeForKey:QTMovieTimeScaleAttribute];
1322         timeScaleValue = [timeScaleObj longValue];
1323
1324         *aQTTime = QTMakeTime(timeValue, timeScaleValue);
1325 }
1326
1327         // set the movie's current time
1328 -(void)setTime:(int)timeValue
1329 {
1330         QTTime movieQTTime;
1331         NSValue *valueForQTTime;
1332         
1333         [self timeToQTTime:timeValue resultTime:&movieQTTime];
1334
1335         valueForQTTime = [NSValue valueWithQTTime:movieQTTime];
1336
1337         [aMovie setAttribute:valueForQTTime forKey:QTMovieCurrentTimeAttribute];
1338 }
1339
1340
1341 @end
1342
1343 @implementation PreviewController (Private)
1344
1345 //
1346 // -[PictureController(Private) optimalViewSizeForImageSize:]
1347 //
1348 // Given the size of the preview image to be shown, returns the best possible
1349 // size for the view.
1350 //
1351 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize
1352 {
1353     // The min size is 480x360
1354     CGFloat minWidth = 480.0;
1355     CGFloat minHeight = 360.0;
1356
1357     NSSize screenSize = [[[self window] screen] visibleFrame].size;
1358     NSSize sheetSize = [[self window] frame].size;
1359     NSSize viewAreaSize = [fPictureViewArea frame].size;
1360     CGFloat paddingX = 0.00;
1361     CGFloat paddingY = 0.00;
1362     
1363     if (fTitle->width > screenSize.width || fTitle->height > screenSize.height)
1364     {
1365         if (scaleToScreen == YES)
1366         {
1367             paddingX = screenSize.width - imageSize.width;
1368             paddingY = screenSize.height - imageSize.height;
1369         }
1370         
1371         else
1372         {
1373             paddingX = sheetSize.width - viewAreaSize.width;
1374             paddingY = sheetSize.height - viewAreaSize.height;  
1375         }
1376
1377     }
1378     
1379     CGFloat maxWidth;
1380     CGFloat maxHeight;
1381     maxWidth =  screenSize.width - paddingX;
1382     maxHeight = screenSize.height - paddingY;
1383     
1384     NSSize resultSize = imageSize;
1385     CGFloat resultPar = resultSize.width / resultSize.height;
1386
1387     //note, a mbp 15" at 1440 x 900 is a 1.6 ar
1388     CGFloat screenAspect = screenSize.width / screenSize.height;
1389     // Note, a standard dvd will use 720 x 480 which is a 1.5
1390     CGFloat viewAreaAspect = viewAreaSize.width / viewAreaSize.height;
1391     
1392     if (scaleToScreen == YES)
1393     {
1394         
1395         if (screenAspect < viewAreaAspect)
1396         {
1397             resultSize.width = screenSize.width;
1398             resultSize.height = (screenSize.width / viewAreaAspect);
1399         }
1400         else
1401         {
1402             resultSize.height = screenSize.height;
1403             resultSize.width = resultSize.height * viewAreaAspect;
1404         }
1405         
1406     }
1407     else if ( resultSize.width > maxWidth || resultSize.height > maxHeight )
1408     {
1409         // Source is larger than screen in one or more dimensions
1410         if ( resultPar > screenAspect )
1411         {
1412             // Source aspect wider than screen aspect, snap to max width and vary height
1413             resultSize.width = maxWidth;
1414             resultSize.height = (maxWidth / resultPar);
1415         }
1416         else
1417         {
1418             // Source aspect narrower than screen aspect, snap to max height vary width
1419             resultSize.height = maxHeight;
1420             resultSize.width = (maxHeight * resultPar);
1421         }
1422     }
1423
1424     // If necessary, grow to minimum dimensions to ensure controls overlay is not obstructed
1425     if ( resultSize.width < minWidth )
1426     {
1427         resultSize.width = minWidth;
1428     }
1429     if ( resultSize.height < minHeight )
1430     {
1431         resultSize.height = minHeight;
1432     }
1433     
1434     return resultSize;
1435
1436     
1437 }
1438
1439 //
1440 // -[PictureController(Private) resizePanelForViewSize:animate:]
1441 //
1442 // Resizes the entire window to accomodate a view of a particular size.
1443 //
1444 - (void)resizeSheetForViewSize: (NSSize)viewSize
1445 {
1446     // Figure out the deltas for the new frame area
1447     NSSize currentSize = [fPictureViewArea frame].size;
1448     CGFloat deltaX = viewSize.width - currentSize.width;
1449     CGFloat deltaY = viewSize.height - currentSize.height;
1450     
1451     // Now resize the whole panel by those same deltas, but don't exceed the min
1452     NSRect frame = [[self window] frame];
1453     NSSize maxSize = [[[self window] screen] visibleFrame].size;
1454     /* if we are not Scale To Screen, put an 85% of visible screen on the window */
1455     if (scaleToScreen == NO )
1456     {
1457         maxSize.width = maxSize.width * 0.85;
1458         maxSize.height = maxSize.height * 0.85;
1459     }
1460     
1461     /* Set our min size to the storage size */
1462     NSSize minSize;
1463     minSize.width = fTitle->width;
1464     minSize.height = fTitle->height;
1465     
1466     frame.size.width += deltaX;
1467     frame.size.height += deltaY;
1468     if( frame.size.width < minSize.width )
1469     {
1470         frame.size.width = minSize.width;
1471     }
1472     
1473     if( frame.size.height < minSize.height )
1474     {
1475         frame.size.height = minSize.height;
1476     }
1477     /* compare frame to max size of screen */
1478     
1479     if( frame.size.width > maxSize.width )
1480     {
1481         frame.size.width = maxSize.width;
1482     }
1483     
1484     if( frame.size.height > maxSize.height )
1485     {
1486         frame.size.height = maxSize.height;
1487     }
1488     
1489     
1490     
1491
1492     
1493     // But now the sheet is off-center, so also shift the origin to center it and
1494     // keep the top aligned.
1495     if( frame.size.width != [[self window] frame].size.width )
1496         frame.origin.x -= (deltaX / 2.0);
1497     
1498         
1499         /* Since upon launch we can open up the preview window if it was open
1500          * the last time we quit (and at the size it was) we want to make
1501          * sure that upon resize we do not have the window off the screen
1502          * So check the origin against the screen origin and adjust if
1503          * necessary.
1504          */
1505         NSSize screenSize = [[[self window] screen] visibleFrame].size;
1506         NSPoint screenOrigin = [[[self window] screen] frame].origin;
1507         if (screenSize.height < frame.size.height)
1508         {
1509             frame.size.height = screenSize.height;
1510         }
1511         if (screenSize.width < frame.size.width)
1512         {
1513             frame.size.width = screenSize.width;
1514         }
1515         
1516         
1517         /* our origin is off the screen to the left*/
1518         if (frame.origin.x < screenOrigin.x)
1519         {
1520             /* so shift our origin to the right */
1521             frame.origin.x = screenOrigin.x;
1522         }
1523         else if ((frame.origin.x + frame.size.width) > (screenOrigin.x + screenSize.width))
1524         {
1525             /* the right side of the preview is off the screen, so shift to the left */
1526             frame.origin.x = (screenOrigin.x + screenSize.width) - frame.size.width;
1527         }
1528         
1529         [[self window] setFrame:frame display:YES animate:YES];
1530     
1531     
1532 }
1533
1534 //
1535 // -[PictureController(Private) setViewSize:]
1536 //
1537 // Changes the view's size and centers it vertically inside of its area.
1538 // Assumes resizeSheetForViewSize: has already been called.
1539 //
1540 - (void)setViewSize: (NSSize)viewSize
1541 {   
1542     
1543     /* special case for scaleToScreen */
1544     NSSize screenSize = [[[self window] screen] visibleFrame].size;
1545     NSSize areaSize = [fPictureViewArea frame].size;
1546     NSSize pictureSize = [fPictureView frame].size;
1547     CGFloat viewSizeAspect = viewSize.width / viewSize.height;
1548     
1549     if (viewSize.width > areaSize.width || viewSize.height > areaSize.height)
1550     {
1551         
1552         if (viewSizeAspect > 1.0) // we are wider than taller, so expand the width to fill the area and scale the height
1553         {
1554             viewSize.width = areaSize.width;
1555             viewSize.height = viewSize.width / viewSizeAspect;
1556         }
1557         else
1558         {
1559             viewSize.height = areaSize.height;
1560             viewSize.width = viewSize.height * viewSizeAspect;
1561         }
1562         
1563     }
1564     
1565     [fPictureView setFrameSize:viewSize];
1566     NSSize newAreaSize = [fPictureViewArea frame].size;
1567     
1568     
1569     // center it vertically and horizontally
1570     NSPoint origin = [fPictureViewArea frame].origin;
1571     origin.y += ([fPictureViewArea frame].size.height -
1572                  [fPictureView frame].size.height) / 2.0;
1573     
1574     origin.x += ([fPictureViewArea frame].size.width -
1575                  [fPictureView frame].size.width) / 2.0; 
1576
1577     origin.x = floor( origin.x );
1578     origin.y = floor( origin.y );
1579     
1580     [fPictureView setFrameOrigin:origin];
1581     
1582 }
1583
1584
1585 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize
1586 {
1587     NSSize viewSize = [fPictureViewArea frame].size;
1588     return (newSize.width != viewSize.width || newSize.height != viewSize.height);
1589 }
1590
1591 @end