OSDN Git Service

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